diff --git a/app/src/main/java/io/timelimit/android/ui/ScreenMultiplexer.kt b/app/src/main/java/io/timelimit/android/ui/ScreenMultiplexer.kt index 791dc81..2dc6189 100644 --- a/app/src/main/java/io/timelimit/android/ui/ScreenMultiplexer.kt +++ b/app/src/main/java/io/timelimit/android/ui/ScreenMultiplexer.kt @@ -19,6 +19,7 @@ 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.permission.ManageDevicePermissionScreen import io.timelimit.android.ui.manage.device.manage.user.ManageDeviceUserScreen import io.timelimit.android.ui.model.Screen import io.timelimit.android.ui.overview.overview.OverviewScreen @@ -38,5 +39,6 @@ fun ScreenMultiplexer( is Screen.ManageDeviceUserScreen -> ManageDeviceUserScreen(screen.items, screen.actions, screen.overlay, modifier) is Screen.DeviceOwnerScreen -> DeviceOwnerScreen(screen.content, modifier = modifier) is Screen.SetupDevicePermissionsScreen -> SetupDevicePermissionsScreen(screen.content, screen.next, modifier) + is Screen.ManageDevicePermissions -> ManageDevicePermissionScreen(screen.content, modifier) } } \ No newline at end of file diff --git a/app/src/main/java/io/timelimit/android/ui/manage/device/manage/permission/ManageDevicePermissionScreen.kt b/app/src/main/java/io/timelimit/android/ui/manage/device/manage/permission/ManageDevicePermissionScreen.kt new file mode 100644 index 0000000..484ba1a --- /dev/null +++ b/app/src/main/java/io/timelimit/android/ui/manage/device/manage/permission/ManageDevicePermissionScreen.kt @@ -0,0 +1,36 @@ +/* + * TimeLimit Copyright 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 . + */ +package io.timelimit.android.ui.manage.device.manage.permission + +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + +@Composable +fun ManageDevicePermissionScreen( + content: PermissionScreenContent, + modifier: Modifier = Modifier +) { + PermissionScreen( + content, + modifier + .verticalScroll(rememberScrollState()) + .padding(8.dp) + ) +} \ No newline at end of file diff --git a/app/src/main/java/io/timelimit/android/ui/manage/device/manage/permission/ManageDevicePermissionsFragment.kt b/app/src/main/java/io/timelimit/android/ui/manage/device/manage/permission/ManageDevicePermissionsFragment.kt index d1f5bc1..e9bed0b 100644 --- a/app/src/main/java/io/timelimit/android/ui/manage/device/manage/permission/ManageDevicePermissionsFragment.kt +++ b/app/src/main/java/io/timelimit/android/ui/manage/device/manage/permission/ManageDevicePermissionsFragment.kt @@ -16,202 +16,40 @@ package io.timelimit.android.ui.manage.device.manage.permission import android.content.Context -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.fragment.app.Fragment -import androidx.lifecycle.LiveData -import androidx.lifecycle.Observer -import androidx.lifecycle.map import io.timelimit.android.R import io.timelimit.android.data.model.Device -import io.timelimit.android.data.model.UserType -import io.timelimit.android.databinding.ManageDevicePermissionsFragmentBinding import io.timelimit.android.integration.platform.NewPermissionStatus import io.timelimit.android.integration.platform.ProtectionLevel import io.timelimit.android.integration.platform.RuntimePermissionStatus -import io.timelimit.android.integration.platform.SystemPermission -import io.timelimit.android.livedata.ignoreUnchanged -import io.timelimit.android.livedata.liveDataFromNonNullValue -import io.timelimit.android.logic.AppLogic -import io.timelimit.android.logic.DefaultAppLogic -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.model.UpdateStateCommand -import io.timelimit.android.ui.model.execute -class ManageDevicePermissionsFragment : Fragment(), FragmentWithCustomTitle { - companion object { - fun getPreviewText(device: Device, context: Context): String { - val permissionLabels = mutableListOf() +object ManageDevicePermissionsFragment { + fun getPreviewText(device: Device, context: Context): String { + val permissionLabels = mutableListOf() - if (device.currentUsageStatsPermission == RuntimePermissionStatus.Granted) { - permissionLabels.add(context.getString(R.string.manage_device_permissions_usagestats_title_short)) - } - - if (device.currentNotificationAccessPermission == NewPermissionStatus.Granted) { - permissionLabels.add(context.getString(R.string.manage_device_permission_notification_access_title)) - } - - if (device.currentProtectionLevel != ProtectionLevel.None) { - permissionLabels.add(context.getString(R.string.manage_device_permission_device_admin_title)) - } - - if (device.currentOverlayPermission == RuntimePermissionStatus.Granted) { - permissionLabels.add(context.getString(R.string.manage_device_permissions_overlay_title)) - } - - if (device.accessibilityServiceEnabled) { - permissionLabels.add(context.getString(R.string.manage_device_permission_accessibility_title)) - } - - return if (permissionLabels.isEmpty()) { - context.getString(R.string.manage_device_permissions_summary_none) - } else { - permissionLabels.joinToString(", ") - } - } - } - - private val activity: ActivityViewModelHolder by lazy { getActivity() as ActivityViewModelHolder } - private val logic: AppLogic by lazy { DefaultAppLogic.with(requireContext()) } - private val auth: ActivityViewModel by lazy { activity.getActivityViewModel() } - private val args: ManageDevicePermissionsFragmentArgs by lazy { ManageDevicePermissionsFragmentArgs.fromBundle(requireArguments()) } - private val deviceEntry: LiveData by lazy { - logic.database.device().getDeviceById(args.deviceId) - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - val binding = ManageDevicePermissionsFragmentBinding.inflate(inflater, container, false) - - // auth - AuthenticationFab.manageAuthenticationFab( - fab = binding.fab, - shouldHighlight = auth.shouldHighlightAuthenticationButton, - authenticatedUser = auth.authenticatedUser, - fragment = this, - doesSupportAuth = liveDataFromNonNullValue(true) - ) - - auth.authenticatedUser.map { it?.second?.type == UserType.Parent }.observe(this, Observer { - binding.isUserSignedIn = it - }) - - // handlers - binding.handlers = object: ManageDevicePermissionsFragmentHandlers { - override fun openUsageStatsSettings() { - if (binding.isThisDevice == true) { - logic.platformIntegration.openSystemPermissionScren( - requireActivity(), - SystemPermission.UsageStats - ) - } - } - - override fun openNotificationAccessSettings() { - if (binding.isThisDevice == true) { - logic.platformIntegration.openSystemPermissionScren( - requireActivity(), - SystemPermission.Notification - ) - } - } - - override fun openDrawOverOtherAppsScreen() { - if (binding.isThisDevice == true) { - logic.platformIntegration.openSystemPermissionScren( - requireActivity(), - SystemPermission.Overlay - ) - } - } - - override fun openAccessibilitySettings() { - if (binding.isThisDevice == true) { - logic.platformIntegration.openSystemPermissionScren( - requireActivity(), - SystemPermission.AccessibilityService - ) - } - } - - override fun manageDeviceAdmin() { - if (binding.isThisDevice == true) { - logic.platformIntegration.openSystemPermissionScren( - requireActivity(), - SystemPermission.DeviceAdmin - ) - } - } - - override fun showAuthenticationScreen() { - activity.showAuthenticationScreen() - } - - override fun helpUsageStatsAccess() { - PermissionInfoHelpDialog.show(requireActivity(), SystemPermission.UsageStats) - } - - override fun helpNotificationAccess() { - PermissionInfoHelpDialog.show(requireActivity(), SystemPermission.Notification) - } - - override fun helpDrawOverOtherApps() { - PermissionInfoHelpDialog.show(requireActivity(), SystemPermission.Overlay) - } - - override fun helpAccesibility() { - PermissionInfoHelpDialog.show(requireActivity(), SystemPermission.AccessibilityService) - } + if (device.currentUsageStatsPermission == RuntimePermissionStatus.Granted) { + permissionLabels.add(context.getString(R.string.manage_device_permissions_usagestats_title_short)) } - // is this device - val isThisDevice = logic.deviceId.map { ownDeviceId -> ownDeviceId == args.deviceId }.ignoreUnchanged() + if (device.currentNotificationAccessPermission == NewPermissionStatus.Granted) { + permissionLabels.add(context.getString(R.string.manage_device_permission_notification_access_title)) + } - isThisDevice.observe(this, Observer { - binding.isThisDevice = it - }) + if (device.currentProtectionLevel != ProtectionLevel.None) { + permissionLabels.add(context.getString(R.string.manage_device_permission_device_admin_title)) + } - // permissions - deviceEntry.observe(this, Observer { - device -> + if (device.currentOverlayPermission == RuntimePermissionStatus.Granted) { + permissionLabels.add(context.getString(R.string.manage_device_permissions_overlay_title)) + } - if (device == null) { - requireActivity().execute(UpdateStateCommand.ManageDevice.Leave) - } else { - binding.usageStatsAccess = device.currentUsageStatsPermission - binding.notificationAccessPermission = device.currentNotificationAccessPermission - binding.protectionLevel = device.currentProtectionLevel - binding.overlayPermission = device.currentOverlayPermission - binding.accessibilityServiceEnabled = device.accessibilityServiceEnabled - } - }) + if (device.accessibilityServiceEnabled) { + permissionLabels.add(context.getString(R.string.manage_device_permission_accessibility_title)) + } - - return binding.root + return if (permissionLabels.isEmpty()) { + context.getString(R.string.manage_device_permissions_summary_none) + } else { + permissionLabels.joinToString(", ") + } } - - override fun onResume() { - super.onResume() - - logic.backgroundTaskLogic.syncDeviceStatusAsync() - } - - override fun getCustomTitle(): LiveData = deviceEntry.map { "${getString(R.string.manage_device_card_permission_title)} < ${it?.name} < ${getString(R.string.main_tab_overview)}" } -} - -interface ManageDevicePermissionsFragmentHandlers { - fun openUsageStatsSettings() - fun openNotificationAccessSettings() - fun openDrawOverOtherAppsScreen() - fun openAccessibilitySettings() - fun manageDeviceAdmin() - fun showAuthenticationScreen() - fun helpUsageStatsAccess() - fun helpNotificationAccess() - fun helpDrawOverOtherApps() - fun helpAccesibility() -} +} \ No newline at end of file diff --git a/app/src/main/java/io/timelimit/android/ui/manage/device/manage/permission/PermissionGoals.kt b/app/src/main/java/io/timelimit/android/ui/manage/device/manage/permission/PermissionGoals.kt index 8fabef7..3595951 100644 --- a/app/src/main/java/io/timelimit/android/ui/manage/device/manage/permission/PermissionGoals.kt +++ b/app/src/main/java/io/timelimit/android/ui/manage/device/manage/permission/PermissionGoals.kt @@ -84,7 +84,9 @@ fun PermissionGoals(status: PermissionScreenContent.Status) { status.protectionLevel == ProtectionLevel.DeviceOwner ) { Text(stringResource( - if (status.maxProtectionLevel == ProtectionLevel.DeviceOwner) + if (status.maxProtectionLevel == null) + R.string.manage_device_permission_goal_manipulation_protection_check_remotely + else if (status.maxProtectionLevel == ProtectionLevel.DeviceOwner) R.string.manage_device_permission_goal_needs_device_owner else R.string.manage_device_permission_goal_manipulation_protection_unavailable diff --git a/app/src/main/java/io/timelimit/android/ui/manage/device/manage/permission/PermissionScreen.kt b/app/src/main/java/io/timelimit/android/ui/manage/device/manage/permission/PermissionScreen.kt index c351b92..e589c01 100644 --- a/app/src/main/java/io/timelimit/android/ui/manage/device/manage/permission/PermissionScreen.kt +++ b/app/src/main/java/io/timelimit/android/ui/manage/device/manage/permission/PermissionScreen.kt @@ -18,14 +18,17 @@ package io.timelimit.android.ui.manage.device.manage.permission import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp @Composable fun PermissionScreen( - content: PermissionScreenContent + content: PermissionScreenContent, + modifier: Modifier = Modifier ) { Column ( - verticalArrangement = Arrangement.spacedBy(8.dp) + verticalArrangement = Arrangement.spacedBy(8.dp), + modifier = modifier ) { PermissionScreenPermissionList(content.status, content.showDetails) PermissionGoals(content.status) diff --git a/app/src/main/java/io/timelimit/android/ui/manage/device/manage/permission/PermissionScreenContent.kt b/app/src/main/java/io/timelimit/android/ui/manage/device/manage/permission/PermissionScreenContent.kt index f8b721a..ee518b0 100644 --- a/app/src/main/java/io/timelimit/android/ui/manage/device/manage/permission/PermissionScreenContent.kt +++ b/app/src/main/java/io/timelimit/android/ui/manage/device/manage/permission/PermissionScreenContent.kt @@ -28,7 +28,7 @@ data class PermissionScreenContent( data class Status( val notificationAccess: NewPermissionStatus, val protectionLevel: ProtectionLevel, - val maxProtectionLevel: ProtectionLevel, + val maxProtectionLevel: ProtectionLevel?, val usageStats: RuntimePermissionStatus, val overlay: RuntimePermissionStatus, val accessibility: Boolean, diff --git a/app/src/main/java/io/timelimit/android/ui/manage/device/manage/permission/PermissionScreenDialog.kt b/app/src/main/java/io/timelimit/android/ui/manage/device/manage/permission/PermissionScreenDialog.kt index 708c5c4..dc9bd48 100644 --- a/app/src/main/java/io/timelimit/android/ui/manage/device/manage/permission/PermissionScreenDialog.kt +++ b/app/src/main/java/io/timelimit/android/ui/manage/device/manage/permission/PermissionScreenDialog.kt @@ -59,7 +59,7 @@ fun PermissionScreenDialog( if (dialog.permission == SystemPermission.DeviceAdmin) { val message = - if (status.maxProtectionLevel != ProtectionLevel.DeviceOwner) R.string.manage_device_permission_device_owner_unsupported + if (status.maxProtectionLevel != ProtectionLevel.DeviceOwner && status.maxProtectionLevel != null) R.string.manage_device_permission_device_owner_unsupported else if (status.protectionLevel != ProtectionLevel.DeviceOwner) R.string.manage_device_permission_device_owner_not_granted else null diff --git a/app/src/main/java/io/timelimit/android/ui/model/Screen.kt b/app/src/main/java/io/timelimit/android/ui/model/Screen.kt index 7ca69f6..7111ba3 100644 --- a/app/src/main/java/io/timelimit/android/ui/model/Screen.kt +++ b/app/src/main/java/io/timelimit/android/ui/model/Screen.kt @@ -178,12 +178,9 @@ sealed class Screen( class ManageDevicePermissions( state: State, - toolbarIcons: List, - toolbarOptions: List, - fragment: FragmentState, - containerId: Int, + val content: PermissionScreenContent, override val backStack: List - ): FragmentScreen(state, toolbarIcons, toolbarOptions, fragment, containerId), ScreenWithBackStack, ScreenWithTitle { + ): Screen(state), ScreenWithBackStack, ScreenWithTitle { override val title = Title.StringResource(R.string.manage_device_card_permission_title) } diff --git a/app/src/main/java/io/timelimit/android/ui/model/State.kt b/app/src/main/java/io/timelimit/android/ui/model/State.kt index 81a49ab..6b0151f 100644 --- a/app/src/main/java/io/timelimit/android/ui/model/State.kt +++ b/app/src/main/java/io/timelimit/android/ui/model/State.kt @@ -36,8 +36,6 @@ import io.timelimit.android.ui.manage.device.manage.advanced.ManageDeviceAdvance import io.timelimit.android.ui.manage.device.manage.advanced.ManageDeviceAdvancedFragmentArgs import io.timelimit.android.ui.manage.device.manage.feature.ManageDeviceFeaturesFragment 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.parent.ManageParentFragment import io.timelimit.android.ui.manage.parent.ManageParentFragmentArgs import io.timelimit.android.ui.manage.parent.link.LinkParentMailFragment @@ -229,9 +227,7 @@ sealed class State (val previous: State?): Serializable { object AdjustDefaultUserTimeout: Overlay() } } - class Permissions(previousMain: Main): Sub(previousMain, ManageDevicePermissionsFragment::class.java) { - override val arguments: Bundle get() = ManageDevicePermissionsFragmentArgs(deviceId).toBundle() - } + data class Permissions(val previousMain: Main, val currentDialog: SystemPermission? = null): Sub(previousMain, Fragment::class.java) class Features(previousMain: Main): Sub(previousMain, ManageDeviceFeaturesFragment::class.java) { override val arguments: Bundle get() = ManageDeviceFeaturesFragmentArgs(deviceId).toBundle() } diff --git a/app/src/main/java/io/timelimit/android/ui/model/managedevice/ManageDeviceHandling.kt b/app/src/main/java/io/timelimit/android/ui/model/managedevice/ManageDeviceHandling.kt index c3c9ea2..0624f44 100644 --- a/app/src/main/java/io/timelimit/android/ui/model/managedevice/ManageDeviceHandling.kt +++ b/app/src/main/java/io/timelimit/android/ui/model/managedevice/ManageDeviceHandling.kt @@ -118,11 +118,15 @@ object ManageDeviceHandling { updateMethod(updateState) ) }, - Case.simple<_, _, State.ManageDevice.Permissions> { - processPermissionsState( - it, + Case.simple<_, _, State.ManageDevice.Permissions> {state -> + ManageDevicePermissions.processState( + scope, + logic, + activityCommand, + state, subBackStackLive, - deviceLive + deviceLive, + updateMethod(updateState) ) }, Case.simple<_, _, State.ManageDevice.Features> { @@ -142,21 +146,6 @@ object ManageDeviceHandling { ) } - private fun processPermissionsState( - stateLive: Flow, - parentBackStackLive: Flow>, - deviceLive: Flow - ): Flow = combine(stateLive, deviceLive, parentBackStackLive) { state, device, backStack -> - Screen.ManageDevicePermissions( - state, - state.toolbarIcons, - state.toolbarOptions, - state, - R.id.fragment_manage_device_permissions, - backStack - ) - } - private fun processFeaturesState( stateLive: Flow, parentBackStackLive: Flow>, diff --git a/app/src/main/java/io/timelimit/android/ui/model/managedevice/ManageDevicePermissions.kt b/app/src/main/java/io/timelimit/android/ui/model/managedevice/ManageDevicePermissions.kt new file mode 100644 index 0000000..52a96ea --- /dev/null +++ b/app/src/main/java/io/timelimit/android/ui/model/managedevice/ManageDevicePermissions.kt @@ -0,0 +1,88 @@ +/* + * TimeLimit Copyright 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 . + */ +package io.timelimit.android.ui.model.managedevice + +import io.timelimit.android.data.model.Device +import io.timelimit.android.logic.AppLogic +import io.timelimit.android.ui.manage.device.manage.permission.PermissionScreenContent +import io.timelimit.android.ui.model.ActivityCommand +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.setup.SetupLocalModePermissions +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.SendChannel +import kotlinx.coroutines.flow.* + +object ManageDevicePermissions { + fun processState( + scope: CoroutineScope, + logic: AppLogic, + activityCommand: SendChannel, + stateLive: Flow, + parentBackStackLive: Flow>, + deviceLive: SharedFlow, + updateState: ((State.ManageDevice.Permissions) -> State) -> Unit + ): Flow { + val isCurrentDeviceLive = isCurrentDevice(logic, deviceLive).shareIn(scope, SharingStarted.Lazily, 1) + val deviceStatusLive = isCurrentDeviceLive.transformLatest { isCurrentDevice -> + if (isCurrentDevice) emitAll(SetupLocalModePermissions.deviceStatus(logic.platformIntegration)) + else emitAll(statusFromDevice(deviceLive)) + } + + return combine( + stateLive, deviceStatusLive, isCurrentDeviceLive, parentBackStackLive + ) { state, deviceStatus, isCurrentDevice, parentBackStack -> + Screen.ManageDevicePermissions( + state, + PermissionScreenContent( + status = deviceStatus, + dialog = state.currentDialog?.let { dialog -> + PermissionScreenContent.Dialog( + permission = dialog, + launchSystemSettings = if (isCurrentDevice) ({ + activityCommand.trySend(ActivityCommand.LaunchSystemSettings(dialog)) + + updateState { it.copy(currentDialog = null) } + }) else null, + close = { updateState { it.copy(currentDialog = null) } } + ) + }, + showDetails = { permission -> updateState { it.copy(currentDialog = permission) } } + ), + parentBackStack + ) + } + } + + private fun isCurrentDevice(logic: AppLogic, deviceLive: Flow): Flow { + val ownDeviceIdLive = logic.database.config().getOwnDeviceIdFlow() + + return combine(ownDeviceIdLive, deviceLive) { deviceId, device -> device.id == deviceId } + } + + private fun statusFromDevice(deviceLive: Flow): Flow = deviceLive.map { device -> + PermissionScreenContent.Status( + notificationAccess = device.currentNotificationAccessPermission, + protectionLevel = device.currentProtectionLevel, + maxProtectionLevel = null, + usageStats = device.currentUsageStatsPermission, + overlay = device.currentOverlayPermission, + accessibility = device.accessibilityServiceEnabled, + isQOrLater = device.qOrLater + ) + }.distinctUntilChanged() +} \ No newline at end of file diff --git a/app/src/main/java/io/timelimit/android/ui/model/setup/SetupLocalModePermissions.kt b/app/src/main/java/io/timelimit/android/ui/model/setup/SetupLocalModePermissions.kt index 0928faf..9113122 100644 --- a/app/src/main/java/io/timelimit/android/ui/model/setup/SetupLocalModePermissions.kt +++ b/app/src/main/java/io/timelimit/android/ui/model/setup/SetupLocalModePermissions.kt @@ -60,7 +60,7 @@ object SetupLocalModePermissions { } } - private fun deviceStatus(platformIntegration: PlatformIntegration): Flow = flow { + fun deviceStatus(platformIntegration: PlatformIntegration): Flow = flow { while (true) { emit( PermissionScreenContent.Status( diff --git a/app/src/main/res/layout/manage_device_permissions_fragment.xml b/app/src/main/res/layout/manage_device_permissions_fragment.xml deleted file mode 100644 index 86b2860..0000000 --- a/app/src/main/res/layout/manage_device_permissions_fragment.xml +++ /dev/null @@ -1,471 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -