mirror of
https://codeberg.org/timelimit/timelimit-android.git
synced 2025-10-03 17:59:51 +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 ->
|
||||
ScreenMultiplexer(
|
||||
screen = screen,
|
||||
executeCommand = ::execute,
|
||||
fragmentManager = supportFragmentManager,
|
||||
fragmentIds = mainModel.fragmentIds,
|
||||
modifier = Modifier
|
||||
|
|
|
@ -19,14 +19,13 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.fragment.app.FragmentManager
|
||||
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.UpdateStateCommand
|
||||
import io.timelimit.android.ui.overview.overview.OverviewScreen
|
||||
|
||||
@Composable
|
||||
fun ScreenMultiplexer(
|
||||
screen: Screen?,
|
||||
executeCommand: (UpdateStateCommand) -> Unit,
|
||||
fragmentManager: FragmentManager,
|
||||
fragmentIds: MutableSet<Int>,
|
||||
modifier: Modifier = Modifier
|
||||
|
@ -35,6 +34,7 @@ fun ScreenMultiplexer(
|
|||
null -> {/* nothing to do */ }
|
||||
is Screen.FragmentScreen -> FragmentScreen(screen, fragmentManager, fragmentIds, 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)
|
||||
}
|
||||
}
|
|
@ -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.Overview> { OverviewHandling.processState(logic, scope, activityCommandInternal, authenticationModelApi, state) },
|
||||
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<_, _, FragmentState> { state ->
|
||||
state.transform {
|
||||
|
|
|
@ -21,6 +21,7 @@ import androidx.compose.material.icons.outlined.Info
|
|||
import io.timelimit.android.R
|
||||
import io.timelimit.android.ui.model.diagnose.DeviceOwnerHandling
|
||||
import io.timelimit.android.ui.model.main.OverviewHandling
|
||||
import io.timelimit.android.ui.model.managedevice.ManageDeviceUser
|
||||
|
||||
sealed class Screen(
|
||||
val state: State,
|
||||
|
@ -163,14 +164,14 @@ sealed class Screen(
|
|||
override val title = Title.Plain(deviceName)
|
||||
}
|
||||
|
||||
class ManageDeviceUser(
|
||||
class ManageDeviceUserScreen(
|
||||
state: State,
|
||||
toolbarIcons: List<Menu.Icon>,
|
||||
toolbarOptions: List<Menu.Dropdown>,
|
||||
fragment: FragmentState,
|
||||
containerId: Int,
|
||||
override val backStack: List<BackStackItem>
|
||||
): FragmentScreen(state, toolbarIcons, toolbarOptions, fragment, containerId), ScreenWithBackStack, ScreenWithTitle {
|
||||
override val backStack: List<BackStackItem>,
|
||||
override val snackbarHostState: SnackbarHostState,
|
||||
val items: List<ManageDeviceUser.UserItem>,
|
||||
val actions: ManageDeviceUser.Actions,
|
||||
val overlay: ManageDeviceUser.Overlay?
|
||||
): Screen(state), ScreenWithBackStack, ScreenWithTitle, ScreenWithAuthenticationFab, ScreenWithSnackbar {
|
||||
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.permission.ManageDevicePermissionsFragment
|
||||
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.ManageParentFragmentArgs
|
||||
import io.timelimit.android.ui.manage.parent.link.LinkParentMailFragment
|
||||
|
@ -239,31 +237,24 @@ sealed class State (val previous: State?): Serializable {
|
|||
fragmentClass
|
||||
)
|
||||
|
||||
class User(
|
||||
previousMain: Main,
|
||||
deviceId: String
|
||||
): Sub(previousMain, ManageDeviceUserFragment::class.java) {
|
||||
@Transient
|
||||
override val arguments: Bundle = ManageDeviceUserFragmentArgs(deviceId).toBundle()
|
||||
}
|
||||
class Permissions(
|
||||
data class User(
|
||||
val previousMain: Main,
|
||||
deviceId: String
|
||||
): Sub(previousMain, ManageDevicePermissionsFragment::class.java) {
|
||||
val overlay: Overlay? = null
|
||||
): 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
|
||||
override val arguments: Bundle = ManageDevicePermissionsFragmentArgs(deviceId).toBundle()
|
||||
}
|
||||
class Features(
|
||||
val previousMain: Main,
|
||||
deviceId: String
|
||||
): Sub(previousMain, ManageDeviceFeaturesFragment::class.java) {
|
||||
class Features(previousMain: Main): Sub(previousMain, ManageDeviceFeaturesFragment::class.java) {
|
||||
@Transient
|
||||
override val arguments: Bundle = ManageDeviceFeaturesFragmentArgs(deviceId).toBundle()
|
||||
}
|
||||
class Advanced(
|
||||
val previousMain: Main,
|
||||
deviceId: String
|
||||
): Sub(previousMain, ManageDeviceAdvancedFragment::class.java) {
|
||||
class Advanced(previousMain: Main): Sub(previousMain, ManageDeviceAdvancedFragment::class.java) {
|
||||
@Transient
|
||||
override val arguments: Bundle = ManageDeviceAdvancedFragmentArgs(deviceId).toBundle()
|
||||
}
|
||||
|
|
|
@ -161,22 +161,22 @@ sealed class UpdateStateCommand {
|
|||
object ManageDevice {
|
||||
data class User(val childId: String): UpdateStateCommand() {
|
||||
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
|
||||
}
|
||||
data class Permissions(val childId: String): UpdateStateCommand() {
|
||||
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
|
||||
}
|
||||
data class Features(val childId: String): UpdateStateCommand() {
|
||||
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
|
||||
}
|
||||
data class Advanced(val childId: String): UpdateStateCommand() {
|
||||
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
|
||||
}
|
||||
object Leave: UpdateStateCommand() {
|
||||
|
|
|
@ -24,13 +24,14 @@ import kotlinx.coroutines.launch
|
|||
|
||||
class CaseScope<LocalStateType>(
|
||||
val scope: CoroutineScope,
|
||||
val className: Class<LocalStateType>
|
||||
val className: Class<LocalStateType>?
|
||||
) {
|
||||
inline fun <SuperStateType, LocalStateType : SuperStateType> updateMethod(
|
||||
crossinline parent: ((SuperStateType) -> SuperStateType) -> Unit
|
||||
): ((LocalStateType) -> SuperStateType) -> Unit = { request ->
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -39,11 +40,16 @@ class CaseScope<LocalStateType>(
|
|||
}
|
||||
|
||||
class Case<T, R>(
|
||||
val className: Class<out Any>,
|
||||
val className: Class<out Any>?,
|
||||
val key: (T) -> Any?,
|
||||
val producer: CaseScope<T>.(Flow<T>, Any?) -> Flow<R>
|
||||
) {
|
||||
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(
|
||||
crossinline producer: CaseScope<C>.(Flow<C>) -> Flow<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>) }
|
||||
}
|
||||
|
||||
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> {
|
||||
|
@ -78,7 +84,7 @@ fun <T, R> Flow<T>.splitConflated(vararg cases: Case<T, R>): Flow<R> {
|
|||
val key = case.key(value)
|
||||
val relayChannel = Channel<T>(Channel.CONFLATED)
|
||||
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) } }
|
||||
|
||||
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.data.model.Device
|
||||
import io.timelimit.android.logic.AppLogic
|
||||
import io.timelimit.android.ui.model.BackStackItem
|
||||
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.*
|
||||
import io.timelimit.android.ui.model.flow.Case
|
||||
import io.timelimit.android.ui.model.flow.splitConflated
|
||||
import kotlinx.coroutines.channels.SendChannel
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
||||
object ManageDeviceHandling {
|
||||
fun processState(
|
||||
logic: AppLogic,
|
||||
activityCommand: SendChannel<ActivityCommand>,
|
||||
authentication: AuthenticationModelApi,
|
||||
state: Flow<State.ManageDevice>,
|
||||
updateState: ((State.ManageDevice) -> State) -> Unit
|
||||
): Flow<Screen> = state.splitConflated(
|
||||
|
@ -60,6 +60,9 @@ object ManageDeviceHandling {
|
|||
},
|
||||
Case.simple<_, _, State.ManageDevice.Sub> {
|
||||
processSubState(
|
||||
logic,
|
||||
activityCommand,
|
||||
authentication,
|
||||
it,
|
||||
baseBackStackLive,
|
||||
share(foundDeviceLive),
|
||||
|
@ -90,6 +93,9 @@ object ManageDeviceHandling {
|
|||
}
|
||||
|
||||
private fun processSubState(
|
||||
logic: AppLogic,
|
||||
activityCommand: SendChannel<ActivityCommand>,
|
||||
authentication: AuthenticationModelApi,
|
||||
stateLive: Flow<State.ManageDevice.Sub>,
|
||||
parentBackStackLive: Flow<List<BackStackItem>>,
|
||||
deviceLive: SharedFlow<Device>,
|
||||
|
@ -101,10 +107,15 @@ object ManageDeviceHandling {
|
|||
|
||||
return stateLive.splitConflated(
|
||||
Case.simple<_, _, State.ManageDevice.User> {
|
||||
processUserState(
|
||||
it,
|
||||
ManageDeviceUser.processUserState(
|
||||
logic,
|
||||
scope,
|
||||
activityCommand,
|
||||
authentication,
|
||||
share(it),
|
||||
subBackStackLive,
|
||||
deviceLive
|
||||
deviceLive,
|
||||
updateMethod(updateState)
|
||||
)
|
||||
},
|
||||
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(
|
||||
stateLive: Flow<State.ManageDevice.Permissions>,
|
||||
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"
|
||||
app:argType="string" />
|
||||
</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
|
||||
android:id="@+id/manageDeviceAdvancedFragment"
|
||||
android:name="io.timelimit.android.ui.manage.device.manage.advanced.ManageDeviceAdvancedFragment"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation version 3 of the License.
|
||||
|
@ -19,6 +19,7 @@
|
|||
<string name="generic_help">Hilfe</string>
|
||||
<string name="generic_undo">Rückgängig</string>
|
||||
<string name="generic_save">Speichern</string>
|
||||
<string name="generic_set">Festlegen</string>
|
||||
<string name="generic_create">Erstellen</string>
|
||||
<string name="generic_delete">Löschen</string>
|
||||
<string name="generic_retry">Erneut versuchen</string>
|
||||
|
@ -830,30 +831,30 @@
|
|||
</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_confirm">Möchten Sie %s als Standardbenutzer festlegen?</string>
|
||||
<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).
|
||||
Das ist sinnvoll bei Geräten, die von verschiedenen Benutzern genutzt werden können.
|
||||
</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_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_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_disable">nie</string>
|
||||
|
||||
|
||||
<string name="manage_device_introduction_title">Was ist das?</string>
|
||||
<string name="manage_device_introduction_text">
|
||||
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_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_feature_summary_none">Keine Funktionen aktiviert</string>
|
||||
|
|
|
@ -40,7 +40,6 @@
|
|||
<item name="fragment_manage_category_advanced" type="id" />
|
||||
<item name="fragment_manage_category_blocked_times" 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_features" type="id" />
|
||||
<item name="fragment_manage_device_advanced" type="id" />
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
<string name="generic_help">Help</string>
|
||||
<string name="generic_undo">Undo</string>
|
||||
<string name="generic_save">Save</string>
|
||||
<string name="generic_set">Set</string>
|
||||
<string name="generic_create">Create</string>
|
||||
<string name="generic_delete">Delete</string>
|
||||
<string name="generic_retry">Retry</string>
|
||||
|
@ -882,21 +883,25 @@
|
|||
</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_confirm">Would you like to set %s as default user?</string>
|
||||
<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).
|
||||
This is useful at devices which can be used by multiple different users.
|
||||
</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_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_change">configure switching automatically to 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_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_feature_summary_none">No features enabled</string>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue