feat: refactor navigation, use navigation3 library

Fix global settings crash (#237)
This commit is contained in:
BinTianqi
2026-02-16 23:16:10 +08:00
parent f31e38f596
commit 2cd2ce4449
24 changed files with 1262 additions and 1205 deletions

View File

@@ -89,12 +89,13 @@ gradle.taskGraph.whenReady {
dependencies {
implementation(libs.androidx.activity.compose)
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.nav3.runtime)
implementation(libs.androidx.nav3.ui)
implementation(libs.androidx.compose.ui.tooling.preview)
debugImplementation(libs.androidx.compose.ui.tooling)
implementation(libs.accompanist.drawablepainter)
implementation(libs.accompanist.permissions)
implementation(libs.androidx.material3)
implementation(libs.androidx.navigation.compose)
implementation(libs.material.icons.core)
implementation(libs.shizuku.provider)
implementation(libs.shizuku.api)

View File

@@ -9,30 +9,13 @@ import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LargeTopAppBar
import androidx.compose.material3.MaterialTheme.colorScheme
import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.material3.Scaffold
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
@@ -41,207 +24,21 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.DialogProperties
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.toRoute
import com.bintianqi.owndroid.dpm.AddApnSetting
import com.bintianqi.owndroid.dpm.AddApnSettingScreen
import com.bintianqi.owndroid.dpm.AddDelegatedAdmin
import com.bintianqi.owndroid.dpm.AddDelegatedAdminScreen
import com.bintianqi.owndroid.dpm.AddPreferentialNetworkServiceConfig
import com.bintianqi.owndroid.dpm.AddPreferentialNetworkServiceConfigScreen
import com.bintianqi.owndroid.dpm.AffiliationId
import com.bintianqi.owndroid.dpm.AffiliationIdScreen
import com.bintianqi.owndroid.dpm.AlwaysOnVpnPackage
import com.bintianqi.owndroid.dpm.AlwaysOnVpnPackageScreen
import com.bintianqi.owndroid.dpm.ApplicationDetails
import com.bintianqi.owndroid.dpm.ApplicationDetailsScreen
import com.bintianqi.owndroid.dpm.ApplicationsFeatures
import com.bintianqi.owndroid.dpm.ApplicationsFeaturesScreen
import com.bintianqi.owndroid.dpm.AutoTimePolicy
import com.bintianqi.owndroid.dpm.AutoTimePolicyScreen
import com.bintianqi.owndroid.dpm.AutoTimeZonePolicy
import com.bintianqi.owndroid.dpm.AutoTimeZonePolicyScreen
import com.bintianqi.owndroid.dpm.BlockUninstall
import com.bintianqi.owndroid.dpm.CaCert
import com.bintianqi.owndroid.dpm.CaCertScreen
import com.bintianqi.owndroid.dpm.ChangeTime
import com.bintianqi.owndroid.dpm.ChangeTimeScreen
import com.bintianqi.owndroid.dpm.ChangeTimeZone
import com.bintianqi.owndroid.dpm.ChangeTimeZoneScreen
import com.bintianqi.owndroid.dpm.ChangeUsername
import com.bintianqi.owndroid.dpm.ChangeUsernameScreen
import com.bintianqi.owndroid.dpm.ClearAppStorage
import com.bintianqi.owndroid.dpm.ClearAppStorageScreen
import com.bintianqi.owndroid.dpm.ContentProtectionPolicy
import com.bintianqi.owndroid.dpm.ContentProtectionPolicyScreen
import com.bintianqi.owndroid.dpm.CreateUser
import com.bintianqi.owndroid.dpm.CreateUserScreen
import com.bintianqi.owndroid.dpm.CreateWorkProfile
import com.bintianqi.owndroid.dpm.CreateWorkProfileScreen
import com.bintianqi.owndroid.dpm.CredentialManagerPolicy
import com.bintianqi.owndroid.dpm.CredentialManagerPolicyScreen
import com.bintianqi.owndroid.dpm.CrossProfileIntentFilter
import com.bintianqi.owndroid.dpm.CrossProfileIntentFilterScreen
import com.bintianqi.owndroid.dpm.CrossProfilePackages
import com.bintianqi.owndroid.dpm.CrossProfileWidgetProviders
import com.bintianqi.owndroid.dpm.DefaultInputMethod
import com.bintianqi.owndroid.dpm.DefaultInputMethodScreen
import com.bintianqi.owndroid.dpm.DelegatedAdmins
import com.bintianqi.owndroid.dpm.DelegatedAdminsScreen
import com.bintianqi.owndroid.dpm.DeleteWorkProfile
import com.bintianqi.owndroid.dpm.DeleteWorkProfileScreen
import com.bintianqi.owndroid.dpm.DeviceInfo
import com.bintianqi.owndroid.dpm.DeviceInfoScreen
import com.bintianqi.owndroid.dpm.DhizukuServerSettings
import com.bintianqi.owndroid.dpm.DhizukuServerSettingsScreen
import com.bintianqi.owndroid.dpm.DisableAccountManagement
import com.bintianqi.owndroid.dpm.DisableAccountManagementScreen
import com.bintianqi.owndroid.dpm.DisableMeteredData
import com.bintianqi.owndroid.dpm.DisableUserControl
import com.bintianqi.owndroid.dpm.EditAppGroup
import com.bintianqi.owndroid.dpm.EditAppGroupScreen
import com.bintianqi.owndroid.dpm.EnableSystemApp
import com.bintianqi.owndroid.dpm.EnableSystemAppScreen
import com.bintianqi.owndroid.dpm.FrpPolicy
import com.bintianqi.owndroid.dpm.FrpPolicyScreen
import com.bintianqi.owndroid.dpm.HardwareMonitor
import com.bintianqi.owndroid.dpm.HardwareMonitorScreen
import com.bintianqi.owndroid.dpm.Hide
import com.bintianqi.owndroid.dpm.InstallExistingApp
import com.bintianqi.owndroid.dpm.InstallExistingAppScreen
import com.bintianqi.owndroid.dpm.InstallSystemUpdate
import com.bintianqi.owndroid.dpm.InstallSystemUpdateScreen
import com.bintianqi.owndroid.dpm.KeepUninstalledPackages
import com.bintianqi.owndroid.dpm.Keyguard
import com.bintianqi.owndroid.dpm.KeyguardDisabledFeatures
import com.bintianqi.owndroid.dpm.KeyguardDisabledFeaturesScreen
import com.bintianqi.owndroid.dpm.KeyguardScreen
import com.bintianqi.owndroid.dpm.LockScreenInfo
import com.bintianqi.owndroid.dpm.LockScreenInfoScreen
import com.bintianqi.owndroid.dpm.LockTaskMode
import com.bintianqi.owndroid.dpm.LockTaskModeScreen
import com.bintianqi.owndroid.dpm.ManageAppGroups
import com.bintianqi.owndroid.dpm.ManageAppGroupsScreen
import com.bintianqi.owndroid.dpm.ManagedConfiguration
import com.bintianqi.owndroid.dpm.ManagedConfigurationScreen
import com.bintianqi.owndroid.dpm.MtePolicy
import com.bintianqi.owndroid.dpm.MtePolicyScreen
import com.bintianqi.owndroid.dpm.NearbyStreamingPolicy
import com.bintianqi.owndroid.dpm.NearbyStreamingPolicyScreen
import com.bintianqi.owndroid.dpm.Network
import com.bintianqi.owndroid.dpm.NetworkLogging
import com.bintianqi.owndroid.dpm.NetworkLoggingScreen
import com.bintianqi.owndroid.dpm.NetworkOptions
import com.bintianqi.owndroid.dpm.NetworkOptionsScreen
import com.bintianqi.owndroid.dpm.NetworkScreen
import com.bintianqi.owndroid.dpm.NetworkStatsScreen
import com.bintianqi.owndroid.dpm.NetworkStatsViewer
import com.bintianqi.owndroid.dpm.NetworkStatsViewerScreen
import com.bintianqi.owndroid.dpm.OrganizationOwnedProfile
import com.bintianqi.owndroid.dpm.OrganizationOwnedProfileScreen
import com.bintianqi.owndroid.dpm.OverrideApn
import com.bintianqi.owndroid.dpm.OverrideApnScreen
import com.bintianqi.owndroid.dpm.PackageFunctionScreen
import com.bintianqi.owndroid.dpm.Password
import com.bintianqi.owndroid.dpm.PasswordInfo
import com.bintianqi.owndroid.dpm.PasswordInfoScreen
import com.bintianqi.owndroid.dpm.PasswordScreen
import com.bintianqi.owndroid.dpm.PermissionPolicy
import com.bintianqi.owndroid.dpm.PermissionPolicyScreen
import com.bintianqi.owndroid.dpm.AppPermissionsManager
import com.bintianqi.owndroid.dpm.AppPermissionsManagerScreen
import com.bintianqi.owndroid.dpm.PermissionDetail
import com.bintianqi.owndroid.dpm.PermissionDetailScreen
import com.bintianqi.owndroid.dpm.PermissionManager
import com.bintianqi.owndroid.dpm.PermissionManagerScreen
import com.bintianqi.owndroid.dpm.PermittedAccessibilityServices
import com.bintianqi.owndroid.dpm.PermittedAsAndImPackages
import com.bintianqi.owndroid.dpm.PermittedInputMethods
import com.bintianqi.owndroid.dpm.PreferentialNetworkService
import com.bintianqi.owndroid.dpm.PreferentialNetworkServiceInfo
import com.bintianqi.owndroid.dpm.PreferentialNetworkServiceScreen
import com.bintianqi.owndroid.dpm.PrivateDns
import com.bintianqi.owndroid.dpm.PrivateDnsScreen
import com.bintianqi.owndroid.dpm.QueryNetworkStats
import com.bintianqi.owndroid.dpm.RecommendedGlobalProxy
import com.bintianqi.owndroid.dpm.RecommendedGlobalProxyScreen
import com.bintianqi.owndroid.dpm.RequiredPasswordComplexity
import com.bintianqi.owndroid.dpm.RequiredPasswordComplexityScreen
import com.bintianqi.owndroid.dpm.RequiredPasswordQuality
import com.bintianqi.owndroid.dpm.RequiredPasswordQualityScreen
import com.bintianqi.owndroid.dpm.ResetPassword
import com.bintianqi.owndroid.dpm.ResetPasswordScreen
import com.bintianqi.owndroid.dpm.ResetPasswordToken
import com.bintianqi.owndroid.dpm.ResetPasswordTokenScreen
import com.bintianqi.owndroid.dpm.SecurityLogging
import com.bintianqi.owndroid.dpm.SecurityLoggingScreen
import com.bintianqi.owndroid.dpm.SetDefaultDialer
import com.bintianqi.owndroid.dpm.SetDefaultDialerScreen
import com.bintianqi.owndroid.dpm.SetSystemUpdatePolicy
import com.bintianqi.owndroid.dpm.SupportMessage
import com.bintianqi.owndroid.dpm.SupportMessageScreen
import com.bintianqi.owndroid.dpm.Suspend
import com.bintianqi.owndroid.dpm.SuspendPersonalApp
import com.bintianqi.owndroid.dpm.SuspendPersonalAppScreen
import com.bintianqi.owndroid.dpm.SystemManager
import com.bintianqi.owndroid.dpm.SystemManagerScreen
import com.bintianqi.owndroid.dpm.SystemOptions
import com.bintianqi.owndroid.dpm.SystemOptionsScreen
import com.bintianqi.owndroid.dpm.SystemUpdatePolicyScreen
import com.bintianqi.owndroid.dpm.TransferOwnership
import com.bintianqi.owndroid.dpm.TransferOwnershipScreen
import com.bintianqi.owndroid.dpm.UninstallApp
import com.bintianqi.owndroid.dpm.UninstallAppScreen
import com.bintianqi.owndroid.dpm.UpdateNetwork
import com.bintianqi.owndroid.dpm.UpdateNetworkScreen
import com.bintianqi.owndroid.dpm.UserInfo
import com.bintianqi.owndroid.dpm.UserInfoScreen
import com.bintianqi.owndroid.dpm.UserOperation
import com.bintianqi.owndroid.dpm.UserOperationScreen
import com.bintianqi.owndroid.dpm.UserRestriction
import com.bintianqi.owndroid.dpm.UserRestrictionEditor
import com.bintianqi.owndroid.dpm.UserRestrictionEditorScreen
import com.bintianqi.owndroid.dpm.UserRestrictionOptions
import com.bintianqi.owndroid.dpm.UserRestrictionOptionsScreen
import com.bintianqi.owndroid.dpm.UserRestrictionScreen
import com.bintianqi.owndroid.dpm.UserSessionMessage
import com.bintianqi.owndroid.dpm.UserSessionMessageScreen
import com.bintianqi.owndroid.dpm.Users
import com.bintianqi.owndroid.dpm.UsersOptions
import com.bintianqi.owndroid.dpm.UsersOptionsScreen
import com.bintianqi.owndroid.dpm.UsersScreen
import com.bintianqi.owndroid.dpm.WiFi
import com.bintianqi.owndroid.dpm.WifiScreen
import com.bintianqi.owndroid.dpm.WifiSecurityLevel
import com.bintianqi.owndroid.dpm.WifiSecurityLevelScreen
import com.bintianqi.owndroid.dpm.WifiSsidPolicyScreen
import com.bintianqi.owndroid.dpm.WipeData
import com.bintianqi.owndroid.dpm.WipeDataScreen
import com.bintianqi.owndroid.dpm.WorkModes
import com.bintianqi.owndroid.dpm.WorkModesScreen
import com.bintianqi.owndroid.dpm.WorkProfile
import com.bintianqi.owndroid.dpm.WorkProfileScreen
import androidx.navigation3.runtime.rememberNavBackStack
import androidx.navigation3.ui.NavDisplay
import com.bintianqi.owndroid.dpm.dhizukuErrorStatus
import com.bintianqi.owndroid.ui.NavTransition
import com.bintianqi.owndroid.ui.navigation.Destination
import com.bintianqi.owndroid.ui.navigation.myEntryProvider
import com.bintianqi.owndroid.ui.theme.OwnDroidTheme
import kotlinx.serialization.Serializable
import java.util.Locale
@ExperimentalMaterial3Api
@@ -267,589 +64,61 @@ class MainActivity : FragmentActivity() {
var appLockDialog by rememberSaveable { mutableStateOf(false) }
val theme by vm.theme.collectAsStateWithLifecycle()
OwnDroidTheme(theme) {
Home(vm) { appLockDialog = true }
Box(Modifier.fillMaxSize().background(MaterialTheme.colorScheme.background))
val backstack = rememberNavBackStack(Destination.Home)
NavDisplay(
backstack,
onBack = {
backstack.removeLastOrNull()
},
transitionSpec = {
NavTransition.transition
},
popTransitionSpec = {
NavTransition.popTransition
},
predictivePopTransitionSpec = {
NavTransition.popTransition
}
) {
myEntryProvider(it as Destination, backstack, vm)
}
val lifecycleOwner = LocalLifecycleOwner.current
if (appLockDialog) {
AppLockDialog({ appLockDialog = false }) { moveTaskToBack(true) }
}
}
}
}
}
@ExperimentalMaterial3Api
@Composable
fun Home(vm: MyViewModel, onLock: () -> Unit) {
val navController = rememberNavController()
val context = LocalContext.current
val focusMgr = LocalFocusManager.current
val lifecycleOwner = LocalLifecycleOwner.current
fun navigateUp() { navController.navigateUp() }
fun navigate(destination: Any) {
navController.navigate(destination) {
launchSingleTop = true
}
}
fun choosePackage() {
navController.navigate(ApplicationsList(false, true))
}
fun chooseSinglePackage() {
navController.navigate(ApplicationsList(false, false))
}
fun navigateToAppGroups() {
navController.navigate(ManageAppGroups)
}
LaunchedEffect(Unit) {
if(!Privilege.status.value.activated) {
navController.navigate(WorkModes(false)) {
popUpTo<Home> { inclusive = true }
}
}
}
@Suppress("NewApi") NavHost(
navController = navController,
startDestination = Home,
modifier = Modifier
.fillMaxSize()
.background(colorScheme.background)
.pointerInput(Unit) { detectTapGestures(onTap = { focusMgr.clearFocus() }) },
enterTransition = { NavTransition.enterTransition },
exitTransition = { NavTransition.exitTransition },
popEnterTransition = { NavTransition.popEnterTransition },
popExitTransition = { NavTransition.popExitTransition }
) {
composable<Home> { HomeScreen(::navigate) }
composable<WorkModes> {
WorkModesScreen(vm, it.toRoute(), ::navigateUp, {
navController.navigate(Home) {
popUpTo<WorkModes> { inclusive = true }
}
}, {
navController.navigate(WorkModes(false)) {
popUpTo(Home) { inclusive = true }
}
}, ::navigate)
}
composable<DhizukuServerSettings> {
DhizukuServerSettingsScreen(vm.dhizukuClients, vm::getDhizukuClients,
vm::updateDhizukuClient, vm::getDhizukuServerEnabled, vm::setDhizukuServerEnabled,
::navigateUp)
}
composable<DelegatedAdmins> {
DelegatedAdminsScreen(vm.delegatedAdmins, vm::getDelegatedAdmins, ::navigateUp, ::navigate)
}
composable<AddDelegatedAdmin>{
AddDelegatedAdminScreen(vm.chosenPackage, ::chooseSinglePackage, it.toRoute(),
vm::setDelegatedAdmin, ::navigateUp)
}
composable<DeviceInfo> { DeviceInfoScreen(vm, ::navigateUp) }
composable<LockScreenInfo> {
LockScreenInfoScreen(vm::getLockScreenInfo, vm::setLockScreenInfo, ::navigateUp)
}
composable<SupportMessage> {
SupportMessageScreen(vm::getShortSupportMessage, vm::getLongSupportMessage,
vm::setShortSupportMessage, vm::setLongSupportMessage, ::navigateUp)
}
composable<TransferOwnership> {
TransferOwnershipScreen(vm.deviceAdminReceivers, vm::getDeviceAdminReceivers,
vm::transferOwnership, ::navigateUp) {
navController.navigate(WorkModes(false)) {
popUpTo(Home) { inclusive = true }
}
}
}
composable<SystemManager> { SystemManagerScreen(vm, ::navigateUp, ::navigate) }
composable<SystemOptions> { SystemOptionsScreen(vm, ::navigateUp) }
composable<Keyguard> {
KeyguardScreen(vm::setKeyguardDisabled, vm::lockScreen, ::navigateUp)
}
composable<HardwareMonitor> {
HardwareMonitorScreen(vm.hardwareProperties, vm::getHardwareProperties,
vm::setHpRefreshInterval, ::navigateUp)
}
composable<DefaultInputMethod> {
DefaultInputMethodScreen(vm::getCurrentInputMethod, vm.inputMethodList,
vm::getInputMethods, vm::setDefaultInputMethod, ::navigateUp)
}
composable<ChangeTime> { ChangeTimeScreen(vm::setTime, ::navigateUp) }
composable<ChangeTimeZone> { ChangeTimeZoneScreen(vm::setTimeZone, ::navigateUp) }
composable<AutoTimePolicy> {
AutoTimePolicyScreen(vm::getAutoTimePolicy, vm::setAutoTimePolicy, ::navigateUp)
}
composable<AutoTimeZonePolicy> {
AutoTimeZonePolicyScreen(vm::getAutoTimeZonePolicy, vm::setAutoTimeZonePolicy,
::navigateUp)
}
//composable<> { KeyPairs(::navigateUp) }
composable<ContentProtectionPolicy> {
ContentProtectionPolicyScreen(vm::getContentProtectionPolicy,
vm::setContentProtectionPolicy, ::navigateUp)
}
composable<PermissionPolicy> {
PermissionPolicyScreen(vm::getPermissionPolicy, vm::setPermissionPolicy, ::navigateUp)
}
composable<MtePolicy> {
MtePolicyScreen(vm::getMtePolicy, vm::setMtePolicy, ::navigateUp)
}
composable<NearbyStreamingPolicy> {
NearbyStreamingPolicyScreen(vm::getNsAppPolicy, vm::setNsAppPolicy,
vm::getNsNotificationPolicy, vm::setNsNotificationPolicy, ::navigateUp)
}
composable<LockTaskMode> {
LockTaskModeScreen(
vm.chosenPackage, ::chooseSinglePackage, ::choosePackage, vm.lockTaskPackages,
vm::getLockTaskPackages, vm::setLockTaskPackage, vm::startLockTaskMode,
vm:: getLockTaskFeatures, vm::setLockTaskFeatures, ::navigateUp
)
}
composable<CaCert> {
CaCertScreen(vm.installedCaCerts, vm::getCaCerts, vm.selectedCaCert, vm::selectCaCert, vm::installCaCert, vm::parseCaCert,
vm::exportCaCert, vm::uninstallCaCert, vm::uninstallAllCaCerts, ::navigateUp)
}
composable<SecurityLogging> {
SecurityLoggingScreen(vm::getSecurityLoggingEnabled, vm::setSecurityLoggingEnabled,
vm::exportSecurityLogs, vm::getSecurityLogsCount, vm::deleteSecurityLogs,
vm::getPreRebootSecurityLogs, vm::exportPreRebootSecurityLogs, ::navigateUp)
}
composable<DisableAccountManagement> {
DisableAccountManagementScreen(vm.mdAccountTypes, vm::getMdAccountTypes,
vm::setMdAccountType, ::navigateUp)
}
composable<SetSystemUpdatePolicy> {
SystemUpdatePolicyScreen(vm::getSystemUpdatePolicy, vm::setSystemUpdatePolicy,
vm::getPendingSystemUpdate, ::navigateUp)
}
composable<InstallSystemUpdate> {
InstallSystemUpdateScreen(vm::installSystemUpdate, ::navigateUp)
}
composable<FrpPolicy> {
FrpPolicyScreen(vm.getFrpPolicy(), vm::setFrpPolicy, ::navigateUp)
}
composable<WipeData> { WipeDataScreen(vm::wipeData, ::navigateUp) }
composable<Network> { NetworkScreen(::navigateUp, ::navigate) }
composable<WiFi> {
WifiScreen(vm, ::navigateUp, ::navigate) { navController.navigate(UpdateNetwork(it)) }
}
composable<NetworkOptions> {
NetworkOptionsScreen(vm::getLanEnabled, vm::setLanEnabled, ::navigateUp)
}
composable<UpdateNetwork> {
val info = vm.configuredNetworks.collectAsStateWithLifecycle().value[
(it.toRoute() as UpdateNetwork).index
]
UpdateNetworkScreen(info, vm::setWifi, ::navigateUp)
}
composable<WifiSecurityLevel> {
WifiSecurityLevelScreen(vm::getMinimumWifiSecurityLevel,
vm::setMinimumWifiSecurityLevel, ::navigateUp)
}
composable<WifiSsidPolicyScreen> {
WifiSsidPolicyScreen(vm::getSsidPolicy, vm::setSsidPolicy, ::navigateUp)
}
composable<QueryNetworkStats> {
NetworkStatsScreen(vm.chosenPackage, ::chooseSinglePackage, vm::getPackageUid,
vm::queryNetworkStats, ::navigateUp) { navController.navigate(NetworkStatsViewer) }
}
composable<NetworkStatsViewer> {
NetworkStatsViewerScreen(vm.networkStatsData, vm::clearNetworkStats, ::navigateUp)
}
composable<PrivateDns> {
PrivateDnsScreen(vm::getPrivateDns, vm::setPrivateDns, ::navigateUp)
}
composable<AlwaysOnVpnPackage> {
AlwaysOnVpnPackageScreen(vm::getAlwaysOnVpnPackage, vm::getAlwaysOnVpnLockdown,
vm::setAlwaysOnVpn, vm.chosenPackage, ::chooseSinglePackage, ::navigateUp)
}
composable<RecommendedGlobalProxy> {
RecommendedGlobalProxyScreen(vm::setRecommendedGlobalProxy, ::navigateUp)
}
composable<NetworkLogging> {
NetworkLoggingScreen(vm::getNetworkLoggingEnabled, vm::setNetworkLoggingEnabled,
vm::getNetworkLogsCount, vm::exportNetworkLogs, vm::deleteNetworkLogs, ::navigateUp)
}
//composable<WifiAuthKeypair> { WifiAuthKeypairScreen(::navigateUp) }
composable<PreferentialNetworkService> {
PreferentialNetworkServiceScreen(vm::getPnsEnabled, vm::setPnsEnabled, vm.pnsConfigs,
vm::getPnsConfigs, ::navigateUp, ::navigate)
}
composable<AddPreferentialNetworkServiceConfig> {
val info = vm.pnsConfigs.collectAsStateWithLifecycle().value.getOrNull(
it.toRoute<AddPreferentialNetworkServiceConfig>().index
) ?: PreferentialNetworkServiceInfo()
AddPreferentialNetworkServiceConfigScreen(info, vm::setPnsConfig, ::navigateUp)
}
composable<OverrideApn> {
OverrideApnScreen(vm.apnConfigs, vm::getApnConfigs, vm::getApnEnabled,
vm::setApnEnabled, ::navigateUp) { navController.navigate(AddApnSetting(it)) }
}
composable<AddApnSetting> {
val origin = vm.apnConfigs.collectAsStateWithLifecycle().value.getOrNull((it.toRoute() as AddApnSetting).index)
AddApnSettingScreen(vm::setApnConfig, vm::removeApnConfig, origin, ::navigateUp)
}
composable<WorkProfile> { WorkProfileScreen(::navigateUp, ::navigate) }
composable<OrganizationOwnedProfile> {
OrganizationOwnedProfileScreen(vm::activateOrgProfileByShizuku, ::navigateUp)
}
composable<CreateWorkProfile> {
CreateWorkProfileScreen(vm::createWorkProfile, ::navigateUp)
}
composable<SuspendPersonalApp> {
SuspendPersonalAppScreen(
vm::getPersonalAppsSuspendedReason, vm::setPersonalAppsSuspended,
vm::getProfileMaxTimeOff, vm::setProfileMaxTimeOff, ::navigateUp
)
}
composable<CrossProfileIntentFilter> {
CrossProfileIntentFilterScreen(
vm::addCrossProfileIntentFilter, vm::clearCrossProfileIntentFilters,
vm::importCrossProfileIntentFilters, vm::exportCrossProfileIntentFilters,
::navigateUp
)
}
composable<DeleteWorkProfile> { DeleteWorkProfileScreen(vm::wipeData, ::navigateUp) }
composable<ApplicationsList> {
val params = it.toRoute<ApplicationsList>()
AppChooserScreen(
params, vm.installedPackages, vm.refreshPackagesProgress, { name ->
if (params.canSwitchView) {
if (name == null) {
navigateUp()
} else {
navigate(ApplicationDetails(name))
DisposableEffect(lifecycleOwner) {
val observer = LifecycleEventObserver { _, event ->
if (
(event == Lifecycle.Event.ON_CREATE && !SP.lockPasswordHash.isNullOrEmpty()) ||
(event == Lifecycle.Event.ON_RESUME && SP.lockWhenLeaving)
) {
appLockDialog = true
}
}
lifecycleOwner.lifecycle.addObserver(observer)
onDispose {
lifecycleOwner.lifecycle.removeObserver(observer)
}
} else {
if (name != null) vm.chosenPackage.trySend(name)
navigateUp()
}
}, {
SP.applicationsListView = false
navController.navigate(ApplicationsFeatures) {
popUpTo(Home)
LaunchedEffect(Unit) {
val profileNotActivated = !SP.managedProfileActivated && Privilege.status.value.work
if(profileNotActivated) {
Privilege.DPM.setProfileEnabled(Privilege.DAR)
SP.managedProfileActivated = true
context.popToast(R.string.work_profile_activated)
}
}
}, vm::refreshPackageList, vm::setPackageSuspended, vm::setPackageHidden)
}
composable<ApplicationsFeatures> {
ApplicationsFeaturesScreen(::navigateUp, ::navigate) {
SP.applicationsListView = true
navController.navigate(ApplicationsList(true, true)) {
popUpTo(Home)
DhizukuErrorDialog {
dhizukuErrorStatus.value = 0
Privilege.updateStatus()
backstack += Destination.WorkingModes(false)
repeat(backstack.size - 1) {
backstack.removeFirstOrNull()
}
}
}
}
composable<ApplicationDetails> {
ApplicationDetailsScreen(it.toRoute(), vm, ::navigateUp, ::navigate)
}
composable<Suspend> {
PackageFunctionScreen(
R.string.suspend, vm.suspendedPackages, vm::getSuspendedPackaged,
vm::setPackageSuspended, ::navigateUp, vm.chosenPackage, ::choosePackage,
::navigateToAppGroups, vm.appGroups, R.string.info_suspend_app
)
}
composable<Hide> {
PackageFunctionScreen(
R.string.hide, vm.hiddenPackages, vm::getHiddenPackages, vm::setPackageHidden,
::navigateUp, vm.chosenPackage, ::choosePackage, ::navigateToAppGroups, vm.appGroups
)
}
composable<BlockUninstall> {
PackageFunctionScreen(
R.string.block_uninstall, vm.ubPackages, vm::getUbPackages, vm::setPackageUb,
::navigateUp, vm.chosenPackage, ::choosePackage, ::navigateToAppGroups, vm.appGroups
)
}
composable<DisableUserControl> {
PackageFunctionScreen(
R.string.disable_user_control, vm.ucdPackages, vm::getUcdPackages,
vm::setPackageUcd, ::navigateUp, vm.chosenPackage, ::choosePackage,
::navigateToAppGroups, vm.appGroups, R.string.info_disable_user_control
)
}
composable<AppPermissionsManager> {
AppPermissionsManagerScreen(
vm::getPackagePermissions, vm::setPackagePermission, ::navigateUp, it.toRoute()
)
}
composable<PermissionManager> {
PermissionManagerScreen(::navigate, ::navigateUp)
}
composable<PermissionDetail> {
PermissionDetailScreen(
it.toRoute(), vm::getPermissionPackages, vm::setPackagePermission, ::navigateUp
)
}
composable<DisableMeteredData> {
PackageFunctionScreen(
R.string.disable_metered_data, vm.mddPackages, vm::getMddPackages,
vm::setPackageMdd, ::navigateUp, vm.chosenPackage, ::choosePackage,
::navigateToAppGroups, vm.appGroups
)
}
composable<ClearAppStorage> {
ClearAppStorageScreen(
vm.chosenPackage, ::chooseSinglePackage, vm::clearAppData, ::navigateUp
)
}
composable<UninstallApp> {
UninstallAppScreen(
vm.chosenPackage, ::chooseSinglePackage, vm::uninstallPackage, ::navigateUp
)
}
composable<KeepUninstalledPackages> {
PackageFunctionScreen(
R.string.keep_uninstalled_packages, vm.kuPackages, vm::getKuPackages,
vm::setPackageKu, ::navigateUp, vm.chosenPackage, ::choosePackage,
::navigateToAppGroups, vm.appGroups, R.string.info_keep_uninstalled_apps
)
}
composable<InstallExistingApp> {
InstallExistingAppScreen(
vm.chosenPackage, ::chooseSinglePackage, vm::installExistingApp, ::navigateUp
)
}
composable<CrossProfilePackages> {
PackageFunctionScreen(
R.string.cross_profile_apps, vm.cpPackages,
vm::getCpPackages, vm::setPackageCp, ::navigateUp, vm.chosenPackage,
::choosePackage, ::navigateToAppGroups, vm.appGroups
)
}
composable<CrossProfileWidgetProviders> {
PackageFunctionScreen(R.string.cross_profile_widget, vm.cpwProviders,
vm::getCpwProviders, vm::setCpwProvider, ::navigateUp, vm.chosenPackage,
::choosePackage, ::navigateToAppGroups, vm.appGroups)
}
composable<CredentialManagerPolicy> {
CredentialManagerPolicyScreen(
vm.chosenPackage, ::choosePackage, vm.cmPackages, vm::getCmPolicy,
vm::setCmPackage, vm::setCmPolicy, ::navigateUp
)
}
composable<PermittedAccessibilityServices> {
PermittedAsAndImPackages(
R.string.permitted_accessibility_services,
R.string.system_accessibility_always_allowed, vm.chosenPackage, ::choosePackage,
vm.pasPackages, vm::getPasPackages, vm::setPasPackage, vm::setPasPolicy,
::navigateUp
)
}
composable<PermittedInputMethods> {
PermittedAsAndImPackages(
R.string.permitted_ime, R.string.system_ime_always_allowed,
vm.chosenPackage, ::choosePackage, vm.pimPackages, vm::getPimPackages,
vm::setPimPackage, vm::setPimPolicy, ::navigateUp
)
}
composable<EnableSystemApp> {
EnableSystemAppScreen(
vm.chosenPackage, ::chooseSinglePackage, vm::enableSystemApp, ::navigateUp
)
}
composable<SetDefaultDialer> {
SetDefaultDialerScreen(
vm.chosenPackage, ::chooseSinglePackage, vm::setDefaultDialer, ::navigateUp
)
}
composable<ManagedConfiguration> {
ManagedConfigurationScreen(
it.toRoute(), vm.appRestrictions, vm::setAppRestrictions,
vm::clearAppRestrictions, ::navigateUp
)
}
composable<ManageAppGroups> {
ManageAppGroupsScreen(
vm.appGroups, vm::exportAppGroups, vm::importAppGroups,
{ id, name, apps -> navController.navigate(EditAppGroup(id, name, apps)) },
::navigateUp
)
}
composable<EditAppGroup> {
EditAppGroupScreen(
it.toRoute(), vm::getAppInfo, ::navigateUp, vm::setAppGroup,
vm::deleteAppGroup, ::choosePackage, vm.chosenPackage
)
}
composable<UserRestriction> {
UserRestrictionScreen(vm::getUserRestrictions, ::navigateUp, ::navigate)
}
composable<UserRestrictionEditor> {
UserRestrictionEditorScreen(vm.userRestrictions, vm::setUserRestriction, ::navigateUp)
}
composable<UserRestrictionOptions> {
UserRestrictionOptionsScreen(it.toRoute(), vm.userRestrictions,
vm::setUserRestriction, vm::createUserRestrictionShortcut, ::navigateUp)
}
composable<Users> { UsersScreen(vm, ::navigateUp, ::navigate) }
composable<UserInfo> { UserInfoScreen(vm::getUserInformation, ::navigateUp) }
composable<UsersOptions> {
UsersOptionsScreen(vm::getLogoutEnabled, vm::setLogoutEnabled, ::navigateUp)
}
composable<UserOperation> {
UserOperationScreen(vm::getUserIdentifiers, vm::doUserOperation,
vm::createUserOperationShortcut, ::navigateUp)
}
composable<CreateUser> { CreateUserScreen(vm::createUser, ::navigateUp) }
composable<ChangeUsername> { ChangeUsernameScreen(vm::setProfileName, ::navigateUp) }
composable<UserSessionMessage> {
UserSessionMessageScreen(vm::getUserSessionMessages, vm::setStartUserSessionMessage,
vm::setEndUserSessionMessage, ::navigateUp)
}
composable<AffiliationId> {
AffiliationIdScreen(vm.affiliationIds, vm::getAffiliationIds, vm::setAffiliationId,
::navigateUp)
}
composable<Password> { PasswordScreen(vm, ::navigateUp, ::navigate) }
composable<PasswordInfo> {
PasswordInfoScreen(vm::getPasswordComplexity, vm::isPasswordComplexitySufficient,
vm::isUsingUnifiedPassword, ::navigateUp)
}
composable<ResetPasswordToken> {
ResetPasswordTokenScreen(vm::getRpTokenState, vm::setRpToken,
vm::createActivateRpTokenIntent, vm::clearRpToken, ::navigateUp)
}
composable<ResetPassword> { ResetPasswordScreen(vm::resetPassword, ::navigateUp) }
composable<RequiredPasswordComplexity> {
RequiredPasswordComplexityScreen(vm::getRequiredPasswordComplexity,
vm::setRequiredPasswordComplexity, ::navigateUp)
}
composable<KeyguardDisabledFeatures> {
KeyguardDisabledFeaturesScreen(vm::getKeyguardDisableConfig,
vm::setKeyguardDisableConfig, ::navigateUp)
}
composable<RequiredPasswordQuality> { RequiredPasswordQualityScreen(::navigateUp) }
composable<Settings> { SettingsScreen(::navigateUp, ::navigate) }
composable<SettingsOptions> {
SettingsOptionsScreen(vm::getDisplayDangerousFeatures, vm::getShortcutsEnabled,
vm::setDisplayDangerousFeatures, vm::setShortcutsEnabled, ::navigateUp)
}
composable<Appearance> {
AppearanceScreen(::navigateUp, vm.theme, vm::changeTheme)
}
composable<AppLockSettings> {
AppLockSettingsScreen(vm.getAppLockConfig(), vm::setAppLockConfig, ::navigateUp)
}
composable<ApiSettings> {
ApiSettings(vm::getApiEnabled, vm::setApiKey, ::navigateUp)
}
composable<Notifications> {
NotificationsScreen(vm.enabledNotifications, vm::getEnabledNotifications,
vm::setNotificationEnabled, ::navigateUp)
}
composable<About> { AboutScreen(::navigateUp) }
}
DisposableEffect(lifecycleOwner) {
val observer = LifecycleEventObserver { _, event ->
if (
(event == Lifecycle.Event.ON_CREATE && !SP.lockPasswordHash.isNullOrEmpty()) ||
(event == Lifecycle.Event.ON_RESUME && SP.lockWhenLeaving)
) {
onLock()
}
}
lifecycleOwner.lifecycle.addObserver(observer)
onDispose {
lifecycleOwner.lifecycle.removeObserver(observer)
}
}
LaunchedEffect(Unit) {
val profileNotActivated = !SP.managedProfileActivated && Privilege.status.value.work
if(profileNotActivated) {
Privilege.DPM.setProfileEnabled(Privilege.DAR)
SP.managedProfileActivated = true
context.popToast(R.string.work_profile_activated)
}
}
DhizukuErrorDialog {
dhizukuErrorStatus.value = 0
Privilege.updateStatus()
navController.navigate(WorkModes(false)) {
popUpTo<Home> { inclusive = true }
launchSingleTop = true
}
}
}
@Serializable private object Home
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun HomeScreen(onNavigate: (Any) -> Unit) {
val privilege by Privilege.status.collectAsStateWithLifecycle()
val sb = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
Scaffold(
Modifier.nestedScroll(sb.nestedScrollConnection),
topBar = {
LargeTopAppBar(
{ Text(stringResource(R.string.app_name)) },
actions = {
IconButton({ onNavigate(WorkModes(true)) }) { Icon(painterResource(R.drawable.security_fill0), null) }
IconButton({ onNavigate(Settings) }) { Icon(Icons.Default.Settings, null) }
},
scrollBehavior = sb
)
},
contentWindowInsets = adaptiveInsets()
) {
Column(Modifier
.fillMaxSize()
.padding(it)
.verticalScroll(rememberScrollState())) {
if(privilege.device || privilege.profile) {
HomePageItem(R.string.system, R.drawable.android_fill0) { onNavigate(SystemManager) }
HomePageItem(R.string.network, R.drawable.wifi_fill0) { onNavigate(Network) }
}
if(privilege.work) {
HomePageItem(R.string.work_profile, R.drawable.work_fill0) {
onNavigate(WorkProfile)
}
}
if(privilege.device || privilege.profile) {
HomePageItem(R.string.applications, R.drawable.apps_fill0) {
onNavigate(
if (SP.applicationsListView) ApplicationsList(true, true)
else ApplicationsFeatures
)
}
if(VERSION.SDK_INT >= 24) {
HomePageItem(R.string.user_restriction, R.drawable.person_off) { onNavigate(UserRestriction) }
}
HomePageItem(R.string.users,R.drawable.manage_accounts_fill0) { onNavigate(Users) }
HomePageItem(R.string.password_and_keyguard, R.drawable.password_fill0) { onNavigate(Password) }
}
Spacer(Modifier.height(BottomPadding))
}
}
}
@Composable
fun HomePageItem(name: Int, imgVector: Int, onClick: () -> Unit) {
Row(
modifier = Modifier
.fillMaxWidth()
.clickable(onClick = onClick)
.padding(vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically
) {
Spacer(Modifier.padding(start = 30.dp))
Icon(
painter = painterResource(imgVector),
contentDescription = null
)
Spacer(Modifier.padding(start = 15.dp))
Text(
text = stringResource(name),
style = typography.headlineSmall,
modifier = Modifier.padding(bottom = if(zhCN) { 2 } else { 0 }.dp)
)
}
}

View File

@@ -9,8 +9,8 @@ import androidx.annotation.RequiresApi
import androidx.core.database.getIntOrNull
import androidx.core.database.getLongOrNull
import androidx.core.database.getStringOrNull
import com.bintianqi.owndroid.dpm.AppGroup
import com.bintianqi.owndroid.dpm.IntentFilterOptions
import com.bintianqi.owndroid.ui.screen.AppGroup
import com.bintianqi.owndroid.ui.screen.IntentFilterOptions
import com.bintianqi.owndroid.dpm.NetworkLog
import com.bintianqi.owndroid.dpm.SecurityEvent
import com.bintianqi.owndroid.dpm.SecurityEventWithData

View File

@@ -60,57 +60,58 @@ import androidx.lifecycle.viewModelScope
import com.bintianqi.owndroid.Privilege.DAR
import com.bintianqi.owndroid.Privilege.DPM
import com.bintianqi.owndroid.dpm.ACTIVATE_DEVICE_OWNER_COMMAND
import com.bintianqi.owndroid.dpm.ApnAuthType
import com.bintianqi.owndroid.dpm.ApnConfig
import com.bintianqi.owndroid.dpm.ApnMvnoType
import com.bintianqi.owndroid.dpm.ApnProtocol
import com.bintianqi.owndroid.dpm.AppGroup
import com.bintianqi.owndroid.dpm.AppRestriction
import com.bintianqi.owndroid.dpm.AppStatus
import com.bintianqi.owndroid.dpm.BasicAppGroup
import com.bintianqi.owndroid.dpm.CaCertInfo
import com.bintianqi.owndroid.dpm.CreateUserResult
import com.bintianqi.owndroid.dpm.CreateWorkProfileOptions
import com.bintianqi.owndroid.dpm.DelegatedAdmin
import com.bintianqi.owndroid.dpm.DeviceAdmin
import com.bintianqi.owndroid.dpm.FrpPolicyInfo
import com.bintianqi.owndroid.dpm.HardwareProperties
import com.bintianqi.owndroid.dpm.IntentFilterOptions
import com.bintianqi.owndroid.dpm.IpMode
import com.bintianqi.owndroid.dpm.KeyguardDisableConfig
import com.bintianqi.owndroid.dpm.KeyguardDisableMode
import com.bintianqi.owndroid.dpm.NetworkStatsData
import com.bintianqi.owndroid.dpm.NetworkStatsTarget
import com.bintianqi.owndroid.dpm.PasswordComplexity
import com.bintianqi.owndroid.dpm.PendingSystemUpdateInfo
import com.bintianqi.owndroid.dpm.PreferentialNetworkServiceInfo
import com.bintianqi.owndroid.dpm.PrivateDnsConfiguration
import com.bintianqi.owndroid.dpm.PrivateDnsMode
import com.bintianqi.owndroid.dpm.ProxyMode
import com.bintianqi.owndroid.dpm.ProxyType
import com.bintianqi.owndroid.dpm.QueryNetworkStatsParams
import com.bintianqi.owndroid.dpm.RecommendedProxyConf
import com.bintianqi.owndroid.dpm.RpTokenState
import com.bintianqi.owndroid.dpm.SsidPolicy
import com.bintianqi.owndroid.dpm.SsidPolicyType
import com.bintianqi.owndroid.dpm.SystemOptionsStatus
import com.bintianqi.owndroid.dpm.SystemUpdatePolicyInfo
import com.bintianqi.owndroid.dpm.UserIdentifier
import com.bintianqi.owndroid.dpm.UserInformation
import com.bintianqi.owndroid.dpm.UserOperationType
import com.bintianqi.owndroid.dpm.WifiInfo
import com.bintianqi.owndroid.dpm.WifiSecurity
import com.bintianqi.owndroid.dpm.WifiStatus
import com.bintianqi.owndroid.dpm.activateOrgProfileCommand
import com.bintianqi.owndroid.dpm.delegatedScopesList
import com.bintianqi.owndroid.dpm.doUserOperationWithContext
import com.bintianqi.owndroid.dpm.getPackageInstaller
import com.bintianqi.owndroid.dpm.globalSettings
import com.bintianqi.owndroid.dpm.handlePrivilegeChange
import com.bintianqi.owndroid.dpm.parsePackageInstallerMessage
import com.bintianqi.owndroid.dpm.runtimePermissions
import com.bintianqi.owndroid.dpm.secureSettings
import com.bintianqi.owndroid.dpm.temperatureTypes
import com.bintianqi.owndroid.ui.screen.ApnAuthType
import com.bintianqi.owndroid.ui.screen.ApnConfig
import com.bintianqi.owndroid.ui.screen.ApnMvnoType
import com.bintianqi.owndroid.ui.screen.ApnProtocol
import com.bintianqi.owndroid.ui.screen.AppGroup
import com.bintianqi.owndroid.ui.screen.AppLockConfig
import com.bintianqi.owndroid.ui.screen.AppRestriction
import com.bintianqi.owndroid.ui.screen.AppStatus
import com.bintianqi.owndroid.ui.screen.BasicAppGroup
import com.bintianqi.owndroid.ui.screen.CaCertInfo
import com.bintianqi.owndroid.ui.screen.CreateUserResult
import com.bintianqi.owndroid.ui.screen.CreateWorkProfileOptions
import com.bintianqi.owndroid.ui.screen.FrpPolicyInfo
import com.bintianqi.owndroid.ui.screen.HardwareProperties
import com.bintianqi.owndroid.ui.screen.IntentFilterOptions
import com.bintianqi.owndroid.ui.screen.IpMode
import com.bintianqi.owndroid.ui.screen.KeyguardDisableConfig
import com.bintianqi.owndroid.ui.screen.KeyguardDisableMode
import com.bintianqi.owndroid.ui.screen.NetworkStatsData
import com.bintianqi.owndroid.ui.screen.NetworkStatsTarget
import com.bintianqi.owndroid.ui.screen.PasswordComplexity
import com.bintianqi.owndroid.ui.screen.PendingSystemUpdateInfo
import com.bintianqi.owndroid.ui.screen.PreferentialNetworkServiceInfo
import com.bintianqi.owndroid.ui.screen.PrivateDnsConfiguration
import com.bintianqi.owndroid.ui.screen.PrivateDnsMode
import com.bintianqi.owndroid.ui.screen.ProxyMode
import com.bintianqi.owndroid.ui.screen.ProxyType
import com.bintianqi.owndroid.ui.screen.QueryNetworkStatsParams
import com.bintianqi.owndroid.ui.screen.RecommendedProxyConf
import com.bintianqi.owndroid.ui.screen.RpTokenState
import com.bintianqi.owndroid.ui.screen.SsidPolicy
import com.bintianqi.owndroid.ui.screen.SsidPolicyType
import com.bintianqi.owndroid.ui.screen.SystemOptionsStatus
import com.bintianqi.owndroid.ui.screen.SystemUpdatePolicyInfo
import com.bintianqi.owndroid.ui.screen.UserIdentifier
import com.bintianqi.owndroid.ui.screen.UserInformation
import com.bintianqi.owndroid.ui.screen.UserOperationType
import com.bintianqi.owndroid.ui.screen.WifiInfo
import com.bintianqi.owndroid.ui.screen.WifiSecurity
import com.bintianqi.owndroid.ui.screen.WifiStatus
import com.bintianqi.owndroid.ui.screen.activateOrgProfileCommand
import com.bintianqi.owndroid.ui.screen.delegatedScopesList
import com.bintianqi.owndroid.ui.screen.globalSettings
import com.bintianqi.owndroid.ui.screen.secureSettings
import com.bintianqi.owndroid.ui.screen.temperatureTypes
import com.rosan.dhizuku.api.Dhizuku
import com.rosan.dhizuku.api.DhizukuRequestPermissionListener
import com.topjohnwu.superuser.Shell
@@ -159,7 +160,9 @@ class MyViewModel(application: Application): AndroidViewModel(application) {
}
fun getAppLockConfig(): AppLockConfig {
val passwordHash = SP.lockPasswordHash
return AppLockConfig(passwordHash?.ifEmpty { null }, SP.biometricsUnlock, SP.lockWhenLeaving)
return AppLockConfig(
passwordHash?.ifEmpty { null }, SP.biometricsUnlock, SP.lockWhenLeaving
)
}
fun setAppLockConfig(config: AppLockConfig) {
if (config.password == null) {

View File

@@ -63,9 +63,9 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.bintianqi.owndroid.ui.navigation.Destination
import com.google.accompanist.drawablepainter.rememberDrawablePainter
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.serialization.Serializable
data class AppInfo(
val name: String,
@@ -77,12 +77,10 @@ data class AppInfo(
private fun searchInString(query: String, content: String)
= query.split(' ').all { content.contains(it, true) }
@Serializable data class ApplicationsList(val canSwitchView: Boolean, val multiSelect: Boolean)
@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class)
@Composable
fun AppChooserScreen(
params: ApplicationsList, packageList: MutableStateFlow<List<AppInfo>>,
params: Destination.ApplicationsList, packageList: MutableStateFlow<List<AppInfo>>,
refreshProgress: MutableStateFlow<Float>, onChoosePackage: (String?) -> Unit,
onSwitchView: () -> Unit, onRefresh: () -> Unit,
setPackagesSuspend: (List<String>, Boolean) -> Unit,

View File

@@ -5,7 +5,7 @@ import android.content.Intent
import androidx.core.content.pm.ShortcutInfoCompat
import androidx.core.content.pm.ShortcutManagerCompat
import androidx.core.graphics.drawable.IconCompat
import com.bintianqi.owndroid.dpm.UserOperationType
import com.bintianqi.owndroid.ui.screen.UserOperationType
object ShortcutUtils {
fun setAllShortcuts(context: Context, enabled: Boolean) {

View File

@@ -3,7 +3,7 @@ package com.bintianqi.owndroid
import android.app.Activity
import android.os.Bundle
import android.util.Log
import com.bintianqi.owndroid.dpm.UserOperationType
import com.bintianqi.owndroid.ui.screen.UserOperationType
import com.bintianqi.owndroid.dpm.doUserOperationWithContext
class ShortcutsReceiverActivity : Activity() {

View File

@@ -2,7 +2,7 @@ package com.bintianqi.owndroid
import android.os.Build
import android.os.UserManager
import com.bintianqi.owndroid.dpm.Restriction
import com.bintianqi.owndroid.ui.screen.Restriction
@Suppress("InlinedApi")
object UserRestrictionsRepository {

View File

@@ -7,6 +7,7 @@ import android.app.admin.DevicePolicyManager
import android.app.admin.DnsEvent
import android.app.admin.IDevicePolicyManager
import android.app.admin.SecurityLog
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.IPackageInstaller
@@ -16,6 +17,7 @@ import android.os.UserHandle
import android.os.UserManager
import android.util.Log
import androidx.annotation.RequiresApi
import com.bintianqi.owndroid.AppInfo
import com.bintianqi.owndroid.MyApplication
import com.bintianqi.owndroid.NotificationType
import com.bintianqi.owndroid.NotificationUtils
@@ -25,6 +27,7 @@ import com.bintianqi.owndroid.Privilege.DPM
import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.SP
import com.bintianqi.owndroid.ShortcutUtils
import com.bintianqi.owndroid.ui.screen.UserOperationType
import com.rosan.dhizuku.api.Dhizuku
import com.rosan.dhizuku.api.DhizukuBinderWrapper
import kotlinx.coroutines.CoroutineScope
@@ -589,3 +592,9 @@ fun doUserOperationWithContext(
UserOperationType.Delete -> DPM.removeUser(DAR, handle)
}
}
const val ACTIVATE_DEVICE_OWNER_COMMAND = "dpm set-device-owner com.bintianqi.owndroid/.Receiver"
data class DelegatedAdmin(val app: AppInfo, val scopes: List<String>)
data class DeviceAdmin(val app: AppInfo, val admin: ComponentName)

View File

@@ -17,9 +17,12 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.automirrored.filled.List
import androidx.compose.material.icons.outlined.Info
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Card
@@ -31,6 +34,7 @@ import androidx.compose.material3.IconButton
import androidx.compose.material3.LargeTopAppBar
import androidx.compose.material3.MaterialTheme.colorScheme
import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Switch
@@ -48,14 +52,18 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import com.bintianqi.owndroid.HorizontalPadding
import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.adaptiveInsets
import com.bintianqi.owndroid.ui.screen.isValidPackageName
import com.bintianqi.owndroid.zhCN
@Composable
@@ -398,3 +406,25 @@ fun CircularProgressDialog(onDismiss: () -> Unit) {
}
}
}
@Composable
fun PackageNameTextField(
value: String, onChoosePackage: () -> Unit,
modifier: Modifier = Modifier, onValueChange: (String) -> Unit
) {
val fm = LocalFocusManager.current
OutlinedTextField(
value, onValueChange, Modifier
.fillMaxWidth()
.then(modifier),
label = { Text(stringResource(R.string.package_name)) },
trailingIcon = {
IconButton(onChoosePackage) {
Icon(Icons.AutoMirrored.Default.List, null)
}
},
isError = value.isNotEmpty() && !value.isValidPackageName,
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Ascii, imeAction = ImeAction.Done),
keyboardActions = KeyboardActions { fm.clearFocus() }
)
}

View File

@@ -10,6 +10,7 @@ import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideOutHorizontally
import androidx.compose.animation.togetherWith
/**
* Learned from AOSP's Activity animation
@@ -42,4 +43,7 @@ object NavTransition {
) + fadeOut(
tween(83, 35, LinearEasing)
)
val transition = enterTransition togetherWith exitTransition
val popTransition = popEnterTransition togetherWith popExitTransition
}

View File

@@ -0,0 +1,127 @@
package com.bintianqi.owndroid.ui.navigation
import androidx.navigation3.runtime.NavKey
import kotlinx.serialization.Serializable
sealed class Destination : NavKey {
@Serializable object Home : Destination()
@Serializable class WorkingModes(val canNavigateUp: Boolean) : Destination()
@Serializable object DhizukuServerSettings : Destination()
@Serializable object DelegatedAdmins : Destination()
@Serializable class DelegatedAdminDetails(
val pkg: String, val scopes: List<String>
) : Destination()
@Serializable object TransferOwnership : Destination()
@Serializable object System : Destination()
@Serializable object SystemOptions : Destination()
@Serializable object Keyguard : Destination()
@Serializable object HardwareMonitor : Destination()
@Serializable object DefaultInputMethod : Destination()
@Serializable object ChangeTime : Destination()
@Serializable object ChangeTimezone : Destination()
@Serializable object AutoTimePolicy : Destination()
@Serializable object AutoTimezonePolicy : Destination()
@Serializable object ContentProtectionPolicy : Destination()
@Serializable object PermissionPolicy : Destination()
@Serializable object MtePolicy : Destination()
@Serializable object NearbyStreamingPolicy : Destination()
@Serializable object LockTaskMode : Destination()
@Serializable object CaCert : Destination()
@Serializable object SecurityLogging : Destination()
@Serializable object DeviceInfo : Destination()
@Serializable object LockScreenInfo : Destination()
@Serializable object SupportMessage : Destination()
@Serializable object DisableAccountManagement : Destination()
@Serializable object FrpPolicy : Destination()
@Serializable object WipeData : Destination()
@Serializable object SystemUpdatePolicy : Destination()
@Serializable object InstallSystemUpdate : Destination()
@Serializable object Network : Destination()
@Serializable object NetworkOptions : Destination()
@Serializable object WiFi : Destination()
@Serializable class UpdateNetwork(val index: Int) : Destination()
@Serializable object WifiSecurityLevel : Destination()
@Serializable object WifiSsidPolicy : Destination()
@Serializable object NetworkStats : Destination()
@Serializable object NetworkStatsViewer : Destination()
@Serializable object PrivateDns : Destination()
@Serializable object AlwaysOnVpnPackage : Destination()
@Serializable object RecommendedGlobalProxy : Destination()
@Serializable object NetworkLogging : Destination()
@Serializable object PreferentialNetworkService : Destination()
@Serializable class AddPreferentialNetworkServiceConfig(val index: Int) : Destination()
@Serializable object OverrideApn : Destination()
@Serializable data class AddApnSetting(val index: Int) : Destination()
@Serializable object WorkProfile : Destination()
@Serializable object CreateWorkProfile : Destination()
@Serializable object OrganizationOwnedProfile : Destination()
@Serializable object SuspendPersonalApp : Destination()
@Serializable object CrossProfileIntentFilter : Destination()
@Serializable object DeleteWorkProfile : Destination()
@Serializable object ApplicationFunctions : Destination()
@Serializable object Suspend : Destination()
@Serializable object Hide : Destination()
@Serializable object BlockUninstall : Destination()
@Serializable object DisableUserControl : Destination()
@Serializable object PermissionManager : Destination()
@Serializable class PermissionDetail(val permission: String) : Destination()
@Serializable object DisableMeteredData : Destination()
@Serializable object ClearAppStorage : Destination()
@Serializable object UninstallApp : Destination()
@Serializable object KeepUninstalledPackages : Destination()
@Serializable object InstallExistingApp : Destination()
@Serializable object CrossProfilePackages : Destination()
@Serializable object CrossProfileWidgetProviders : Destination()
@Serializable object CredentialManagerPolicy : Destination()
@Serializable object PermittedAccessibilityServices : Destination()
@Serializable object PermittedInputMethods : Destination()
@Serializable object EnableSystemApp : Destination()
@Serializable object SetDefaultDialer : Destination()
@Serializable object ManageAppGroups : Destination()
@Serializable class EditAppGroup(
val id: Int?, val name: String, val apps: List<String>
) : Destination()
@Serializable class ApplicationDetails(val packageName: String) : Destination()
@Serializable class AppPermissionsManager(val packageName: String) : Destination()
@Serializable class ManagedConfiguration(val packageName: String) : Destination()
@Serializable data class ApplicationsList(
val canSwitchView: Boolean, val multiSelect: Boolean
) : Destination()
@Serializable object UserRestriction : Destination()
@Serializable data class UserRestrictionOptions(val id: String) : Destination()
@Serializable object UserRestrictionEditor : Destination()
@Serializable object Users : Destination()
@Serializable object UsersOptions : Destination()
@Serializable object UserInfo : Destination()
@Serializable object UserOperation : Destination()
@Serializable object CreateUser : Destination()
@Serializable object AffiliationId : Destination()
@Serializable object ChangeUsername : Destination()
@Serializable object UserSessionMessage : Destination()
@Serializable object Password : Destination()
@Serializable object PasswordInfo : Destination()
@Serializable object ResetPasswordToken : Destination()
@Serializable object ResetPassword : Destination()
@Serializable object RequiredPasswordComplexity : Destination()
@Serializable object KeyguardDisabledFeatures : Destination()
@Serializable object RequiredPasswordQuality : Destination()
@Serializable object Settings : Destination()
@Serializable object SettingsOptions : Destination()
@Serializable object AppearanceSettings : Destination()
@Serializable object AppLockSettings : Destination()
@Serializable object ApiSettings : Destination()
@Serializable object NotificationSettings : Destination()
@Serializable object About : Destination()
}

View File

@@ -0,0 +1,557 @@
package com.bintianqi.owndroid.ui.navigation
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation3.runtime.NavBackStack
import androidx.navigation3.runtime.NavEntry
import androidx.navigation3.runtime.NavKey
import androidx.navigation3.runtime.entryProvider
import com.bintianqi.owndroid.AppChooserScreen
import com.bintianqi.owndroid.MyViewModel
import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.SP
import com.bintianqi.owndroid.ui.screen.AboutScreen
import com.bintianqi.owndroid.ui.screen.AddApnSettingScreen
import com.bintianqi.owndroid.ui.screen.AddDelegatedAdminScreen
import com.bintianqi.owndroid.ui.screen.AddPreferentialNetworkServiceConfigScreen
import com.bintianqi.owndroid.ui.screen.AffiliationIdScreen
import com.bintianqi.owndroid.ui.screen.AlwaysOnVpnPackageScreen
import com.bintianqi.owndroid.ui.screen.ApiSettings
import com.bintianqi.owndroid.ui.screen.AppLockSettingsScreen
import com.bintianqi.owndroid.ui.screen.AppPermissionsManagerScreen
import com.bintianqi.owndroid.ui.screen.AppearanceScreen
import com.bintianqi.owndroid.ui.screen.ApplicationDetailsScreen
import com.bintianqi.owndroid.ui.screen.ApplicationsFeaturesScreen
import com.bintianqi.owndroid.ui.screen.AutoTimePolicyScreen
import com.bintianqi.owndroid.ui.screen.AutoTimeZonePolicyScreen
import com.bintianqi.owndroid.ui.screen.CaCertScreen
import com.bintianqi.owndroid.ui.screen.ChangeTimeScreen
import com.bintianqi.owndroid.ui.screen.ChangeTimeZoneScreen
import com.bintianqi.owndroid.ui.screen.ChangeUsernameScreen
import com.bintianqi.owndroid.ui.screen.ClearAppStorageScreen
import com.bintianqi.owndroid.ui.screen.ContentProtectionPolicyScreen
import com.bintianqi.owndroid.ui.screen.CreateUserScreen
import com.bintianqi.owndroid.ui.screen.CreateWorkProfileScreen
import com.bintianqi.owndroid.ui.screen.CredentialManagerPolicyScreen
import com.bintianqi.owndroid.ui.screen.CrossProfileIntentFilterScreen
import com.bintianqi.owndroid.ui.screen.DefaultInputMethodScreen
import com.bintianqi.owndroid.ui.screen.DelegatedAdminsScreen
import com.bintianqi.owndroid.ui.screen.DeleteWorkProfileScreen
import com.bintianqi.owndroid.ui.screen.DeviceInfoScreen
import com.bintianqi.owndroid.ui.screen.DhizukuServerSettingsScreen
import com.bintianqi.owndroid.ui.screen.DisableAccountManagementScreen
import com.bintianqi.owndroid.ui.screen.EditAppGroupScreen
import com.bintianqi.owndroid.ui.screen.EnableSystemAppScreen
import com.bintianqi.owndroid.ui.screen.FrpPolicyScreen
import com.bintianqi.owndroid.ui.screen.HardwareMonitorScreen
import com.bintianqi.owndroid.ui.screen.HomeScreen
import com.bintianqi.owndroid.ui.screen.InstallExistingAppScreen
import com.bintianqi.owndroid.ui.screen.InstallSystemUpdateScreen
import com.bintianqi.owndroid.ui.screen.KeyguardDisabledFeaturesScreen
import com.bintianqi.owndroid.ui.screen.KeyguardScreen
import com.bintianqi.owndroid.ui.screen.LockScreenInfoScreen
import com.bintianqi.owndroid.ui.screen.LockTaskModeScreen
import com.bintianqi.owndroid.ui.screen.ManageAppGroupsScreen
import com.bintianqi.owndroid.ui.screen.ManagedConfigurationScreen
import com.bintianqi.owndroid.ui.screen.MtePolicyScreen
import com.bintianqi.owndroid.ui.screen.NearbyStreamingPolicyScreen
import com.bintianqi.owndroid.ui.screen.NetworkLoggingScreen
import com.bintianqi.owndroid.ui.screen.NetworkOptionsScreen
import com.bintianqi.owndroid.ui.screen.NetworkScreen
import com.bintianqi.owndroid.ui.screen.NetworkStatsScreen
import com.bintianqi.owndroid.ui.screen.NetworkStatsViewerScreen
import com.bintianqi.owndroid.ui.screen.NotificationsScreen
import com.bintianqi.owndroid.ui.screen.OrganizationOwnedProfileScreen
import com.bintianqi.owndroid.ui.screen.OverrideApnScreen
import com.bintianqi.owndroid.ui.screen.PackageFunctionScreen
import com.bintianqi.owndroid.ui.screen.PasswordInfoScreen
import com.bintianqi.owndroid.ui.screen.PasswordScreen
import com.bintianqi.owndroid.ui.screen.PermissionDetailScreen
import com.bintianqi.owndroid.ui.screen.PermissionManagerScreen
import com.bintianqi.owndroid.ui.screen.PermissionPolicyScreen
import com.bintianqi.owndroid.ui.screen.PermittedAsAndImPackages
import com.bintianqi.owndroid.ui.screen.PreferentialNetworkServiceInfo
import com.bintianqi.owndroid.ui.screen.PreferentialNetworkServiceScreen
import com.bintianqi.owndroid.ui.screen.PrivateDnsScreen
import com.bintianqi.owndroid.ui.screen.RecommendedGlobalProxyScreen
import com.bintianqi.owndroid.ui.screen.RequiredPasswordComplexityScreen
import com.bintianqi.owndroid.ui.screen.RequiredPasswordQualityScreen
import com.bintianqi.owndroid.ui.screen.ResetPasswordScreen
import com.bintianqi.owndroid.ui.screen.ResetPasswordTokenScreen
import com.bintianqi.owndroid.ui.screen.SecurityLoggingScreen
import com.bintianqi.owndroid.ui.screen.SetDefaultDialerScreen
import com.bintianqi.owndroid.ui.screen.SettingsOptionsScreen
import com.bintianqi.owndroid.ui.screen.SettingsScreen
import com.bintianqi.owndroid.ui.screen.SupportMessageScreen
import com.bintianqi.owndroid.ui.screen.SuspendPersonalAppScreen
import com.bintianqi.owndroid.ui.screen.SystemManagerScreen
import com.bintianqi.owndroid.ui.screen.SystemOptionsScreen
import com.bintianqi.owndroid.ui.screen.SystemUpdatePolicyScreen
import com.bintianqi.owndroid.ui.screen.TransferOwnershipScreen
import com.bintianqi.owndroid.ui.screen.UninstallAppScreen
import com.bintianqi.owndroid.ui.screen.UpdateNetworkScreen
import com.bintianqi.owndroid.ui.screen.UserInfoScreen
import com.bintianqi.owndroid.ui.screen.UserOperationScreen
import com.bintianqi.owndroid.ui.screen.UserRestrictionEditorScreen
import com.bintianqi.owndroid.ui.screen.UserRestrictionOptionsScreen
import com.bintianqi.owndroid.ui.screen.UserRestrictionScreen
import com.bintianqi.owndroid.ui.screen.UserSessionMessageScreen
import com.bintianqi.owndroid.ui.screen.UsersOptionsScreen
import com.bintianqi.owndroid.ui.screen.UsersScreen
import com.bintianqi.owndroid.ui.screen.WifiScreen
import com.bintianqi.owndroid.ui.screen.WifiSecurityLevelScreen
import com.bintianqi.owndroid.ui.screen.WifiSsidPolicyScreen
import com.bintianqi.owndroid.ui.screen.WipeDataScreen
import com.bintianqi.owndroid.ui.screen.WorkModesScreen
import com.bintianqi.owndroid.ui.screen.WorkProfileScreen
fun myEntryProvider(
destination: Destination, backstack: NavBackStack<NavKey>, vm: MyViewModel
) = entryProvider {
fun navigate(dest: Destination) {
backstack += dest
}
fun navigateUp() {
backstack.removeLastOrNull()
}
fun navigateToAppGroups() {
navigate(Destination.ManageAppGroups)
}
fun navigateAndPopAll(dest: Destination) {
navigate(dest)
repeat(backstack.size - 1) {
backstack.removeFirst()
}
}
fun choosePackage() {
navigate(Destination.ApplicationsList(false, true))
}
fun chooseSinglePackage() {
navigate(Destination.ApplicationsList(false, false))
}
entry<Destination.Home> {
HomeScreen(::navigate)
}
entry<Destination.WorkingModes> {
WorkModesScreen(vm, it, ::navigateUp, {
navigateAndPopAll(Destination.Home)
}, {
navigateAndPopAll(Destination.WorkingModes(false))
}, ::navigate)
}
entry<Destination.DhizukuServerSettings> {
DhizukuServerSettingsScreen(vm.dhizukuClients, vm::getDhizukuClients,
vm::updateDhizukuClient, vm::getDhizukuServerEnabled, vm::setDhizukuServerEnabled,
::navigateUp)
}
entry<Destination.DelegatedAdmins> {
DelegatedAdminsScreen(vm.delegatedAdmins, vm::getDelegatedAdmins, ::navigateUp, ::navigate)
}
entry<Destination.DelegatedAdminDetails>{
AddDelegatedAdminScreen(vm.chosenPackage, ::chooseSinglePackage, it,
vm::setDelegatedAdmin, ::navigateUp)
}
entry<Destination.DeviceInfo> { DeviceInfoScreen(vm, ::navigateUp) }
entry<Destination.LockScreenInfo> {
LockScreenInfoScreen(vm::getLockScreenInfo, vm::setLockScreenInfo, ::navigateUp)
}
entry<Destination.SupportMessage> {
SupportMessageScreen(vm::getShortSupportMessage, vm::getLongSupportMessage,
vm::setShortSupportMessage, vm::setLongSupportMessage, ::navigateUp)
}
entry<Destination.TransferOwnership> {
TransferOwnershipScreen(vm.deviceAdminReceivers, vm::getDeviceAdminReceivers,
vm::transferOwnership, ::navigateUp
) {
navigate(Destination.WorkingModes(false))
}
}
entry<Destination.System> { SystemManagerScreen(vm, ::navigateUp, ::navigate) }
entry<Destination.SystemOptions> { SystemOptionsScreen(vm, ::navigateUp) }
entry<Destination.Keyguard> {
KeyguardScreen(vm::setKeyguardDisabled, vm::lockScreen, ::navigateUp)
}
entry<Destination.HardwareMonitor> {
HardwareMonitorScreen(vm.hardwareProperties, vm::getHardwareProperties,
vm::setHpRefreshInterval, ::navigateUp)
}
entry<Destination.DefaultInputMethod> {
DefaultInputMethodScreen(vm::getCurrentInputMethod, vm.inputMethodList,
vm::getInputMethods, vm::setDefaultInputMethod, ::navigateUp)
}
entry<Destination.ChangeTime> { ChangeTimeScreen(vm::setTime, ::navigateUp) }
entry<Destination.ChangeTimezone> { ChangeTimeZoneScreen(vm::setTimeZone, ::navigateUp) }
entry<Destination.AutoTimePolicy> {
AutoTimePolicyScreen(vm::getAutoTimePolicy, vm::setAutoTimePolicy, ::navigateUp)
}
entry<Destination.AutoTimezonePolicy> {
AutoTimeZonePolicyScreen(vm::getAutoTimeZonePolicy, vm::setAutoTimeZonePolicy,
::navigateUp)
}
//entry<Destination.> { KeyPairs(::navigateUp) }
entry<Destination.ContentProtectionPolicy> {
ContentProtectionPolicyScreen(vm::getContentProtectionPolicy,
vm::setContentProtectionPolicy, ::navigateUp)
}
entry<Destination.PermissionPolicy> {
PermissionPolicyScreen(vm::getPermissionPolicy, vm::setPermissionPolicy, ::navigateUp)
}
entry<Destination.MtePolicy> {
MtePolicyScreen(vm::getMtePolicy, vm::setMtePolicy, ::navigateUp)
}
entry<Destination.NearbyStreamingPolicy> {
NearbyStreamingPolicyScreen(vm::getNsAppPolicy, vm::setNsAppPolicy,
vm::getNsNotificationPolicy, vm::setNsNotificationPolicy, ::navigateUp)
}
entry<Destination.LockTaskMode> {
LockTaskModeScreen(
vm.chosenPackage, ::chooseSinglePackage, ::choosePackage, vm.lockTaskPackages,
vm::getLockTaskPackages, vm::setLockTaskPackage, vm::startLockTaskMode,
vm:: getLockTaskFeatures, vm::setLockTaskFeatures, ::navigateUp
)
}
entry<Destination.CaCert> {
CaCertScreen(vm.installedCaCerts, vm::getCaCerts, vm.selectedCaCert, vm::selectCaCert, vm::installCaCert, vm::parseCaCert,
vm::exportCaCert, vm::uninstallCaCert, vm::uninstallAllCaCerts, ::navigateUp)
}
entry<Destination.SecurityLogging> {
SecurityLoggingScreen(vm::getSecurityLoggingEnabled, vm::setSecurityLoggingEnabled,
vm::exportSecurityLogs, vm::getSecurityLogsCount, vm::deleteSecurityLogs,
vm::getPreRebootSecurityLogs, vm::exportPreRebootSecurityLogs, ::navigateUp)
}
entry<Destination.DisableAccountManagement> {
DisableAccountManagementScreen(vm.mdAccountTypes, vm::getMdAccountTypes,
vm::setMdAccountType, ::navigateUp)
}
entry<Destination.SystemUpdatePolicy> {
SystemUpdatePolicyScreen(vm::getSystemUpdatePolicy, vm::setSystemUpdatePolicy,
vm::getPendingSystemUpdate, ::navigateUp)
}
entry<Destination.InstallSystemUpdate> {
InstallSystemUpdateScreen(vm::installSystemUpdate, ::navigateUp)
}
entry<Destination.FrpPolicy> {
FrpPolicyScreen(vm.getFrpPolicy(), vm::setFrpPolicy, ::navigateUp)
}
entry<Destination.WipeData> { WipeDataScreen(vm::wipeData, ::navigateUp) }
entry<Destination.Network> { NetworkScreen(::navigateUp, ::navigate) }
entry<Destination.WiFi> {
WifiScreen(vm, ::navigateUp, ::navigate) { navigate(Destination.UpdateNetwork(it)) }
}
entry<Destination.NetworkOptions> {
NetworkOptionsScreen(vm::getLanEnabled, vm::setLanEnabled, ::navigateUp)
}
entry<Destination.UpdateNetwork> {
val info = vm.configuredNetworks.collectAsStateWithLifecycle().value[
it.index
]
UpdateNetworkScreen(info, vm::setWifi, ::navigateUp)
}
entry<Destination.WifiSecurityLevel> {
WifiSecurityLevelScreen(vm::getMinimumWifiSecurityLevel,
vm::setMinimumWifiSecurityLevel, ::navigateUp)
}
entry<Destination.WifiSsidPolicy> {
WifiSsidPolicyScreen(vm::getSsidPolicy, vm::setSsidPolicy, ::navigateUp)
}
entry<Destination.NetworkStats> {
NetworkStatsScreen(vm.chosenPackage, ::chooseSinglePackage, vm::getPackageUid,
vm::queryNetworkStats, ::navigateUp) { navigate(Destination.NetworkStatsViewer) }
}
entry<Destination.NetworkStatsViewer> {
NetworkStatsViewerScreen(vm.networkStatsData, vm::clearNetworkStats, ::navigateUp)
}
entry<Destination.PrivateDns> {
PrivateDnsScreen(vm::getPrivateDns, vm::setPrivateDns, ::navigateUp)
}
entry<Destination.AlwaysOnVpnPackage> {
AlwaysOnVpnPackageScreen(vm::getAlwaysOnVpnPackage, vm::getAlwaysOnVpnLockdown,
vm::setAlwaysOnVpn, vm.chosenPackage, ::chooseSinglePackage, ::navigateUp)
}
entry<Destination.RecommendedGlobalProxy> {
RecommendedGlobalProxyScreen(vm::setRecommendedGlobalProxy, ::navigateUp)
}
entry<Destination.NetworkLogging> {
NetworkLoggingScreen(vm::getNetworkLoggingEnabled, vm::setNetworkLoggingEnabled,
vm::getNetworkLogsCount, vm::exportNetworkLogs, vm::deleteNetworkLogs, ::navigateUp)
}
//entry<Destination.WifiAuthKeypair> { WifiAuthKeypairScreen(::navigateUp) }
entry<Destination.PreferentialNetworkService> {
PreferentialNetworkServiceScreen(vm::getPnsEnabled, vm::setPnsEnabled, vm.pnsConfigs,
vm::getPnsConfigs, ::navigateUp, ::navigate)
}
entry<Destination.AddPreferentialNetworkServiceConfig> {
val info = vm.pnsConfigs.collectAsStateWithLifecycle().value.getOrNull(
it.index
) ?: PreferentialNetworkServiceInfo()
AddPreferentialNetworkServiceConfigScreen(info, vm::setPnsConfig, ::navigateUp)
}
entry<Destination.OverrideApn> {
OverrideApnScreen(vm.apnConfigs, vm::getApnConfigs, vm::getApnEnabled,
vm::setApnEnabled, ::navigateUp) { navigate(Destination.AddApnSetting(it)) }
}
entry<Destination.AddApnSetting> {
val origin = vm.apnConfigs.collectAsStateWithLifecycle().value.getOrNull(it.index)
AddApnSettingScreen(vm::setApnConfig, vm::removeApnConfig, origin, ::navigateUp)
}
entry<Destination.WorkProfile> { WorkProfileScreen(::navigateUp, ::navigate) }
entry<Destination.OrganizationOwnedProfile> {
OrganizationOwnedProfileScreen(vm::activateOrgProfileByShizuku, ::navigateUp)
}
entry<Destination.CreateWorkProfile> {
CreateWorkProfileScreen(vm::createWorkProfile, ::navigateUp)
}
entry<Destination.SuspendPersonalApp> {
SuspendPersonalAppScreen(
vm::getPersonalAppsSuspendedReason, vm::setPersonalAppsSuspended,
vm::getProfileMaxTimeOff, vm::setProfileMaxTimeOff, ::navigateUp
)
}
entry<Destination.CrossProfileIntentFilter> {
CrossProfileIntentFilterScreen(
vm::addCrossProfileIntentFilter, vm::clearCrossProfileIntentFilters,
vm::importCrossProfileIntentFilters, vm::exportCrossProfileIntentFilters,
::navigateUp
)
}
entry<Destination.DeleteWorkProfile> { DeleteWorkProfileScreen(vm::wipeData, ::navigateUp) }
entry<Destination.ApplicationsList> { params ->
AppChooserScreen(
params, vm.installedPackages, vm.refreshPackagesProgress, { name ->
if (params.canSwitchView) {
if (name == null) {
navigateUp()
} else {
navigate(Destination.ApplicationDetails(name))
}
} else {
if (name != null) vm.chosenPackage.trySend(name)
navigateUp()
}
}, {
SP.applicationsListView = false
navigate(Destination.ApplicationFunctions)
backstack.removeAt(backstack.size - 2)
}, vm::refreshPackageList, vm::setPackageSuspended, vm::setPackageHidden)
}
entry<Destination.ApplicationFunctions> {
ApplicationsFeaturesScreen(::navigateUp, ::navigate) {
SP.applicationsListView = true
navigate(Destination.ApplicationsList(true, true))
backstack.removeAt(backstack.size - 2)
}
}
entry<Destination.ApplicationDetails> {
ApplicationDetailsScreen(it, vm, ::navigateUp, ::navigate)
}
entry<Destination.Suspend> {
PackageFunctionScreen(
R.string.suspend, vm.suspendedPackages, vm::getSuspendedPackaged,
vm::setPackageSuspended, ::navigateUp, vm.chosenPackage, ::choosePackage,
::navigateToAppGroups, vm.appGroups, R.string.info_suspend_app
)
}
entry<Destination.Hide> {
PackageFunctionScreen(
R.string.hide, vm.hiddenPackages, vm::getHiddenPackages, vm::setPackageHidden,
::navigateUp, vm.chosenPackage, ::choosePackage, ::navigateToAppGroups, vm.appGroups
)
}
entry<Destination.BlockUninstall> {
PackageFunctionScreen(
R.string.block_uninstall, vm.ubPackages, vm::getUbPackages, vm::setPackageUb,
::navigateUp, vm.chosenPackage, ::choosePackage, ::navigateToAppGroups, vm.appGroups
)
}
entry<Destination.DisableUserControl> {
PackageFunctionScreen(
R.string.disable_user_control, vm.ucdPackages, vm::getUcdPackages,
vm::setPackageUcd, ::navigateUp, vm.chosenPackage, ::choosePackage,
::navigateToAppGroups, vm.appGroups, R.string.info_disable_user_control
)
}
entry<Destination.AppPermissionsManager> {
AppPermissionsManagerScreen(
vm::getPackagePermissions, vm::setPackagePermission, ::navigateUp, it
)
}
entry<Destination.PermissionManager> {
PermissionManagerScreen(::navigate, ::navigateUp)
}
entry<Destination.PermissionDetail> {
PermissionDetailScreen(
it, vm::getPermissionPackages, vm::setPackagePermission, ::navigateUp
)
}
entry<Destination.DisableMeteredData> {
PackageFunctionScreen(
R.string.disable_metered_data, vm.mddPackages, vm::getMddPackages,
vm::setPackageMdd, ::navigateUp, vm.chosenPackage, ::choosePackage,
::navigateToAppGroups, vm.appGroups
)
}
entry<Destination.ClearAppStorage> {
ClearAppStorageScreen(
vm.chosenPackage, ::chooseSinglePackage, vm::clearAppData, ::navigateUp
)
}
entry<Destination.UninstallApp> {
UninstallAppScreen(
vm.chosenPackage, ::chooseSinglePackage, vm::uninstallPackage, ::navigateUp
)
}
entry<Destination.KeepUninstalledPackages> {
PackageFunctionScreen(
R.string.keep_uninstalled_packages, vm.kuPackages, vm::getKuPackages,
vm::setPackageKu, ::navigateUp, vm.chosenPackage, ::choosePackage,
::navigateToAppGroups, vm.appGroups, R.string.info_keep_uninstalled_apps
)
}
entry<Destination.InstallExistingApp> {
InstallExistingAppScreen(
vm.chosenPackage, ::chooseSinglePackage, vm::installExistingApp, ::navigateUp
)
}
entry<Destination.CrossProfilePackages> {
PackageFunctionScreen(
R.string.cross_profile_apps, vm.cpPackages,
vm::getCpPackages, vm::setPackageCp, ::navigateUp, vm.chosenPackage,
::choosePackage, ::navigateToAppGroups, vm.appGroups
)
}
entry<Destination.CrossProfileWidgetProviders> {
PackageFunctionScreen(R.string.cross_profile_widget, vm.cpwProviders,
vm::getCpwProviders, vm::setCpwProvider, ::navigateUp, vm.chosenPackage,
::choosePackage, ::navigateToAppGroups, vm.appGroups)
}
entry<Destination.CredentialManagerPolicy> {
CredentialManagerPolicyScreen(
vm.chosenPackage, ::choosePackage, vm.cmPackages, vm::getCmPolicy,
vm::setCmPackage, vm::setCmPolicy, ::navigateUp
)
}
entry<Destination.PermittedAccessibilityServices> {
PermittedAsAndImPackages(
R.string.permitted_accessibility_services,
R.string.system_accessibility_always_allowed, vm.chosenPackage, ::choosePackage,
vm.pasPackages, vm::getPasPackages, vm::setPasPackage, vm::setPasPolicy,
::navigateUp
)
}
entry<Destination.PermittedInputMethods> {
PermittedAsAndImPackages(
R.string.permitted_ime, R.string.system_ime_always_allowed,
vm.chosenPackage, ::choosePackage, vm.pimPackages, vm::getPimPackages,
vm::setPimPackage, vm::setPimPolicy, ::navigateUp
)
}
entry<Destination.EnableSystemApp> {
EnableSystemAppScreen(
vm.chosenPackage, ::chooseSinglePackage, vm::enableSystemApp, ::navigateUp
)
}
entry<Destination.SetDefaultDialer> {
SetDefaultDialerScreen(
vm.chosenPackage, ::chooseSinglePackage, vm::setDefaultDialer, ::navigateUp
)
}
entry<Destination.ManagedConfiguration> {
ManagedConfigurationScreen(
it, vm.appRestrictions, vm::setAppRestrictions,
vm::clearAppRestrictions, ::navigateUp
)
}
entry<Destination.ManageAppGroups> {
ManageAppGroupsScreen(
vm.appGroups, vm::exportAppGroups, vm::importAppGroups,
{ id, name, apps -> navigate(Destination.EditAppGroup(id, name, apps)) },
::navigateUp
)
}
entry<Destination.EditAppGroup> {
EditAppGroupScreen(
it, vm::getAppInfo, ::navigateUp, vm::setAppGroup,
vm::deleteAppGroup, ::choosePackage, vm.chosenPackage
)
}
entry<Destination.UserRestriction> {
UserRestrictionScreen(vm::getUserRestrictions, ::navigateUp, ::navigate)
}
entry<Destination.UserRestrictionEditor> {
UserRestrictionEditorScreen(vm.userRestrictions, vm::setUserRestriction, ::navigateUp)
}
entry<Destination.UserRestrictionOptions> {
UserRestrictionOptionsScreen(it, vm.userRestrictions,
vm::setUserRestriction, vm::createUserRestrictionShortcut, ::navigateUp)
}
entry<Destination.Users> { UsersScreen(vm, ::navigateUp, ::navigate) }
entry<Destination.UserInfo> { UserInfoScreen(vm::getUserInformation, ::navigateUp) }
entry<Destination.UsersOptions> {
UsersOptionsScreen(vm::getLogoutEnabled, vm::setLogoutEnabled, ::navigateUp)
}
entry<Destination.UserOperation> {
UserOperationScreen(vm::getUserIdentifiers, vm::doUserOperation,
vm::createUserOperationShortcut, ::navigateUp)
}
entry<Destination.CreateUser> { CreateUserScreen(vm::createUser, ::navigateUp) }
entry<Destination.ChangeUsername> { ChangeUsernameScreen(vm::setProfileName, ::navigateUp) }
entry<Destination.UserSessionMessage> {
UserSessionMessageScreen(vm::getUserSessionMessages, vm::setStartUserSessionMessage,
vm::setEndUserSessionMessage, ::navigateUp)
}
entry<Destination.AffiliationId> {
AffiliationIdScreen(vm.affiliationIds, vm::getAffiliationIds, vm::setAffiliationId,
::navigateUp)
}
entry<Destination.Password> { PasswordScreen(vm, ::navigateUp, ::navigate) }
entry<Destination.PasswordInfo> {
PasswordInfoScreen(vm::getPasswordComplexity, vm::isPasswordComplexitySufficient,
vm::isUsingUnifiedPassword, ::navigateUp)
}
entry<Destination.ResetPasswordToken> {
ResetPasswordTokenScreen(vm::getRpTokenState, vm::setRpToken,
vm::createActivateRpTokenIntent, vm::clearRpToken, ::navigateUp)
}
entry<Destination.ResetPassword> { ResetPasswordScreen(vm::resetPassword, ::navigateUp) }
entry<Destination.RequiredPasswordComplexity> {
RequiredPasswordComplexityScreen(vm::getRequiredPasswordComplexity,
vm::setRequiredPasswordComplexity, ::navigateUp)
}
entry<Destination.KeyguardDisabledFeatures> {
KeyguardDisabledFeaturesScreen(vm::getKeyguardDisableConfig,
vm::setKeyguardDisableConfig, ::navigateUp)
}
entry<Destination.RequiredPasswordQuality> { RequiredPasswordQualityScreen(::navigateUp) }
entry<Destination.Settings> { SettingsScreen(::navigate, ::navigateUp) }
entry<Destination.SettingsOptions> {
SettingsOptionsScreen(
vm::getDisplayDangerousFeatures, vm::getShortcutsEnabled,
vm::setDisplayDangerousFeatures, vm::setShortcutsEnabled, ::navigateUp
)
}
entry<Destination.AppearanceSettings> {
AppearanceScreen(::navigateUp, vm.theme, vm::changeTheme)
}
entry<Destination.AppLockSettings> {
AppLockSettingsScreen(vm.getAppLockConfig(), vm::setAppLockConfig, ::navigateUp)
}
entry<Destination.ApiSettings> {
ApiSettings(vm::getApiEnabled, vm::setApiKey, ::navigateUp)
}
entry<Destination.NotificationSettings> {
NotificationsScreen(
vm.enabledNotifications, vm::getEnabledNotifications,
vm::setNotificationEnabled, ::navigateUp
)
}
entry<Destination.About> { AboutScreen(::navigateUp) }
}(destination) as NavEntry<NavKey>

View File

@@ -1,4 +1,4 @@
package com.bintianqi.owndroid.dpm
package com.bintianqi.owndroid.ui.screen
import android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT
import android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED
@@ -33,12 +33,10 @@ import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.List
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.Clear
@@ -99,12 +97,10 @@ import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
@@ -117,6 +113,8 @@ import com.bintianqi.owndroid.MyViewModel
import com.bintianqi.owndroid.Privilege
import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.adaptiveInsets
import com.bintianqi.owndroid.dpm.PermissionItem
import com.bintianqi.owndroid.dpm.runtimePermissions
import com.bintianqi.owndroid.parsePackageNames
import com.bintianqi.owndroid.showOperationResultToast
import com.bintianqi.owndroid.ui.FullWidthRadioButtonItem
@@ -126,7 +124,9 @@ import com.bintianqi.owndroid.ui.MyScaffold
import com.bintianqi.owndroid.ui.MySmallTitleScaffold
import com.bintianqi.owndroid.ui.NavIcon
import com.bintianqi.owndroid.ui.Notes
import com.bintianqi.owndroid.ui.PackageNameTextField
import com.bintianqi.owndroid.ui.SwitchItem
import com.bintianqi.owndroid.ui.navigation.Destination
import com.google.accompanist.drawablepainter.rememberDrawablePainter
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableStateFlow
@@ -166,33 +166,11 @@ fun LazyItemScope.ApplicationItem(info: AppInfo, onClear: () -> Unit) {
}
}
@Composable
fun PackageNameTextField(
value: String, onChoosePackage: () -> Unit,
modifier: Modifier = Modifier, onValueChange: (String) -> Unit
) {
val fm = LocalFocusManager.current
OutlinedTextField(
value, onValueChange, Modifier
.fillMaxWidth()
.then(modifier),
label = { Text(stringResource(R.string.package_name)) },
trailingIcon = {
IconButton(onChoosePackage) {
Icon(Icons.AutoMirrored.Default.List, null)
}
},
isError = value.isNotEmpty() && !value.isValidPackageName,
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Ascii, imeAction = ImeAction.Done),
keyboardActions = KeyboardActions { fm.clearFocus() }
)
}
@Serializable object ApplicationsFeatures
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ApplicationsFeaturesScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit, onSwitchView: () -> Unit) {
fun ApplicationsFeaturesScreen(
onNavigateUp: () -> Unit, onNavigate: (Destination) -> Unit, onSwitchView: () -> Unit
) {
val context = LocalContext.current
val sb = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
Scaffold(
@@ -219,56 +197,82 @@ fun ApplicationsFeaturesScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Un
.padding(bottom = 80.dp)
) {
val privilege by Privilege.status.collectAsStateWithLifecycle()
if(VERSION.SDK_INT >= 24) FunctionItem(R.string.suspend, icon = R.drawable.block_fill0) { onNavigate(Suspend) }
FunctionItem(R.string.hide, icon = R.drawable.visibility_off_fill0) { onNavigate(Hide) }
FunctionItem(R.string.block_uninstall, icon = R.drawable.delete_forever_fill0) { onNavigate(BlockUninstall) }
if(VERSION.SDK_INT >= 24) FunctionItem(R.string.suspend, icon = R.drawable.block_fill0) {
onNavigate(Destination.Suspend)
}
FunctionItem(R.string.hide, icon = R.drawable.visibility_off_fill0) {
onNavigate(Destination.Hide)
}
FunctionItem(R.string.block_uninstall, icon = R.drawable.delete_forever_fill0) {
onNavigate(Destination.BlockUninstall)
}
if(VERSION.SDK_INT >= 30 && (privilege.device || (VERSION.SDK_INT >= 33 && privilege.profile))) {
FunctionItem(R.string.disable_user_control, icon = R.drawable.do_not_touch_fill0) { onNavigate(DisableUserControl) }
FunctionItem(R.string.disable_user_control, icon = R.drawable.do_not_touch_fill0) {
onNavigate(Destination.DisableUserControl)
}
}
FunctionItem(R.string.permissions, icon = R.drawable.shield_fill0) {
onNavigate(PermissionManager)
onNavigate(Destination.PermissionManager)
}
if(VERSION.SDK_INT >= 28) {
FunctionItem(R.string.disable_metered_data, icon = R.drawable.money_off_fill0) { onNavigate(DisableMeteredData) }
FunctionItem(R.string.disable_metered_data, icon = R.drawable.money_off_fill0) {
onNavigate(Destination.DisableMeteredData)
}
}
if(VERSION.SDK_INT >= 28) {
FunctionItem(R.string.clear_app_storage, icon = R.drawable.mop_fill0) { onNavigate(ClearAppStorage) }
FunctionItem(R.string.clear_app_storage, icon = R.drawable.mop_fill0) {
onNavigate(Destination.ClearAppStorage)
}
}
FunctionItem(R.string.install_app, icon = R.drawable.install_mobile_fill0) {
context.startActivity(Intent(context, AppInstallerActivity::class.java))
}
FunctionItem(R.string.uninstall_app, icon = R.drawable.delete_fill0) { onNavigate(UninstallApp) }
FunctionItem(R.string.uninstall_app, icon = R.drawable.delete_fill0) {
onNavigate(Destination.UninstallApp)
}
if(VERSION.SDK_INT >= 28 && privilege.device) {
FunctionItem(R.string.keep_uninstalled_packages, icon = R.drawable.delete_fill0) { onNavigate(KeepUninstalledPackages) }
FunctionItem(R.string.keep_uninstalled_packages, icon = R.drawable.delete_fill0) {
onNavigate(Destination.KeepUninstalledPackages)
}
}
if (VERSION.SDK_INT >= 28 && (privilege.device || (privilege.profile && privilege.affiliated))) {
FunctionItem(R.string.install_existing_app, icon = R.drawable.install_mobile_fill0) {
onNavigate(InstallExistingApp)
onNavigate(Destination.InstallExistingApp)
}
}
if(VERSION.SDK_INT >= 30 && privilege.work) {
FunctionItem(R.string.cross_profile_apps, icon = R.drawable.work_fill0) { onNavigate(CrossProfilePackages) }
FunctionItem(R.string.cross_profile_apps, icon = R.drawable.work_fill0) {
onNavigate(Destination.CrossProfilePackages)
}
}
if(privilege.work) {
FunctionItem(R.string.cross_profile_widget, icon = R.drawable.widgets_fill0) { onNavigate(CrossProfileWidgetProviders) }
FunctionItem(R.string.cross_profile_widget, icon = R.drawable.widgets_fill0) {
onNavigate(Destination.CrossProfileWidgetProviders)
}
}
if(VERSION.SDK_INT >= 34 && privilege.device) {
FunctionItem(R.string.credential_manager_policy, icon = R.drawable.license_fill0) { onNavigate(CredentialManagerPolicy) }
FunctionItem(R.string.credential_manager_policy, icon = R.drawable.license_fill0) {
onNavigate(Destination.CredentialManagerPolicy)
}
}
FunctionItem(R.string.permitted_accessibility_services, icon = R.drawable.settings_accessibility_fill0) {
onNavigate(PermittedAccessibilityServices)
onNavigate(Destination.PermittedAccessibilityServices)
}
FunctionItem(R.string.permitted_ime, icon = R.drawable.keyboard_fill0) {
onNavigate(Destination.PermittedInputMethods)
}
FunctionItem(R.string.enable_system_app, icon = R.drawable.enable_fill0) {
onNavigate(Destination.EnableSystemApp)
}
FunctionItem(R.string.permitted_ime, icon = R.drawable.keyboard_fill0) { onNavigate(PermittedInputMethods) }
FunctionItem(R.string.enable_system_app, icon = R.drawable.enable_fill0) { onNavigate(EnableSystemApp) }
if(VERSION.SDK_INT >= 34 && (privilege.device || privilege.work)) {
FunctionItem(R.string.set_default_dialer, icon = R.drawable.call_fill0) { onNavigate(SetDefaultDialer) }
FunctionItem(R.string.set_default_dialer, icon = R.drawable.call_fill0) {
onNavigate(Destination.SetDefaultDialer)
}
}
}
}
}
@Serializable data class ApplicationDetails(val packageName: String)
data class AppStatus(
val suspend: Boolean,
val hide: Boolean,
@@ -280,7 +284,8 @@ data class AppStatus(
@Composable
fun ApplicationDetailsScreen(
param: ApplicationDetails, vm: MyViewModel, onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit
param: Destination.ApplicationDetails, vm: MyViewModel, onNavigateUp: () -> Unit,
onNavigate: (Destination) -> Unit
) {
val packageName = param.packageName
val privilege by Privilege.status.collectAsStateWithLifecycle()
@@ -302,7 +307,9 @@ fun ApplicationDetailsScreen(
.alpha(0.7F)
.padding(bottom = 8.dp), style = typography.bodyMedium)
}
FunctionItem(R.string.permissions, icon = R.drawable.shield_fill0) { onNavigate(AppPermissionsManager(packageName)) }
FunctionItem(R.string.permissions, icon = R.drawable.shield_fill0) {
onNavigate(Destination.AppPermissionsManager(packageName))
}
if(VERSION.SDK_INT >= 24) SwitchItem(
R.string.suspend, icon = R.drawable.block_fill0, state = status.suspend,
onCheckedChange = { vm.adSetPackageSuspended(packageName, it) }
@@ -334,7 +341,7 @@ fun ApplicationDetailsScreen(
)
if (appRestrictions.isNotEmpty()) {
FunctionItem(R.string.managed_configuration, icon = R.drawable.description_fill0) {
onNavigate(ManagedConfiguration(packageName))
onNavigate(Destination.ManagedConfiguration(packageName))
}
}
if(VERSION.SDK_INT >= 28) FunctionItem(R.string.clear_app_storage, icon = R.drawable.mop_fill0) { dialog = 1 }
@@ -349,21 +356,11 @@ fun ApplicationDetailsScreen(
}
}
@Serializable object Suspend
@Serializable object Hide
@Serializable object BlockUninstall
@Serializable object DisableUserControl
@Serializable data class AppPermissionsManager(val packageName: String)
@Composable
fun AppPermissionsManagerScreen(
getPackagePermissions: (String) -> Map<String, Int>,
setPackagePermission: (String, String, Int) -> Boolean, onNavigateUp: () -> Unit,
param: AppPermissionsManager
param: Destination.AppPermissionsManager
) {
val context = LocalContext.current
val privilege by Privilege.status.collectAsStateWithLifecycle()
@@ -453,10 +450,10 @@ fun PackagePermissionDialog(
)
}
@Serializable object PermissionManager
@Composable
fun PermissionManagerScreen(onNavigate: (PermissionDetail) -> Unit, onNavigateUp: () -> Unit) {
fun PermissionManagerScreen(
onNavigate: (Destination.PermissionDetail) -> Unit, onNavigateUp: () -> Unit
) {
MyLazyScaffold(R.string.permissions, onNavigateUp) {
items(runtimePermissions) {
Row(
@@ -464,7 +461,7 @@ fun PermissionManagerScreen(onNavigate: (PermissionDetail) -> Unit, onNavigateUp
modifier = Modifier
.fillMaxWidth()
.clickable {
onNavigate(PermissionDetail(it.id))
onNavigate(Destination.PermissionDetail(it.id))
}
.padding(8.dp, 12.dp)
) {
@@ -478,12 +475,10 @@ fun PermissionManagerScreen(onNavigate: (PermissionDetail) -> Unit, onNavigateUp
}
}
@Serializable class PermissionDetail(val permission: String)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun PermissionDetailScreen(
param: PermissionDetail, getPermissionPackages: (String) -> List<Pair<AppInfo, Int>>,
param: Destination.PermissionDetail, getPermissionPackages: (String) -> List<Pair<AppInfo, Int>>,
setPackagePermission: (String, String, Int) -> Boolean, onNavigateUp: () -> Unit
) {
val context = LocalContext.current
@@ -586,10 +581,6 @@ fun PermissionDetailScreen(
) { selectedPackage = null }
}
@Serializable object DisableMeteredData
@Serializable object ClearAppStorage
@RequiresApi(28)
@Composable
fun ClearAppStorageScreen(
@@ -653,8 +644,6 @@ private fun ClearAppStorageDialog(
)
}
@Serializable object UninstallApp
@Composable
fun UninstallAppScreen(
chosenPackage: Channel<String>, onChoosePackage: () -> Unit,
@@ -722,10 +711,6 @@ private fun UninstallAppDialog(
)
}
@Serializable object KeepUninstalledPackages
@Serializable object InstallExistingApp
@RequiresApi(28)
@Composable
fun InstallExistingAppScreen(
@@ -752,12 +737,6 @@ fun InstallExistingAppScreen(
}
}
@Serializable object CrossProfilePackages
@Serializable object CrossProfileWidgetProviders
@Serializable object CredentialManagerPolicy
@RequiresApi(34)
@Composable
fun CredentialManagerPolicyScreen(
@@ -819,10 +798,6 @@ fun CredentialManagerPolicyScreen(
}
}
@Serializable object PermittedAccessibilityServices
@Serializable object PermittedInputMethods
@Composable
fun PermittedAsAndImPackages(
title: Int, note: Int, chosenPackage: Channel<String>, onChoosePackage: () -> Unit,
@@ -879,8 +854,6 @@ fun PermittedAsAndImPackages(
}
}
@Serializable object EnableSystemApp
@Composable
fun EnableSystemAppScreen(
chosenPackage: Channel<String>, onChoosePackage: () -> Unit,
@@ -910,8 +883,6 @@ fun EnableSystemAppScreen(
}
}
@Serializable object SetDefaultDialer
@RequiresApi(34)
@Composable
fun SetDefaultDialerScreen(
@@ -1072,8 +1043,6 @@ class AppGroup(
val id: Int, override val name: String, override val apps: List<String>
) : BasicAppGroup(name, apps)
@Serializable object ManageAppGroups
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ManageAppGroupsScreen(
@@ -1158,12 +1127,10 @@ fun ManageAppGroupsScreen(
}
}
@Serializable class EditAppGroup(val id: Int?, val name: String, val apps: List<String>)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun EditAppGroupScreen(
params: EditAppGroup, getAppInfo: (String) -> AppInfo, navigateUp: () -> Unit,
params: Destination.EditAppGroup, getAppInfo: (String) -> AppInfo, navigateUp: () -> Unit,
setGroup: (Int?, String, List<String>) -> Unit, deleteGroup: (Int) -> Unit,
onChoosePackage: () -> Unit, chosenPackage: Channel<String>
) {
@@ -1239,12 +1206,10 @@ fun EditAppGroupScreen(
}
}
@Serializable class ManagedConfiguration(val packageName: String)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ManagedConfigurationScreen(
params: ManagedConfiguration, appRestrictions: StateFlow<List<AppRestriction>>,
params: Destination.ManagedConfiguration, appRestrictions: StateFlow<List<AppRestriction>>,
setRestriction: (String, AppRestriction) -> Unit, clearRestriction: (String) -> Unit,
navigateUp: () -> Unit
) {

View File

@@ -0,0 +1,120 @@
package com.bintianqi.owndroid.ui.screen
import android.os.Build.VERSION
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LargeTopAppBar
import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.bintianqi.owndroid.BottomPadding
import com.bintianqi.owndroid.Privilege
import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.SP
import com.bintianqi.owndroid.adaptiveInsets
import com.bintianqi.owndroid.ui.navigation.Destination
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun HomeScreen(onNavigate: (Destination) -> Unit) {
val privilege by Privilege.status.collectAsStateWithLifecycle()
val sb = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
Scaffold(
Modifier.nestedScroll(sb.nestedScrollConnection),
topBar = {
LargeTopAppBar(
{ Text(stringResource(R.string.app_name)) },
actions = {
IconButton({ onNavigate(Destination.WorkingModes(true)) }) {
Icon(
painterResource(R.drawable.security_fill0), null
)
}
IconButton({ onNavigate(Destination.Settings) }) {
Icon(Icons.Default.Settings, null)
}
},
scrollBehavior = sb
)
},
contentWindowInsets = adaptiveInsets()
) {
Column(
Modifier
.fillMaxSize()
.padding(it)
.verticalScroll(rememberScrollState())
) {
if (privilege.device || privilege.profile) {
HomePageItem(R.string.system, R.drawable.android_fill0) {
onNavigate(Destination.System)
}
HomePageItem(R.string.network, R.drawable.wifi_fill0) { onNavigate(Destination.Network) }
}
if (privilege.work) {
HomePageItem(R.string.work_profile, R.drawable.work_fill0) {
onNavigate(Destination.WorkProfile)
}
}
if (privilege.device || privilege.profile) {
HomePageItem(R.string.applications, R.drawable.apps_fill0) {
onNavigate(
if (SP.applicationsListView) Destination.ApplicationsList(true, true)
else Destination.ApplicationFunctions
)
}
if (VERSION.SDK_INT >= 24) {
HomePageItem(R.string.user_restriction, R.drawable.person_off) {
onNavigate(Destination.UserRestriction)
}
}
HomePageItem(R.string.users, R.drawable.manage_accounts_fill0) {
onNavigate(Destination.Users)
}
HomePageItem(
R.string.password_and_keyguard, R.drawable.password_fill0
) { onNavigate(Destination.Password) }
}
Spacer(Modifier.height(BottomPadding))
}
}
}
@Composable
fun HomePageItem(name: Int, icon: Int, onClick: () -> Unit) {
Row(
Modifier
.fillMaxWidth()
.clickable(onClick = onClick)
.padding(vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically
) {
Spacer(Modifier.padding(start = 30.dp))
Icon(painterResource(icon), null)
Spacer(Modifier.padding(start = 15.dp))
Text(stringResource(name), style = typography.headlineSmall)
}
}

View File

@@ -1,4 +1,4 @@
package com.bintianqi.owndroid.dpm
package com.bintianqi.owndroid.ui.screen
import android.Manifest
import android.annotation.SuppressLint
@@ -128,7 +128,9 @@ import com.bintianqi.owndroid.ui.MyScaffold
import com.bintianqi.owndroid.ui.MySmallTitleScaffold
import com.bintianqi.owndroid.ui.NavIcon
import com.bintianqi.owndroid.ui.Notes
import com.bintianqi.owndroid.ui.PackageNameTextField
import com.bintianqi.owndroid.ui.SwitchItem
import com.bintianqi.owndroid.ui.navigation.Destination
import com.bintianqi.owndroid.writeClipBoard
import com.bintianqi.owndroid.yesOrNo
import com.google.accompanist.permissions.ExperimentalPermissionsApi
@@ -142,44 +144,58 @@ import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
@Serializable object Network
@Composable
fun NetworkScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) {
fun NetworkScreen(onNavigateUp: () -> Unit, onNavigate: (Destination) -> Unit) {
val privilege by Privilege.status.collectAsStateWithLifecycle()
MyScaffold(R.string.network, onNavigateUp, 0.dp) {
if(!privilege.dhizuku) FunctionItem(R.string.wifi, icon = R.drawable.wifi_fill0) { onNavigate(WiFi) }
if(!privilege.dhizuku) FunctionItem(R.string.wifi, icon = R.drawable.wifi_fill0) {
onNavigate(Destination.WiFi)
}
if(VERSION.SDK_INT >= 30) {
FunctionItem(R.string.options, icon = R.drawable.tune_fill0) { onNavigate(NetworkOptions) }
FunctionItem(R.string.options, icon = R.drawable.tune_fill0) {
onNavigate(Destination.NetworkOptions)
}
}
if (!privilege.dhizuku)
FunctionItem(R.string.network_stats, icon = R.drawable.query_stats_fill0) { onNavigate(QueryNetworkStats) }
FunctionItem(R.string.network_stats, icon = R.drawable.query_stats_fill0) {
onNavigate(Destination.NetworkStats)
}
if(VERSION.SDK_INT >= 29 && privilege.device) {
FunctionItem(R.string.private_dns, icon = R.drawable.dns_fill0) { onNavigate(PrivateDns) }
FunctionItem(R.string.private_dns, icon = R.drawable.dns_fill0) {
onNavigate(Destination.PrivateDns)
}
}
if(VERSION.SDK_INT >= 24) {
FunctionItem(R.string.always_on_vpn, icon = R.drawable.vpn_key_fill0) { onNavigate(AlwaysOnVpnPackage) }
FunctionItem(R.string.always_on_vpn, icon = R.drawable.vpn_key_fill0) {
onNavigate(Destination.AlwaysOnVpnPackage)
}
}
if(privilege.device) {
FunctionItem(R.string.recommended_global_proxy, icon = R.drawable.vpn_key_fill0) { onNavigate(RecommendedGlobalProxy) }
FunctionItem(R.string.recommended_global_proxy, icon = R.drawable.vpn_key_fill0) {
onNavigate(Destination.RecommendedGlobalProxy)
}
}
if(VERSION.SDK_INT >= 26 && !privilege.dhizuku && (privilege.device || privilege.work)) {
FunctionItem(R.string.network_logging, icon = R.drawable.description_fill0) { onNavigate(NetworkLogging) }
FunctionItem(R.string.network_logging, icon = R.drawable.description_fill0) {
onNavigate(Destination.NetworkLogging)
}
}
/*if(VERSION.SDK_INT >= 31) {
FunctionItem(R.string.wifi_auth_keypair, icon = R.drawable.key_fill0) { onNavigate(WifiAuthKeypair) }
FunctionItem(R.string.wifi_auth_keypair, icon = R.drawable.key_fill0) { onNavigate(Destination.WifiAuthKeypair) }
}*/
if (VERSION.SDK_INT >= 33 && (privilege.work || privilege.device)) {
FunctionItem(R.string.preferential_network_service, icon = R.drawable.globe_fill0) { onNavigate(PreferentialNetworkService) }
FunctionItem(R.string.preferential_network_service, icon = R.drawable.globe_fill0) {
onNavigate(Destination.PreferentialNetworkService)
}
}
if(VERSION.SDK_INT >= 28 && privilege.device) {
FunctionItem(R.string.override_apn, icon = R.drawable.cell_tower_fill0) { onNavigate(OverrideApn) }
FunctionItem(R.string.override_apn, icon = R.drawable.cell_tower_fill0) {
onNavigate(Destination.OverrideApn)
}
}
}
}
@Serializable object NetworkOptions
@Composable
fun NetworkOptionsScreen(
getLanEnabled: () -> Boolean, setLanEnabled: (Boolean) -> Unit, onNavigateUp: () -> Unit
@@ -208,12 +224,10 @@ fun NetworkOptionsScreen(
)
}
@Serializable object WiFi
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun WifiScreen(
vm: MyViewModel, onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit,
vm: MyViewModel, onNavigateUp: () -> Unit, onNavigate: (Destination) -> Unit,
editNetwork: (Int) -> Unit
) {
val coroutine = rememberCoroutineScope()
@@ -266,7 +280,7 @@ fun WifiScreen(
@Composable
fun WifiOverviewScreen(
setWifiEnabled: (Boolean) -> Boolean, disconnect: () -> Boolean, reconnect: () -> Boolean,
getMac: () -> String?, navigate: (Any) -> Unit
getMac: () -> String?, navigate: (Destination) -> Unit
) {
val context = LocalContext.current
val privilege by Privilege.status.collectAsStateWithLifecycle()
@@ -305,8 +319,12 @@ fun WifiOverviewScreen(
FunctionItem(R.string.wifi_mac_address) { macDialog = true }
}
if(VERSION.SDK_INT >= 33 && (privilege.device || privilege.org)) {
FunctionItem(R.string.min_wifi_security_level) { navigate(WifiSecurityLevel) }
FunctionItem(R.string.wifi_ssid_policy) { navigate(WifiSsidPolicyScreen) }
FunctionItem(R.string.min_wifi_security_level) {
navigate(Destination.WifiSecurityLevel)
}
FunctionItem(R.string.wifi_ssid_policy) {
navigate(Destination.WifiSsidPolicy)
}
}
}
if (macDialog && VERSION.SDK_INT >= 24) {
@@ -503,9 +521,6 @@ private fun SavedNetworks(
)
}
@Serializable
data class UpdateNetwork(val index: Int)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun UpdateNetworkScreen(info: WifiInfo, setNetwork: (WifiInfo) -> Boolean, onNavigateUp: () -> Unit) {
@@ -813,8 +828,6 @@ private fun AddNetworkScreen(
}
}
@Serializable object WifiSecurityLevel
@RequiresApi(33)
@Composable
fun WifiSecurityLevelScreen(
@@ -849,8 +862,6 @@ enum class SsidPolicyType(val id: Int, val text: Int) {
Blacklist(WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_DENYLIST, R.string.blacklist)
}
@Serializable object WifiSsidPolicyScreen
@RequiresApi(33)
@Composable
fun WifiSsidPolicyScreen(
@@ -948,8 +959,6 @@ data class QueryNetworkStatsParams(
val startTime: Long, val endTime: Long, val uid: Int, val tag: Int, val state: NetworkStatsState
)
@Serializable object QueryNetworkStats
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun NetworkStatsScreen(
@@ -1266,8 +1275,6 @@ data class NetworkStatsData(
val metered: Int?
)
@Serializable object NetworkStatsViewer
@Composable
fun NetworkStatsViewerScreen(
data: List<NetworkStatsData>, clearData: () -> Unit, onNavigateUp: () -> Unit
@@ -1372,8 +1379,6 @@ enum class PrivateDnsMode(val id: Int, val text: Int) {
data class PrivateDnsConfiguration(val mode: PrivateDnsMode?, val host: String)
@Serializable object PrivateDns
@RequiresApi(29)
@Composable
fun PrivateDnsScreen(
@@ -1413,8 +1418,6 @@ fun PrivateDnsScreen(
}
}
@Serializable object AlwaysOnVpnPackage
@RequiresApi(24)
@Composable
fun AlwaysOnVpnPackageScreen(
@@ -1464,8 +1467,6 @@ data class RecommendedProxyConf(
val port: Int, val exclude: List<String>
)
@Serializable object RecommendedGlobalProxy
@Composable
fun RecommendedGlobalProxyScreen(
setProxy: (RecommendedProxyConf) -> Unit, onNavigateUp: () -> Unit
@@ -1538,8 +1539,6 @@ fun RecommendedGlobalProxyScreen(
}
}
@Serializable object NetworkLogging
@RequiresApi(26)
@Composable
fun NetworkLoggingScreen(
@@ -1620,14 +1619,12 @@ fun NetworkLoggingScreen(
)
}
@Serializable object PreferentialNetworkService
@RequiresApi(33)
@Composable
fun PreferentialNetworkServiceScreen(
getEnabled: () -> Boolean, setEnabled: (Boolean) -> Unit,
pnsConfigs: StateFlow<List<PreferentialNetworkServiceInfo>>, getConfigs: () -> Unit,
onNavigateUp: () -> Unit, onNavigate: (AddPreferentialNetworkServiceConfig) -> Unit
onNavigateUp: () -> Unit, onNavigate: (Destination.AddPreferentialNetworkServiceConfig) -> Unit
) {
var masterEnabled by rememberSaveable { mutableStateOf(getEnabled()) }
val configs by pnsConfigs.collectAsStateWithLifecycle()
@@ -1647,7 +1644,7 @@ fun PreferentialNetworkServiceScreen(
) {
Text(config.id.toString())
IconButton({
onNavigate(AddPreferentialNetworkServiceConfig(index))
onNavigate(Destination.AddPreferentialNetworkServiceConfig(index))
}) {
Icon(Icons.Default.Edit, stringResource(R.string.edit))
}
@@ -1656,7 +1653,7 @@ fun PreferentialNetworkServiceScreen(
Row(
Modifier.fillMaxWidth()
.padding(top = 4.dp)
.clickable { onNavigate(AddPreferentialNetworkServiceConfig(-1)) }
.clickable { onNavigate(Destination.AddPreferentialNetworkServiceConfig(-1)) }
.padding(horizontal = 8.dp, vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically
) {
@@ -1675,9 +1672,6 @@ data class PreferentialNetworkServiceInfo(
val includedUids: List<Int> = emptyList()
)
@Serializable
data class AddPreferentialNetworkServiceConfig(val index: Int)
@OptIn(ExperimentalMaterial3Api::class)
@RequiresApi(33)
@Composable
@@ -1769,8 +1763,6 @@ fun AddPreferentialNetworkServiceConfigScreen(
}
}
@Serializable object OverrideApn
@RequiresApi(28)
@Composable
fun OverrideApnScreen(
@@ -1901,8 +1893,6 @@ data class ApnConfig(
val persistent: Boolean, val alwaysOn: Boolean, val id: Int = -1
)
@Serializable data class AddApnSetting(val index: Int)
@OptIn(ExperimentalMaterial3Api::class)
@RequiresApi(28)
@Composable

View File

@@ -1,4 +1,4 @@
package com.bintianqi.owndroid.dpm
package com.bintianqi.owndroid.ui.screen
import android.annotation.SuppressLint
import android.app.Activity
@@ -71,29 +71,37 @@ import com.bintianqi.owndroid.ui.InfoItem
import com.bintianqi.owndroid.ui.MyScaffold
import com.bintianqi.owndroid.ui.Notes
import com.bintianqi.owndroid.ui.RadioButtonItem
import com.bintianqi.owndroid.ui.navigation.Destination
import com.bintianqi.owndroid.yesOrNo
import kotlinx.serialization.Serializable
@Serializable object Password
@SuppressLint("NewApi")
@Composable
fun PasswordScreen(vm: MyViewModel,onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) {
fun PasswordScreen(vm: MyViewModel,onNavigateUp: () -> Unit, onNavigate: (Destination) -> Unit) {
val context = LocalContext.current
val privilege by Privilege.status.collectAsStateWithLifecycle()
var dialog by rememberSaveable { mutableIntStateOf(0) }
MyScaffold(R.string.password_and_keyguard, onNavigateUp, 0.dp) {
FunctionItem(R.string.password_info, icon = R.drawable.info_fill0) { onNavigate(PasswordInfo) }
FunctionItem(R.string.password_info, icon = R.drawable.info_fill0) {
onNavigate(Destination.PasswordInfo)
}
if (SP.displayDangerousFeatures) {
if(VERSION.SDK_INT >= 26) {
FunctionItem(R.string.reset_password_token, icon = R.drawable.key_vertical_fill0) { onNavigate(ResetPasswordToken) }
FunctionItem(R.string.reset_password_token, icon = R.drawable.key_vertical_fill0) {
onNavigate(Destination.ResetPasswordToken)
}
}
FunctionItem(R.string.reset_password, icon = R.drawable.lock_reset_fill0) {
onNavigate(Destination.ResetPassword)
}
FunctionItem(R.string.reset_password, icon = R.drawable.lock_reset_fill0) { onNavigate(ResetPassword) }
}
if(VERSION.SDK_INT >= 31) {
FunctionItem(R.string.required_password_complexity, icon = R.drawable.password_fill0) { onNavigate(RequiredPasswordComplexity) }
FunctionItem(R.string.required_password_complexity, icon = R.drawable.password_fill0) {
onNavigate(Destination.RequiredPasswordComplexity)
}
}
FunctionItem(R.string.disable_keyguard_features, icon = R.drawable.screen_lock_portrait_fill0) {
onNavigate(Destination.KeyguardDisabledFeatures)
}
FunctionItem(R.string.disable_keyguard_features, icon = R.drawable.screen_lock_portrait_fill0) { onNavigate(KeyguardDisabledFeatures) }
if(privilege.device) {
FunctionItem(R.string.max_time_to_lock, icon = R.drawable.schedule_fill0) { dialog = 1 }
FunctionItem(R.string.pwd_expiration_timeout, icon = R.drawable.lock_clock_fill0) { dialog = 3 }
@@ -106,7 +114,9 @@ fun PasswordScreen(vm: MyViewModel,onNavigateUp: () -> Unit, onNavigate: (Any) -
}
FunctionItem(R.string.pwd_history, icon = R.drawable.history_fill0) { dialog = 5 }
if(VERSION.SDK_INT < 31) {
FunctionItem(R.string.required_password_quality, icon = R.drawable.password_fill0) { onNavigate(RequiredPasswordQuality) }
FunctionItem(R.string.required_password_quality, icon = R.drawable.password_fill0) {
onNavigate(Destination.RequiredPasswordQuality)
}
}
}
if(dialog != 0) {
@@ -205,8 +215,6 @@ enum class PasswordComplexity(val id: Int, val text: Int) {
High(DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH, R.string.high)
}
@Serializable object PasswordInfo
@Composable
fun PasswordInfoScreen(
getComplexity: () -> PasswordComplexity, isSufficient: () -> Boolean, isUnified: () -> Boolean,
@@ -236,8 +244,6 @@ fun PasswordInfoScreen(
data class RpTokenState(val set: Boolean, val active: Boolean)
@Serializable object ResetPasswordToken
@RequiresApi(26)
@Composable
fun ResetPasswordTokenScreen(
@@ -303,8 +309,6 @@ fun ResetPasswordTokenScreen(
}
}
@Serializable object ResetPassword
@Composable
fun ResetPasswordScreen(resetPassword: (String, String, Int) -> Boolean, onNavigateUp: () -> Unit) {
val context = LocalContext.current
@@ -358,8 +362,6 @@ fun ResetPasswordScreen(resetPassword: (String, String, Int) -> Boolean, onNavig
}
}
@Serializable object RequiredPasswordComplexity
@RequiresApi(31)
@Composable
fun RequiredPasswordComplexityScreen(
@@ -408,8 +410,6 @@ enum class KeyguardDisableMode(val text: Int) {
data class KeyguardDisableConfig(val mode: KeyguardDisableMode, val flags: Int)
@Serializable object KeyguardDisabledFeatures
@Composable
fun KeyguardDisabledFeaturesScreen(
getConfig: () -> KeyguardDisableConfig, setConfig: (KeyguardDisableConfig) -> Unit,
@@ -449,8 +449,6 @@ fun KeyguardDisabledFeaturesScreen(
}
}
@Serializable object RequiredPasswordQuality
@Composable
fun RequiredPasswordQualityScreen(onNavigateUp: () -> Unit) {
val context = LocalContext.current

View File

@@ -1,4 +1,4 @@
package com.bintianqi.owndroid
package com.bintianqi.owndroid.ui.screen
import android.content.Context
import android.content.Intent
@@ -14,6 +14,7 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardOptions
@@ -54,23 +55,31 @@ import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.dp
import androidx.core.net.toUri
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.bintianqi.owndroid.BottomPadding
import com.bintianqi.owndroid.MyNotificationChannel
import com.bintianqi.owndroid.NotificationType
import com.bintianqi.owndroid.Privilege
import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.ThemeSettings
import com.bintianqi.owndroid.adaptiveInsets
import com.bintianqi.owndroid.exportLogs
import com.bintianqi.owndroid.generateBase64Key
import com.bintianqi.owndroid.showOperationResultToast
import com.bintianqi.owndroid.ui.FunctionItem
import com.bintianqi.owndroid.ui.MyScaffold
import com.bintianqi.owndroid.ui.NavIcon
import com.bintianqi.owndroid.ui.Notes
import com.bintianqi.owndroid.ui.SwitchItem
import com.bintianqi.owndroid.ui.navigation.Destination
import kotlinx.coroutines.flow.StateFlow
import kotlinx.serialization.Serializable
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import kotlin.system.exitProcess
@Serializable object Settings
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SettingsScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) {
fun SettingsScreen(onNavigate: (Destination) -> Unit, onNavigateUp: () -> Unit) {
val context = LocalContext.current
val privilege by Privilege.status.collectAsStateWithLifecycle()
val exportLogsLauncher = rememberLauncherForActivityResult(ActivityResultContracts.CreateDocument("text/plain")) {
@@ -120,22 +129,34 @@ fun SettingsScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) {
.fillMaxSize()
.padding(paddingValues)
.verticalScroll(rememberScrollState())
.padding(bottom = 80.dp)
) {
FunctionItem(title = R.string.options, icon = R.drawable.tune_fill0) { onNavigate(SettingsOptions) }
FunctionItem(title = R.string.appearance, icon = R.drawable.format_paint_fill0) { onNavigate(Appearance) }
FunctionItem(R.string.app_lock, icon = R.drawable.lock_fill0) { onNavigate(AppLockSettings) }
if (privilege.device || privilege.profile)
FunctionItem(title = R.string.api, icon = R.drawable.code_fill0) { onNavigate(ApiSettings) }
if (privilege.device && !privilege.dhizuku)
FunctionItem(R.string.notifications, icon = R.drawable.notifications_fill0) { onNavigate(Notifications) }
FunctionItem(title = R.string.about, icon = R.drawable.info_fill0) { onNavigate(About) }
FunctionItem(R.string.options, icon = R.drawable.tune_fill0) {
onNavigate(Destination.SettingsOptions)
}
FunctionItem(R.string.appearance, icon = R.drawable.format_paint_fill0) {
onNavigate(Destination.AppearanceSettings)
}
FunctionItem(R.string.app_lock, icon = R.drawable.lock_fill0) {
onNavigate(Destination.AppLockSettings)
}
if (privilege.device || privilege.profile) {
FunctionItem(R.string.api, icon = R.drawable.code_fill0) {
onNavigate(Destination.ApiSettings)
}
}
if (privilege.device && !privilege.dhizuku) {
FunctionItem(R.string.notifications, icon = R.drawable.notifications_fill0) {
onNavigate(Destination.NotificationSettings)
}
}
FunctionItem(R.string.about, icon = R.drawable.info_fill0) {
onNavigate(Destination.About)
}
Spacer(Modifier.height(BottomPadding))
}
}
}
@Serializable object SettingsOptions
@Composable
fun SettingsOptionsScreen(
getDisplayDangerousFeatures: () -> Boolean, getShortcutsEnabled: () -> Boolean,
@@ -160,8 +181,6 @@ fun SettingsOptionsScreen(
}
}
@Serializable object Appearance
@Composable
fun AppearanceScreen(
onNavigateUp: () -> Unit, currentTheme: StateFlow<ThemeSettings>,
@@ -225,8 +244,6 @@ data class AppLockConfig(
val password: String?, val biometrics: Boolean, val whenLeaving: Boolean
)
@Serializable object AppLockSettings
@Composable
fun AppLockSettingsScreen(
config: AppLockConfig, setConfig: (AppLockConfig) -> Unit,
@@ -286,8 +303,6 @@ fun AppLockSettingsScreen(
}
}
@Serializable object ApiSettings
@Composable
fun ApiSettings(
getEnabled: () -> Boolean, setKey: (String) -> Unit, onNavigateUp: () -> Unit
@@ -326,8 +341,6 @@ fun ApiSettings(
}
}
@Serializable object Notifications
@Composable
fun NotificationsScreen(
enabledNotifications: StateFlow<List<Int>>, getState: () -> Unit,
@@ -344,8 +357,6 @@ fun NotificationsScreen(
}
}
@Serializable object About
@Composable
fun AboutScreen(onNavigateUp: () -> Unit) {
val context = LocalContext.current

View File

@@ -1,4 +1,4 @@
package com.bintianqi.owndroid.dpm
package com.bintianqi.owndroid.ui.screen
import android.annotation.SuppressLint
import android.app.admin.DevicePolicyManager
@@ -131,7 +131,9 @@ import com.bintianqi.owndroid.ui.MyScaffold
import com.bintianqi.owndroid.ui.MySmallTitleScaffold
import com.bintianqi.owndroid.ui.NavIcon
import com.bintianqi.owndroid.ui.Notes
import com.bintianqi.owndroid.ui.PackageNameTextField
import com.bintianqi.owndroid.ui.SwitchItem
import com.bintianqi.owndroid.ui.navigation.Destination
import com.bintianqi.owndroid.yesOrNo
import com.google.accompanist.drawablepainter.rememberDrawablePainter
import kotlinx.coroutines.channels.Channel
@@ -139,30 +141,33 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import kotlinx.serialization.Serializable
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import java.util.TimeZone
import kotlin.math.roundToLong
@Serializable object SystemManager
@Composable
fun SystemManagerScreen(
vm: MyViewModel, onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit
vm: MyViewModel, onNavigateUp: () -> Unit, onNavigate: (Destination) -> Unit
) {
val context = LocalContext.current
val privilege by Privilege.status.collectAsStateWithLifecycle()
/** 1: reboot, 2: bug report, 3: org name, 4: org id, 5: enrollment specific id*/
var dialog by rememberSaveable { mutableIntStateOf(0) }
MyScaffold(R.string.system, onNavigateUp, 0.dp) {
FunctionItem(R.string.options, icon = R.drawable.tune_fill0) { onNavigate(SystemOptions) }
FunctionItem(R.string.keyguard, icon = R.drawable.screen_lock_portrait_fill0) { onNavigate(Keyguard) }
FunctionItem(R.string.options, icon = R.drawable.tune_fill0) {
onNavigate(Destination.SystemOptions)
}
FunctionItem(R.string.keyguard, icon = R.drawable.screen_lock_portrait_fill0) {
onNavigate(Destination.Keyguard)
}
if(VERSION.SDK_INT >= 24 && privilege.device && !privilege.dhizuku)
FunctionItem(R.string.hardware_monitor, icon = R.drawable.memory_fill0) { onNavigate(HardwareMonitor) }
FunctionItem(R.string.hardware_monitor, icon = R.drawable.memory_fill0) {
onNavigate(Destination.HardwareMonitor)
}
FunctionItem(R.string.default_input_method, icon = R.drawable.keyboard_fill0) {
onNavigate(DefaultInputMethod)
onNavigate(Destination.DefaultInputMethod)
}
if(VERSION.SDK_INT >= 24 && privilege.device) {
FunctionItem(R.string.reboot, icon = R.drawable.restart_alt_fill0) { dialog = 1 }
@@ -171,32 +176,56 @@ fun SystemManagerScreen(
FunctionItem(R.string.bug_report, icon = R.drawable.bug_report_fill0) { dialog = 2 }
}
if(VERSION.SDK_INT >= 28 && (privilege.device || privilege.org)) {
FunctionItem(R.string.change_time, icon = R.drawable.schedule_fill0) { onNavigate(ChangeTime) }
FunctionItem(R.string.change_timezone, icon = R.drawable.globe_fill0) { onNavigate(ChangeTimeZone) }
FunctionItem(R.string.change_time, icon = R.drawable.schedule_fill0) {
onNavigate(Destination.ChangeTime)
}
FunctionItem(R.string.change_timezone, icon = R.drawable.globe_fill0) {
onNavigate(Destination.ChangeTimezone)
}
}
if (VERSION.SDK_INT >= 36 && (privilege.device || privilege.org)) {
FunctionItem(R.string.auto_time_policy, icon = R.drawable.schedule_fill0) { onNavigate(AutoTimePolicy) }
FunctionItem(R.string.auto_timezone_policy, icon = R.drawable.globe_fill0) { onNavigate(AutoTimeZonePolicy) }
FunctionItem(R.string.auto_time_policy, icon = R.drawable.schedule_fill0) {
onNavigate(Destination.AutoTimePolicy)
}
FunctionItem(R.string.auto_timezone_policy, icon = R.drawable.globe_fill0) {
onNavigate(Destination.AutoTimezonePolicy)
}
}
/*if(VERSION.SDK_INT >= 28 && (deviceOwner || profileOwner))
FunctionItem(R.string.key_pairs, icon = R.drawable.key_vertical_fill0) { navCtrl.navigate("KeyPairs") }*/
if(VERSION.SDK_INT >= 35 && (privilege.device || (privilege.profile && privilege.affiliated)))
FunctionItem(R.string.content_protection_policy, icon = R.drawable.search_fill0) { onNavigate(ContentProtectionPolicy) }
FunctionItem(R.string.permission_policy, icon = R.drawable.key_fill0) { onNavigate(PermissionPolicy) }
FunctionItem(R.string.content_protection_policy, icon = R.drawable.search_fill0) {
onNavigate(Destination.ContentProtectionPolicy)
}
FunctionItem(R.string.permission_policy, icon = R.drawable.key_fill0) {
onNavigate(Destination.PermissionPolicy)
}
if(VERSION.SDK_INT >= 34 && privilege.device) {
FunctionItem(R.string.mte_policy, icon = R.drawable.memory_fill0) { onNavigate(MtePolicy) }
FunctionItem(R.string.mte_policy, icon = R.drawable.memory_fill0) {
onNavigate(Destination.MtePolicy)
}
}
if(VERSION.SDK_INT >= 31) {
FunctionItem(R.string.nearby_streaming_policy, icon = R.drawable.share_fill0) { onNavigate(NearbyStreamingPolicy) }
FunctionItem(R.string.nearby_streaming_policy, icon = R.drawable.share_fill0) {
onNavigate(Destination.NearbyStreamingPolicy)
}
}
if (VERSION.SDK_INT >= 28 && privilege.device) {
FunctionItem(R.string.lock_task_mode, icon = R.drawable.lock_fill0) { onNavigate(LockTaskMode) }
FunctionItem(R.string.lock_task_mode, icon = R.drawable.lock_fill0) {
onNavigate(Destination.LockTaskMode)
}
}
FunctionItem(R.string.ca_cert, icon = R.drawable.license_fill0) {
onNavigate(Destination.CaCert)
}
FunctionItem(R.string.ca_cert, icon = R.drawable.license_fill0) { onNavigate(CaCert) }
if(VERSION.SDK_INT >= 26 && !privilege.dhizuku && (privilege.device || privilege.org)) {
FunctionItem(R.string.security_logging, icon = R.drawable.description_fill0) { onNavigate(SecurityLogging) }
FunctionItem(R.string.security_logging, icon = R.drawable.description_fill0) {
onNavigate(Destination.SecurityLogging)
}
}
FunctionItem(R.string.device_info, icon = R.drawable.perm_device_information_fill0) {
onNavigate(Destination.DeviceInfo)
}
FunctionItem(R.string.device_info, icon = R.drawable.perm_device_information_fill0) { onNavigate(DeviceInfo) }
if(VERSION.SDK_INT >= 24 && (privilege.profile || (VERSION.SDK_INT >= 26 && privilege.device))) {
FunctionItem(R.string.org_name, icon = R.drawable.corporate_fare_fill0) { dialog = 3 }
}
@@ -207,23 +236,37 @@ fun SystemManagerScreen(
FunctionItem(R.string.enrollment_specific_id, icon = R.drawable.id_card_fill0) { dialog = 5 }
}
if(VERSION.SDK_INT >= 24 && (privilege.device || privilege.org)) {
FunctionItem(R.string.lock_screen_info, icon = R.drawable.screen_lock_portrait_fill0) { onNavigate(LockScreenInfo) }
FunctionItem(R.string.lock_screen_info, icon = R.drawable.screen_lock_portrait_fill0) {
onNavigate(Destination.LockScreenInfo)
}
}
if(VERSION.SDK_INT >= 24) {
FunctionItem(R.string.support_messages, icon = R.drawable.chat_fill0) { onNavigate(SupportMessage) }
FunctionItem(R.string.support_messages, icon = R.drawable.chat_fill0) {
onNavigate(Destination.SupportMessage)
}
}
FunctionItem(R.string.disable_account_management, icon = R.drawable.account_circle_fill0) {
onNavigate(Destination.DisableAccountManagement)
}
FunctionItem(R.string.disable_account_management, icon = R.drawable.account_circle_fill0) { onNavigate(DisableAccountManagement) }
if (privilege.device || privilege.org) {
FunctionItem(R.string.system_update_policy, icon = R.drawable.system_update_fill0) { onNavigate(SetSystemUpdatePolicy) }
FunctionItem(R.string.system_update_policy, icon = R.drawable.system_update_fill0) {
onNavigate(Destination.SystemUpdatePolicy)
}
}
if(VERSION.SDK_INT >= 29 && (privilege.device || privilege.org)) {
FunctionItem(R.string.install_system_update, icon = R.drawable.system_update_fill0) { onNavigate(InstallSystemUpdate) }
FunctionItem(R.string.install_system_update, icon = R.drawable.system_update_fill0) {
onNavigate(Destination.InstallSystemUpdate)
}
}
if(VERSION.SDK_INT >= 30 && (privilege.device || privilege.org)) {
FunctionItem(R.string.frp_policy, icon = R.drawable.device_reset_fill0) { onNavigate(FrpPolicy) }
FunctionItem(R.string.frp_policy, icon = R.drawable.device_reset_fill0) {
onNavigate(Destination.FrpPolicy)
}
}
if(SP.displayDangerousFeatures && !privilege.work) {
FunctionItem(R.string.wipe_data, icon = R.drawable.device_reset_fill0) { onNavigate(WipeData) }
FunctionItem(R.string.wipe_data, icon = R.drawable.device_reset_fill0) {
onNavigate(Destination.WipeData)
}
}
}
if((dialog == 1 || dialog == 2) && VERSION.SDK_INT >= 24) AlertDialog(
@@ -329,13 +372,11 @@ data class SystemOptionsStatus(
val stayOnWhilePluggedIn: Boolean = false
)
@Serializable object SystemOptions
class GlobalSetting(val icon: Int, val name: Int, val setting: String) // also for secure settings
// STAY_ON_WHILE_PLUGGED_IN is set separately
val globalSettings = listOf(
GlobalSetting(R.drawable.cell_tower_fill0, R.string.data_roaming, Settings.Global.DATA_ROAMING),
//GlobalSetting(R.drawable.cell_tower_fill0, R.string.data_roaming, Settings.Global.DATA_ROAMING),
GlobalSetting(R.drawable.adb_fill0, R.string.enable_adb, Settings.Global.ADB_ENABLED),
GlobalSetting(R.drawable.usb_fill0, R.string.enable_usb_mass_storage,
Settings.Global.USB_MASS_STORAGE_ENABLED),
@@ -457,8 +498,6 @@ fun SystemOptionsScreen(vm: MyViewModel, onNavigateUp: () -> Unit) {
)
}
@Serializable object Keyguard
@Composable
fun KeyguardScreen(
setKeyguardDisabled: (Boolean) -> Boolean, lock: (Boolean) -> Unit, onNavigateUp: () -> Unit
@@ -519,8 +558,6 @@ val temperatureTypes = mapOf(
HardwarePropertiesManager.DEVICE_TEMPERATURE_SKIN to R.string.skin_temp
)
@Serializable object HardwareMonitor
@RequiresApi(24)
@Composable
fun HardwareMonitorScreen(
@@ -586,8 +623,6 @@ fun HardwareMonitorScreen(
}
}
@Serializable object DefaultInputMethod
@Composable
fun DefaultInputMethodScreen(
getCurrentIm: () -> String, imListState: StateFlow<List<Pair<String, AppInfo>>>,
@@ -632,8 +667,6 @@ fun DefaultInputMethodScreen(
}
}
@Serializable object ChangeTime
@OptIn(ExperimentalMaterial3Api::class)
@RequiresApi(28)
@Composable
@@ -768,8 +801,6 @@ fun ChangeTimeScreen(setTime: (Long, Boolean) -> Boolean, onNavigateUp: () -> Un
}
}
@Serializable object ChangeTimeZone
@RequiresApi(28)
@Composable
fun ChangeTimeZoneScreen(setTimeZone: (String) -> Boolean, onNavigateUp: () -> Unit) {
@@ -835,8 +866,6 @@ fun ChangeTimeZoneScreen(setTimeZone: (String) -> Boolean, onNavigateUp: () -> U
)
}
@Serializable object AutoTimePolicy
@RequiresApi(36)
@Composable
fun AutoTimePolicyScreen(
@@ -866,8 +895,6 @@ fun AutoTimePolicyScreen(
}
}
@Serializable object AutoTimeZonePolicy
@RequiresApi(36)
@Composable
fun AutoTimeZonePolicyScreen(
@@ -1074,8 +1101,6 @@ fun KeyPairs(navCtrl: NavHostController) {
}
}*/
@Serializable object ContentProtectionPolicy
@RequiresApi(35)
@Composable
fun ContentProtectionPolicyScreen(
@@ -1106,8 +1131,6 @@ fun ContentProtectionPolicyScreen(
}
}
@Serializable object PermissionPolicy
@Composable
fun PermissionPolicyScreen(
getPolicy: () -> Int, setPolicy: (Int) -> Unit, onNavigateUp: () -> Unit
@@ -1139,8 +1162,6 @@ fun PermissionPolicyScreen(
}
}
@Serializable object MtePolicy
@RequiresApi(34)
@Composable
fun MtePolicyScreen(
@@ -1167,8 +1188,6 @@ fun MtePolicyScreen(
}
}
@Serializable object NearbyStreamingPolicy
@RequiresApi(31)
@Composable
fun NearbyStreamingPolicyScreen(
@@ -1241,8 +1260,6 @@ fun NearbyStreamingPolicyScreen(
}
}
@Serializable object LockTaskMode
@OptIn(ExperimentalMaterial3Api::class)
@RequiresApi(28)
@Composable
@@ -1467,8 +1484,6 @@ data class CaCertInfo(
val bytes: ByteArray
)
@Serializable object CaCert
@OptIn(ExperimentalMaterial3Api::class, ExperimentalStdlibApi::class)
@Composable
fun CaCertScreen(
@@ -1639,8 +1654,6 @@ fun CaCertScreen(
}
}
@Serializable object SecurityLogging
@RequiresApi(24)
@Composable
fun SecurityLoggingScreen(
@@ -1745,8 +1758,6 @@ fun SecurityLoggingScreen(
)
}
@Serializable object DisableAccountManagement
@Composable
fun DisableAccountManagementScreen(
mdAccounts: StateFlow<List<String>>, getMdAccounts: () -> Unit,
@@ -1798,8 +1809,6 @@ data class FrpPolicyInfo(
val accounts: List<String>
)
@Serializable object FrpPolicy
@RequiresApi(30)
@Composable
fun FrpPolicyScreen(
@@ -1874,8 +1883,6 @@ fun FrpPolicyScreen(
}
}
@Serializable object WipeData
@Composable
fun WipeDataScreen(
wipeData: (Boolean, Int, String) -> Unit, onNavigateUp: () -> Unit
@@ -1988,8 +1995,6 @@ fun WipeDataScreen(
data class SystemUpdatePolicyInfo(val type: Int, val start: Int, val end: Int)
data class PendingSystemUpdateInfo(val exists: Boolean, val time: Long, val securityPatch: Boolean)
@Serializable object SetSystemUpdatePolicy
@Composable
fun SystemUpdatePolicyScreen(
getPolicy: () -> SystemUpdatePolicyInfo, setPolicy: (SystemUpdatePolicyInfo) -> Unit,
@@ -2083,8 +2088,6 @@ fun SystemUpdatePolicyScreen(
}
}
@Serializable object InstallSystemUpdate
@SuppressLint("NewApi")
@Composable
fun InstallSystemUpdateScreen(

View File

@@ -1,4 +1,4 @@
package com.bintianqi.owndroid.dpm
package com.bintianqi.owndroid.ui.screen
import androidx.annotation.RequiresApi
import androidx.compose.foundation.background
@@ -63,6 +63,7 @@ import com.bintianqi.owndroid.showOperationResultToast
import com.bintianqi.owndroid.ui.FunctionItem
import com.bintianqi.owndroid.ui.MyLazyScaffold
import com.bintianqi.owndroid.ui.NavIcon
import com.bintianqi.owndroid.ui.navigation.Destination
import kotlinx.coroutines.flow.StateFlow
import kotlinx.serialization.Serializable
@@ -74,13 +75,11 @@ data class Restriction(
val requiresApi: Int = 0
)
@Serializable object UserRestriction
@OptIn(ExperimentalMaterial3Api::class)
@RequiresApi(24)
@Composable
fun UserRestrictionScreen(
getRestrictions: () -> Unit,onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit
getRestrictions: () -> Unit,onNavigateUp: () -> Unit, onNavigate: (Destination) -> Unit
) {
val privilege by Privilege.status.collectAsStateWithLifecycle()
val sb = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
@@ -92,7 +91,7 @@ fun UserRestrictionScreen(
{ Text(stringResource(R.string.user_restriction)) },
navigationIcon = { NavIcon(onNavigateUp) },
actions = {
IconButton({ onNavigate(UserRestrictionEditor) }) {
IconButton({ onNavigate(Destination.UserRestrictionEditor) }) {
Icon(Icons.Default.Edit, null)
}
},
@@ -119,7 +118,7 @@ fun UserRestrictionScreen(
Spacer(Modifier.padding(vertical = 2.dp))
UserRestrictionCategory.entries.forEach {
FunctionItem(it.title, icon = it.icon) {
onNavigate(UserRestrictionOptions(it.name))
onNavigate(Destination.UserRestrictionOptions(it.name))
}
}
Row(
@@ -137,13 +136,10 @@ fun UserRestrictionScreen(
}
}
@Serializable
data class UserRestrictionOptions(val id: String)
@RequiresApi(24)
@Composable
fun UserRestrictionOptionsScreen(
args: UserRestrictionOptions, userRestrictions: StateFlow<Map<String, Boolean>>,
args: Destination.UserRestrictionOptions, userRestrictions: StateFlow<Map<String, Boolean>>,
setRestriction: (String, Boolean) -> Boolean, setShortcut: (String) -> Boolean,
onNavigateUp: () -> Unit
) {
@@ -188,8 +184,6 @@ fun UserRestrictionOptionsScreen(
}
}
@Serializable object UserRestrictionEditor
@OptIn(ExperimentalMaterial3Api::class)
@RequiresApi(24)
@Composable

View File

@@ -1,4 +1,4 @@
package com.bintianqi.owndroid.dpm
package com.bintianqi.owndroid.ui.screen
import android.app.admin.DevicePolicyManager
import android.graphics.Bitmap
@@ -77,15 +77,13 @@ import com.bintianqi.owndroid.ui.ListItem
import com.bintianqi.owndroid.ui.MyScaffold
import com.bintianqi.owndroid.ui.Notes
import com.bintianqi.owndroid.ui.SwitchItem
import com.bintianqi.owndroid.ui.navigation.Destination
import com.bintianqi.owndroid.uriToStream
import com.bintianqi.owndroid.yesOrNo
import kotlinx.coroutines.flow.StateFlow
import kotlinx.serialization.Serializable
@Serializable object Users
@Composable
fun UsersScreen(vm: MyViewModel, onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) {
fun UsersScreen(vm: MyViewModel, onNavigateUp: () -> Unit, onNavigate: (Destination) -> Unit) {
val context = LocalContext.current
val privilege by Privilege.status.collectAsStateWithLifecycle()
/** 1: logout */
@@ -94,17 +92,27 @@ fun UsersScreen(vm: MyViewModel, onNavigateUp: () -> Unit, onNavigate: (Any) ->
if(VERSION.SDK_INT >= 28 && privilege.profile && privilege.affiliated) {
FunctionItem(R.string.logout, icon = R.drawable.logout_fill0) { dialog = 1 }
}
FunctionItem(R.string.user_info, icon = R.drawable.person_fill0) { onNavigate(UserInfo) }
FunctionItem(R.string.user_info, icon = R.drawable.person_fill0) {
onNavigate(Destination.UserInfo)
}
if(VERSION.SDK_INT >= 28 && privilege.device) {
FunctionItem(R.string.options, icon = R.drawable.tune_fill0) { onNavigate(UsersOptions) }
FunctionItem(R.string.options, icon = R.drawable.tune_fill0) {
onNavigate(Destination.UsersOptions)
}
}
if(privilege.device) {
FunctionItem(R.string.user_operation, icon = R.drawable.sync_alt_fill0) { onNavigate(UserOperation) }
FunctionItem(R.string.user_operation, icon = R.drawable.sync_alt_fill0) {
onNavigate(Destination.UserOperation)
}
}
if(VERSION.SDK_INT >= 24 && privilege.device) {
FunctionItem(R.string.create_user, icon = R.drawable.person_add_fill0) { onNavigate(CreateUser) }
FunctionItem(R.string.create_user, icon = R.drawable.person_add_fill0) {
onNavigate(Destination.CreateUser)
}
}
FunctionItem(R.string.change_username, icon = R.drawable.edit_fill0) {
onNavigate(Destination.ChangeUsername)
}
FunctionItem(R.string.change_username, icon = R.drawable.edit_fill0) { onNavigate(ChangeUsername) }
var changeUserIconDialog by remember { mutableStateOf(false) }
var bitmap: Bitmap? by remember { mutableStateOf(null) }
val launcher = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) {
@@ -123,10 +131,14 @@ fun UsersScreen(vm: MyViewModel, onNavigateUp: () -> Unit, onNavigate: (Any) ->
changeUserIconDialog = false
}) { changeUserIconDialog = false }
if(VERSION.SDK_INT >= 28 && privilege.device) {
FunctionItem(R.string.user_session_msg, icon = R.drawable.notifications_fill0) { onNavigate(UserSessionMessage) }
FunctionItem(R.string.user_session_msg, icon = R.drawable.notifications_fill0) {
onNavigate(Destination.UserSessionMessage)
}
}
if(VERSION.SDK_INT >= 26) {
FunctionItem(R.string.affiliation_id, icon = R.drawable.id_card_fill0) { onNavigate(AffiliationId) }
FunctionItem(R.string.affiliation_id, icon = R.drawable.id_card_fill0) {
onNavigate(Destination.AffiliationId)
}
}
}
if (VERSION.SDK_INT >= 28 && dialog == 1) AlertDialog(
@@ -151,8 +163,6 @@ fun UsersScreen(vm: MyViewModel, onNavigateUp: () -> Unit, onNavigate: (Any) ->
)
}
@Serializable object UsersOptions
@Composable
fun UsersOptionsScreen(
getLogoutEnabled: () -> Boolean, setLogoutEnabled: (Boolean) -> Unit, onNavigateUp: () -> Unit
@@ -176,8 +186,6 @@ data class UserInformation(
val serial: Long = 0
)
@Serializable object UserInfo
@Composable
fun UserInfoScreen(getInfo: () -> UserInformation, onNavigateUp: () -> Unit) {
var info by remember { mutableStateOf(UserInformation()) }
@@ -219,8 +227,6 @@ enum class UserOperationType {
Start, Switch, Stop, Delete
}
@Serializable object UserOperation
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun UserOperationScreen(
@@ -370,8 +376,6 @@ fun UserOperationScreen(
data class CreateUserResult(val message: Int, val serial: Long = -1)
@Serializable object CreateUser
@RequiresApi(24)
@Composable
fun CreateUserScreen(
@@ -436,8 +440,6 @@ fun CreateUserScreen(
}
}
@Serializable object AffiliationId
@RequiresApi(26)
@Composable
fun AffiliationIdScreen(
@@ -478,8 +480,6 @@ fun AffiliationIdScreen(
}
}
@Serializable object ChangeUsername
@Composable
fun ChangeUsernameScreen(setName: (String) -> Unit, onNavigateUp: () -> Unit) {
val context = LocalContext.current
@@ -507,8 +507,6 @@ fun ChangeUsernameScreen(setName: (String) -> Unit, onNavigateUp: () -> Unit) {
}
}
@Serializable object UserSessionMessage
@RequiresApi(28)
@Composable
fun UserSessionMessageScreen(

View File

@@ -1,4 +1,4 @@
package com.bintianqi.owndroid.dpm
package com.bintianqi.owndroid.ui.screen
import android.app.admin.DevicePolicyManager
import android.app.admin.DevicePolicyManager.WIPE_EUICC
@@ -77,23 +77,30 @@ import com.bintianqi.owndroid.ui.MyScaffold
import com.bintianqi.owndroid.ui.NavIcon
import com.bintianqi.owndroid.ui.Notes
import com.bintianqi.owndroid.ui.SwitchItem
import com.bintianqi.owndroid.ui.navigation.Destination
import com.bintianqi.owndroid.yesOrNo
import kotlinx.serialization.Serializable
@Serializable object WorkProfile
@Composable
fun WorkProfileScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) {
fun WorkProfileScreen(onNavigateUp: () -> Unit, onNavigate: (Destination) -> Unit) {
val privilege by Privilege.status.collectAsStateWithLifecycle()
MyScaffold(R.string.work_profile, onNavigateUp, 0.dp) {
if(VERSION.SDK_INT >= 30 && !privilege.org) {
FunctionItem(R.string.org_owned_work_profile, icon = R.drawable.corporate_fare_fill0) { onNavigate(OrganizationOwnedProfile) }
FunctionItem(R.string.org_owned_work_profile, icon = R.drawable.corporate_fare_fill0) {
onNavigate(Destination.OrganizationOwnedProfile)
}
}
if(privilege.org) {
FunctionItem(R.string.suspend_personal_app, icon = R.drawable.block_fill0) { onNavigate(SuspendPersonalApp) }
FunctionItem(R.string.suspend_personal_app, icon = R.drawable.block_fill0) {
onNavigate(Destination.SuspendPersonalApp)
}
}
FunctionItem(R.string.intent_filter, icon = R.drawable.filter_alt_fill0) {
onNavigate(Destination.CrossProfileIntentFilter)
}
FunctionItem(R.string.delete_work_profile, icon = R.drawable.delete_forever_fill0) {
onNavigate(Destination.DeleteWorkProfile)
}
FunctionItem(R.string.intent_filter, icon = R.drawable.filter_alt_fill0) { onNavigate(CrossProfileIntentFilter) }
FunctionItem(R.string.delete_work_profile, icon = R.drawable.delete_forever_fill0) { onNavigate(DeleteWorkProfile) }
}
}
@@ -102,8 +109,6 @@ data class CreateWorkProfileOptions(
val accountName: String, val accountType: String, val keepAccount: Boolean
)
@Serializable object CreateWorkProfile
@Composable
fun CreateWorkProfileScreen(
createIntent: (CreateWorkProfileOptions) -> Intent, onNavigateUp: () -> Unit
@@ -165,8 +170,6 @@ fun CreateWorkProfileScreen(
}
}
@Serializable object OrganizationOwnedProfile
@RequiresApi(30)
@Composable
fun OrganizationOwnedProfileScreen(
@@ -205,8 +208,6 @@ fun OrganizationOwnedProfileScreen(
val activateOrgProfileCommand = "dpm mark-profile-owner-on-organization-owned-device --user " +
"${Binder.getCallingUid()/100000} com.bintianqi.owndroid/com.bintianqi.owndroid.Receiver"
@Serializable object SuspendPersonalApp
@RequiresApi(30)
@Composable
fun SuspendPersonalAppScreen(
@@ -267,8 +268,6 @@ val crossProfileIntentFilterPresets = mapOf(
IntentFilterOptions(Intent.ACTION_SEND, Intent.CATEGORY_DEFAULT, "*/*", 3)
)
@Serializable object CrossProfileIntentFilter
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CrossProfileIntentFilterScreen(
@@ -452,8 +451,6 @@ fun CrossProfileIntentFilterScreen(
}
}
@Serializable object DeleteWorkProfile
@Composable
fun DeleteWorkProfileScreen(
deleteProfile: (Boolean, Int, String) -> Unit, onNavigateUp: () -> Unit

View File

@@ -1,4 +1,4 @@
package com.bintianqi.owndroid.dpm
package com.bintianqi.owndroid.ui.screen
import android.app.admin.DevicePolicyManager
import android.content.ComponentName
@@ -88,8 +88,10 @@ import com.bintianqi.owndroid.HorizontalPadding
import com.bintianqi.owndroid.MyViewModel
import com.bintianqi.owndroid.Privilege
import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.Settings
import com.bintianqi.owndroid.adaptiveInsets
import com.bintianqi.owndroid.dpm.ACTIVATE_DEVICE_OWNER_COMMAND
import com.bintianqi.owndroid.dpm.DelegatedAdmin
import com.bintianqi.owndroid.dpm.DeviceAdmin
import com.bintianqi.owndroid.showOperationResultToast
import com.bintianqi.owndroid.ui.CircularProgressDialog
import com.bintianqi.owndroid.ui.InfoItem
@@ -98,21 +100,20 @@ import com.bintianqi.owndroid.ui.MyScaffold
import com.bintianqi.owndroid.ui.MySmallTitleScaffold
import com.bintianqi.owndroid.ui.NavIcon
import com.bintianqi.owndroid.ui.Notes
import com.bintianqi.owndroid.ui.PackageNameTextField
import com.bintianqi.owndroid.ui.SwitchItem
import com.bintianqi.owndroid.ui.navigation.Destination
import com.bintianqi.owndroid.yesOrNo
import com.google.accompanist.drawablepainter.rememberDrawablePainter
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.StateFlow
import kotlinx.serialization.Serializable
@Serializable data class WorkModes(val canNavigateUp: Boolean)
@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class)
@Composable
fun WorkModesScreen(
vm: MyViewModel, params: WorkModes, onNavigateUp: () -> Unit, onActivate: () -> Unit,
onDeactivate: () -> Unit, onNavigate: (Any) -> Unit
vm: MyViewModel, params: Destination.WorkingModes, onNavigateUp: () -> Unit,
onActivate: () -> Unit, onDeactivate: () -> Unit, onNavigate: (Destination) -> Unit
) {
val privilege by Privilege.status.collectAsStateWithLifecycle()
/** 0: none, 1: device owner, 2: circular progress indicator, 3: result, 4: deactivate, 5: command */
@@ -162,7 +163,7 @@ fun WorkModesScreen(
{ Text(stringResource(R.string.delegated_admins)) },
{
expanded = false
onNavigate(DelegatedAdmins)
onNavigate(Destination.DelegatedAdmins)
},
leadingIcon = { Icon(painterResource(R.drawable.admin_panel_settings_fill0), null) }
)
@@ -170,13 +171,13 @@ fun WorkModesScreen(
{ Text(stringResource(R.string.transfer_ownership)) },
{
expanded = false
onNavigate(TransferOwnership)
onNavigate(Destination.TransferOwnership)
},
leadingIcon = { Icon(painterResource(R.drawable.swap_horiz_fill0), null) }
)
}
}
if(!params.canNavigateUp) IconButton({ onNavigate(Settings) }) {
if(!params.canNavigateUp) IconButton({ onNavigate(Destination.Settings) }) {
Icon(Icons.Default.Settings, null)
}
}
@@ -210,14 +211,14 @@ fun WorkModesScreen(
privilege.work || (VERSION.SDK_INT < 24 || vm.isCreatingWorkProfileAllowed())
) {
WorkingModeItem(R.string.work_profile, privilege.work) {
if (!privilege.work) onNavigate(CreateWorkProfile)
if (!privilege.work) onNavigate(Destination.CreateWorkProfile)
}
}
if (privilege.activated && !privilege.dhizuku) Row(
Modifier
.padding(top = 20.dp)
.fillMaxWidth()
.clickable { onNavigate(DhizukuServerSettings) }
.clickable { onNavigate(Destination.DhizukuServerSettings) }
.padding(vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically
) {
@@ -357,10 +358,6 @@ fun WorkingModeItem(text: Int, active: Boolean, onClick: () -> Unit) {
}
}
const val ACTIVATE_DEVICE_OWNER_COMMAND = "dpm set-device-owner com.bintianqi.owndroid/.Receiver"
@Serializable object DhizukuServerSettings
@Composable
fun DhizukuServerSettingsScreen(
dhizukuClients: StateFlow<List<Pair<DhizukuClientInfo, AppInfo>>>,
@@ -446,8 +443,6 @@ fun DhizukuServerSettingsScreen(
}
}
@Serializable object LockScreenInfo
@RequiresApi(24)
@Composable
fun LockScreenInfoScreen(
@@ -509,15 +504,11 @@ val delegatedScopesList = listOf(
DelegatedScope(DevicePolicyManager.DELEGATION_SECURITY_LOGGING, R.string.security_logging, 31)
).filter { VERSION.SDK_INT >= it.requiresApi }
data class DelegatedAdmin(val app: AppInfo, val scopes: List<String>)
@Serializable object DelegatedAdmins
@RequiresApi(26)
@Composable
fun DelegatedAdminsScreen(
delegatedAdmins: StateFlow<List<DelegatedAdmin>>, getDelegatedAdmins: () -> Unit,
onNavigateUp: () -> Unit, onNavigate: (AddDelegatedAdmin) -> Unit
onNavigateUp: () -> Unit, onNavigate: (Destination.DelegatedAdminDetails) -> Unit
) {
val admins by delegatedAdmins.collectAsStateWithLifecycle()
LaunchedEffect(Unit) { getDelegatedAdmins() }
@@ -537,7 +528,7 @@ fun DelegatedAdminsScreen(
Text(app.name, Modifier.alpha(0.8F), style = typography.bodyMedium)
}
}
IconButton({ onNavigate(AddDelegatedAdmin(app.name, scopes)) }) {
IconButton({ onNavigate(Destination.DelegatedAdminDetails(app.name, scopes)) }) {
Icon(Icons.Outlined.Edit, null)
}
}
@@ -546,7 +537,7 @@ fun DelegatedAdminsScreen(
Row(
modifier = Modifier
.fillMaxWidth()
.clickable { onNavigate(AddDelegatedAdmin()) }
.clickable { onNavigate(Destination.DelegatedAdminDetails("", emptyList())) }
.padding(12.dp),
verticalAlignment = Alignment.CenterVertically
) {
@@ -557,12 +548,10 @@ fun DelegatedAdminsScreen(
}
}
@Serializable data class AddDelegatedAdmin(val pkg: String = "", val scopes: List<String> = emptyList())
@RequiresApi(26)
@Composable
fun AddDelegatedAdminScreen(
chosenPackage: Channel<String>, onChoosePackage: () -> Unit, data: AddDelegatedAdmin,
chosenPackage: Channel<String>, onChoosePackage: () -> Unit, data: Destination.DelegatedAdminDetails,
setDelegatedAdmin: (String, List<String>) -> Unit, onNavigateUp: () -> Unit
) {
val updateMode = data.pkg.isNotEmpty()
@@ -620,8 +609,6 @@ fun AddDelegatedAdminScreen(
}
}
@Serializable object DeviceInfo
@Composable
fun DeviceInfoScreen(vm: MyViewModel, onNavigateUp: () -> Unit) {
val privilege by Privilege.status.collectAsStateWithLifecycle()
@@ -657,8 +644,6 @@ fun DeviceInfoScreen(vm: MyViewModel, onNavigateUp: () -> Unit) {
)
}
@Serializable object SupportMessage
@RequiresApi(24)
@Composable
fun SupportMessageScreen(
@@ -739,10 +724,6 @@ fun SupportMessageScreen(
}
}
data class DeviceAdmin(val app: AppInfo, val admin: ComponentName)
@Serializable object TransferOwnership
@RequiresApi(28)
@Composable
fun TransferOwnershipScreen(

View File

@@ -2,7 +2,7 @@
agp = "9.0.1"
kotlin = "2.3.10"
navigation-compose = "2.9.7"
nav3 = "1.0.1"
composeBom = "2026.02.00"
accompanist-drawablepainter = "0.37.3"
accompanist-permissions = "0.37.3"
@@ -21,10 +21,12 @@ androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "u
androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
androidx-activity-compose = { module = "androidx.activity:activity-compose" }
androidx-material3 = { module = "androidx.compose.material3:material3" }
androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigation-compose" }
androidx-fragment = { group = "androidx.fragment", name = "fragment", version.ref = "fragment" }
material-icons-core = { group = "androidx.compose.material", name = "material-icons-core" }
androidx-nav3-runtime = { group = "androidx.navigation3", name = "navigation3-runtime", version.ref = "nav3" }
androidx-nav3-ui = { group = "androidx.navigation3", name = "navigation3-ui", version.ref = "nav3" }
accompanist-drawablepainter = { module = "com.google.accompanist:accompanist-drawablepainter", version.ref = "accompanist-drawablepainter" }
accompanist-permissions = { group = "com.google.accompanist", name = "accompanist-permissions", version.ref = "accompanist-permissions" }