mirror of
https://codeberg.org/timelimit/timelimit-android.git
synced 2025-10-05 19:42:20 +02:00
Move device owner settings to the device settings
This commit is contained in:
parent
34f7bab46d
commit
a53883e87a
10 changed files with 69 additions and 47 deletions
|
@ -80,10 +80,6 @@ class DiagnoseMainFragment : Fragment(), FragmentWithCustomTitle {
|
|||
}
|
||||
})
|
||||
|
||||
binding.diagnoseDom.setOnClickListener {
|
||||
requireActivity().execute(UpdateStateCommand.Diagnose.DeviceOwner)
|
||||
}
|
||||
|
||||
binding.diagnoseExitReasonsButton.setOnClickListener {
|
||||
requireActivity().execute(UpdateStateCommand.Diagnose.ExitReasons)
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ import androidx.compose.ui.text.style.TextAlign
|
|||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.timelimit.android.R
|
||||
import io.timelimit.android.ui.model.diagnose.DeviceOwnerHandling
|
||||
import io.timelimit.android.ui.model.managedevice.DeviceOwnerHandling
|
||||
import io.timelimit.android.ui.overview.overview.ListCardCommon
|
||||
import io.timelimit.android.ui.overview.overview.ListCommon
|
||||
|
||||
|
|
|
@ -24,7 +24,6 @@ import io.timelimit.android.data.model.UserType
|
|||
import io.timelimit.android.logic.DefaultAppLogic
|
||||
import io.timelimit.android.ui.main.ActivityViewModel
|
||||
import io.timelimit.android.ui.model.account.AccountDeletion
|
||||
import io.timelimit.android.ui.model.diagnose.DeviceOwnerHandling
|
||||
import io.timelimit.android.ui.model.flow.Case
|
||||
import io.timelimit.android.ui.model.flow.splitConflated
|
||||
import io.timelimit.android.ui.model.launch.LaunchHandling
|
||||
|
@ -114,7 +113,6 @@ class MainModel(application: Application): AndroidViewModel(application) {
|
|||
Case.simple<_, _, State.Overview> { OverviewHandling.processState(logic, scope, activityCommandInternal, authenticationModelApi, state) },
|
||||
Case.simple<_, _, State.ManageChild> { state -> ManageChildHandling.processState(logic, activityCommandInternal, authenticationModelApi, 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.Setup> { state -> SetupHandling.handle(logic, activityCommandInternal, permissionsChanged, state, updateMethod(::updateState)) },
|
||||
Case.simple<_, _, State.DeleteAccount> { AccountDeletion.handle(logic, scope, share(it), updateMethod(::updateState)) },
|
||||
Case.simple<_, _, FragmentState> { state ->
|
||||
|
|
|
@ -22,7 +22,7 @@ import androidx.compose.material.icons.outlined.Info
|
|||
import io.timelimit.android.R
|
||||
import io.timelimit.android.ui.manage.device.manage.permission.PermissionScreenContent
|
||||
import io.timelimit.android.ui.model.account.AccountDeletion
|
||||
import io.timelimit.android.ui.model.diagnose.DeviceOwnerHandling
|
||||
import io.timelimit.android.ui.model.managedevice.DeviceOwnerHandling
|
||||
import io.timelimit.android.ui.model.intro.IntroHandling
|
||||
import io.timelimit.android.ui.model.mailauthentication.MailAuthentication
|
||||
import io.timelimit.android.ui.model.main.OverviewHandling
|
||||
|
@ -222,8 +222,9 @@ sealed class Screen(
|
|||
class DeviceOwnerScreen(
|
||||
state: State,
|
||||
val content: DeviceOwnerHandling.OwnerScreen,
|
||||
override val backStack: List<BackStackItem>,
|
||||
override val snackbarHostState: SnackbarHostState
|
||||
): Screen(state), ScreenWithAuthenticationFab, ScreenWithSnackbar, ScreenWithTitle {
|
||||
): Screen(state), ScreenWithAuthenticationFab, ScreenWithSnackbar, ScreenWithTitle, ScreenWithBackStack {
|
||||
override val title = Title.StringResource(R.string.diagnose_dom_title)
|
||||
}
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ import io.timelimit.android.ui.manage.parent.password.restore.RestoreParentPassw
|
|||
import io.timelimit.android.ui.manage.parent.u2fkey.ManageParentU2FKeyFragment
|
||||
import io.timelimit.android.ui.manage.parent.u2fkey.ManageParentU2FKeyFragmentArgs
|
||||
import io.timelimit.android.ui.model.account.AccountDeletion
|
||||
import io.timelimit.android.ui.model.diagnose.DeviceOwnerHandling
|
||||
import io.timelimit.android.ui.model.managedevice.DeviceOwnerHandling
|
||||
import io.timelimit.android.ui.model.mailauthentication.MailAuthentication
|
||||
import io.timelimit.android.ui.model.main.OverviewHandling
|
||||
import io.timelimit.android.ui.model.managechild.ManageCategoryBlockedTimes
|
||||
|
@ -67,11 +67,12 @@ import io.timelimit.android.ui.view.NotifyPermissionCard
|
|||
import java.io.Serializable
|
||||
|
||||
sealed class State (val previous: State?): Serializable {
|
||||
fun hasPrevious(other: State): Boolean = this.previous == other || this.previous?.hasPrevious(other) ?: false
|
||||
fun hasPrevious(other: State): Boolean = this.previous != null && (this.previous.matches(other) || this.previous.hasPrevious(other))
|
||||
fun find(predicate: (State) -> Boolean): State? =
|
||||
if (predicate(this)) this
|
||||
else previous?.find(predicate)
|
||||
fun first(): State = previous?.first() ?: this
|
||||
open fun matches(other: State) = this == other
|
||||
object LaunchState: State(previous = null)
|
||||
data class Overview(
|
||||
val state: OverviewHandling.OverviewState = OverviewHandling.OverviewState.empty
|
||||
|
@ -214,9 +215,10 @@ sealed class State (val previous: State?): Serializable {
|
|||
|
||||
sealed class Sub(
|
||||
val previousManageDeviceMain: Main,
|
||||
fragmentClass: Class<out Fragment>
|
||||
fragmentClass: Class<out Fragment>,
|
||||
previous: State = previousManageDeviceMain
|
||||
): ManageDevice(
|
||||
previousManageDeviceMain,
|
||||
previous,
|
||||
previousManageDeviceMain.previousOverview,
|
||||
previousManageDeviceMain.deviceId,
|
||||
fragmentClass
|
||||
|
@ -231,7 +233,12 @@ sealed class State (val previous: State?): Serializable {
|
|||
object AdjustDefaultUserTimeout: Overlay()
|
||||
}
|
||||
}
|
||||
data class Permissions(val previousMain: Main, val currentDialog: SystemPermission? = null): Sub(previousMain, Fragment::class.java)
|
||||
data class Permissions(val previousMain: Main, val currentDialog: SystemPermission? = null): Sub(previousMain, Fragment::class.java) {
|
||||
override fun matches(other: State): Boolean =
|
||||
if (other is Permissions) this.previousMain.matches(other.previousMain)
|
||||
else false
|
||||
}
|
||||
data class DeviceOwner(val previousPermissions: Permissions, val details: DeviceOwnerHandling.OwnerState = DeviceOwnerHandling.OwnerState()): Sub(previousPermissions.previousMain, Fragment::class.java, previousPermissions)
|
||||
class Features(previousMain: Main): Sub(previousMain, ManageDeviceFeaturesFragment::class.java) {
|
||||
override val arguments: Bundle get() = ManageDeviceFeaturesFragmentArgs(deviceId).toBundle()
|
||||
}
|
||||
|
@ -255,7 +262,6 @@ sealed class State (val previous: State?): Serializable {
|
|||
class Crypto(previous: Main): FragmentStateLegacy(previous, DiagnoseCryptoFragment::class.java)
|
||||
class ForegroundApp(previous: Main): FragmentStateLegacy(previous, DiagnoseForegroundAppFragment::class.java)
|
||||
class Sync(previous: Main): FragmentStateLegacy(previous, DiagnoseSyncFragment::class.java)
|
||||
data class DeviceOwner(val previousMain: Main, val details: DeviceOwnerHandling.OwnerState = DeviceOwnerHandling.OwnerState()): State(previousMain)
|
||||
}
|
||||
sealed class Setup(previous: State): State(previous) {
|
||||
class SetupTerms: FragmentStateLegacy(previous = null, fragmentClass = SetupTermsFragment::class.java)
|
||||
|
|
|
@ -316,11 +316,6 @@ sealed class UpdateStateCommand {
|
|||
if (state is State.DiagnoseScreen.Main) State.DiagnoseScreen.Sync(state)
|
||||
else null
|
||||
}
|
||||
object DeviceOwner: UpdateStateCommand() {
|
||||
override fun transform(state: State): State? =
|
||||
if (state is State.DiagnoseScreen.Main) State.DiagnoseScreen.DeviceOwner(state)
|
||||
else null
|
||||
}
|
||||
}
|
||||
object Setup {
|
||||
object Help: UpdateStateCommand() {
|
||||
|
|
|
@ -13,22 +13,24 @@
|
|||
* 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.diagnose
|
||||
package io.timelimit.android.ui.model.managedevice
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.util.Log
|
||||
import androidx.compose.material.SnackbarHostState
|
||||
import androidx.lifecycle.asFlow
|
||||
import io.timelimit.android.BuildConfig
|
||||
import io.timelimit.android.R
|
||||
import io.timelimit.android.async.Threads
|
||||
import io.timelimit.android.coroutines.executeAndWait
|
||||
import io.timelimit.android.data.IdGenerator
|
||||
import io.timelimit.android.data.model.App
|
||||
import io.timelimit.android.extensions.whileTrue
|
||||
import io.timelimit.android.data.model.Device
|
||||
import io.timelimit.android.integration.platform.DeviceOwnerApi
|
||||
import io.timelimit.android.integration.platform.PlatformIntegration
|
||||
import io.timelimit.android.logic.AppLogic
|
||||
import io.timelimit.android.ui.model.AuthenticationModelApi
|
||||
import io.timelimit.android.ui.model.BackStackItem
|
||||
import io.timelimit.android.ui.model.Screen
|
||||
import io.timelimit.android.ui.model.State
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
@ -96,39 +98,40 @@ object DeviceOwnerHandling {
|
|||
logic: AppLogic,
|
||||
scope: CoroutineScope,
|
||||
authentication: AuthenticationModelApi,
|
||||
stateLive: MutableStateFlow<State>
|
||||
deviceLive: SharedFlow<Device>,
|
||||
ownerStateLive: SharedFlow<State.ManageDevice.DeviceOwner>,
|
||||
backStackLive: Flow<List<BackStackItem>>,
|
||||
updateState: ((State.ManageDevice.DeviceOwner) -> State) -> Unit
|
||||
): Flow<Screen> {
|
||||
val snackbarHostState = SnackbarHostState()
|
||||
|
||||
val hasMatchingState = stateLive.map { it is State.DiagnoseScreen.DeviceOwner }
|
||||
val ownerStateLive = stateLive.transform { if (it is State.DiagnoseScreen.DeviceOwner) emit (it) }
|
||||
val isMatchingDeviceLive = combine(deviceLive, logic.deviceId.asFlow()) { device, id ->
|
||||
device.id == id
|
||||
}.distinctUntilChanged()
|
||||
|
||||
val screenLive = getScreen(
|
||||
logic,
|
||||
ownerStateLive.map { it.details },
|
||||
isMatchingDeviceLive,
|
||||
scope,
|
||||
authentication,
|
||||
snackbarHostState,
|
||||
updateState = { transformState ->
|
||||
stateLive.update { oldState ->
|
||||
if (oldState is State.DiagnoseScreen.DeviceOwner)
|
||||
oldState.copy(details = transformState(oldState.details))
|
||||
else
|
||||
oldState
|
||||
updateState {
|
||||
it.copy(details = transformState(it.details))
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
return hasMatchingState.whileTrue {
|
||||
ownerStateLive.combine(screenLive) { state, screen ->
|
||||
Screen.DeviceOwnerScreen(state, screen, snackbarHostState) as Screen
|
||||
}
|
||||
return combine(ownerStateLive, screenLive, backStackLive) { state, screen, backStack ->
|
||||
Screen.DeviceOwnerScreen(state, screen, backStack, snackbarHostState) as Screen
|
||||
}
|
||||
}
|
||||
|
||||
private fun getScreen(
|
||||
logic: AppLogic,
|
||||
state: Flow<OwnerState>,
|
||||
isMatchingDeviceLive: Flow<Boolean>,
|
||||
scope: CoroutineScope,
|
||||
authentication: AuthenticationModelApi,
|
||||
snackbarHostState: SnackbarHostState,
|
||||
|
@ -233,9 +236,13 @@ object DeviceOwnerHandling {
|
|||
|
||||
emitAll(
|
||||
combine(
|
||||
appsLive, dialogLive, hadUpdateOrganizationNameErrorLive, isParentAuthenticatedLive, organizationNameLive
|
||||
) { apps, dialog, hadUpdateOrganizationNameError, isParentAuthenticated, organizationName ->
|
||||
OwnerScreen.Normal(
|
||||
combine(appsLive, dialogLive) { a, b -> Pair(a, b) },
|
||||
hadUpdateOrganizationNameErrorLive, isParentAuthenticatedLive, organizationNameLive,
|
||||
isMatchingDeviceLive
|
||||
) { appsAndDialog, hadUpdateOrganizationNameError, isParentAuthenticated, organizationName, isMatchingDevice ->
|
||||
val (apps, dialog) = appsAndDialog
|
||||
|
||||
if (isMatchingDevice) OwnerScreen.Normal(
|
||||
isParentAuthenticated = isParentAuthenticated,
|
||||
organizationName = organizationName,
|
||||
appListDialog = dialog,
|
||||
|
@ -246,7 +253,7 @@ object DeviceOwnerHandling {
|
|||
if (hadUpdateOrganizationNameError || !isParentAuthenticated) null
|
||||
else actions.updateOrganizationName
|
||||
)
|
||||
)
|
||||
) else OwnerScreen.Error
|
||||
}
|
||||
)
|
||||
}.catch {
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package io.timelimit.android.ui.model.managedevice
|
||||
|
||||
import androidx.lifecycle.asFlow
|
||||
import io.timelimit.android.R
|
||||
import io.timelimit.android.data.model.Device
|
||||
import io.timelimit.android.logic.AppLogic
|
||||
|
@ -129,6 +130,23 @@ object ManageDeviceHandling {
|
|||
updateMethod(updateState)
|
||||
)
|
||||
},
|
||||
Case.simple<_, _, State.ManageDevice.DeviceOwner> {
|
||||
val subUpdateState = updateMethod<State, State.ManageDevice.DeviceOwner>(updateState)
|
||||
|
||||
DeviceOwnerHandling.processState(
|
||||
logic,
|
||||
scope,
|
||||
authentication,
|
||||
deviceLive,
|
||||
share(it),
|
||||
subBackStackLive.map {
|
||||
it + BackStackItem(Title.StringResource(R.string.manage_device_card_permission_title)) {
|
||||
subUpdateState { it.previousPermissions }
|
||||
}
|
||||
},
|
||||
subUpdateState
|
||||
)
|
||||
},
|
||||
Case.simple<_, _, State.ManageDevice.Features> {
|
||||
processFeaturesState(
|
||||
it,
|
||||
|
|
|
@ -17,6 +17,8 @@ package io.timelimit.android.ui.model.managedevice
|
|||
|
||||
import io.timelimit.android.data.model.Device
|
||||
import io.timelimit.android.data.model.DevicePlatform
|
||||
import io.timelimit.android.integration.platform.ProtectionLevel
|
||||
import io.timelimit.android.integration.platform.SystemPermission
|
||||
import io.timelimit.android.logic.AppLogic
|
||||
import io.timelimit.android.ui.manage.device.manage.permission.PermissionScreenContent
|
||||
import io.timelimit.android.ui.model.ActivityCommand
|
||||
|
@ -55,9 +57,15 @@ object ManageDevicePermissions {
|
|||
PermissionScreenContent.Dialog(
|
||||
permission = dialog,
|
||||
launchSystemSettings = if (isCurrentDevice) ({
|
||||
activityCommand.trySend(ActivityCommand.LaunchSystemSettings(dialog))
|
||||
if (dialog == SystemPermission.DeviceAdmin && deviceStatus.protectionLevel == ProtectionLevel.DeviceOwner) {
|
||||
updateState {
|
||||
State.ManageDevice.DeviceOwner(it.copy(currentDialog = null))
|
||||
}
|
||||
} else {
|
||||
activityCommand.trySend(ActivityCommand.LaunchSystemSettings(dialog))
|
||||
|
||||
updateState { it.copy(currentDialog = null) }
|
||||
updateState { it.copy(currentDialog = null) }
|
||||
}
|
||||
}) else null,
|
||||
close = { updateState { it.copy(currentDialog = null) } }
|
||||
)
|
||||
|
|
|
@ -87,13 +87,6 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<Button
|
||||
style="?materialButtonOutlinedStyle"
|
||||
android:id="@+id/diagnose_dom"
|
||||
android:text="@string/diagnose_dom_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<Button
|
||||
style="?materialButtonOutlinedStyle"
|
||||
android:id="@+id/diagnose_exit_reasons_button"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue