mirror of
https://codeberg.org/timelimit/timelimit-android.git
synced 2025-10-04 10:19:18 +02:00
Rebuild user selection screen
This commit is contained in:
parent
35ccf48507
commit
ae02d2fdff
20 changed files with 557 additions and 868 deletions
|
@ -285,7 +285,6 @@ class MainActivity : AppCompatActivity(), ActivityViewModelHolder, U2fManager.De
|
||||||
content = { paddingValues ->
|
content = { paddingValues ->
|
||||||
ScreenMultiplexer(
|
ScreenMultiplexer(
|
||||||
screen = screen,
|
screen = screen,
|
||||||
executeCommand = ::execute,
|
|
||||||
fragmentManager = supportFragmentManager,
|
fragmentManager = supportFragmentManager,
|
||||||
fragmentIds = mainModel.fragmentIds,
|
fragmentIds = mainModel.fragmentIds,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
|
@ -19,14 +19,13 @@ import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.fragment.app.FragmentManager
|
import androidx.fragment.app.FragmentManager
|
||||||
import io.timelimit.android.ui.diagnose.deviceowner.DeviceOwnerScreen
|
import io.timelimit.android.ui.diagnose.deviceowner.DeviceOwnerScreen
|
||||||
|
import io.timelimit.android.ui.manage.device.manage.user.ManageDeviceUserScreen
|
||||||
import io.timelimit.android.ui.model.Screen
|
import io.timelimit.android.ui.model.Screen
|
||||||
import io.timelimit.android.ui.model.UpdateStateCommand
|
|
||||||
import io.timelimit.android.ui.overview.overview.OverviewScreen
|
import io.timelimit.android.ui.overview.overview.OverviewScreen
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ScreenMultiplexer(
|
fun ScreenMultiplexer(
|
||||||
screen: Screen?,
|
screen: Screen?,
|
||||||
executeCommand: (UpdateStateCommand) -> Unit,
|
|
||||||
fragmentManager: FragmentManager,
|
fragmentManager: FragmentManager,
|
||||||
fragmentIds: MutableSet<Int>,
|
fragmentIds: MutableSet<Int>,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
|
@ -35,6 +34,7 @@ fun ScreenMultiplexer(
|
||||||
null -> {/* nothing to do */ }
|
null -> {/* nothing to do */ }
|
||||||
is Screen.FragmentScreen -> FragmentScreen(screen, fragmentManager, fragmentIds, modifier = modifier)
|
is Screen.FragmentScreen -> FragmentScreen(screen, fragmentManager, fragmentIds, modifier = modifier)
|
||||||
is Screen.OverviewScreen -> OverviewScreen(screen.content, modifier = modifier)
|
is Screen.OverviewScreen -> OverviewScreen(screen.content, modifier = modifier)
|
||||||
|
is Screen.ManageDeviceUserScreen -> ManageDeviceUserScreen(screen.items, screen.actions, screen.overlay, modifier)
|
||||||
is Screen.DeviceOwnerScreen -> DeviceOwnerScreen(screen.content, modifier = modifier)
|
is Screen.DeviceOwnerScreen -> DeviceOwnerScreen(screen.content, modifier = modifier)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,117 +0,0 @@
|
||||||
/*
|
|
||||||
* TimeLimit Copyright <C> 2019 - 2020 Jonas Lochmann
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation version 3 of the License.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package io.timelimit.android.ui.manage.device.manage.defaultuser
|
|
||||||
|
|
||||||
import androidx.fragment.app.FragmentManager
|
|
||||||
import androidx.lifecycle.LifecycleOwner
|
|
||||||
import androidx.lifecycle.LiveData
|
|
||||||
import androidx.lifecycle.Observer
|
|
||||||
import io.timelimit.android.R
|
|
||||||
import io.timelimit.android.coroutines.runAsync
|
|
||||||
import io.timelimit.android.data.model.Device
|
|
||||||
import io.timelimit.android.data.model.User
|
|
||||||
import io.timelimit.android.databinding.ManageDeviceDefaultUserBinding
|
|
||||||
import io.timelimit.android.livedata.map
|
|
||||||
import io.timelimit.android.livedata.switchMap
|
|
||||||
import io.timelimit.android.sync.actions.SignOutAtDeviceAction
|
|
||||||
import io.timelimit.android.sync.actions.apply.ApplyActionUtil
|
|
||||||
import io.timelimit.android.ui.help.HelpDialogFragment
|
|
||||||
import io.timelimit.android.ui.main.ActivityViewModel
|
|
||||||
import io.timelimit.android.ui.payment.RequiresPurchaseDialogFragment
|
|
||||||
import io.timelimit.android.util.TimeTextUtil
|
|
||||||
|
|
||||||
object ManageDeviceDefaultUser {
|
|
||||||
fun bind(
|
|
||||||
view: ManageDeviceDefaultUserBinding,
|
|
||||||
users: LiveData<List<User>>,
|
|
||||||
lifecycleOwner: LifecycleOwner,
|
|
||||||
device: LiveData<Device?>,
|
|
||||||
isThisDevice: LiveData<Boolean>,
|
|
||||||
auth: ActivityViewModel,
|
|
||||||
fragmentManager: FragmentManager
|
|
||||||
) {
|
|
||||||
val context = view.root.context
|
|
||||||
|
|
||||||
view.titleView.setOnClickListener {
|
|
||||||
HelpDialogFragment.newInstance(
|
|
||||||
title = R.string.manage_device_default_user_title,
|
|
||||||
text = R.string.manage_device_default_user_info
|
|
||||||
).show(fragmentManager)
|
|
||||||
}
|
|
||||||
|
|
||||||
device.switchMap { deviceEntry ->
|
|
||||||
users.map { users ->
|
|
||||||
deviceEntry to users.find { it.id == deviceEntry?.defaultUser }
|
|
||||||
}
|
|
||||||
}.observe(lifecycleOwner, Observer { (deviceEntry, defaultUser) ->
|
|
||||||
view.hasDefaultUser = defaultUser != null
|
|
||||||
view.isAlreadyUsingDefaultUser = defaultUser != null && deviceEntry?.currentUserId == defaultUser.id
|
|
||||||
view.defaultUserTitle = defaultUser?.name
|
|
||||||
})
|
|
||||||
|
|
||||||
isThisDevice.observe(lifecycleOwner, Observer {
|
|
||||||
view.isCurrentDevice = it
|
|
||||||
})
|
|
||||||
|
|
||||||
device.observe(lifecycleOwner, Observer { deviceEntry ->
|
|
||||||
view.setDefaultUserButton.setOnClickListener {
|
|
||||||
if (deviceEntry != null && auth.requestAuthenticationOrReturnTrue()) {
|
|
||||||
SetDeviceDefaultUserDialogFragment.newInstance(
|
|
||||||
deviceId = deviceEntry.id
|
|
||||||
).show(fragmentManager)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
view.configureAutoLogoutButton.setOnClickListener {
|
|
||||||
if (deviceEntry != null && auth.requestAuthenticationOrReturnTrue()) {
|
|
||||||
SetDeviceDefaultUserTimeoutDialogFragment
|
|
||||||
.newInstance(deviceId = deviceEntry.id)
|
|
||||||
.show(fragmentManager)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val defaultUserTimeout = deviceEntry?.defaultUserTimeout ?: 0
|
|
||||||
|
|
||||||
view.isAutomaticallySwitchingToDefaultUserEnabled = defaultUserTimeout != 0
|
|
||||||
view.defaultUserSwitchText = if (defaultUserTimeout == 0)
|
|
||||||
context.getString(R.string.manage_device_default_user_timeout_off)
|
|
||||||
else
|
|
||||||
context.getString(
|
|
||||||
R.string.manage_device_default_user_timeout_on,
|
|
||||||
if (defaultUserTimeout < 1000 * 60)
|
|
||||||
TimeTextUtil.seconds(defaultUserTimeout / 1000, context)
|
|
||||||
else
|
|
||||||
TimeTextUtil.time(defaultUserTimeout, context)
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
auth.logic.fullVersion.shouldProvideFullVersionFunctions.observe(lifecycleOwner, Observer { fullVersion ->
|
|
||||||
view.switchToDefaultUserButton.setOnClickListener {
|
|
||||||
if (fullVersion) {
|
|
||||||
runAsync {
|
|
||||||
ApplyActionUtil.applyAppLogicAction(
|
|
||||||
action = SignOutAtDeviceAction,
|
|
||||||
appLogic = auth.logic,
|
|
||||||
ignoreIfDeviceIsNotConfigured = true
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
RequiresPurchaseDialogFragment().show(fragmentManager)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,131 +0,0 @@
|
||||||
/*
|
|
||||||
* TimeLimit Copyright <C> 2019 Jonas Lochmann
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation version 3 of the License.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package io.timelimit.android.ui.manage.device.manage.defaultuser
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import android.widget.CheckedTextView
|
|
||||||
import androidx.fragment.app.FragmentManager
|
|
||||||
import androidx.lifecycle.Observer
|
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
|
||||||
import io.timelimit.android.R
|
|
||||||
import io.timelimit.android.data.Database
|
|
||||||
import io.timelimit.android.data.model.UserType
|
|
||||||
import io.timelimit.android.databinding.BottomSheetSelectionListBinding
|
|
||||||
import io.timelimit.android.extensions.showSafe
|
|
||||||
import io.timelimit.android.livedata.ignoreUnchanged
|
|
||||||
import io.timelimit.android.livedata.map
|
|
||||||
import io.timelimit.android.livedata.switchMap
|
|
||||||
import io.timelimit.android.logic.AppLogic
|
|
||||||
import io.timelimit.android.logic.DefaultAppLogic
|
|
||||||
import io.timelimit.android.sync.actions.SetDeviceDefaultUserAction
|
|
||||||
import io.timelimit.android.ui.main.ActivityViewModel
|
|
||||||
import io.timelimit.android.ui.main.ActivityViewModelHolder
|
|
||||||
|
|
||||||
class SetDeviceDefaultUserDialogFragment: BottomSheetDialogFragment() {
|
|
||||||
companion object {
|
|
||||||
private const val EXTRA_DEVICE_ID = "deviceId"
|
|
||||||
private const val DIALOG_TAG = "sddudf"
|
|
||||||
|
|
||||||
fun newInstance(deviceId: String) = SetDeviceDefaultUserDialogFragment().apply {
|
|
||||||
arguments = Bundle().apply {
|
|
||||||
putString(EXTRA_DEVICE_ID, deviceId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val deviceId: String by lazy { arguments!!.getString(EXTRA_DEVICE_ID)!! }
|
|
||||||
val logic: AppLogic by lazy { DefaultAppLogic.with(context!!) }
|
|
||||||
val database: Database by lazy { logic.database }
|
|
||||||
val auth: ActivityViewModel by lazy { (activity as ActivityViewModelHolder).getActivityViewModel() }
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
|
|
||||||
auth.authenticatedUser.observe(this, Observer {
|
|
||||||
if (it?.second?.type != UserType.Parent) {
|
|
||||||
dismissAllowingStateLoss()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
|
||||||
val binding = BottomSheetSelectionListBinding.inflate(inflater, container, false)
|
|
||||||
|
|
||||||
binding.title = getString(R.string.manage_device_default_user_title)
|
|
||||||
|
|
||||||
val list = binding.list
|
|
||||||
val users = database.user().getAllUsersLive()
|
|
||||||
val deviceEntry = database.device().getDeviceById(deviceId)
|
|
||||||
val currentDefaultUserId = deviceEntry.map { it?.defaultUser }.ignoreUnchanged()
|
|
||||||
|
|
||||||
currentDefaultUserId.switchMap { v1 ->
|
|
||||||
users.map { v2 -> v1 to v2 }
|
|
||||||
}.observe(this, Observer { (defaultUserId, userList) ->
|
|
||||||
list.removeAllViews()
|
|
||||||
|
|
||||||
fun buildRow(): CheckedTextView = LayoutInflater.from(context!!).inflate(
|
|
||||||
android.R.layout.simple_list_item_single_choice,
|
|
||||||
list,
|
|
||||||
false
|
|
||||||
) as CheckedTextView
|
|
||||||
|
|
||||||
val hasDefaultUser = userList.find { it.id == defaultUserId } != null
|
|
||||||
|
|
||||||
userList.forEach { user ->
|
|
||||||
buildRow().let { row ->
|
|
||||||
row.text = user.name
|
|
||||||
row.isChecked = defaultUserId == user.id
|
|
||||||
row.setOnClickListener {
|
|
||||||
auth.tryDispatchParentAction(
|
|
||||||
SetDeviceDefaultUserAction(
|
|
||||||
deviceId = deviceId,
|
|
||||||
defaultUserId = user.id
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
|
|
||||||
list.addView(row)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buildRow().let { row ->
|
|
||||||
row.setText(R.string.manage_device_default_user_selection_none)
|
|
||||||
row.isChecked = !hasDefaultUser
|
|
||||||
row.setOnClickListener {
|
|
||||||
auth.tryDispatchParentAction(
|
|
||||||
SetDeviceDefaultUserAction(
|
|
||||||
deviceId = deviceId,
|
|
||||||
defaultUserId = ""
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
|
|
||||||
list.addView(row)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return binding.root
|
|
||||||
}
|
|
||||||
|
|
||||||
fun show(fragmentManager: FragmentManager) = showSafe(fragmentManager, DIALOG_TAG)
|
|
||||||
}
|
|
|
@ -1,125 +0,0 @@
|
||||||
/*
|
|
||||||
* TimeLimit Copyright <C> 2019 Jonas Lochmann
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation version 3 of the License.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package io.timelimit.android.ui.manage.device.manage.defaultuser
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import android.widget.CheckedTextView
|
|
||||||
import androidx.fragment.app.FragmentManager
|
|
||||||
import androidx.lifecycle.LiveData
|
|
||||||
import androidx.lifecycle.Observer
|
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
|
||||||
import io.timelimit.android.R
|
|
||||||
import io.timelimit.android.data.model.Device
|
|
||||||
import io.timelimit.android.data.model.UserType
|
|
||||||
import io.timelimit.android.databinding.BottomSheetSelectionListBinding
|
|
||||||
import io.timelimit.android.extensions.showSafe
|
|
||||||
import io.timelimit.android.logic.DefaultAppLogic
|
|
||||||
import io.timelimit.android.sync.actions.SetDeviceDefaultUserTimeoutAction
|
|
||||||
import io.timelimit.android.ui.main.ActivityViewModel
|
|
||||||
import io.timelimit.android.ui.main.getActivityViewModel
|
|
||||||
import io.timelimit.android.util.TimeTextUtil
|
|
||||||
|
|
||||||
class SetDeviceDefaultUserTimeoutDialogFragment: BottomSheetDialogFragment() {
|
|
||||||
companion object {
|
|
||||||
private const val EXTRA_DEVICE_ID = "deviceId"
|
|
||||||
private const val DIALOG_TAG = "sddutdf"
|
|
||||||
private val OPTIONS = listOf(
|
|
||||||
0,
|
|
||||||
1000 * 5,
|
|
||||||
1000 * 60,
|
|
||||||
1000 * 60 * 5,
|
|
||||||
1000 * 60 * 15,
|
|
||||||
1000 * 60 * 30,
|
|
||||||
1000 * 60 * 60
|
|
||||||
)
|
|
||||||
|
|
||||||
fun newInstance(deviceId: String) = SetDeviceDefaultUserTimeoutDialogFragment().apply {
|
|
||||||
arguments = Bundle().apply {
|
|
||||||
putString(EXTRA_DEVICE_ID, deviceId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val deviceId: String by lazy { arguments!!.getString(EXTRA_DEVICE_ID)!! }
|
|
||||||
val deviceEntry: LiveData<Device?> by lazy {
|
|
||||||
DefaultAppLogic.with(context!!).database.device().getDeviceById(deviceId)
|
|
||||||
}
|
|
||||||
val auth: ActivityViewModel by lazy { getActivityViewModel(activity!!) }
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
|
|
||||||
auth.authenticatedUser.observe(this, Observer {
|
|
||||||
if (it?.second?.type != UserType.Parent) {
|
|
||||||
dismissAllowingStateLoss()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
deviceEntry.observe(this, Observer {
|
|
||||||
if (it == null) {
|
|
||||||
dismissAllowingStateLoss()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
|
||||||
val binding = BottomSheetSelectionListBinding.inflate(inflater, container, false)
|
|
||||||
binding.title = getString(R.string.manage_device_default_user_timeout_dialog_title)
|
|
||||||
val list = binding.list
|
|
||||||
|
|
||||||
deviceEntry.observe(this, Observer { device ->
|
|
||||||
val timeout = device?.defaultUserTimeout ?: 0
|
|
||||||
|
|
||||||
fun buildRow(): CheckedTextView = LayoutInflater.from(context!!).inflate(
|
|
||||||
android.R.layout.simple_list_item_single_choice,
|
|
||||||
list,
|
|
||||||
false
|
|
||||||
) as CheckedTextView
|
|
||||||
|
|
||||||
list.removeAllViews()
|
|
||||||
|
|
||||||
OPTIONS.forEach { option ->
|
|
||||||
buildRow().let { row ->
|
|
||||||
row.text = if (option == 0)
|
|
||||||
getString(R.string.manage_device_default_user_timeout_dialog_disable)
|
|
||||||
else if (option < 1000 * 60)
|
|
||||||
TimeTextUtil.seconds(option / 1000, context!!)
|
|
||||||
else
|
|
||||||
TimeTextUtil.time(option, context!!)
|
|
||||||
|
|
||||||
row.isChecked = option == timeout
|
|
||||||
row.setOnClickListener {
|
|
||||||
auth.tryDispatchParentAction(SetDeviceDefaultUserTimeoutAction(
|
|
||||||
deviceId = deviceId,
|
|
||||||
timeout = option
|
|
||||||
))
|
|
||||||
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
|
|
||||||
list.addView(row)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return binding.root
|
|
||||||
}
|
|
||||||
|
|
||||||
fun show(fragmentManager: FragmentManager) = showSafe(fragmentManager, DIALOG_TAG)
|
|
||||||
}
|
|
|
@ -1,179 +0,0 @@
|
||||||
/*
|
|
||||||
* TimeLimit Copyright <C> 2019 - 2023 Jonas Lochmann
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation version 3 of the License.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package io.timelimit.android.ui.manage.device.manage.user
|
|
||||||
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import android.widget.RadioButton
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import androidx.lifecycle.LiveData
|
|
||||||
import androidx.lifecycle.Observer
|
|
||||||
import io.timelimit.android.R
|
|
||||||
import io.timelimit.android.data.model.Device
|
|
||||||
import io.timelimit.android.databinding.ManageDeviceUserFragmentBinding
|
|
||||||
import io.timelimit.android.livedata.ignoreUnchanged
|
|
||||||
import io.timelimit.android.livedata.liveDataFromNonNullValue
|
|
||||||
import io.timelimit.android.livedata.map
|
|
||||||
import io.timelimit.android.livedata.mergeLiveData
|
|
||||||
import io.timelimit.android.logic.AppLogic
|
|
||||||
import io.timelimit.android.logic.DefaultAppLogic
|
|
||||||
import io.timelimit.android.sync.actions.SetDeviceUserAction
|
|
||||||
import io.timelimit.android.ui.main.ActivityViewModel
|
|
||||||
import io.timelimit.android.ui.main.ActivityViewModelHolder
|
|
||||||
import io.timelimit.android.ui.main.AuthenticationFab
|
|
||||||
import io.timelimit.android.ui.main.FragmentWithCustomTitle
|
|
||||||
import io.timelimit.android.ui.manage.device.manage.defaultuser.ManageDeviceDefaultUser
|
|
||||||
import io.timelimit.android.ui.model.UpdateStateCommand
|
|
||||||
import io.timelimit.android.ui.model.execute
|
|
||||||
|
|
||||||
class ManageDeviceUserFragment : Fragment(), FragmentWithCustomTitle {
|
|
||||||
private val activity: ActivityViewModelHolder by lazy { getActivity() as ActivityViewModelHolder }
|
|
||||||
private val logic: AppLogic by lazy { DefaultAppLogic.with(context!!) }
|
|
||||||
private val auth: ActivityViewModel by lazy { activity.getActivityViewModel() }
|
|
||||||
private val args: ManageDeviceUserFragmentArgs by lazy { ManageDeviceUserFragmentArgs.fromBundle(arguments!!) }
|
|
||||||
private val deviceEntry: LiveData<Device?> by lazy {
|
|
||||||
logic.database.device().getDeviceById(args.deviceId)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
|
||||||
val binding = ManageDeviceUserFragmentBinding.inflate(inflater, container, false)
|
|
||||||
val userEntries = logic.database.user().getAllUsersLive()
|
|
||||||
|
|
||||||
var isUpdatingSelectedUser = false
|
|
||||||
|
|
||||||
// auth
|
|
||||||
AuthenticationFab.manageAuthenticationFab(
|
|
||||||
fab = binding.fab,
|
|
||||||
shouldHighlight = auth.shouldHighlightAuthenticationButton,
|
|
||||||
authenticatedUser = auth.authenticatedUser,
|
|
||||||
fragment = this,
|
|
||||||
doesSupportAuth = liveDataFromNonNullValue(true)
|
|
||||||
)
|
|
||||||
|
|
||||||
// label, id
|
|
||||||
val userListItems = ArrayList<Pair<String, String>>()
|
|
||||||
|
|
||||||
fun bindUserListItems() {
|
|
||||||
userListItems.forEachIndexed { index, listItem ->
|
|
||||||
val oldRadio = binding.userList.getChildAt(index) as RadioButton?
|
|
||||||
val radio = oldRadio ?: RadioButton(requireContext())
|
|
||||||
|
|
||||||
radio.text = listItem.first
|
|
||||||
|
|
||||||
if (oldRadio == null) {
|
|
||||||
radio.layoutParams = ViewGroup.LayoutParams(
|
|
||||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
||||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
|
||||||
)
|
|
||||||
radio.id = index
|
|
||||||
|
|
||||||
binding.userList.addView(radio)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while (binding.userList.childCount > userListItems.size) {
|
|
||||||
binding.userList.removeViewAt(userListItems.size)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun bindUserListSelection() {
|
|
||||||
isUpdatingSelectedUser = true
|
|
||||||
|
|
||||||
val selectedUserId = deviceEntry.value?.currentUserId
|
|
||||||
val selectedIndex = userListItems.indexOfFirst { it.second == selectedUserId }
|
|
||||||
|
|
||||||
if (selectedIndex != -1) {
|
|
||||||
binding.userList.check(selectedIndex)
|
|
||||||
} else {
|
|
||||||
val fallbackSelectedIndex = userListItems.indexOfFirst { it.second == "" }
|
|
||||||
|
|
||||||
if (fallbackSelectedIndex != -1) {
|
|
||||||
binding.userList.check(fallbackSelectedIndex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
isUpdatingSelectedUser = false
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.handlers = object: ManageDeviceUserFragmentHandlers {
|
|
||||||
override fun showAuthenticationScreen() {
|
|
||||||
activity.showAuthenticationScreen()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.userList.setOnCheckedChangeListener { _, checkedId ->
|
|
||||||
val userId = userListItems[checkedId].second
|
|
||||||
val device = deviceEntry.value
|
|
||||||
|
|
||||||
if (device != null && device.currentUserId != userId && !isUpdatingSelectedUser) {
|
|
||||||
if (!auth.tryDispatchParentAction(
|
|
||||||
SetDeviceUserAction(
|
|
||||||
deviceId = args.deviceId,
|
|
||||||
userId = userId
|
|
||||||
)
|
|
||||||
)) {
|
|
||||||
bindUserListSelection()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
deviceEntry.observe(this, Observer {
|
|
||||||
device ->
|
|
||||||
|
|
||||||
if (device == null) {
|
|
||||||
requireActivity().execute(UpdateStateCommand.ManageDevice.Leave)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
val isThisDevice = logic.deviceId.map { ownDeviceId -> ownDeviceId == args.deviceId }.ignoreUnchanged()
|
|
||||||
|
|
||||||
mergeLiveData(deviceEntry, userEntries).observe(this, Observer {
|
|
||||||
val (device, users) = it!!
|
|
||||||
|
|
||||||
if (device != null && users != null) {
|
|
||||||
userListItems.clear()
|
|
||||||
userListItems.addAll(
|
|
||||||
users.map { user -> Pair(user.name, user.id) }
|
|
||||||
)
|
|
||||||
userListItems.add(Pair(getString(R.string.manage_device_current_user_none), ""))
|
|
||||||
|
|
||||||
bindUserListItems()
|
|
||||||
bindUserListSelection()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
ManageDeviceDefaultUser.bind(
|
|
||||||
view = binding.defaultUser,
|
|
||||||
device = deviceEntry,
|
|
||||||
users = userEntries,
|
|
||||||
lifecycleOwner = this,
|
|
||||||
isThisDevice = isThisDevice,
|
|
||||||
auth = auth,
|
|
||||||
fragmentManager = parentFragmentManager
|
|
||||||
)
|
|
||||||
|
|
||||||
return binding.root
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getCustomTitle(): LiveData<String?> = deviceEntry.map { "${getString(R.string.manage_device_card_user_title)} < ${it?.name} < ${getString(R.string.main_tab_overview)}" }
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ManageDeviceUserFragmentHandlers {
|
|
||||||
fun showAuthenticationScreen()
|
|
||||||
}
|
|
|
@ -0,0 +1,180 @@
|
||||||
|
/*
|
||||||
|
* TimeLimit Copyright <C> 2019 - 2023 Jonas Lochmann
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation version 3 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package io.timelimit.android.ui.manage.device.manage.user
|
||||||
|
|
||||||
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import io.timelimit.android.R
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.material.*
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import io.timelimit.android.data.model.UserType
|
||||||
|
import io.timelimit.android.ui.model.managedevice.ManageDeviceUser
|
||||||
|
import io.timelimit.android.util.TimeTextUtil
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterialApi::class, ExperimentalFoundationApi::class)
|
||||||
|
@Composable
|
||||||
|
fun ManageDeviceUserScreen(
|
||||||
|
items: List<ManageDeviceUser.UserItem>,
|
||||||
|
actions: ManageDeviceUser.Actions,
|
||||||
|
overlay: ManageDeviceUser.Overlay?,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
LazyColumn(
|
||||||
|
modifier,
|
||||||
|
contentPadding = PaddingValues(8.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
items(items, key = { it.id }) { item ->
|
||||||
|
Card(
|
||||||
|
onClick = { actions.select(item) },
|
||||||
|
modifier = Modifier
|
||||||
|
.animateItemPlacement()
|
||||||
|
.fillMaxWidth(),
|
||||||
|
backgroundColor = when (item.selected) {
|
||||||
|
true -> MaterialTheme.colors.secondary
|
||||||
|
false -> MaterialTheme.colors.surface
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
val buttonColors =
|
||||||
|
if (item.selected) ButtonDefaults.textButtonColors(
|
||||||
|
contentColor = MaterialTheme.colors.onSecondary.copy(alpha = .8f),
|
||||||
|
disabledContentColor = MaterialTheme.colors.onSecondary
|
||||||
|
)
|
||||||
|
else ButtonDefaults.textButtonColors()
|
||||||
|
|
||||||
|
Column(
|
||||||
|
Modifier.padding(8.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
item.name,
|
||||||
|
style = MaterialTheme.typography.h5
|
||||||
|
)
|
||||||
|
|
||||||
|
if (item.defaultUser is ManageDeviceUser.UserItem.DefaultUser.Yes) {
|
||||||
|
Text(stringResource(R.string.manage_device_user_is_default_user))
|
||||||
|
|
||||||
|
if (item.defaultUser.timeout > 0) {
|
||||||
|
Text(stringResource(
|
||||||
|
R.string.manage_device_user_default_user_timeout,
|
||||||
|
if (item.defaultUser.timeout < 1000 * 60) TimeTextUtil.seconds(item.defaultUser.timeout / 1000, LocalContext.current)
|
||||||
|
else TimeTextUtil.time(item.defaultUser.timeout, LocalContext.current)
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
TextButton(onClick = actions.disableDefaultUser, colors = buttonColors) {
|
||||||
|
Text(stringResource(R.string.manage_device_user_disable_default_user))
|
||||||
|
}
|
||||||
|
|
||||||
|
TextButton(onClick = actions.configureAutoSwitching, colors = buttonColors) {
|
||||||
|
Text(stringResource(
|
||||||
|
if (item.defaultUser.timeout == 0) R.string.manage_device_default_user_timeout_btn_enable
|
||||||
|
else R.string.manage_device_default_user_timeout_btn_change
|
||||||
|
))
|
||||||
|
}
|
||||||
|
} else if (item.selected) {
|
||||||
|
TextButton(onClick = { actions.makeDefaultUser(item) }, colors = buttonColors) {
|
||||||
|
Text(stringResource(R.string.manage_device_user_make_default_user))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Text(when (item.type) {
|
||||||
|
UserType.Child -> stringResource(R.string.add_user_type_child)
|
||||||
|
UserType.Parent -> stringResource(R.string.add_user_type_parent)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
when (overlay) {
|
||||||
|
is ManageDeviceUser.Overlay.EnableDefaultUser -> AlertDialog(
|
||||||
|
title = { Text(stringResource(R.string.manage_device_default_user_title)) },
|
||||||
|
text = {
|
||||||
|
Column (
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
Text(stringResource(R.string.manage_device_default_user_info))
|
||||||
|
Text(stringResource(R.string.manage_device_default_user_confirm, overlay.userTitle))
|
||||||
|
Text(stringResource(R.string.purchase_required_info_local_mode_free))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(onClick = overlay.confirm) {
|
||||||
|
Text(stringResource(R.string.generic_set))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(onClick = overlay.cancel) {
|
||||||
|
Text(stringResource(R.string.generic_cancel))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onDismissRequest = overlay.cancel
|
||||||
|
)
|
||||||
|
is ManageDeviceUser.Overlay.ConfigureTimeout -> AlertDialog(
|
||||||
|
title = { Text(stringResource(R.string.manage_device_default_user_timeout_dialog_title)) },
|
||||||
|
text = {
|
||||||
|
val options = listOf(
|
||||||
|
0,
|
||||||
|
1000 * 5,
|
||||||
|
1000 * 60,
|
||||||
|
1000 * 60 * 5,
|
||||||
|
1000 * 60 * 15,
|
||||||
|
1000 * 60 * 30,
|
||||||
|
1000 * 60 * 60
|
||||||
|
)
|
||||||
|
|
||||||
|
Column {
|
||||||
|
for (option in options) {
|
||||||
|
val onClick = { overlay.confirm(option) }
|
||||||
|
|
||||||
|
val label =
|
||||||
|
if (option == 0) stringResource(R.string.manage_device_default_user_timeout_dialog_disable)
|
||||||
|
else if (option < 1000 * 60) TimeTextUtil.seconds(option / 1000, LocalContext.current)
|
||||||
|
else TimeTextUtil.time(option, LocalContext.current)
|
||||||
|
|
||||||
|
Row(
|
||||||
|
Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clickable(onClickLabel = label, onClick = onClick),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
RadioButton(selected = option == overlay.currentValue, onClick = onClick)
|
||||||
|
Text(label)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text(stringResource(R.string.purchase_required_info_local_mode_free))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
confirmButton = {},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(onClick = overlay.cancel) {
|
||||||
|
Text(stringResource(R.string.generic_cancel))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onDismissRequest = overlay.cancel
|
||||||
|
)
|
||||||
|
null -> Unit
|
||||||
|
}
|
||||||
|
}
|
|
@ -110,7 +110,7 @@ class MainModel(application: Application): AndroidViewModel(application) {
|
||||||
Case.simple<_, _, State.LaunchState> { LaunchHandling.processLaunchState(state, logic) },
|
Case.simple<_, _, State.LaunchState> { LaunchHandling.processLaunchState(state, logic) },
|
||||||
Case.simple<_, _, State.Overview> { OverviewHandling.processState(logic, scope, activityCommandInternal, authenticationModelApi, state) },
|
Case.simple<_, _, State.Overview> { OverviewHandling.processState(logic, scope, activityCommandInternal, authenticationModelApi, state) },
|
||||||
Case.simple<_, _, State.ManageChild> { state -> ManageChildHandling.processState(logic, state, updateMethod(::updateState)) },
|
Case.simple<_, _, State.ManageChild> { state -> ManageChildHandling.processState(logic, state, updateMethod(::updateState)) },
|
||||||
Case.simple<_, _, State.ManageDevice> { state -> ManageDeviceHandling.processState(logic, state, updateMethod(::updateState)) },
|
Case.simple<_, _, State.ManageDevice> { state -> ManageDeviceHandling.processState(logic, activityCommandInternal, authenticationModelApi, state, updateMethod(::updateState)) },
|
||||||
Case.simple<_, _, State.DiagnoseScreen.DeviceOwner> { DeviceOwnerHandling.processState(logic, scope, authenticationModelApi, state) },
|
Case.simple<_, _, State.DiagnoseScreen.DeviceOwner> { DeviceOwnerHandling.processState(logic, scope, authenticationModelApi, state) },
|
||||||
Case.simple<_, _, FragmentState> { state ->
|
Case.simple<_, _, FragmentState> { state ->
|
||||||
state.transform {
|
state.transform {
|
||||||
|
|
|
@ -21,6 +21,7 @@ import androidx.compose.material.icons.outlined.Info
|
||||||
import io.timelimit.android.R
|
import io.timelimit.android.R
|
||||||
import io.timelimit.android.ui.model.diagnose.DeviceOwnerHandling
|
import io.timelimit.android.ui.model.diagnose.DeviceOwnerHandling
|
||||||
import io.timelimit.android.ui.model.main.OverviewHandling
|
import io.timelimit.android.ui.model.main.OverviewHandling
|
||||||
|
import io.timelimit.android.ui.model.managedevice.ManageDeviceUser
|
||||||
|
|
||||||
sealed class Screen(
|
sealed class Screen(
|
||||||
val state: State,
|
val state: State,
|
||||||
|
@ -163,14 +164,14 @@ sealed class Screen(
|
||||||
override val title = Title.Plain(deviceName)
|
override val title = Title.Plain(deviceName)
|
||||||
}
|
}
|
||||||
|
|
||||||
class ManageDeviceUser(
|
class ManageDeviceUserScreen(
|
||||||
state: State,
|
state: State,
|
||||||
toolbarIcons: List<Menu.Icon>,
|
override val backStack: List<BackStackItem>,
|
||||||
toolbarOptions: List<Menu.Dropdown>,
|
override val snackbarHostState: SnackbarHostState,
|
||||||
fragment: FragmentState,
|
val items: List<ManageDeviceUser.UserItem>,
|
||||||
containerId: Int,
|
val actions: ManageDeviceUser.Actions,
|
||||||
override val backStack: List<BackStackItem>
|
val overlay: ManageDeviceUser.Overlay?
|
||||||
): FragmentScreen(state, toolbarIcons, toolbarOptions, fragment, containerId), ScreenWithBackStack, ScreenWithTitle {
|
): Screen(state), ScreenWithBackStack, ScreenWithTitle, ScreenWithAuthenticationFab, ScreenWithSnackbar {
|
||||||
override val title = Title.StringResource(R.string.manage_device_card_user_title)
|
override val title = Title.StringResource(R.string.manage_device_card_user_title)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,8 +37,6 @@ import io.timelimit.android.ui.manage.device.manage.feature.ManageDeviceFeatures
|
||||||
import io.timelimit.android.ui.manage.device.manage.feature.ManageDeviceFeaturesFragmentArgs
|
import io.timelimit.android.ui.manage.device.manage.feature.ManageDeviceFeaturesFragmentArgs
|
||||||
import io.timelimit.android.ui.manage.device.manage.permission.ManageDevicePermissionsFragment
|
import io.timelimit.android.ui.manage.device.manage.permission.ManageDevicePermissionsFragment
|
||||||
import io.timelimit.android.ui.manage.device.manage.permission.ManageDevicePermissionsFragmentArgs
|
import io.timelimit.android.ui.manage.device.manage.permission.ManageDevicePermissionsFragmentArgs
|
||||||
import io.timelimit.android.ui.manage.device.manage.user.ManageDeviceUserFragment
|
|
||||||
import io.timelimit.android.ui.manage.device.manage.user.ManageDeviceUserFragmentArgs
|
|
||||||
import io.timelimit.android.ui.manage.parent.ManageParentFragment
|
import io.timelimit.android.ui.manage.parent.ManageParentFragment
|
||||||
import io.timelimit.android.ui.manage.parent.ManageParentFragmentArgs
|
import io.timelimit.android.ui.manage.parent.ManageParentFragmentArgs
|
||||||
import io.timelimit.android.ui.manage.parent.link.LinkParentMailFragment
|
import io.timelimit.android.ui.manage.parent.link.LinkParentMailFragment
|
||||||
|
@ -239,31 +237,24 @@ sealed class State (val previous: State?): Serializable {
|
||||||
fragmentClass
|
fragmentClass
|
||||||
)
|
)
|
||||||
|
|
||||||
class User(
|
data class User(
|
||||||
previousMain: Main,
|
|
||||||
deviceId: String
|
|
||||||
): Sub(previousMain, ManageDeviceUserFragment::class.java) {
|
|
||||||
@Transient
|
|
||||||
override val arguments: Bundle = ManageDeviceUserFragmentArgs(deviceId).toBundle()
|
|
||||||
}
|
|
||||||
class Permissions(
|
|
||||||
val previousMain: Main,
|
val previousMain: Main,
|
||||||
deviceId: String
|
val overlay: Overlay? = null
|
||||||
): Sub(previousMain, ManageDevicePermissionsFragment::class.java) {
|
): Sub(previousMain, Fragment::class.java) {
|
||||||
|
sealed class Overlay: Serializable {
|
||||||
|
data class EnableDefaultUserDialog(val userId: String): Overlay()
|
||||||
|
object AdjustDefaultUserTimeout: Overlay()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class Permissions(previousMain: Main): Sub(previousMain, ManageDevicePermissionsFragment::class.java) {
|
||||||
@Transient
|
@Transient
|
||||||
override val arguments: Bundle = ManageDevicePermissionsFragmentArgs(deviceId).toBundle()
|
override val arguments: Bundle = ManageDevicePermissionsFragmentArgs(deviceId).toBundle()
|
||||||
}
|
}
|
||||||
class Features(
|
class Features(previousMain: Main): Sub(previousMain, ManageDeviceFeaturesFragment::class.java) {
|
||||||
val previousMain: Main,
|
|
||||||
deviceId: String
|
|
||||||
): Sub(previousMain, ManageDeviceFeaturesFragment::class.java) {
|
|
||||||
@Transient
|
@Transient
|
||||||
override val arguments: Bundle = ManageDeviceFeaturesFragmentArgs(deviceId).toBundle()
|
override val arguments: Bundle = ManageDeviceFeaturesFragmentArgs(deviceId).toBundle()
|
||||||
}
|
}
|
||||||
class Advanced(
|
class Advanced(previousMain: Main): Sub(previousMain, ManageDeviceAdvancedFragment::class.java) {
|
||||||
val previousMain: Main,
|
|
||||||
deviceId: String
|
|
||||||
): Sub(previousMain, ManageDeviceAdvancedFragment::class.java) {
|
|
||||||
@Transient
|
@Transient
|
||||||
override val arguments: Bundle = ManageDeviceAdvancedFragmentArgs(deviceId).toBundle()
|
override val arguments: Bundle = ManageDeviceAdvancedFragmentArgs(deviceId).toBundle()
|
||||||
}
|
}
|
||||||
|
|
|
@ -161,22 +161,22 @@ sealed class UpdateStateCommand {
|
||||||
object ManageDevice {
|
object ManageDevice {
|
||||||
data class User(val childId: String): UpdateStateCommand() {
|
data class User(val childId: String): UpdateStateCommand() {
|
||||||
override fun transform(state: State): State? =
|
override fun transform(state: State): State? =
|
||||||
if (state is State.ManageDevice.Main) State.ManageDevice.User(state, childId)
|
if (state is State.ManageDevice.Main) State.ManageDevice.User(state)
|
||||||
else null
|
else null
|
||||||
}
|
}
|
||||||
data class Permissions(val childId: String): UpdateStateCommand() {
|
data class Permissions(val childId: String): UpdateStateCommand() {
|
||||||
override fun transform(state: State): State? =
|
override fun transform(state: State): State? =
|
||||||
if (state is State.ManageDevice.Main) State.ManageDevice.Permissions(state, childId)
|
if (state is State.ManageDevice.Main) State.ManageDevice.Permissions(state)
|
||||||
else null
|
else null
|
||||||
}
|
}
|
||||||
data class Features(val childId: String): UpdateStateCommand() {
|
data class Features(val childId: String): UpdateStateCommand() {
|
||||||
override fun transform(state: State): State? =
|
override fun transform(state: State): State? =
|
||||||
if (state is State.ManageDevice.Main) State.ManageDevice.Features(state, childId)
|
if (state is State.ManageDevice.Main) State.ManageDevice.Features(state)
|
||||||
else null
|
else null
|
||||||
}
|
}
|
||||||
data class Advanced(val childId: String): UpdateStateCommand() {
|
data class Advanced(val childId: String): UpdateStateCommand() {
|
||||||
override fun transform(state: State): State? =
|
override fun transform(state: State): State? =
|
||||||
if (state is State.ManageDevice.Main) State.ManageDevice.Advanced(state, childId)
|
if (state is State.ManageDevice.Main) State.ManageDevice.Advanced(state)
|
||||||
else null
|
else null
|
||||||
}
|
}
|
||||||
object Leave: UpdateStateCommand() {
|
object Leave: UpdateStateCommand() {
|
||||||
|
|
|
@ -24,13 +24,14 @@ import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class CaseScope<LocalStateType>(
|
class CaseScope<LocalStateType>(
|
||||||
val scope: CoroutineScope,
|
val scope: CoroutineScope,
|
||||||
val className: Class<LocalStateType>
|
val className: Class<LocalStateType>?
|
||||||
) {
|
) {
|
||||||
inline fun <SuperStateType, LocalStateType : SuperStateType> updateMethod(
|
inline fun <SuperStateType, LocalStateType : SuperStateType> updateMethod(
|
||||||
crossinline parent: ((SuperStateType) -> SuperStateType) -> Unit
|
crossinline parent: ((SuperStateType) -> SuperStateType) -> Unit
|
||||||
): ((LocalStateType) -> SuperStateType) -> Unit = { request ->
|
): ((LocalStateType) -> SuperStateType) -> Unit = { request ->
|
||||||
parent { oldState ->
|
parent { oldState ->
|
||||||
if (scope.isActive && className.isInstance(oldState)) request(oldState as LocalStateType)
|
if (!scope.isActive) oldState
|
||||||
|
else if (className != null && className.isInstance(oldState)) request(oldState as LocalStateType)
|
||||||
else oldState
|
else oldState
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,11 +40,16 @@ class CaseScope<LocalStateType>(
|
||||||
}
|
}
|
||||||
|
|
||||||
class Case<T, R>(
|
class Case<T, R>(
|
||||||
val className: Class<out Any>,
|
val className: Class<out Any>?,
|
||||||
val key: (T) -> Any?,
|
val key: (T) -> Any?,
|
||||||
val producer: CaseScope<T>.(Flow<T>, Any?) -> Flow<R>
|
val producer: CaseScope<T>.(Flow<T>, Any?) -> Flow<R>
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
|
fun <T, R> nil(producer: CaseScope<Unit>.(Flow<Unit>) -> Flow<R>) = Case<T, R>(
|
||||||
|
className = null,
|
||||||
|
key = {}
|
||||||
|
) { flow, _ -> producer(this as CaseScope<Unit>, flow.map { Unit }) }
|
||||||
|
|
||||||
inline fun <T, R, reified C : Any> simple(
|
inline fun <T, R, reified C : Any> simple(
|
||||||
crossinline producer: CaseScope<C>.(Flow<C>) -> Flow<R>
|
crossinline producer: CaseScope<C>.(Flow<C>) -> Flow<R>
|
||||||
) = Case<T, R>(
|
) = Case<T, R>(
|
||||||
|
@ -60,7 +66,7 @@ class Case<T, R>(
|
||||||
) { flow, key -> producer(this as CaseScope<C>, key as K, flow as Flow<C>) }
|
) { flow, key -> producer(this as CaseScope<C>, key as K, flow as Flow<C>) }
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun doesMatch(value: Any?): Boolean = className.isInstance(value)
|
internal fun doesMatch(value: Any?): Boolean = (className == null && value == null) || (className != null && className.isInstance(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T, R> Flow<T>.splitConflated(vararg cases: Case<T, R>): Flow<R> {
|
fun <T, R> Flow<T>.splitConflated(vararg cases: Case<T, R>): Flow<R> {
|
||||||
|
@ -78,7 +84,7 @@ fun <T, R> Flow<T>.splitConflated(vararg cases: Case<T, R>): Flow<R> {
|
||||||
val key = case.key(value)
|
val key = case.key(value)
|
||||||
val relayChannel = Channel<T>(Channel.CONFLATED)
|
val relayChannel = Channel<T>(Channel.CONFLATED)
|
||||||
val job = launch {
|
val job = launch {
|
||||||
val scope = CaseScope<T>(this, case.className as Class<T>)
|
val scope = CaseScope<T>(this, case.className as Class<T>?)
|
||||||
val inputFlow = flow { relayChannel.consumeEach { emit(it) } }
|
val inputFlow = flow { relayChannel.consumeEach { emit(it) } }
|
||||||
|
|
||||||
case.producer(scope, inputFlow, key).collect { send(it) }
|
case.producer(scope, inputFlow, key).collect { send(it) }
|
||||||
|
|
|
@ -18,17 +18,17 @@ package io.timelimit.android.ui.model.managedevice
|
||||||
import io.timelimit.android.R
|
import io.timelimit.android.R
|
||||||
import io.timelimit.android.data.model.Device
|
import io.timelimit.android.data.model.Device
|
||||||
import io.timelimit.android.logic.AppLogic
|
import io.timelimit.android.logic.AppLogic
|
||||||
import io.timelimit.android.ui.model.BackStackItem
|
import io.timelimit.android.ui.model.*
|
||||||
import io.timelimit.android.ui.model.Screen
|
|
||||||
import io.timelimit.android.ui.model.State
|
|
||||||
import io.timelimit.android.ui.model.Title
|
|
||||||
import io.timelimit.android.ui.model.flow.Case
|
import io.timelimit.android.ui.model.flow.Case
|
||||||
import io.timelimit.android.ui.model.flow.splitConflated
|
import io.timelimit.android.ui.model.flow.splitConflated
|
||||||
|
import kotlinx.coroutines.channels.SendChannel
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
|
|
||||||
object ManageDeviceHandling {
|
object ManageDeviceHandling {
|
||||||
fun processState(
|
fun processState(
|
||||||
logic: AppLogic,
|
logic: AppLogic,
|
||||||
|
activityCommand: SendChannel<ActivityCommand>,
|
||||||
|
authentication: AuthenticationModelApi,
|
||||||
state: Flow<State.ManageDevice>,
|
state: Flow<State.ManageDevice>,
|
||||||
updateState: ((State.ManageDevice) -> State) -> Unit
|
updateState: ((State.ManageDevice) -> State) -> Unit
|
||||||
): Flow<Screen> = state.splitConflated(
|
): Flow<Screen> = state.splitConflated(
|
||||||
|
@ -60,6 +60,9 @@ object ManageDeviceHandling {
|
||||||
},
|
},
|
||||||
Case.simple<_, _, State.ManageDevice.Sub> {
|
Case.simple<_, _, State.ManageDevice.Sub> {
|
||||||
processSubState(
|
processSubState(
|
||||||
|
logic,
|
||||||
|
activityCommand,
|
||||||
|
authentication,
|
||||||
it,
|
it,
|
||||||
baseBackStackLive,
|
baseBackStackLive,
|
||||||
share(foundDeviceLive),
|
share(foundDeviceLive),
|
||||||
|
@ -90,6 +93,9 @@ object ManageDeviceHandling {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun processSubState(
|
private fun processSubState(
|
||||||
|
logic: AppLogic,
|
||||||
|
activityCommand: SendChannel<ActivityCommand>,
|
||||||
|
authentication: AuthenticationModelApi,
|
||||||
stateLive: Flow<State.ManageDevice.Sub>,
|
stateLive: Flow<State.ManageDevice.Sub>,
|
||||||
parentBackStackLive: Flow<List<BackStackItem>>,
|
parentBackStackLive: Flow<List<BackStackItem>>,
|
||||||
deviceLive: SharedFlow<Device>,
|
deviceLive: SharedFlow<Device>,
|
||||||
|
@ -101,10 +107,15 @@ object ManageDeviceHandling {
|
||||||
|
|
||||||
return stateLive.splitConflated(
|
return stateLive.splitConflated(
|
||||||
Case.simple<_, _, State.ManageDevice.User> {
|
Case.simple<_, _, State.ManageDevice.User> {
|
||||||
processUserState(
|
ManageDeviceUser.processUserState(
|
||||||
it,
|
logic,
|
||||||
|
scope,
|
||||||
|
activityCommand,
|
||||||
|
authentication,
|
||||||
|
share(it),
|
||||||
subBackStackLive,
|
subBackStackLive,
|
||||||
deviceLive
|
deviceLive,
|
||||||
|
updateMethod(updateState)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
Case.simple<_, _, State.ManageDevice.Permissions> {
|
Case.simple<_, _, State.ManageDevice.Permissions> {
|
||||||
|
@ -131,21 +142,6 @@ object ManageDeviceHandling {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun processUserState(
|
|
||||||
stateLive: Flow<State.ManageDevice.User>,
|
|
||||||
parentBackStackLive: Flow<List<BackStackItem>>,
|
|
||||||
deviceLive: Flow<Device>
|
|
||||||
): Flow<Screen> = combine(stateLive, deviceLive, parentBackStackLive) { state, device, backStack ->
|
|
||||||
Screen.ManageDeviceUser(
|
|
||||||
state,
|
|
||||||
state.toolbarIcons,
|
|
||||||
state.toolbarOptions,
|
|
||||||
state,
|
|
||||||
R.id.fragment_manage_device_user,
|
|
||||||
backStack
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun processPermissionsState(
|
private fun processPermissionsState(
|
||||||
stateLive: Flow<State.ManageDevice.Permissions>,
|
stateLive: Flow<State.ManageDevice.Permissions>,
|
||||||
parentBackStackLive: Flow<List<BackStackItem>>,
|
parentBackStackLive: Flow<List<BackStackItem>>,
|
||||||
|
|
|
@ -0,0 +1,293 @@
|
||||||
|
/*
|
||||||
|
* TimeLimit Copyright <C> 2019 - 2023 Jonas Lochmann
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation version 3 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package io.timelimit.android.ui.model.managedevice
|
||||||
|
|
||||||
|
import androidx.compose.material.SnackbarHostState
|
||||||
|
import androidx.compose.material.SnackbarResult
|
||||||
|
import io.timelimit.android.R
|
||||||
|
import io.timelimit.android.data.model.Device
|
||||||
|
import io.timelimit.android.data.model.UserType
|
||||||
|
import io.timelimit.android.logic.AppLogic
|
||||||
|
import io.timelimit.android.sync.actions.SetDeviceDefaultUserAction
|
||||||
|
import io.timelimit.android.sync.actions.SetDeviceDefaultUserTimeoutAction
|
||||||
|
import io.timelimit.android.sync.actions.SetDeviceUserAction
|
||||||
|
import io.timelimit.android.sync.actions.SignOutAtDeviceAction
|
||||||
|
import io.timelimit.android.sync.actions.apply.ApplyActionUtil
|
||||||
|
import io.timelimit.android.ui.model.*
|
||||||
|
import io.timelimit.android.ui.model.flow.Case
|
||||||
|
import io.timelimit.android.ui.model.flow.splitConflated
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.channels.SendChannel
|
||||||
|
import kotlinx.coroutines.flow.*
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
object ManageDeviceUser {
|
||||||
|
data class UserItem(
|
||||||
|
val id: String,
|
||||||
|
val type: UserType,
|
||||||
|
val name: String,
|
||||||
|
val selected: Boolean,
|
||||||
|
val defaultUser: DefaultUser
|
||||||
|
) {
|
||||||
|
sealed class DefaultUser {
|
||||||
|
object No: DefaultUser()
|
||||||
|
data class Yes(val timeout: Int): DefaultUser()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class Actions(
|
||||||
|
val select: (UserItem) -> Unit,
|
||||||
|
val makeDefaultUser: (UserItem) -> Unit,
|
||||||
|
val disableDefaultUser: () -> Unit,
|
||||||
|
val configureAutoSwitching: () -> Unit
|
||||||
|
)
|
||||||
|
|
||||||
|
sealed class Overlay {
|
||||||
|
data class EnableDefaultUser(
|
||||||
|
val userTitle: String,
|
||||||
|
val confirm: () -> Unit,
|
||||||
|
val cancel: () -> Unit
|
||||||
|
): Overlay()
|
||||||
|
|
||||||
|
data class ConfigureTimeout(
|
||||||
|
val currentValue: Int,
|
||||||
|
val confirm: (Int) -> Unit,
|
||||||
|
val cancel: () -> Unit
|
||||||
|
): Overlay()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun processUserState(
|
||||||
|
logic: AppLogic,
|
||||||
|
scope: CoroutineScope,
|
||||||
|
activityCommand: SendChannel<ActivityCommand>,
|
||||||
|
authentication: AuthenticationModelApi,
|
||||||
|
stateLive: SharedFlow<State.ManageDevice.User>,
|
||||||
|
parentBackStackLive: Flow<List<BackStackItem>>,
|
||||||
|
deviceLive: Flow<Device>,
|
||||||
|
updateState: ((State.ManageDevice.User) -> State) -> Unit
|
||||||
|
): Flow<Screen> {
|
||||||
|
val snackbar = SnackbarHostState()
|
||||||
|
|
||||||
|
fun launch(action: suspend () -> Unit) {
|
||||||
|
scope.launch {
|
||||||
|
try {
|
||||||
|
action()
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
snackbar.showSnackbar(logic.context.getString(R.string.error_general))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val actions = Actions(
|
||||||
|
select = { user -> launch {
|
||||||
|
if (user.selected) {
|
||||||
|
val result = snackbar.showSnackbar(
|
||||||
|
logic.context.getString(R.string.manage_device_user_already_selected_text),
|
||||||
|
logic.context.getString(R.string.manage_device_user_already_selected_action)
|
||||||
|
)
|
||||||
|
|
||||||
|
if (result == SnackbarResult.ActionPerformed) {
|
||||||
|
val userEntry = logic.database.user().getUserByIdFlow(user.id).first()!!
|
||||||
|
|
||||||
|
updateState { state ->
|
||||||
|
when (userEntry.type) {
|
||||||
|
UserType.Child -> State.ManageChild.Main(state.previousOverview, user.id, false)
|
||||||
|
UserType.Parent -> State.ManageParent.Main(state.previousOverview, user.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
user.defaultUser is UserItem.DefaultUser.Yes &&
|
||||||
|
logic.fullVersion.shouldProvideFullVersionFunctions() &&
|
||||||
|
deviceLive.first().id == logic.database.config().getOwnDeviceIdFlow().first()
|
||||||
|
) {
|
||||||
|
ApplyActionUtil.applyAppLogicAction(
|
||||||
|
SignOutAtDeviceAction,
|
||||||
|
logic,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
val parent = authentication.authenticatedParentOnly.firstOrNull()
|
||||||
|
|
||||||
|
if (parent != null) {
|
||||||
|
ApplyActionUtil.applyParentAction(
|
||||||
|
SetDeviceUserAction(
|
||||||
|
deviceId = deviceLive.first().id,
|
||||||
|
userId = user.id
|
||||||
|
),
|
||||||
|
parent.authentication,
|
||||||
|
logic
|
||||||
|
)
|
||||||
|
} else authentication.triggerAuthenticationScreen()
|
||||||
|
}
|
||||||
|
} },
|
||||||
|
makeDefaultUser = { user -> launch {
|
||||||
|
if (authentication.doParentAuthentication() != null) {
|
||||||
|
updateState { it.copy(overlay = State.ManageDevice.User.Overlay.EnableDefaultUserDialog(user.id)) }
|
||||||
|
}
|
||||||
|
} },
|
||||||
|
disableDefaultUser = { launch {
|
||||||
|
val parent = authentication.authenticatedParentOnly.firstOrNull()
|
||||||
|
|
||||||
|
if (parent != null) {
|
||||||
|
val currentDefaultUser = deviceLive.first().defaultUser
|
||||||
|
|
||||||
|
ApplyActionUtil.applyParentAction(
|
||||||
|
SetDeviceDefaultUserAction(
|
||||||
|
deviceId = deviceLive.first().id,
|
||||||
|
defaultUserId = ""
|
||||||
|
),
|
||||||
|
parent.authentication,
|
||||||
|
logic
|
||||||
|
)
|
||||||
|
|
||||||
|
val result = snackbar.showSnackbar(
|
||||||
|
logic.context.getString(R.string.manage_device_user_disable_default_user_toast),
|
||||||
|
logic.context.getString(R.string.generic_undo)
|
||||||
|
)
|
||||||
|
|
||||||
|
if (result == SnackbarResult.ActionPerformed) {
|
||||||
|
ApplyActionUtil.applyParentAction(
|
||||||
|
SetDeviceDefaultUserAction(
|
||||||
|
deviceId = deviceLive.first().id,
|
||||||
|
defaultUserId = currentDefaultUser
|
||||||
|
),
|
||||||
|
parent.authentication,
|
||||||
|
logic
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else authentication.triggerAuthenticationScreen()
|
||||||
|
} },
|
||||||
|
configureAutoSwitching = { launch {
|
||||||
|
if (authentication.doParentAuthentication() != null) {
|
||||||
|
updateState { it.copy(overlay = State.ManageDevice.User.Overlay.AdjustDefaultUserTimeout) }
|
||||||
|
}
|
||||||
|
} }
|
||||||
|
)
|
||||||
|
|
||||||
|
val usersLive = listUsers(logic, deviceLive)
|
||||||
|
|
||||||
|
val overlayLive: Flow<Overlay?> = stateLive
|
||||||
|
.map { it.overlay }
|
||||||
|
.splitConflated(
|
||||||
|
Case.withKey<_, _, State.ManageDevice.User.Overlay.EnableDefaultUserDialog, _>(
|
||||||
|
withKey = { it.userId },
|
||||||
|
producer = { userId, _ ->
|
||||||
|
val userLive = logic.database.user().getUserByIdFlow(userId)
|
||||||
|
val closeOverlay = { updateState {
|
||||||
|
if (it.overlay is State.ManageDevice.User.Overlay.EnableDefaultUserDialog) it.copy(overlay = null)
|
||||||
|
else it
|
||||||
|
} }
|
||||||
|
|
||||||
|
userLive.transformLatest { user ->
|
||||||
|
if (user == null) closeOverlay()
|
||||||
|
else emit(Overlay.EnableDefaultUser(
|
||||||
|
userTitle = user.name,
|
||||||
|
cancel = closeOverlay,
|
||||||
|
confirm = { launch {
|
||||||
|
closeOverlay()
|
||||||
|
|
||||||
|
if (logic.fullVersion.shouldProvideFullVersionFunctions()) authentication.authenticatedParentOnly.first()!!.let { parent ->
|
||||||
|
ApplyActionUtil.applyParentAction(
|
||||||
|
SetDeviceDefaultUserAction(
|
||||||
|
deviceId = deviceLive.first().id,
|
||||||
|
defaultUserId = userId
|
||||||
|
),
|
||||||
|
parent.authentication,
|
||||||
|
logic
|
||||||
|
)
|
||||||
|
|
||||||
|
snackbar.showSnackbar(logic.context.getString(R.string.manage_device_user_make_default_user_toast))
|
||||||
|
}
|
||||||
|
else activityCommand.send(ActivityCommand.ShowMissingPremiumDialog)
|
||||||
|
} }
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
Case.simple<_, _, State.ManageDevice.User.Overlay.AdjustDefaultUserTimeout> {
|
||||||
|
val closeOverlay = { updateState {
|
||||||
|
if (it.overlay is State.ManageDevice.User.Overlay.AdjustDefaultUserTimeout) it.copy(overlay = null)
|
||||||
|
else it
|
||||||
|
} }
|
||||||
|
|
||||||
|
deviceLive.transform { device ->
|
||||||
|
if (device.defaultUser == "") closeOverlay()
|
||||||
|
else emit(Overlay.ConfigureTimeout(
|
||||||
|
currentValue = device.defaultUserTimeout,
|
||||||
|
cancel = closeOverlay,
|
||||||
|
confirm = { newValue -> launch {
|
||||||
|
closeOverlay()
|
||||||
|
|
||||||
|
if (logic.fullVersion.shouldProvideFullVersionFunctions()) authentication.authenticatedParentOnly.first()!!.let { parent ->
|
||||||
|
ApplyActionUtil.applyParentAction(
|
||||||
|
SetDeviceDefaultUserTimeoutAction(
|
||||||
|
deviceId = deviceLive.first().id,
|
||||||
|
timeout = newValue
|
||||||
|
),
|
||||||
|
parent.authentication,
|
||||||
|
logic
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else activityCommand.send(ActivityCommand.ShowMissingPremiumDialog)
|
||||||
|
} }
|
||||||
|
))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Case.nil { flowOf(null) }
|
||||||
|
)
|
||||||
|
|
||||||
|
return combine(stateLive, parentBackStackLive, usersLive, overlayLive) { state, parentBackStack, users, overlay ->
|
||||||
|
Screen.ManageDeviceUserScreen(
|
||||||
|
state,
|
||||||
|
parentBackStack,
|
||||||
|
snackbar,
|
||||||
|
users,
|
||||||
|
actions,
|
||||||
|
overlay
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun listUsers(
|
||||||
|
logic: AppLogic,
|
||||||
|
deviceLive: Flow<Device>
|
||||||
|
): Flow<List<UserItem>> {
|
||||||
|
val usersLive = logic.database.user().getAllUsersFlow()
|
||||||
|
|
||||||
|
return combine(usersLive, deviceLive) { users, device ->
|
||||||
|
users.map { user ->
|
||||||
|
val selected = device.currentUserId == user.id
|
||||||
|
val defaultUser =
|
||||||
|
if (device.defaultUser == user.id) UserItem.DefaultUser.Yes(
|
||||||
|
timeout = device.defaultUserTimeout
|
||||||
|
)
|
||||||
|
else UserItem.DefaultUser.No
|
||||||
|
|
||||||
|
UserItem(
|
||||||
|
id = user.id,
|
||||||
|
type = user.type,
|
||||||
|
name = user.name,
|
||||||
|
selected = selected,
|
||||||
|
defaultUser = defaultUser
|
||||||
|
)
|
||||||
|
}.sortedBy { when (it.defaultUser) {
|
||||||
|
is UserItem.DefaultUser.Yes -> 0
|
||||||
|
UserItem.DefaultUser.No -> 1
|
||||||
|
} }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,138 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
TimeLimit Copyright <C> 2019 - 2020 Jonas Lochmann
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation version 3 of the License.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
-->
|
|
||||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
|
||||||
|
|
||||||
<data>
|
|
||||||
<variable
|
|
||||||
name="defaultUserTitle"
|
|
||||||
type="String" />
|
|
||||||
|
|
||||||
<variable
|
|
||||||
name="hasDefaultUser"
|
|
||||||
type="boolean" />
|
|
||||||
|
|
||||||
<variable
|
|
||||||
name="isCurrentDevice"
|
|
||||||
type="boolean" />
|
|
||||||
|
|
||||||
<variable
|
|
||||||
name="isAlreadyUsingDefaultUser"
|
|
||||||
type="boolean" />
|
|
||||||
|
|
||||||
<variable
|
|
||||||
name="defaultUserSwitchText"
|
|
||||||
type="String" />
|
|
||||||
|
|
||||||
<variable
|
|
||||||
name="isAutomaticallySwitchingToDefaultUserEnabled"
|
|
||||||
type="boolean" />
|
|
||||||
|
|
||||||
<import type="android.view.View" />
|
|
||||||
</data>
|
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView
|
|
||||||
app:cardUseCompatPadding="true"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
<LinearLayout
|
|
||||||
android:padding="8dp"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
tools:ignore="UnusedAttribute"
|
|
||||||
android:drawableTint="?colorOnSurface"
|
|
||||||
android:id="@+id/title_view"
|
|
||||||
android:drawableEnd="@drawable/ic_info_outline_black_24dp"
|
|
||||||
android:background="?selectableItemBackground"
|
|
||||||
android:textAppearance="?android:textAppearanceLarge"
|
|
||||||
android:text="@string/manage_device_default_user_title"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:visibility="@{hasDefaultUser ? View.VISIBLE : View.GONE}"
|
|
||||||
android:textAppearance="?android:textAppearanceMedium"
|
|
||||||
tools:text="@string/manage_device_default_user_status"
|
|
||||||
android:text="@{@string/manage_device_default_user_status(defaultUserTitle)}"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
style="?materialButtonOutlinedStyle"
|
|
||||||
android:id="@+id/set_default_user_button"
|
|
||||||
tools:text="@string/manage_device_default_user_set_btn_set"
|
|
||||||
android:text="@{hasDefaultUser ? @string/manage_device_default_user_set_btn_change : @string/manage_device_default_user_set_btn_set}"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/switch_to_default_user_button"
|
|
||||||
android:enabled="@{hasDefaultUser && isCurrentDevice && (!isAlreadyUsingDefaultUser)}"
|
|
||||||
android:text="@string/manage_device_default_user_switch_btn"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:visibility="@{hasDefaultUser ? View.GONE : View.VISIBLE}"
|
|
||||||
android:textAppearance="?android:textAppearanceSmall"
|
|
||||||
android:text="@string/manage_device_default_user_switch_reason_unset"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:visibility="@{isCurrentDevice ? View.GONE : View.VISIBLE}"
|
|
||||||
android:textAppearance="?android:textAppearanceSmall"
|
|
||||||
android:text="@string/manage_device_default_user_switch_reason_remote"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:visibility="@{isAlreadyUsingDefaultUser ? View.VISIBLE : View.GONE}"
|
|
||||||
android:textAppearance="?android:textAppearanceSmall"
|
|
||||||
android:text="@string/manage_device_default_user_switch_reason_already_done"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:visibility="@{hasDefaultUser ? View.VISIBLE : View.GONE}"
|
|
||||||
android:textAppearance="?android:textAppearanceMedium"
|
|
||||||
android:text="@{defaultUserSwitchText}"
|
|
||||||
tools:text="@string/manage_device_default_user_timeout_off"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
style="?materialButtonOutlinedStyle"
|
|
||||||
android:visibility="@{hasDefaultUser ? View.VISIBLE : View.GONE}"
|
|
||||||
android:id="@+id/configure_auto_logout_button"
|
|
||||||
android:text="@{isAutomaticallySwitchingToDefaultUserEnabled ? @string/manage_device_default_user_timeout_btn_change : @string/manage_device_default_user_timeout_btn_enable}"
|
|
||||||
tools:text="@string/manage_device_default_user_timeout_btn_enable"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:textAppearance="?android:textAppearanceSmall"
|
|
||||||
android:text="@string/purchase_required_info_local_mode_free"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
</androidx.cardview.widget.CardView>
|
|
||||||
</layout>
|
|
|
@ -1,81 +0,0 @@
|
||||||
<!--
|
|
||||||
TimeLimit Copyright <C> 2019 Jonas Lochmann
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation version 3 of the License.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
-->
|
|
||||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
tools:context="io.timelimit.android.ui.manage.device.manage.user.ManageDeviceUserFragment">
|
|
||||||
|
|
||||||
<data>
|
|
||||||
<variable
|
|
||||||
name="handlers"
|
|
||||||
type="io.timelimit.android.ui.manage.device.manage.user.ManageDeviceUserFragmentHandlers" />
|
|
||||||
</data>
|
|
||||||
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
<ScrollView
|
|
||||||
android:id="@+id/scroll"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
<LinearLayout
|
|
||||||
android:padding="8dp"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView
|
|
||||||
app:cardUseCompatPadding="true"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
<LinearLayout
|
|
||||||
android:padding="8dp"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:textAppearance="?android:textAppearanceLarge"
|
|
||||||
android:text="@string/manage_device_current_user_title"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
<RadioGroup
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:id="@+id/user_list"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
</androidx.cardview.widget.CardView>
|
|
||||||
|
|
||||||
<include android:id="@+id/default_user"
|
|
||||||
layout="@layout/manage_device_default_user" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
</ScrollView>
|
|
||||||
|
|
||||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
|
||||||
android:onClick="@{() -> handlers.showAuthenticationScreen()}"
|
|
||||||
android:id="@+id/fab"
|
|
||||||
app:fabSize="normal"
|
|
||||||
android:src="@drawable/ic_lock_open_white_24dp"
|
|
||||||
android:layout_margin="16dp"
|
|
||||||
android:layout_gravity="end|bottom"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
||||||
</layout>
|
|
|
@ -102,15 +102,6 @@
|
||||||
android:name="deviceId"
|
android:name="deviceId"
|
||||||
app:argType="string" />
|
app:argType="string" />
|
||||||
</fragment>
|
</fragment>
|
||||||
<fragment
|
|
||||||
android:id="@+id/manageDeviceUserFragment"
|
|
||||||
android:name="io.timelimit.android.ui.manage.device.manage.user.ManageDeviceUserFragment"
|
|
||||||
android:label="manage_device_user_fragment"
|
|
||||||
tools:layout="@layout/manage_device_user_fragment" >
|
|
||||||
<argument
|
|
||||||
android:name="deviceId"
|
|
||||||
app:argType="string" />
|
|
||||||
</fragment>
|
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/manageDeviceAdvancedFragment"
|
android:id="@+id/manageDeviceAdvancedFragment"
|
||||||
android:name="io.timelimit.android.ui.manage.device.manage.advanced.ManageDeviceAdvancedFragment"
|
android:name="io.timelimit.android.ui.manage.device.manage.advanced.ManageDeviceAdvancedFragment"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!--
|
<!--
|
||||||
TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
TimeLimit Copyright <C> 2019 - 2023 Jonas Lochmann
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
the Free Software Foundation version 3 of the License.
|
the Free Software Foundation version 3 of the License.
|
||||||
|
@ -19,6 +19,7 @@
|
||||||
<string name="generic_help">Hilfe</string>
|
<string name="generic_help">Hilfe</string>
|
||||||
<string name="generic_undo">Rückgängig</string>
|
<string name="generic_undo">Rückgängig</string>
|
||||||
<string name="generic_save">Speichern</string>
|
<string name="generic_save">Speichern</string>
|
||||||
|
<string name="generic_set">Festlegen</string>
|
||||||
<string name="generic_create">Erstellen</string>
|
<string name="generic_create">Erstellen</string>
|
||||||
<string name="generic_delete">Löschen</string>
|
<string name="generic_delete">Löschen</string>
|
||||||
<string name="generic_retry">Erneut versuchen</string>
|
<string name="generic_retry">Erneut versuchen</string>
|
||||||
|
@ -830,30 +831,30 @@
|
||||||
</string>
|
</string>
|
||||||
<string name="manage_device_activity_level_blocking_checkbox">Sperren auf Activity-Ebene aktivieren</string>
|
<string name="manage_device_activity_level_blocking_checkbox">Sperren auf Activity-Ebene aktivieren</string>
|
||||||
|
|
||||||
|
<string name="manage_device_user_is_default_user">Standardbenutzer</string>
|
||||||
|
<string name="manage_device_user_default_user_timeout">wird nach %s aktiviert</string>
|
||||||
|
|
||||||
|
<string name="manage_device_user_make_default_user">zum Standardbenutzer machen</string>
|
||||||
|
<string name="manage_device_user_make_default_user_toast">Standardbenutzer gesetzt</string>
|
||||||
|
<string name="manage_device_user_disable_default_user">Festlegung als Standardbenutzer aufheben</string>
|
||||||
|
<string name="manage_device_user_disable_default_user_toast">Standardbenutzer entfernt</string>
|
||||||
|
|
||||||
|
<string name="manage_device_user_already_selected_text">Dieser Benutzer ist bereits ausgewählt</string>
|
||||||
|
<string name="manage_device_user_already_selected_action">Benutzereinstellungen aufrufen</string>
|
||||||
|
|
||||||
<string name="manage_device_default_user_title">Standardbenutzer</string>
|
<string name="manage_device_default_user_title">Standardbenutzer</string>
|
||||||
|
<string name="manage_device_default_user_confirm">Möchten Sie %s als Standardbenutzer festlegen?</string>
|
||||||
<string name="manage_device_default_user_info">Der Standardbenutzer ist ein Benutzer,
|
<string name="manage_device_default_user_info">Der Standardbenutzer ist ein Benutzer,
|
||||||
zu dem jeder an dem Gerät ohne Eingabe eines Passworts wechseln kann (um sich abzumelden).
|
zu dem jeder an dem Gerät ohne Eingabe eines Passworts wechseln kann (um sich abzumelden).
|
||||||
Das ist sinnvoll bei Geräten, die von verschiedenen Benutzern genutzt werden können.
|
Das ist sinnvoll bei Geräten, die von verschiedenen Benutzern genutzt werden können.
|
||||||
</string>
|
</string>
|
||||||
<string name="manage_device_default_user_status">Der Standardbenutzer ist %s</string>
|
|
||||||
<string name="manage_device_default_user_switch_btn">Zum Standardbenutzer wechseln</string>
|
<string name="manage_device_default_user_switch_btn">Zum Standardbenutzer wechseln</string>
|
||||||
<string name="manage_device_default_user_switch_reason_remote">Sie können nur am Gerät selbst zum Standardbenutzer wechseln</string>
|
|
||||||
<string name="manage_device_default_user_switch_reason_unset">Sie können nicht zum Standardbenutzer wechseln,
|
|
||||||
solange es keinen Standardbenutzer gibt</string>
|
|
||||||
<string name="manage_device_default_user_switch_reason_already_done">Sie können
|
|
||||||
nicht zum Standardbenutzer wechseln, wenn der Standardbenutzer bereits der aktuelle Benutzer ist
|
|
||||||
</string>
|
|
||||||
<string name="manage_device_default_user_set_btn_set">Standardbenutzer wählen</string>
|
|
||||||
<string name="manage_device_default_user_set_btn_change">Standardbenutzer ändern</string>
|
|
||||||
<string name="manage_device_default_user_selection_none">kein Standardbenutzer</string>
|
|
||||||
<string name="manage_device_default_user_timeout_off">Dieses Gerät wechselt nicht automatisch zum Standardbenutzer</string>
|
|
||||||
<string name="manage_device_default_user_timeout_on">Dieses Gerät wechselt automatisch zum Standardbenutzer, wenn es %s nicht benutzt wurde</string>
|
|
||||||
<string name="manage_device_default_user_timeout_btn_enable">automatischen Wechsel zum Standardbenutzer aktivieren</string>
|
<string name="manage_device_default_user_timeout_btn_enable">automatischen Wechsel zum Standardbenutzer aktivieren</string>
|
||||||
<string name="manage_device_default_user_timeout_btn_change">automatischen Wechsel zum Standardbenutzer konfigurieren</string>
|
<string name="manage_device_default_user_timeout_btn_change">automatischen Wechsel zum Standardbenutzer konfigurieren</string>
|
||||||
<string name="manage_device_default_user_timeout_dialog_title">automatisch zum Standardbenutzer wechseln</string>
|
<string name="manage_device_default_user_timeout_dialog_title">automatisch zum Standardbenutzer wechseln</string>
|
||||||
<string name="manage_device_default_user_timeout_dialog_disable">nie</string>
|
<string name="manage_device_default_user_timeout_dialog_disable">nie</string>
|
||||||
|
|
||||||
|
|
||||||
<string name="manage_device_introduction_title">Was ist das?</string>
|
<string name="manage_device_introduction_title">Was ist das?</string>
|
||||||
<string name="manage_device_introduction_text">
|
<string name="manage_device_introduction_text">
|
||||||
Das ist die Ansicht, um ein Gerät zu verwalten.
|
Das ist die Ansicht, um ein Gerät zu verwalten.
|
||||||
|
@ -910,7 +911,6 @@
|
||||||
<string name="manage_device_is_this_device">Das ist das Gerät, das Sie gerade vor sich haben</string>
|
<string name="manage_device_is_this_device">Das ist das Gerät, das Sie gerade vor sich haben</string>
|
||||||
<string name="manage_device_signing_key">Signaturschlüssel: %s</string>
|
<string name="manage_device_signing_key">Signaturschlüssel: %s</string>
|
||||||
|
|
||||||
<string name="manage_device_current_user_title">Benutzer des Geräts</string>
|
|
||||||
<string name="manage_device_current_user_none">Keine Angabe - keine Begrenzungen</string>
|
<string name="manage_device_current_user_none">Keine Angabe - keine Begrenzungen</string>
|
||||||
|
|
||||||
<string name="manage_device_feature_summary_none">Keine Funktionen aktiviert</string>
|
<string name="manage_device_feature_summary_none">Keine Funktionen aktiviert</string>
|
||||||
|
|
|
@ -40,7 +40,6 @@
|
||||||
<item name="fragment_manage_category_advanced" type="id" />
|
<item name="fragment_manage_category_advanced" type="id" />
|
||||||
<item name="fragment_manage_category_blocked_times" type="id" />
|
<item name="fragment_manage_category_blocked_times" type="id" />
|
||||||
<item name="fragment_manage_device_main" type="id" />
|
<item name="fragment_manage_device_main" type="id" />
|
||||||
<item name="fragment_manage_device_user" type="id" />
|
|
||||||
<item name="fragment_manage_device_permissions" type="id" />
|
<item name="fragment_manage_device_permissions" type="id" />
|
||||||
<item name="fragment_manage_device_features" type="id" />
|
<item name="fragment_manage_device_features" type="id" />
|
||||||
<item name="fragment_manage_device_advanced" type="id" />
|
<item name="fragment_manage_device_advanced" type="id" />
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
<string name="generic_help">Help</string>
|
<string name="generic_help">Help</string>
|
||||||
<string name="generic_undo">Undo</string>
|
<string name="generic_undo">Undo</string>
|
||||||
<string name="generic_save">Save</string>
|
<string name="generic_save">Save</string>
|
||||||
|
<string name="generic_set">Set</string>
|
||||||
<string name="generic_create">Create</string>
|
<string name="generic_create">Create</string>
|
||||||
<string name="generic_delete">Delete</string>
|
<string name="generic_delete">Delete</string>
|
||||||
<string name="generic_retry">Retry</string>
|
<string name="generic_retry">Retry</string>
|
||||||
|
@ -882,21 +883,25 @@
|
||||||
</string>
|
</string>
|
||||||
<string name="manage_device_activity_level_blocking_checkbox">Enable activity level blocking</string>
|
<string name="manage_device_activity_level_blocking_checkbox">Enable activity level blocking</string>
|
||||||
|
|
||||||
|
<string name="manage_device_user_is_default_user">Default User</string>
|
||||||
|
<string name="manage_device_user_default_user_timeout">enabled after %s</string>
|
||||||
|
|
||||||
|
<string name="manage_device_user_make_default_user">make default user</string>
|
||||||
|
<string name="manage_device_user_make_default_user_toast">default user set</string>
|
||||||
|
<string name="manage_device_user_disable_default_user">disable default user role</string>
|
||||||
|
<string name="manage_device_user_disable_default_user_toast">default user removed</string>
|
||||||
|
|
||||||
|
<string name="manage_device_user_already_selected_text">This is the current user</string>
|
||||||
|
<string name="manage_device_user_already_selected_action">Open user settings</string>
|
||||||
|
|
||||||
<string name="manage_device_default_user_title">Default user</string>
|
<string name="manage_device_default_user_title">Default user</string>
|
||||||
|
<string name="manage_device_default_user_confirm">Would you like to set %s as default user?</string>
|
||||||
<string name="manage_device_default_user_info">The default user is a user
|
<string name="manage_device_default_user_info">The default user is a user
|
||||||
to which any user at the device can switch without a password (to sign out).
|
to which any user at the device can switch without a password (to sign out).
|
||||||
This is useful at devices which can be used by multiple different users.
|
This is useful at devices which can be used by multiple different users.
|
||||||
</string>
|
</string>
|
||||||
<string name="manage_device_default_user_status">The current default user is %s</string>
|
|
||||||
<string name="manage_device_default_user_switch_btn">Switch to the default user</string>
|
<string name="manage_device_default_user_switch_btn">Switch to the default user</string>
|
||||||
<string name="manage_device_default_user_switch_reason_remote">You can only switch to the default user at the device itself</string>
|
|
||||||
<string name="manage_device_default_user_switch_reason_unset">You can only switch to the default user if a default user was configured</string>
|
|
||||||
<string name="manage_device_default_user_switch_reason_already_done">You can not switch to the default user if the default user is already the current user</string>
|
|
||||||
<string name="manage_device_default_user_set_btn_set">Set default user</string>
|
|
||||||
<string name="manage_device_default_user_set_btn_change">Change default user</string>
|
|
||||||
<string name="manage_device_default_user_selection_none">No default user</string>
|
|
||||||
<string name="manage_device_default_user_timeout_off">The device will not switch automatically to the default user</string>
|
|
||||||
<string name="manage_device_default_user_timeout_on">The device will switch automatically to the default user when it was not used for %s</string>
|
|
||||||
<string name="manage_device_default_user_timeout_btn_enable">enable switching automatically to default user</string>
|
<string name="manage_device_default_user_timeout_btn_enable">enable switching automatically to default user</string>
|
||||||
<string name="manage_device_default_user_timeout_btn_change">configure switching automatically to default user</string>
|
<string name="manage_device_default_user_timeout_btn_change">configure switching automatically to default user</string>
|
||||||
<string name="manage_device_default_user_timeout_dialog_title">Automatically switch to the default user</string>
|
<string name="manage_device_default_user_timeout_dialog_title">Automatically switch to the default user</string>
|
||||||
|
@ -955,7 +960,6 @@
|
||||||
<string name="manage_device_is_this_device">This is the device which is in front of you</string>
|
<string name="manage_device_is_this_device">This is the device which is in front of you</string>
|
||||||
<string name="manage_device_signing_key">Signing Key: %s</string>
|
<string name="manage_device_signing_key">Signing Key: %s</string>
|
||||||
|
|
||||||
<string name="manage_device_current_user_title">User of the device</string>
|
|
||||||
<string name="manage_device_current_user_none">No selection - no limits</string>
|
<string name="manage_device_current_user_none">No selection - no limits</string>
|
||||||
|
|
||||||
<string name="manage_device_feature_summary_none">No features enabled</string>
|
<string name="manage_device_feature_summary_none">No features enabled</string>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue