From 380675cf8faad1e341072cc6b385f049b57b1060 Mon Sep 17 00:00:00 2001 From: BinTianqi Date: Sat, 7 Dec 2024 12:31:13 +0800 Subject: [PATCH] Use main NavHost to navigate across all pages Upgrade dependencies Upload dependencies to GitHub in workflow --- .github/workflows/build.yml | 3 + .../com/bintianqi/owndroid/MainActivity.kt | 270 +++++++++++++----- .../java/com/bintianqi/owndroid/Settings.kt | 97 ++----- .../bintianqi/owndroid/dpm/Applications.kt | 32 +-- .../java/com/bintianqi/owndroid/dpm/DPM.kt | 16 +- .../bintianqi/owndroid/dpm/ManagedProfile.kt | 104 ++----- .../com/bintianqi/owndroid/dpm/Network.kt | 217 +++++--------- .../com/bintianqi/owndroid/dpm/Password.kt | 149 +++------- .../com/bintianqi/owndroid/dpm/Permissions.kt | 135 +++------ .../dpm/{ShizukuActivate.kt => Shizuku.kt} | 46 +-- .../java/com/bintianqi/owndroid/dpm/System.kt | 238 ++++----------- .../bintianqi/owndroid/dpm/UserRestriction.kt | 124 ++------ .../java/com/bintianqi/owndroid/dpm/Users.kt | 143 ++-------- .../com/bintianqi/owndroid/ui/Components.kt | 81 ++++-- app/src/main/res/values-ru/strings.xml | 20 +- app/src/main/res/values-tr/strings.xml | 20 +- app/src/main/res/values-zh-rCN/strings.xml | 20 +- app/src/main/res/values/strings.xml | 22 +- gradle/libs.versions.toml | 10 +- 19 files changed, 647 insertions(+), 1100 deletions(-) rename app/src/main/java/com/bintianqi/owndroid/dpm/{ShizukuActivate.kt => Shizuku.kt} (88%) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b628522..c9b8903 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -58,6 +58,9 @@ jobs: name: OwnDroid-CI-${{ env.SHORT_SHA }}-release-signed path: app/build/outputs/apk/release/app-release.apk + - name: Generate and submit dependency graph + uses: gradle/actions/dependency-submission@v4 + upload-telegram: name: Upload Builds if: ${{ success() }} diff --git a/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt b/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt index 16ce7d6..c3bee73 100644 --- a/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt +++ b/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt @@ -18,7 +18,6 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll @@ -27,6 +26,7 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme.colorScheme import androidx.compose.material3.MaterialTheme.typography +import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable @@ -53,15 +53,70 @@ import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController +import com.bintianqi.owndroid.dpm.AffiliationID +import com.bintianqi.owndroid.dpm.AlwaysOnVPNPackage import com.bintianqi.owndroid.dpm.ApplicationManage -import com.bintianqi.owndroid.dpm.DpmPermissions -import com.bintianqi.owndroid.dpm.ManagedProfile +import com.bintianqi.owndroid.dpm.CACert +import com.bintianqi.owndroid.dpm.ChangeTime +import com.bintianqi.owndroid.dpm.ChangeTimeZone +import com.bintianqi.owndroid.dpm.ChangeUserIcon +import com.bintianqi.owndroid.dpm.ChangeUsername +import com.bintianqi.owndroid.dpm.CreateUser +import com.bintianqi.owndroid.dpm.CreateWorkProfile +import com.bintianqi.owndroid.dpm.CurrentUserInfo +import com.bintianqi.owndroid.dpm.DeleteWorkProfile +import com.bintianqi.owndroid.dpm.DeviceAdmin +import com.bintianqi.owndroid.dpm.DeviceInfo +import com.bintianqi.owndroid.dpm.DeviceOwner +import com.bintianqi.owndroid.dpm.DisableAccountManagement +import com.bintianqi.owndroid.dpm.DisableKeyguardFeatures +import com.bintianqi.owndroid.dpm.FRPPolicy +import com.bintianqi.owndroid.dpm.InstallSystemUpdate +import com.bintianqi.owndroid.dpm.IntentFilter +import com.bintianqi.owndroid.dpm.Keyguard +import com.bintianqi.owndroid.dpm.LockScreenInfo +import com.bintianqi.owndroid.dpm.LockTaskMode +import com.bintianqi.owndroid.dpm.MTEPolicy +import com.bintianqi.owndroid.dpm.WorkProfile +import com.bintianqi.owndroid.dpm.NearbyStreamingPolicy import com.bintianqi.owndroid.dpm.Network +import com.bintianqi.owndroid.dpm.NetworkLogging +import com.bintianqi.owndroid.dpm.NetworkOptions +import com.bintianqi.owndroid.dpm.OrgOwnedProfile +import com.bintianqi.owndroid.dpm.OverrideAPN import com.bintianqi.owndroid.dpm.Password +import com.bintianqi.owndroid.dpm.PasswordComplexity +import com.bintianqi.owndroid.dpm.PasswordInfo +import com.bintianqi.owndroid.dpm.PasswordQuality +import com.bintianqi.owndroid.dpm.PermissionPolicy +import com.bintianqi.owndroid.dpm.Permissions +import com.bintianqi.owndroid.dpm.PreferentialNetworkService +import com.bintianqi.owndroid.dpm.PrivateDNS +import com.bintianqi.owndroid.dpm.ProfileOwner +import com.bintianqi.owndroid.dpm.RecommendedGlobalProxy +import com.bintianqi.owndroid.dpm.ResetPassword +import com.bintianqi.owndroid.dpm.ResetPasswordToken +import com.bintianqi.owndroid.dpm.RestrictionData +import com.bintianqi.owndroid.dpm.SecurityLogging +import com.bintianqi.owndroid.dpm.Shizuku +import com.bintianqi.owndroid.dpm.SupportMessages +import com.bintianqi.owndroid.dpm.SuspendPersonalApp import com.bintianqi.owndroid.dpm.SystemManage -import com.bintianqi.owndroid.dpm.UserManage +import com.bintianqi.owndroid.dpm.SystemOptions +import com.bintianqi.owndroid.dpm.SystemUpdatePolicy +import com.bintianqi.owndroid.dpm.TransferOwnership +import com.bintianqi.owndroid.dpm.UserOperation +import com.bintianqi.owndroid.dpm.UserOptions import com.bintianqi.owndroid.dpm.UserRestriction +import com.bintianqi.owndroid.dpm.UserRestrictionItem +import com.bintianqi.owndroid.dpm.UserSessionMessage +import com.bintianqi.owndroid.dpm.Users +import com.bintianqi.owndroid.dpm.WifiAuthKeypair +import com.bintianqi.owndroid.dpm.WifiSecurityLevel +import com.bintianqi.owndroid.dpm.WifiSsidPolicy +import com.bintianqi.owndroid.dpm.WipeData import com.bintianqi.owndroid.dpm.dhizukuErrorStatus +import com.bintianqi.owndroid.dpm.dhizukuPermissionGranted import com.bintianqi.owndroid.dpm.getDPM import com.bintianqi.owndroid.dpm.getReceiver import com.bintianqi.owndroid.dpm.isDeviceAdmin @@ -70,6 +125,7 @@ import com.bintianqi.owndroid.dpm.isProfileOwner import com.bintianqi.owndroid.dpm.setDefaultAffiliationID import com.bintianqi.owndroid.dpm.toggleInstallAppActivity import com.bintianqi.owndroid.ui.Animations +import com.bintianqi.owndroid.ui.MyScaffold import com.bintianqi.owndroid.ui.theme.OwnDroidTheme import com.rosan.dhizuku.api.Dhizuku import kotlinx.coroutines.delay @@ -119,7 +175,7 @@ class MainActivity : FragmentActivity() { } if (sharedPref.getBoolean("dhizuku", false)) { if (Dhizuku.init(applicationContext)) { - if (!Dhizuku.isPermissionGranted()) { dhizukuErrorStatus.value = 2 } + if (!dhizukuPermissionGranted()) { dhizukuErrorStatus.value = 2 } } else { sharedPref.edit().putBoolean("dhizuku", false).apply() dhizukuErrorStatus.value = 1 @@ -157,20 +213,105 @@ fun Home(vm: MyViewModel) { popExitTransition = Animations.navHostPopExitTransition ) { composable(route = "HomePage") { HomePage(navCtrl) } + + composable(route = "Permissions") { Permissions(navCtrl) } + composable(route = "Shizuku") { Shizuku(navCtrl) } + composable(route = "DeviceAdmin") { DeviceAdmin(navCtrl) } + composable(route = "ProfileOwner") { ProfileOwner(navCtrl) } + composable(route = "DeviceOwner") { DeviceOwner(navCtrl) } + composable(route = "DeviceInfo") { DeviceInfo(navCtrl) } + composable(route = "LockScreenInfo") { LockScreenInfo(navCtrl) } + composable(route = "SupportMessages") { SupportMessages(navCtrl) } + composable(route = "TransferOwnership") { TransferOwnership(navCtrl) } + composable(route = "System") { SystemManage(navCtrl) } - composable(route = "ManagedProfile") { ManagedProfile(navCtrl) } - composable(route = "Permissions") { DpmPermissions(navCtrl) } + composable(route = "SystemOptions") { SystemOptions(navCtrl) } + composable(route = "Keyguard") { Keyguard(navCtrl) } + composable(route = "ChangeTime") { ChangeTime(navCtrl) } + composable(route = "ChangeTimeZone") { ChangeTimeZone(navCtrl) } + composable(route = "PermissionPolicy") { PermissionPolicy(navCtrl) } + composable(route = "MTEPolicy") { MTEPolicy(navCtrl) } + composable(route = "NearbyStreamingPolicy") { NearbyStreamingPolicy(navCtrl) } + composable(route = "LockTaskMode") { LockTaskMode(navCtrl) } + composable(route = "CACert") { CACert(navCtrl) } + composable(route = "SecurityLogs") { SecurityLogging(navCtrl) } + composable(route = "DisableAccountManagement") { DisableAccountManagement(navCtrl) } + composable(route = "SystemUpdatePolicy") { SystemUpdatePolicy(navCtrl) } + composable(route = "InstallSystemUpdate") { InstallSystemUpdate(navCtrl) } + composable(route = "FRPPolicy") { FRPPolicy(navCtrl) } + composable(route = "WipeData") { WipeData(navCtrl) } + + composable(route = "Network") { Network(navCtrl) } + composable(route = "NetworkOptions") { NetworkOptions(navCtrl) } + composable(route = "MinWifiSecurityLevel") { WifiSecurityLevel(navCtrl) } + composable(route = "WifiSsidPolicy") { WifiSsidPolicy(navCtrl) } + composable(route = "PrivateDNS") { PrivateDNS(navCtrl) } + composable(route = "AlwaysOnVpn") { AlwaysOnVPNPackage(navCtrl) } + composable(route = "RecommendedGlobalProxy") { RecommendedGlobalProxy(navCtrl) } + composable(route = "NetworkLog") { NetworkLogging(navCtrl) } + composable(route = "WifiAuthKeypair") { WifiAuthKeypair(navCtrl) } + composable(route = "PreferentialNetworkService") { PreferentialNetworkService(navCtrl) } + composable(route = "OverrideAPN") { OverrideAPN(navCtrl) } + + composable(route = "WorkProfile") { WorkProfile(navCtrl) } + composable(route = "OrgOwnedWorkProfile") { OrgOwnedProfile(navCtrl) } + composable(route = "CreateWorkProfile") { CreateWorkProfile(navCtrl) } + composable(route = "SuspendPersonalApp") { SuspendPersonalApp(navCtrl) } + composable(route = "IntentFilter") { IntentFilter(navCtrl) } + composable(route = "DeleteWorkProfile") { DeleteWorkProfile(navCtrl) } + composable(route = "Applications") { ApplicationManage(navCtrl, dialogStatus) } + composable(route = "UserRestriction") { UserRestriction(navCtrl) } - composable(route = "Users") { UserManage(navCtrl) } + composable(route = "UR-Internet") { + MyScaffold(R.string.network_and_internet, 0.dp, navCtrl) { RestrictionData.internet.forEach { UserRestrictionItem(it) } } + } + composable(route = "UR-Connectivity") { + MyScaffold(R.string.connectivity, 0.dp, navCtrl) { RestrictionData.connectivity.forEach { UserRestrictionItem(it) } } + } + composable(route = "UR-Applications") { + MyScaffold(R.string.applications, 0.dp, navCtrl) { RestrictionData.applications.forEach { UserRestrictionItem(it) } } + } + composable(route = "UR-Users") { + MyScaffold(R.string.users, 0.dp, navCtrl) { RestrictionData.users.forEach { UserRestrictionItem(it) } } + } + composable(route = "UR-Media") { + MyScaffold(R.string.media, 0.dp, navCtrl) { RestrictionData.media.forEach { UserRestrictionItem(it) } } + } + composable(route = "UR-Other") { + MyScaffold(R.string.other, 0.dp, navCtrl) { RestrictionData.other.forEach { UserRestrictionItem(it) } } + } + + composable(route = "Users") { Users(navCtrl) } + composable(route = "UserInfo") { CurrentUserInfo(navCtrl) } + composable(route = "UserOptions") { UserOptions(navCtrl) } + composable(route = "UserOperation") { UserOperation(navCtrl) } + composable(route = "CreateUser") { CreateUser(navCtrl) } + composable(route = "ChangeUsername") { ChangeUsername(navCtrl) } + composable(route = "ChangeUserIcon") { ChangeUserIcon(navCtrl) } + composable(route = "UserSessionMessage") { UserSessionMessage(navCtrl) } + composable(route = "AffiliationID") { AffiliationID(navCtrl) } + composable(route = "Password") { Password(navCtrl) } - composable(route = "Settings") { AppSetting(navCtrl, vm) } - composable(route = "Network") { Network(navCtrl) } + composable(route = "PasswordInfo") { PasswordInfo(navCtrl) } + composable(route = "ResetPasswordToken") { ResetPasswordToken(navCtrl) } + composable(route = "ResetPassword") { ResetPassword(navCtrl) } + composable(route = "RequirePasswordComplexity") { PasswordComplexity(navCtrl) } + composable(route = "DisableKeyguardFeatures") { DisableKeyguardFeatures(navCtrl) } + composable(route = "RequirePasswordQuality") { PasswordQuality(navCtrl) } + + composable(route = "Settings") { Settings(navCtrl) } + composable(route = "Options") { SettingsOptions(navCtrl) } + composable(route = "Appearance") { Appearance(navCtrl, vm) } + composable(route = "AuthSettings") { AuthSettings(navCtrl) } + composable(route = "Automation") { Automation(navCtrl) } + composable(route = "About") { About(navCtrl) } + composable(route = "PackageSelector") { PackageSelector(navCtrl) } } LaunchedEffect(Unit) { - val profileInited = sharedPref.getBoolean("ManagedProfileActivated", false) - val profileNotActivated = !profileInited && context.isProfileOwner && (VERSION.SDK_INT < 24 || (VERSION.SDK_INT >= 24 && dpm.isManagedProfile(receiver))) + val profileInitialized = sharedPref.getBoolean("ManagedProfileActivated", false) + val profileNotActivated = !profileInitialized && context.isProfileOwner && (VERSION.SDK_INT < 24 || dpm.isManagedProfile(receiver)) if(profileNotActivated) { dpm.setProfileEnabled(receiver) sharedPref.edit().putBoolean("ManagedProfileActivated", true).apply() @@ -203,58 +344,60 @@ private fun HomePage(navCtrl:NavHostController) { else if(deviceAdmin) R.string.device_admin else R.string.click_to_activate ) } - Column(modifier = Modifier.background(colorScheme.background).statusBarsPadding().verticalScroll(rememberScrollState())) { - Spacer(Modifier.padding(vertical = 25.dp)) - Text( - text = stringResource(R.string.app_name), style = typography.headlineLarge, - modifier = Modifier.padding(start = 10.dp), color = colorScheme.onBackground - ) - Spacer(Modifier.padding(vertical = 8.dp)) - Row( - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 8.dp, horizontal = 8.dp) - .clip(RoundedCornerShape(15)) - .background(color = colorScheme.primary) - .clickable(onClick = { navCtrl.navigate("Permissions") }) - .padding(vertical = 16.dp), - verticalAlignment = Alignment.CenterVertically - ) { - Spacer(modifier = Modifier.padding(start = 22.dp)) - Icon( - painter = painterResource(if(activated) R.drawable.check_circle_fill1 else R.drawable.block_fill0), - contentDescription = null, - tint = colorScheme.onPrimary + Scaffold { + Column(modifier = Modifier.padding(it).verticalScroll(rememberScrollState())) { + Spacer(Modifier.padding(vertical = 25.dp)) + Text( + text = stringResource(R.string.app_name), style = typography.headlineLarge, + modifier = Modifier.padding(start = 10.dp) ) - Spacer(modifier = Modifier.padding(start = 10.dp)) - Column { - Text( - text = stringResource(if(activated) R.string.activated else R.string.deactivated), - style = typography.headlineSmall, - color = colorScheme.onPrimary, - modifier = Modifier.padding(bottom = 2.dp) + Spacer(Modifier.padding(vertical = 8.dp)) + Row( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 8.dp, horizontal = 8.dp) + .clip(RoundedCornerShape(15)) + .background(color = colorScheme.primary) + .clickable(onClick = { navCtrl.navigate("Permissions") }) + .padding(vertical = 16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Spacer(modifier = Modifier.padding(start = 22.dp)) + Icon( + painter = painterResource(if(activated) R.drawable.check_circle_fill1 else R.drawable.block_fill0), + contentDescription = null, + tint = colorScheme.onPrimary ) - if(activateType != "") { Text(text = activateType, color = colorScheme.onPrimary) } + Spacer(modifier = Modifier.padding(start = 10.dp)) + Column { + Text( + text = stringResource(if(activated) R.string.activated else R.string.deactivated), + style = typography.headlineSmall, + color = colorScheme.onPrimary, + modifier = Modifier.padding(bottom = 2.dp) + ) + if(activateType != "") { Text(text = activateType, color = colorScheme.onPrimary) } + } } + HomePageItem(R.string.system, R.drawable.android_fill0, "System", navCtrl) + if(deviceOwner || profileOwner) { HomePageItem(R.string.network, R.drawable.wifi_fill0, "Network", navCtrl) } + if( + (VERSION.SDK_INT < 24 && !deviceOwner) || ( + VERSION.SDK_INT >= 24 && (dpm.isProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE) || + (profileOwner && dpm.isManagedProfile(receiver))) + ) + ) { + HomePageItem(R.string.work_profile, R.drawable.work_fill0, "ManagedProfile", navCtrl) + } + if(deviceOwner || profileOwner) HomePageItem(R.string.applications, R.drawable.apps_fill0, "Applications", navCtrl) + if(VERSION.SDK_INT >= 24 && (profileOwner || deviceOwner)) { + HomePageItem(R.string.user_restriction, R.drawable.person_off, "UserRestriction", navCtrl) + } + HomePageItem(R.string.users,R.drawable.manage_accounts_fill0,"Users", navCtrl) + HomePageItem(R.string.password_and_keyguard, R.drawable.password_fill0, "Password", navCtrl) + HomePageItem(R.string.settings, R.drawable.settings_fill0, "Settings", navCtrl) + Spacer(Modifier.padding(vertical = 20.dp)) } - HomePageItem(R.string.system, R.drawable.android_fill0, "System", navCtrl) - if(deviceOwner || profileOwner) { HomePageItem(R.string.network, R.drawable.wifi_fill0, "Network", navCtrl) } - if( - (VERSION.SDK_INT < 24 && !deviceOwner) || ( - VERSION.SDK_INT >= 24 && (dpm.isProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE) || - (profileOwner && dpm.isManagedProfile(receiver))) - ) - ) { - HomePageItem(R.string.work_profile, R.drawable.work_fill0, "ManagedProfile", navCtrl) - } - if(deviceOwner || profileOwner) HomePageItem(R.string.applications, R.drawable.apps_fill0, "Applications", navCtrl) - if(VERSION.SDK_INT >= 24 && (profileOwner || deviceOwner)) { - HomePageItem(R.string.user_restrict, R.drawable.person_off, "UserRestriction", navCtrl) - } - HomePageItem(R.string.users,R.drawable.manage_accounts_fill0,"Users", navCtrl) - HomePageItem(R.string.password_and_keyguard, R.drawable.password_fill0, "Password", navCtrl) - HomePageItem(R.string.settings, R.drawable.settings_fill0, "Settings", navCtrl) - Spacer(Modifier.padding(vertical = 20.dp)) } } @@ -263,22 +406,19 @@ fun HomePageItem(name: Int, imgVector: Int, navTo: String, navCtrl: NavHostContr Row( modifier = Modifier .fillMaxWidth() - .clip(RoundedCornerShape(25)) .clickable(onClick = { navCtrl.navigate(navTo) }) - .padding(vertical = 13.dp), + .padding(vertical = 12.dp), verticalAlignment = Alignment.CenterVertically ) { Spacer(Modifier.padding(start = 30.dp)) Icon( painter = painterResource(imgVector), - contentDescription = null, - tint = colorScheme.onBackground + contentDescription = null ) Spacer(Modifier.padding(start = 15.dp)) Text( text = stringResource(name), style = typography.headlineSmall, - color = colorScheme.onBackground, modifier = Modifier.padding(bottom = if(zhCN) { 2 } else { 0 }.dp) ) } diff --git a/app/src/main/java/com/bintianqi/owndroid/Settings.kt b/app/src/main/java/com/bintianqi/owndroid/Settings.kt index db21e8a..44f3b18 100644 --- a/app/src/main/java/com/bintianqi/owndroid/Settings.kt +++ b/app/src/main/java/com/bintianqi/owndroid/Settings.kt @@ -8,21 +8,15 @@ import android.widget.Toast import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Button import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.Icon import androidx.compose.material3.IconButton -import androidx.compose.material3.MaterialTheme.typography import androidx.compose.material3.OutlinedTextField -import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -37,68 +31,36 @@ import androidx.compose.ui.unit.DpOffset import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavHostController -import androidx.navigation.compose.NavHost -import androidx.navigation.compose.composable -import androidx.navigation.compose.currentBackStackEntryAsState -import androidx.navigation.compose.rememberNavController -import com.bintianqi.owndroid.ui.Animations -import com.bintianqi.owndroid.ui.SubPageItem +import com.bintianqi.owndroid.ui.FunctionItem +import com.bintianqi.owndroid.ui.MyScaffold import com.bintianqi.owndroid.ui.SwitchItem -import com.bintianqi.owndroid.ui.TopBar import java.security.SecureRandom @Composable -fun AppSetting(navCtrl:NavHostController, vm: MyViewModel) { - val localNavCtrl = rememberNavController() - val backStackEntry by localNavCtrl.currentBackStackEntryAsState() - Scaffold( - topBar = { - TopBar(backStackEntry, navCtrl, localNavCtrl) - } - ) { - NavHost( - navController = localNavCtrl, startDestination = "Home", - enterTransition = Animations.navHostEnterTransition, - exitTransition = Animations.navHostExitTransition, - popEnterTransition = Animations.navHostPopEnterTransition, - popExitTransition = Animations.navHostPopExitTransition, - modifier = Modifier.padding(top = it.calculateTopPadding()) - ) { - composable(route = "Home") { Home(localNavCtrl) } - composable(route = "Options") { Options() } - composable(route = "Theme") { ThemeSettings(vm) } - composable(route = "Auth") { AuthSettings() } - composable(route = "Automation") { Automation() } - composable(route = "About") { About() } - } +fun Settings(navCtrl: NavHostController) { + MyScaffold(R.string.settings, 0.dp, navCtrl) { + FunctionItem(R.string.options, "", R.drawable.tune_fill0) { navCtrl.navigate("Options") } + FunctionItem(R.string.appearance, "", R.drawable.format_paint_fill0) { navCtrl.navigate("Appearance") } + FunctionItem(R.string.security, "", R.drawable.lock_fill0) { navCtrl.navigate("AuthSettings") } + FunctionItem(R.string.automation_api, "", R.drawable.apps_fill0) { navCtrl.navigate("Automation") } + FunctionItem(R.string.about, "", R.drawable.info_fill0) { navCtrl.navigate("About") } } } @Composable -private fun Home(navCtrl: NavHostController) { - Column(modifier = Modifier.fillMaxSize()) { - SubPageItem(R.string.options, "", R.drawable.tune_fill0) { navCtrl.navigate("Options") } - SubPageItem(R.string.appearance, "", R.drawable.format_paint_fill0) { navCtrl.navigate("Theme") } - SubPageItem(R.string.security, "", R.drawable.lock_fill0) { navCtrl.navigate("Auth") } - SubPageItem(R.string.automation_api, "", R.drawable.apps_fill0) { navCtrl.navigate("Automation") } - SubPageItem(R.string.about, "", R.drawable.info_fill0) { navCtrl.navigate("About") } - } -} - -@Composable -private fun Options() { +fun SettingsOptions(navCtrl: NavHostController) { val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE) - Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(start = 20.dp, end = 16.dp)) { + MyScaffold(R.string.options, 0.dp, navCtrl) { SwitchItem( R.string.show_dangerous_features, "", R.drawable.warning_fill0, { sharedPref.getBoolean("dangerous_features", false) }, - { sharedPref.edit().putBoolean("dangerous_features", it).apply() }, padding = false + { sharedPref.edit().putBoolean("dangerous_features", it).apply() } ) } } @Composable -private fun ThemeSettings(vm: MyViewModel) { +fun Appearance(navCtrl: NavHostController, vm: MyViewModel) { val theme by vm.theme.collectAsStateWithLifecycle() var darkThemeMenu by remember { mutableStateOf(false) } val darkThemeTextID = when(theme.darkTheme) { @@ -106,7 +68,7 @@ private fun ThemeSettings(vm: MyViewModel) { false -> R.string.off null -> R.string.follow_system } - Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())) { + MyScaffold(R.string.appearance, 0.dp, navCtrl) { if(VERSION.SDK_INT >= 31) { SwitchItem( R.string.material_you_color, "", null, @@ -115,7 +77,7 @@ private fun ThemeSettings(vm: MyViewModel) { ) } Box { - SubPageItem(R.string.dark_theme, stringResource(darkThemeTextID)) { darkThemeMenu = true } + FunctionItem(R.string.dark_theme, stringResource(darkThemeTextID)) { darkThemeMenu = true } DropdownMenu( expanded = darkThemeMenu, onDismissRequest = { darkThemeMenu = false }, offset = DpOffset(x = 25.dp, y = 0.dp) @@ -154,44 +116,42 @@ private fun ThemeSettings(vm: MyViewModel) { } @Composable -private fun AuthSettings() { +fun AuthSettings(navCtrl: NavHostController) { val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE) var auth by remember{ mutableStateOf(sharedPref.getBoolean("auth",false)) } - Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(start = 20.dp, end = 16.dp)) { + MyScaffold(R.string.security, 0.dp, navCtrl) { SwitchItem( R.string.lock_owndroid, "", null, auth, { sharedPref.edit().putBoolean("auth", it).apply() auth = sharedPref.getBoolean("auth", false) - }, padding = false + } ) if(auth) { SwitchItem( R.string.enable_bio_auth, "", null, { sharedPref.getBoolean("bio_auth", false) }, - { sharedPref.edit().putBoolean("bio_auth", it).apply() }, padding = false + { sharedPref.edit().putBoolean("bio_auth", it).apply() } ) SwitchItem( R.string.lock_in_background, stringResource(R.string.developing), null, { sharedPref.getBoolean("lock_in_background", false) }, - { sharedPref.edit().putBoolean("lock_in_background", it).apply() }, padding = false + { sharedPref.edit().putBoolean("lock_in_background", it).apply() } ) } SwitchItem( R.string.protect_storage, "", null, { sharedPref.getBoolean("protect_storage", false) }, - { sharedPref.edit().putBoolean("protect_storage", it).apply() }, padding = false + { sharedPref.edit().putBoolean("protect_storage", it).apply() } ) } } @Composable -private fun Automation() { +fun Automation(navCtrl: NavHostController) { val context = LocalContext.current val sharedPref = context.getSharedPreferences("data", Context.MODE_PRIVATE) - Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.automation_api), style = typography.headlineLarge) + MyScaffold(R.string.automation_api, 8.dp, navCtrl) { Spacer(Modifier.padding(vertical = 5.dp)) var key by remember { mutableStateOf("") } OutlinedTextField( @@ -229,18 +189,15 @@ private fun Automation() { } @Composable -private fun About() { +fun About(navCtrl: NavHostController) { val context = LocalContext.current val pkgInfo = context.packageManager.getPackageInfo(context.packageName,0) val verCode = pkgInfo.versionCode val verName = pkgInfo.versionName - Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())) { - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.about), style = typography.headlineLarge, modifier = Modifier.padding(start = 26.dp)) - Spacer(Modifier.padding(vertical = 5.dp)) - Text(text = stringResource(R.string.app_name)+" v$verName ($verCode)", modifier = Modifier.padding(start = 26.dp)) + MyScaffold(R.string.about, 0.dp, navCtrl) { + Text(text = stringResource(R.string.app_name)+" v$verName ($verCode)", modifier = Modifier.padding(start = 16.dp)) Spacer(Modifier.padding(vertical = 5.dp)) - SubPageItem(R.string.project_homepage, "GitHub", R.drawable.open_in_new) { shareLink(context, "https://github.com/BinTianqi/OwnDroid") } + FunctionItem(R.string.project_homepage, "GitHub", R.drawable.open_in_new) { shareLink(context, "https://github.com/BinTianqi/OwnDroid") } } } diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/Applications.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/Applications.kt index b604aa7..3ebb4c7 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/Applications.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/Applications.kt @@ -90,7 +90,7 @@ import com.bintianqi.owndroid.ui.Information import com.bintianqi.owndroid.ui.ListItem import com.bintianqi.owndroid.ui.NavIcon import com.bintianqi.owndroid.ui.RadioButtonItem -import com.bintianqi.owndroid.ui.SubPageItem +import com.bintianqi.owndroid.ui.FunctionItem import com.bintianqi.owndroid.ui.SwitchItem import java.util.concurrent.Executors @@ -119,7 +119,7 @@ fun ApplicationManage(navCtrl:NavHostController, dialogStatus: MutableIntState) keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Ascii, imeAction = ImeAction.Done), keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }), trailingIcon = { - Icon(painter = painterResource(R.drawable.checklist_fill0), contentDescription = null, + Icon(painter = painterResource(R.drawable.list_fill0), contentDescription = null, modifier = Modifier .clip(RoundedCornerShape(50)) .clickable(onClick = { @@ -216,7 +216,7 @@ private fun Home( if(VERSION.SDK_INT >= 24 && profileOwner && dpm.isManagedProfile(receiver)) { Text(text = stringResource(R.string.scope_is_work_profile), textAlign = TextAlign.Center,modifier = Modifier.fillMaxWidth()) } - SubPageItem(R.string.app_info,"", R.drawable.open_in_new) { + FunctionItem(R.string.app_info,"", R.drawable.open_in_new) { val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) intent.setData(Uri.parse("package:$pkgName")) startActivity(context, intent, null) @@ -242,37 +242,37 @@ private fun Home( onClickBlank = { appControlAction = 3; appControlDialog = true } ) if((VERSION.SDK_INT >= 33 && profileOwner) || (VERSION.SDK_INT >= 30 && deviceOwner)) { - SubPageItem(R.string.ucd, "", R.drawable.do_not_touch_fill0) { navCtrl.navigate("UserControlDisabled") } + FunctionItem(R.string.ucd, "", R.drawable.do_not_touch_fill0) { navCtrl.navigate("UserControlDisabled") } } if(VERSION.SDK_INT>=23) { - SubPageItem(R.string.permission_manage, "", R.drawable.key_fill0) { navCtrl.navigate("PermissionManage") } + FunctionItem(R.string.permission_manage, "", R.drawable.key_fill0) { navCtrl.navigate("PermissionManage") } } if(VERSION.SDK_INT >= 30 && profileOwner && dpm.isManagedProfile(receiver)) { - SubPageItem(R.string.cross_profile_package, "", R.drawable.work_fill0) { navCtrl.navigate("CrossProfilePackage") } + FunctionItem(R.string.cross_profile_package, "", R.drawable.work_fill0) { navCtrl.navigate("CrossProfilePackage") } } if(profileOwner) { - SubPageItem(R.string.cross_profile_widget, "", R.drawable.widgets_fill0) { navCtrl.navigate("CrossProfileWidget") } + FunctionItem(R.string.cross_profile_widget, "", R.drawable.widgets_fill0) { navCtrl.navigate("CrossProfileWidget") } } if(VERSION.SDK_INT >= 34 && deviceOwner) { - SubPageItem(R.string.credential_manage_policy, "", R.drawable.license_fill0) { navCtrl.navigate("CredentialManagePolicy") } + FunctionItem(R.string.credential_manage_policy, "", R.drawable.license_fill0) { navCtrl.navigate("CredentialManagePolicy") } } - SubPageItem(R.string.permitted_accessibility_services, "", R.drawable.settings_accessibility_fill0) { navCtrl.navigate("Accessibility") } - SubPageItem(R.string.permitted_ime, "", R.drawable.keyboard_fill0) { navCtrl.navigate("IME") } - SubPageItem(R.string.enable_system_app, "", R.drawable.enable_fill0) { + FunctionItem(R.string.permitted_accessibility_services, "", R.drawable.settings_accessibility_fill0) { navCtrl.navigate("Accessibility") } + FunctionItem(R.string.permitted_ime, "", R.drawable.keyboard_fill0) { navCtrl.navigate("IME") } + FunctionItem(R.string.enable_system_app, "", R.drawable.enable_fill0) { if(pkgName != "") dialogStatus.intValue = 1 } if(VERSION.SDK_INT >= 28 && deviceOwner) { - SubPageItem(R.string.keep_uninstalled_packages, "", R.drawable.delete_fill0) { navCtrl.navigate("KeepUninstalled") } + FunctionItem(R.string.keep_uninstalled_packages, "", R.drawable.delete_fill0) { navCtrl.navigate("KeepUninstalled") } } if(VERSION.SDK_INT >= 28) { - SubPageItem(R.string.clear_app_storage, "", R.drawable.mop_fill0) { + FunctionItem(R.string.clear_app_storage, "", R.drawable.mop_fill0) { if(pkgName != "") dialogStatus.intValue = 2 } } - SubPageItem(R.string.install_app, "", R.drawable.install_mobile_fill0) { navCtrl.navigate("InstallApp") } - SubPageItem(R.string.uninstall_app, "", R.drawable.delete_fill0) { navCtrl.navigate("UninstallApp") } + FunctionItem(R.string.install_app, "", R.drawable.install_mobile_fill0) { navCtrl.navigate("InstallApp") } + FunctionItem(R.string.uninstall_app, "", R.drawable.delete_fill0) { navCtrl.navigate("UninstallApp") } if(VERSION.SDK_INT >= 34 && (deviceOwner || dpm.isOrgProfile(receiver))) { - SubPageItem(R.string.set_default_dialer, "", R.drawable.call_fill0) { + FunctionItem(R.string.set_default_dialer, "", R.drawable.call_fill0) { if(pkgName != "") dialogStatus.intValue = 3 } } diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/DPM.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/DPM.kt index 9691977..1529bd9 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/DPM.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/DPM.kt @@ -153,7 +153,7 @@ private fun binderWrapperPackageInstaller(appContext: Context): PackageInstaller fun Context.getPI(): PackageInstaller { val sharedPref = this.getSharedPreferences("data", Context.MODE_PRIVATE) if(sharedPref.getBoolean("dhizuku", false)) { - if (!Dhizuku.isPermissionGranted()) { + if (!dhizukuPermissionGranted()) { dhizukuErrorStatus.value = 2 backToHomeStateFlow.value = true return this.packageManager.packageInstaller @@ -167,7 +167,7 @@ fun Context.getPI(): PackageInstaller { fun Context.getDPM(): DevicePolicyManager { val sharedPref = this.getSharedPreferences("data", Context.MODE_PRIVATE) if(sharedPref.getBoolean("dhizuku", false)) { - if (!Dhizuku.isPermissionGranted()) { + if (!dhizukuPermissionGranted()) { dhizukuErrorStatus.value = 2 backToHomeStateFlow.value = true return this.getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager @@ -193,7 +193,7 @@ fun Context.resetDevicePolicy() { val dpm = getDPM() val receiver = getReceiver() RestrictionData.getAllRestrictions().forEach { - dpm.clearUserRestriction(receiver, it) + dpm.clearUserRestriction(receiver, it.id) } dpm.accountTypesWithManagementDisabled?.forEach { dpm.setAccountManagementDisabled(receiver, it, false) @@ -428,7 +428,7 @@ fun parseSecurityEventData(event: SecurityLog.SecurityEvent): JsonElement? { val payload = event.data as Array<*> buildJsonObject { put("mac", payload[0] as String) - (payload[2] as String).let { if(it != "") put("reason", it) } + (payload[1] as String).let { if(it != "") put("reason", it) } } } SecurityLog.TAG_CAMERA_POLICY_SET -> { @@ -619,3 +619,11 @@ fun setDefaultAffiliationID(context: Context) { } } } + +fun dhizukuPermissionGranted() = + try { + Dhizuku.isPermissionGranted() + } catch(_: Exception) { + false + } + diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/ManagedProfile.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/ManagedProfile.kt index 88f9b95..937c1ca 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/ManagedProfile.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/ManagedProfile.kt @@ -22,21 +22,17 @@ import android.widget.Toast import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.rememberScrollState 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.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.MaterialTheme.colorScheme import androidx.compose.material3.MaterialTheme.typography import androidx.compose.material3.OutlinedTextField -import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable @@ -55,88 +51,47 @@ import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import androidx.navigation.NavHostController -import androidx.navigation.compose.NavHost -import androidx.navigation.compose.composable -import androidx.navigation.compose.currentBackStackEntryAsState -import androidx.navigation.compose.rememberNavController import com.bintianqi.owndroid.R -import com.bintianqi.owndroid.ui.Animations +import com.bintianqi.owndroid.ui.CardItem import com.bintianqi.owndroid.ui.CheckBoxItem import com.bintianqi.owndroid.ui.CopyTextButton import com.bintianqi.owndroid.ui.InfoCard -import com.bintianqi.owndroid.ui.SubPageItem +import com.bintianqi.owndroid.ui.FunctionItem +import com.bintianqi.owndroid.ui.MyScaffold import com.bintianqi.owndroid.ui.SwitchItem -import com.bintianqi.owndroid.ui.TopBar +import com.bintianqi.owndroid.yesOrNo @Composable -fun ManagedProfile(navCtrl: NavHostController) { - val localNavCtrl = rememberNavController() - val backStackEntry by localNavCtrl.currentBackStackEntryAsState() - Scaffold( - topBar = { - TopBar(backStackEntry, navCtrl, localNavCtrl) - } - ) { - NavHost( - navController = localNavCtrl, startDestination = "Home", - enterTransition = Animations.navHostEnterTransition, - exitTransition = Animations.navHostExitTransition, - popEnterTransition = Animations.navHostPopEnterTransition, - popExitTransition = Animations.navHostPopExitTransition, - modifier = Modifier.padding(top = it.calculateTopPadding()) - ) { - composable(route = "Home") { Home(localNavCtrl) } - composable(route = "OrgOwnedWorkProfile") { OrgOwnedProfile() } - composable(route = "CreateWorkProfile") { CreateWorkProfile() } - composable(route = "SuspendPersonalApp") { SuspendPersonalApp() } - composable(route = "IntentFilter") { IntentFilter() } - composable(route = "DeleteWorkProfile") { DeleteWorkProfile() } - } - } -} - -@Composable -private fun Home(navCtrl: NavHostController) { +fun WorkProfile(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() val profileOwner = context.isProfileOwner - Column( - modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()) - ) { - Text( - text = stringResource(R.string.work_profile), - style = typography.headlineLarge, - modifier = Modifier.padding(top = 8.dp, bottom = 5.dp, start = 16.dp) - ) + MyScaffold(R.string.work_profile, 0.dp, navCtrl) { if(VERSION.SDK_INT >= 30 && profileOwner && dpm.isManagedProfile(receiver)) { - SubPageItem(R.string.org_owned_work_profile, "", R.drawable.corporate_fare_fill0) { navCtrl.navigate("OrgOwnedWorkProfile") } + FunctionItem(R.string.org_owned_work_profile, "", R.drawable.corporate_fare_fill0) { navCtrl.navigate("OrgOwnedWorkProfile") } } if(VERSION.SDK_INT<24 || (VERSION.SDK_INT>=24 && dpm.isProvisioningAllowed(ACTION_PROVISION_MANAGED_PROFILE))) { - SubPageItem(R.string.create_work_profile, "", R.drawable.work_fill0) { navCtrl.navigate("CreateWorkProfile") } + FunctionItem(R.string.create_work_profile, "", R.drawable.work_fill0) { navCtrl.navigate("CreateWorkProfile") } } if(dpm.isOrgProfile(receiver)) { - SubPageItem(R.string.suspend_personal_app, "", R.drawable.block_fill0) { navCtrl.navigate("SuspendPersonalApp") } + FunctionItem(R.string.suspend_personal_app, "", R.drawable.block_fill0) { navCtrl.navigate("SuspendPersonalApp") } } if(profileOwner && (VERSION.SDK_INT < 24 || (VERSION.SDK_INT >= 24 && dpm.isManagedProfile(receiver)))) { - SubPageItem(R.string.intent_filter, "", R.drawable.filter_alt_fill0) { navCtrl.navigate("IntentFilter") } + FunctionItem(R.string.intent_filter, "", R.drawable.filter_alt_fill0) { navCtrl.navigate("IntentFilter") } } if(profileOwner && (VERSION.SDK_INT < 24 || (VERSION.SDK_INT >= 24 && dpm.isManagedProfile(receiver)))) { - SubPageItem(R.string.delete_work_profile, "", R.drawable.delete_forever_fill0) { navCtrl.navigate("DeleteWorkProfile") } + FunctionItem(R.string.delete_work_profile, "", R.drawable.delete_forever_fill0) { navCtrl.navigate("DeleteWorkProfile") } } - Spacer(Modifier.padding(vertical = 30.dp)) } } @Composable -private fun CreateWorkProfile() { +fun CreateWorkProfile(navCtrl: NavHostController) { val context = LocalContext.current val receiver = context.getReceiver() val focusMgr = LocalFocusManager.current - Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.create_work_profile), style = typography.headlineLarge) - Spacer(Modifier.padding(vertical = 5.dp)) + MyScaffold(R.string.create_work_profile, 8.dp, navCtrl) { var skipEncrypt by remember { mutableStateOf(false) } var offlineProvisioning by remember { mutableStateOf(true) } var migrateAccount by remember { mutableStateOf(false) } @@ -206,14 +161,11 @@ private fun CreateWorkProfile() { @SuppressLint("NewApi") @Composable -private fun OrgOwnedProfile() { +fun OrgOwnedProfile(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() - Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.org_owned_work_profile), style = typography.headlineLarge) - Spacer(Modifier.padding(vertical = 5.dp)) - Text(text = stringResource(R.string.is_org_owned_profile,dpm.isOrganizationOwnedDeviceWithManagedProfile)) + MyScaffold(R.string.org_owned_work_profile, 8.dp, navCtrl, false) { + CardItem(R.string.org_owned_work_profile, dpm.isOrganizationOwnedDeviceWithManagedProfile.yesOrNo) Spacer(Modifier.padding(vertical = 5.dp)) if(!dpm.isOrganizationOwnedDeviceWithManagedProfile) { SelectionContainer { @@ -229,14 +181,13 @@ private fun OrgOwnedProfile() { @SuppressLint("NewApi") @Composable -private fun SuspendPersonalApp() { +fun SuspendPersonalApp(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() val focusMgr = LocalFocusManager.current var suspend by remember { mutableStateOf(dpm.getPersonalAppsSuspendedReasons(receiver) != PERSONAL_APPS_NOT_SUSPENDED) } - Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { - Spacer(Modifier.padding(vertical = 10.dp)) + MyScaffold(R.string.suspend_personal_app, 8.dp, navCtrl) { SwitchItem( R.string.suspend_personal_app, "", null, suspend, @@ -277,16 +228,13 @@ private fun SuspendPersonalApp() { } @Composable -private fun IntentFilter() { +fun IntentFilter(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() val focusMgr = LocalFocusManager.current - Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { + MyScaffold(R.string.intent_filter, 8.dp, navCtrl) { var action by remember { mutableStateOf("") } - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.intent_filter), style = typography.headlineLarge) - Spacer(Modifier.padding(vertical = 5.dp)) OutlinedTextField( value = action, onValueChange = { action = it }, label = { Text("Action") }, @@ -328,7 +276,7 @@ private fun IntentFilter() { } @Composable -private fun DeleteWorkProfile() { +fun DeleteWorkProfile(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val focusMgr = LocalFocusManager.current @@ -337,14 +285,7 @@ private fun DeleteWorkProfile() { var euicc by remember { mutableStateOf(false) } var silent by remember { mutableStateOf(false) } var reason by remember { mutableStateOf("") } - Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { - Spacer(Modifier.padding(vertical = 10.dp)) - Text( - text = stringResource(R.string.delete_work_profile), - style = typography.headlineLarge, - modifier = Modifier.padding(6.dp),color = colorScheme.error - ) - Spacer(Modifier.padding(vertical = 5.dp)) + MyScaffold(R.string.delete_work_profile, 8.dp, navCtrl) { CheckBoxItem(R.string.wipe_external_storage, externalStorage, { externalStorage = it }) if(VERSION.SDK_INT >= 28) { CheckBoxItem(R.string.wipe_euicc, euicc, { euicc = it }) } CheckBoxItem(R.string.wipe_silently, silent, { silent = it }) @@ -367,7 +308,6 @@ private fun DeleteWorkProfile() { ) { Text(stringResource(R.string.delete)) } - Spacer(Modifier.padding(vertical = 30.dp)) } if(warning) { LaunchedEffect(Unit) { silent = reason == "" } diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/Network.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/Network.kt index 334acde..f7cdd3e 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/Network.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/Network.kt @@ -43,21 +43,17 @@ import android.telephony.data.ApnSetting.PROTOCOL_UNSTRUCTURED import android.widget.Toast import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.animateContentSize -import androidx.compose.foundation.ScrollState import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box 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.padding -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.KeyboardArrowLeft import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight @@ -69,14 +65,12 @@ import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme.typography import androidx.compose.material3.OutlinedTextField -import androidx.compose.material3.Scaffold import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.material3.TextField import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.MutableState import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf @@ -88,7 +82,6 @@ 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.draw.alpha import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalFocusManager @@ -99,92 +92,23 @@ import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import androidx.core.net.toUri import androidx.navigation.NavHostController -import androidx.navigation.compose.NavHost -import androidx.navigation.compose.composable -import androidx.navigation.compose.currentBackStackEntryAsState -import androidx.navigation.compose.rememberNavController import com.bintianqi.owndroid.R import com.bintianqi.owndroid.exportFile import com.bintianqi.owndroid.exportFilePath import com.bintianqi.owndroid.formatFileSize import com.bintianqi.owndroid.selectedPackage -import com.bintianqi.owndroid.ui.Animations import com.bintianqi.owndroid.ui.CheckBoxItem +import com.bintianqi.owndroid.ui.FunctionItem import com.bintianqi.owndroid.ui.InfoCard import com.bintianqi.owndroid.ui.ListItem +import com.bintianqi.owndroid.ui.MyScaffold import com.bintianqi.owndroid.ui.RadioButtonItem -import com.bintianqi.owndroid.ui.SubPageItem import com.bintianqi.owndroid.ui.SwitchItem -import com.bintianqi.owndroid.ui.TopBar import com.bintianqi.owndroid.writeClipBoard import kotlin.math.max @Composable -fun Network(navCtrl: NavHostController) { - val localNavCtrl = rememberNavController() - val backStackEntry by localNavCtrl.currentBackStackEntryAsState() - val scrollState = rememberScrollState() - val wifiMacDialog = remember { mutableStateOf(false) } - Scaffold( - topBar = { - TopBar(backStackEntry,navCtrl,localNavCtrl) { - if(backStackEntry?.destination?.route == "Home" && scrollState.maxValue > 80) { - Text( - text = stringResource(R.string.network), - modifier = Modifier.alpha((maxOf(scrollState.value-30,0)).toFloat()/80) - ) - } - } - } - ) { - NavHost( - navController = localNavCtrl, startDestination = "Home", - enterTransition = Animations.navHostEnterTransition, - exitTransition = Animations.navHostExitTransition, - popEnterTransition = Animations.navHostPopEnterTransition, - popExitTransition = Animations.navHostPopExitTransition, - modifier = Modifier.padding(top = it.calculateTopPadding()) - ) { - composable(route = "Home") { Home(localNavCtrl, scrollState, wifiMacDialog) } - composable(route = "Switches") { Switches() } - composable(route = "MinWifiSecurityLevel") { WifiSecLevel() } - composable(route = "WifiSsidPolicy") { WifiSsidPolicy() } - composable(route = "PrivateDNS") { PrivateDNS() } - composable(route = "AlwaysOnVpn") { AlwaysOnVPNPackage(navCtrl) } - composable(route = "RecommendedGlobalProxy") { RecommendedGlobalProxy() } - composable(route = "NetworkLog") { NetworkLog() } - composable(route = "WifiAuthKeypair") { WifiAuthKeypair() } - composable(route = "PreferentialNetworkService") { PreferentialNetworkService() } - composable(route = "APN") { APN() } - } - } - if(wifiMacDialog.value && VERSION.SDK_INT >= 24) { - val context = LocalContext.current - val dpm = context.getDPM() - val receiver = context.getReceiver() - AlertDialog( - onDismissRequest = { wifiMacDialog.value = false }, - confirmButton = { TextButton(onClick = { wifiMacDialog.value = false }) { Text(stringResource(R.string.confirm)) } }, - title = { Text(stringResource(R.string.wifi_mac_address)) }, - text = { - val mac = dpm.getWifiMacAddress(receiver) - OutlinedTextField( - value = mac ?: stringResource(R.string.none), - onValueChange = {}, readOnly = true, modifier = Modifier.fillMaxWidth(), textStyle = typography.bodyLarge, - trailingIcon = { - if(mac != null) IconButton(onClick = { writeClipBoard(context, mac) }) { - Icon(painter = painterResource(R.drawable.content_copy_fill0), contentDescription = stringResource(R.string.copy)) - } - } - ) - }, - modifier = Modifier.fillMaxWidth() - ) - } -} - -@Composable -private fun Home(navCtrl:NavHostController, scrollState: ScrollState, wifiMacDialog: MutableState) { +fun Network(navCtrl:NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() @@ -192,58 +116,75 @@ private fun Home(navCtrl:NavHostController, scrollState: ScrollState, wifiMacDia val profileOwner = context.isProfileOwner val sharedPref = context.getSharedPreferences("data", Context.MODE_PRIVATE) val dhizuku = sharedPref.getBoolean("dhizuku", false) - Column(modifier = Modifier.fillMaxSize().verticalScroll(scrollState)) { - Text( - text = stringResource(R.string.network), - style = typography.headlineLarge, - modifier = Modifier.padding(top = 8.dp, bottom = 5.dp, start = 16.dp) - ) + var wifiMacDialog by remember { mutableStateOf(false) } + MyScaffold(R.string.network, 0.dp, navCtrl) { if(VERSION.SDK_INT >= 24 && (deviceOwner || dpm.isOrgProfile(receiver))) { - SubPageItem(R.string.wifi_mac_address, "", R.drawable.wifi_fill0) { wifiMacDialog.value = true } + FunctionItem(R.string.wifi_mac_address, "", R.drawable.wifi_fill0) { wifiMacDialog = true } } if(VERSION.SDK_INT >= 30) { - SubPageItem(R.string.options, "", R.drawable.tune_fill0) { navCtrl.navigate("Switches") } + FunctionItem(R.string.options, "", R.drawable.tune_fill0) { navCtrl.navigate("NetworkOptions") } } if(VERSION.SDK_INT >= 33 && (deviceOwner || dpm.isOrgProfile(receiver))) { - SubPageItem(R.string.min_wifi_security_level, "", R.drawable.wifi_password_fill0) { navCtrl.navigate("MinWifiSecurityLevel") } + FunctionItem(R.string.min_wifi_security_level, "", R.drawable.wifi_password_fill0) { navCtrl.navigate("MinWifiSecurityLevel") } } if(VERSION.SDK_INT >= 33 && (deviceOwner || dpm.isOrgProfile(receiver))) { - SubPageItem(R.string.wifi_ssid_policy, "", R.drawable.wifi_fill0) { navCtrl.navigate("WifiSsidPolicy") } + FunctionItem(R.string.wifi_ssid_policy, "", R.drawable.wifi_fill0) { navCtrl.navigate("WifiSsidPolicy") } } if(VERSION.SDK_INT >= 29 && deviceOwner) { - SubPageItem(R.string.private_dns, "", R.drawable.dns_fill0) { navCtrl.navigate("PrivateDNS") } + FunctionItem(R.string.private_dns, "", R.drawable.dns_fill0) { navCtrl.navigate("PrivateDNS") } } if(VERSION.SDK_INT >= 24 && (deviceOwner || profileOwner)) { - SubPageItem(R.string.always_on_vpn, "", R.drawable.vpn_key_fill0) { navCtrl.navigate("AlwaysOnVpn") } + FunctionItem(R.string.always_on_vpn, "", R.drawable.vpn_key_fill0) { navCtrl.navigate("AlwaysOnVpn") } } if(deviceOwner) { - SubPageItem(R.string.recommended_global_proxy, "", R.drawable.vpn_key_fill0) { navCtrl.navigate("RecommendedGlobalProxy") } + FunctionItem(R.string.recommended_global_proxy, "", R.drawable.vpn_key_fill0) { navCtrl.navigate("RecommendedGlobalProxy") } } if(VERSION.SDK_INT >= 26 && !dhizuku && (deviceOwner || (profileOwner && dpm.isManagedProfile(receiver)))) { - SubPageItem(R.string.retrieve_net_logs, "", R.drawable.description_fill0) { navCtrl.navigate("NetworkLog") } + FunctionItem(R.string.network_logging, "", R.drawable.description_fill0) { navCtrl.navigate("NetworkLog") } } if(VERSION.SDK_INT >= 31 && (deviceOwner || profileOwner)) { - SubPageItem(R.string.wifi_auth_keypair, "", R.drawable.key_fill0) { navCtrl.navigate("WifiAuthKeypair") } + FunctionItem(R.string.wifi_auth_keypair, "", R.drawable.key_fill0) { navCtrl.navigate("WifiAuthKeypair") } } if(VERSION.SDK_INT >= 33 && (deviceOwner || profileOwner)) { - SubPageItem(R.string.preferential_network_service, "", R.drawable.globe_fill0) { navCtrl.navigate("PreferentialNetworkService") } + FunctionItem(R.string.preferential_network_service, "", R.drawable.globe_fill0) { navCtrl.navigate("PreferentialNetworkService") } } if(VERSION.SDK_INT >= 28 && deviceOwner) { - SubPageItem(R.string.override_apn_settings, "", R.drawable.cell_tower_fill0) { navCtrl.navigate("APN") } + FunctionItem(R.string.override_apn_settings, "", R.drawable.cell_tower_fill0) { navCtrl.navigate("OverrideAPN") } } - Spacer(Modifier.padding(vertical = 30.dp)) + } + if(wifiMacDialog && VERSION.SDK_INT >= 24) { + val context = LocalContext.current + val dpm = context.getDPM() + val receiver = context.getReceiver() + AlertDialog( + onDismissRequest = { wifiMacDialog = false }, + confirmButton = { TextButton(onClick = { wifiMacDialog = false }) { Text(stringResource(R.string.confirm)) } }, + title = { Text(stringResource(R.string.wifi_mac_address)) }, + text = { + val mac = dpm.getWifiMacAddress(receiver) + OutlinedTextField( + value = mac ?: stringResource(R.string.none), + onValueChange = {}, readOnly = true, modifier = Modifier.fillMaxWidth(), textStyle = typography.bodyLarge, + trailingIcon = { + if(mac != null) IconButton(onClick = { writeClipBoard(context, mac) }) { + Icon(painter = painterResource(R.drawable.content_copy_fill0), contentDescription = stringResource(R.string.copy)) + } + } + ) + }, + modifier = Modifier.fillMaxWidth() + ) } } @Composable -private fun Switches() { +fun NetworkOptions(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() val deviceOwner = context.isDeviceOwner var dialog by remember { mutableIntStateOf(0) } - Column(modifier = Modifier.fillMaxSize()) { - Spacer(Modifier.padding(vertical = 5.dp)) + MyScaffold(R.string.options, 0.dp, navCtrl) { if(VERSION.SDK_INT>=30 && (deviceOwner || dpm.isOrgProfile(receiver))) { SwitchItem(R.string.lockdown_admin_configured_network, "", R.drawable.wifi_password_fill0, { dpm.hasLockdownAdminConfiguredNetworks(receiver) }, { dpm.setConfiguredNetworksLockdownState(receiver,it) }, @@ -262,15 +203,12 @@ private fun Switches() { @SuppressLint("NewApi") @Composable -private fun WifiSecLevel() { +fun WifiSecurityLevel(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() var selectedWifiSecLevel by remember { mutableIntStateOf(0) } LaunchedEffect(Unit) { selectedWifiSecLevel = dpm.minimumRequiredWifiSecurityLevel } - Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.min_wifi_security_level), style = typography.headlineLarge) - Spacer(Modifier.padding(vertical = 5.dp)) + MyScaffold(R.string.min_wifi_security_level, 8.dp, navCtrl) { RadioButtonItem( R.string.wifi_security_open, selectedWifiSecLevel == WIFI_SECURITY_OPEN, @@ -307,11 +245,11 @@ private fun WifiSecLevel() { @SuppressLint("NewApi") @Composable -private fun WifiSsidPolicy() { +fun WifiSsidPolicy(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val focusMgr = LocalFocusManager.current - Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { + MyScaffold(R.string.wifi_ssid_policy, 8.dp, navCtrl) { var selectedPolicyType by remember { mutableIntStateOf(-1) } val ssidList = remember { mutableStateListOf() } val refreshPolicy = { @@ -321,9 +259,6 @@ private fun WifiSsidPolicy() { ssidList.addAll(policy?.ssids ?: mutableSetOf()) } LaunchedEffect(Unit) { refreshPolicy() } - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.wifi_ssid_policy), style = typography.headlineLarge) - Spacer(Modifier.padding(vertical = 5.dp)) RadioButtonItem( R.string.none, selectedPolicyType == -1, @@ -387,20 +322,17 @@ private fun WifiSsidPolicy() { ) { Text(stringResource(R.string.apply)) } - Spacer(Modifier.padding(vertical = 30.dp)) } } @SuppressLint("NewApi") @Composable -private fun PrivateDNS() { +fun PrivateDNS(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() val focusMgr = LocalFocusManager.current - Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.private_dns), style = typography.headlineLarge) + MyScaffold(R.string.private_dns, 8.dp, navCtrl) { val dnsStatus = mapOf( PRIVATE_DNS_MODE_UNKNOWN to stringResource(R.string.unknown), PRIVATE_DNS_MODE_OFF to stringResource(R.string.disabled), @@ -462,7 +394,6 @@ private fun PrivateDNS() { Text(stringResource(R.string.set_dns_host)) } InfoCard(R.string.info_set_private_dns_host) - Spacer(Modifier.padding(vertical = 30.dp)) } } @@ -499,9 +430,7 @@ fun AlwaysOnVPNPackage(navCtrl: NavHostController) { false } } - Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(horizontal = 8.dp)) { - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.always_on_vpn), style = typography.headlineLarge, modifier = Modifier.padding(vertical = 8.dp)) + MyScaffold(R.string.always_on_vpn, 8.dp, navCtrl) { OutlinedTextField( value = pkgName, onValueChange = { pkgName = it }, @@ -509,7 +438,7 @@ fun AlwaysOnVPNPackage(navCtrl: NavHostController) { keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }), trailingIcon = { - Icon(painter = painterResource(R.drawable.checklist_fill0), contentDescription = null, + Icon(painter = painterResource(R.drawable.list_fill0), contentDescription = null, modifier = Modifier .clip(RoundedCornerShape(50)) .clickable(onClick = { @@ -536,12 +465,11 @@ fun AlwaysOnVPNPackage(navCtrl: NavHostController) { Text(stringResource(R.string.clear_current_config)) } InfoCard(R.string.info_always_on_vpn) - Spacer(Modifier.padding(vertical = 30.dp)) } } @Composable -private fun RecommendedGlobalProxy() { +fun RecommendedGlobalProxy(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() @@ -551,10 +479,7 @@ private fun RecommendedGlobalProxy() { var specifyPort by remember { mutableStateOf(false) } var proxyPort by remember { mutableStateOf("") } var exclList by remember { mutableStateOf("") } - Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.recommended_global_proxy), style = typography.headlineLarge) - Spacer(Modifier.padding(vertical = 5.dp)) + MyScaffold(R.string.recommended_global_proxy, 8.dp, navCtrl) { RadioButtonItem(R.string.proxy_type_off, proxyType == 0, { proxyType = 0 }) RadioButtonItem(R.string.proxy_type_pac, proxyType == 1, { proxyType = 1 }) RadioButtonItem(R.string.proxy_type_direct, proxyType == 2, { proxyType = 2 }) @@ -641,7 +566,7 @@ private fun RecommendedGlobalProxy() { @SuppressLint("NewApi") @Composable -private fun NetworkLog() { +fun NetworkLogging(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() @@ -650,10 +575,7 @@ private fun NetworkLog() { LaunchedEffect(Unit) { fileSize = logFile.length() } - Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.retrieve_net_logs), style = typography.headlineLarge) - Spacer(Modifier.padding(vertical = 5.dp)) + MyScaffold(R.string.network_logging, 8.dp, navCtrl) { SwitchItem(R.string.enable, "", null, { dpm.isNetworkLoggingEnabled(receiver) }, { dpm.setNetworkLoggingEnabled(receiver,it) }, padding = false) Text(stringResource(R.string.log_file_size_is, formatFileSize(fileSize))) Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) { @@ -688,15 +610,12 @@ private fun NetworkLog() { @SuppressLint("NewApi") @Composable -private fun WifiAuthKeypair() { +fun WifiAuthKeypair(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val focusMgr = LocalFocusManager.current - Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { - var keyPair by remember { mutableStateOf("") } - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.wifi_auth_keypair), style = typography.headlineLarge) - Spacer(Modifier.padding(vertical = 5.dp)) + var keyPair by remember { mutableStateOf("") } + MyScaffold(R.string.wifi_auth_keypair, 8.dp, navCtrl) { OutlinedTextField( value = keyPair, label = { Text(stringResource(R.string.alias)) }, @@ -739,7 +658,7 @@ private fun WifiAuthKeypair() { @SuppressLint("NewApi") @Composable -fun PreferentialNetworkService() { +fun PreferentialNetworkService(navCtrl: NavHostController) { val focusMgr = LocalFocusManager.current val context = LocalContext.current val dpm = context.getDPM() @@ -778,10 +697,7 @@ fun PreferentialNetworkService() { refresh() } LaunchedEffect(Unit) { initialize() } - Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.preferential_network_service), style = typography.headlineLarge) - Spacer(Modifier.padding(vertical = 5.dp)) + MyScaffold(R.string.preferential_network_service, 8.dp, navCtrl) { SwitchItem( title = R.string.enabled, desc = "", icon = null, state = masterEnabled, onCheckedChange = { masterEnabled = it }, padding = false @@ -895,25 +811,21 @@ fun PreferentialNetworkService() { ) { Text(stringResource(R.string.apply)) } - Spacer(Modifier.padding(vertical = 30.dp)) } } @SuppressLint("NewApi") @Composable -private fun APN() { +fun OverrideAPN(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() val focusMgr = LocalFocusManager.current - Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { - val setting = dpm.getOverrideApns(receiver) - var inputNum by remember { mutableStateOf("0") } - var nextStep by remember { mutableStateOf(false) } - val builder = Builder() - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.override_apn_settings), style = typography.headlineLarge) - Spacer(Modifier.padding(vertical = 5.dp)) + val setting = dpm.getOverrideApns(receiver) + var inputNum by remember { mutableStateOf("0") } + var nextStep by remember { mutableStateOf(false) } + val builder = Builder() + MyScaffold(R.string.override_apn_settings, 8.dp, navCtrl) { Text(text = stringResource(id = R.string.developing)) Spacer(Modifier.padding(vertical = 5.dp)) SwitchItem(R.string.enable, "", null, { dpm.isOverrideApnEnabled(receiver) }, { dpm.setOverrideApnsEnabled(receiver,it) }, padding = false) @@ -1275,6 +1187,5 @@ private fun APN() { } } } - Spacer(Modifier.padding(vertical = 30.dp)) } } diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/Password.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/Password.kt index 69a291d..4e486ec 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/Password.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/Password.kt @@ -35,25 +35,20 @@ import android.os.Build.VERSION import android.os.UserManager import android.widget.Toast import androidx.compose.animation.AnimatedVisibility -import androidx.compose.foundation.ScrollState import androidx.compose.foundation.layout.Arrangement 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.padding -import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.foundation.verticalScroll import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.MaterialTheme.colorScheme import androidx.compose.material3.MaterialTheme.typography import androidx.compose.material3.OutlinedTextField -import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable @@ -65,7 +60,6 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.alpha import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.stringResource @@ -75,103 +69,56 @@ import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.unit.dp import androidx.core.content.ContextCompat.startActivity import androidx.navigation.NavHostController -import androidx.navigation.compose.NavHost -import androidx.navigation.compose.composable -import androidx.navigation.compose.currentBackStackEntryAsState -import androidx.navigation.compose.rememberNavController import com.bintianqi.owndroid.R import com.bintianqi.owndroid.toggle -import com.bintianqi.owndroid.ui.Animations import com.bintianqi.owndroid.ui.CardItem import com.bintianqi.owndroid.ui.CheckBoxItem +import com.bintianqi.owndroid.ui.FunctionItem import com.bintianqi.owndroid.ui.InfoCard import com.bintianqi.owndroid.ui.Information +import com.bintianqi.owndroid.ui.MyScaffold import com.bintianqi.owndroid.ui.RadioButtonItem -import com.bintianqi.owndroid.ui.SubPageItem -import com.bintianqi.owndroid.ui.TopBar import com.bintianqi.owndroid.yesOrNo -@Composable -fun Password(navCtrl: NavHostController) { - val localNavCtrl = rememberNavController() - val backStackEntry by localNavCtrl.currentBackStackEntryAsState() - val scrollState = rememberScrollState() - Scaffold( - topBar = { - TopBar(backStackEntry,navCtrl,localNavCtrl) { - if(backStackEntry?.destination?.route == "Home" && scrollState.maxValue > 100) { - Text( - text = stringResource(R.string.password_and_keyguard), - modifier = Modifier.alpha((maxOf(scrollState.value-30,0)).toFloat()/80) - ) - } - } - } - ) { - NavHost( - navController = localNavCtrl, startDestination = "Home", - enterTransition = Animations.navHostEnterTransition, - exitTransition = Animations.navHostExitTransition, - popEnterTransition = Animations.navHostPopEnterTransition, - popExitTransition = Animations.navHostPopExitTransition, - modifier = Modifier.padding(top = it.calculateTopPadding()) - ) { - composable(route = "Home") { Home(localNavCtrl,scrollState) } - composable(route = "PasswordInfo") { PasswordInfo() } - composable(route = "ResetPasswordToken") { ResetPasswordToken() } - composable(route = "ResetPassword") { ResetPassword() } - composable(route = "RequirePasswordComplexity") { PasswordComplexity() } - composable(route = "DisableKeyguardFeatures") { DisableKeyguardFeatures() } - composable(route = "RequirePasswordQuality") { PasswordQuality() } - } - } -} - @SuppressLint("NewApi") @Composable -private fun Home(navCtrl:NavHostController, scrollState: ScrollState) { +fun Password(navCtrl: NavHostController) { val context = LocalContext.current val sharedPrefs = context.getSharedPreferences("data", Context.MODE_PRIVATE) val deviceAdmin = context.isDeviceAdmin val deviceOwner = context.isDeviceOwner val profileOwner = context.isProfileOwner var dialog by remember { mutableIntStateOf(0) } - Column(modifier = Modifier.fillMaxSize().verticalScroll(scrollState)) { - Text( - text = stringResource(R.string.password_and_keyguard), - style = typography.headlineLarge, - modifier = Modifier.padding(top = 8.dp, bottom = 5.dp, start = 16.dp) - ) - SubPageItem(R.string.password_info, "", R.drawable.info_fill0) { navCtrl.navigate("PasswordInfo") } + MyScaffold(R.string.password_and_keyguard, 0.dp, navCtrl) { + FunctionItem(R.string.password_info, "", R.drawable.info_fill0) { navCtrl.navigate("PasswordInfo") } if(sharedPrefs.getBoolean("dangerous_features", false)) { if(VERSION.SDK_INT >= 26 && (deviceOwner || profileOwner)) { - SubPageItem(R.string.reset_password_token, "", R.drawable.key_vertical_fill0) { navCtrl.navigate("ResetPasswordToken") } + FunctionItem(R.string.reset_password_token, "", R.drawable.key_vertical_fill0) { navCtrl.navigate("ResetPasswordToken") } } if(deviceAdmin || deviceOwner || profileOwner) { - SubPageItem(R.string.reset_password, "", R.drawable.lock_reset_fill0) { navCtrl.navigate("ResetPassword") } + FunctionItem(R.string.reset_password, "", R.drawable.lock_reset_fill0) { navCtrl.navigate("ResetPassword") } } } if(VERSION.SDK_INT >= 31 && (deviceOwner || profileOwner)) { - SubPageItem(R.string.required_password_complexity, "", R.drawable.password_fill0) { navCtrl.navigate("RequirePasswordComplexity") } + FunctionItem(R.string.required_password_complexity, "", R.drawable.password_fill0) { navCtrl.navigate("RequirePasswordComplexity") } } if(deviceAdmin) { - SubPageItem(R.string.disable_keyguard_features, "", R.drawable.screen_lock_portrait_fill0) { navCtrl.navigate("DisableKeyguardFeatures") } + FunctionItem(R.string.disable_keyguard_features, "", R.drawable.screen_lock_portrait_fill0) { navCtrl.navigate("DisableKeyguardFeatures") } } if(deviceOwner) { - SubPageItem(R.string.max_time_to_lock, "", R.drawable.schedule_fill0) { dialog = 1 } - SubPageItem(R.string.pwd_expiration_timeout, "", R.drawable.lock_clock_fill0) { dialog = 3 } - SubPageItem(R.string.max_pwd_fail, "", R.drawable.no_encryption_fill0) { dialog = 4 } + FunctionItem(R.string.max_time_to_lock, "", R.drawable.schedule_fill0) { dialog = 1 } + FunctionItem(R.string.pwd_expiration_timeout, "", R.drawable.lock_clock_fill0) { dialog = 3 } + FunctionItem(R.string.max_pwd_fail, "", R.drawable.no_encryption_fill0) { dialog = 4 } } if(VERSION.SDK_INT >= 26 && (deviceOwner || profileOwner)) { - SubPageItem(R.string.required_strong_auth_timeout, "", R.drawable.fingerprint_off_fill0) { dialog = 2 } + FunctionItem(R.string.required_strong_auth_timeout, "", R.drawable.fingerprint_off_fill0) { dialog = 2 } } if(deviceAdmin){ - SubPageItem(R.string.pwd_history, "", R.drawable.history_fill0) { dialog = 5 } + FunctionItem(R.string.pwd_history, "", R.drawable.history_fill0) { dialog = 5 } } if(VERSION.SDK_INT < 31 && (deviceOwner || profileOwner)) { - SubPageItem(R.string.required_password_quality, "", R.drawable.password_fill0) { navCtrl.navigate("RequirePasswordQuality") } + FunctionItem(R.string.required_password_quality, "", R.drawable.password_fill0) { navCtrl.navigate("RequirePasswordQuality") } } - Spacer(Modifier.padding(vertical = 30.dp)) } if(dialog != 0) { val dpm = context.getDPM() @@ -263,16 +210,13 @@ private fun Home(navCtrl:NavHostController, scrollState: ScrollState) { } @Composable -private fun PasswordInfo() { +fun PasswordInfo(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() val deviceOwner = context.isDeviceOwner val profileOwner = context.isProfileOwner - Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.password_info), style = typography.headlineLarge) - Spacer(Modifier.padding(vertical = 5.dp)) + MyScaffold(R.string.password_info, 8.dp, navCtrl) { if(VERSION.SDK_INT >= 29) { val passwordComplexity = mapOf( PASSWORD_COMPLEXITY_NONE to R.string.password_complexity_none, @@ -293,17 +237,14 @@ private fun PasswordInfo() { @SuppressLint("NewApi") @Composable -private fun ResetPasswordToken() { +fun ResetPasswordToken(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() var token by remember { mutableStateOf("") } val tokenByteArray = token.toByteArray() val focusMgr = LocalFocusManager.current - Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.reset_password_token), style = typography.headlineLarge) - Spacer(Modifier.padding(vertical = 5.dp)) + MyScaffold(R.string.reset_password_token, 8.dp, navCtrl) { OutlinedTextField( value = token, onValueChange = { token = it }, label = { Text(stringResource(R.string.token)) }, @@ -367,7 +308,7 @@ private fun ResetPasswordToken() { } @Composable -private fun ResetPassword() { +fun ResetPassword(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() @@ -378,10 +319,7 @@ private fun ResetPassword() { val tokenByteArray = token.toByteArray() val flags = remember { mutableStateListOf() } var confirmDialog by remember { mutableStateOf(false) } - Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.reset_password),style = typography.headlineLarge) - Spacer(Modifier.padding(vertical = 5.dp)) + MyScaffold(R.string.reset_password, 8.dp, navCtrl) { if(VERSION.SDK_INT >= 26) { OutlinedTextField( value = token, onValueChange = { token = it }, @@ -444,7 +382,6 @@ private fun ResetPassword() { } } InfoCard(R.string.info_reset_password) - Spacer(Modifier.padding(vertical = 30.dp)) } if(confirmDialog) { var confirmPassword by remember { mutableStateOf("") } @@ -494,7 +431,7 @@ private fun ResetPassword() { @SuppressLint("NewApi") @Composable -private fun PasswordComplexity() { +fun PasswordComplexity(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val passwordComplexity = mapOf( @@ -502,19 +439,12 @@ private fun PasswordComplexity() { PASSWORD_COMPLEXITY_LOW to R.string.password_complexity_low, PASSWORD_COMPLEXITY_MEDIUM to R.string.password_complexity_medium, PASSWORD_COMPLEXITY_HIGH to R.string.password_complexity_high - ).toList() - var selectedItem by remember { mutableIntStateOf(passwordComplexity[0].first) } + ) + var selectedItem by remember { mutableIntStateOf(PASSWORD_COMPLEXITY_NONE) } LaunchedEffect(Unit) { selectedItem = dpm.requiredPasswordComplexity } - Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.required_password_complexity), style = typography.headlineLarge) - Spacer(Modifier.padding(vertical = 5.dp)) - for(index in 0..3) { - RadioButtonItem( - passwordComplexity[index].second, - selectedItem == passwordComplexity[index].first, - { selectedItem = passwordComplexity[index].first } - ) + MyScaffold(R.string.required_password_complexity, 8.dp, navCtrl) { + passwordComplexity.forEach { + RadioButtonItem(it.value, selectedItem == it.key, { selectedItem = it.key }) } Spacer(Modifier.padding(vertical = 5.dp)) Button( @@ -533,12 +463,11 @@ private fun PasswordComplexity() { ) { Text(stringResource(R.string.require_set_new_password)) } - Spacer(Modifier.padding(vertical = 30.dp)) } } @Composable -private fun DisableKeyguardFeatures() { +fun DisableKeyguardFeatures(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() @@ -579,10 +508,7 @@ private fun DisableKeyguardFeatures() { } calculateCustomFeature() } - Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.disable_keyguard_features), style = typography.headlineLarge) - Spacer(Modifier.padding(vertical = 5.dp)) + MyScaffold(R.string.disable_keyguard_features, 8.dp, navCtrl) { RadioButtonItem(R.string.enable_all, state == 0, { state = 0 }) RadioButtonItem(R.string.disable_all, state == 1, { state = 1 }) RadioButtonItem(R.string.custom, state == 2 , { state = 2 }) @@ -630,12 +556,11 @@ private fun DisableKeyguardFeatures() { ) { Text(text = stringResource(R.string.apply)) } - Spacer(Modifier.padding(vertical = 30.dp)) } } @Composable -private fun PasswordQuality() { +fun PasswordQuality(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() @@ -647,15 +572,12 @@ private fun PasswordQuality() { PASSWORD_QUALITY_ALPHANUMERIC to R.string.password_quality_alphanumeric, PASSWORD_QUALITY_BIOMETRIC_WEAK to R.string.password_quality_biometrics_weak, PASSWORD_QUALITY_NUMERIC_COMPLEX to R.string.password_quality_numeric_complex - ).toList() - var selectedItem by remember { mutableIntStateOf(passwordQuality[0].first) } + ) + var selectedItem by remember { mutableIntStateOf(PASSWORD_QUALITY_UNSPECIFIED) } LaunchedEffect(Unit) { selectedItem=dpm.getPasswordQuality(receiver) } - Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.required_password_quality), style = typography.headlineLarge) - Spacer(Modifier.padding(vertical = 5.dp)) - for(index in 1..6) { - RadioButtonItem(passwordQuality[index].second, selectedItem == passwordQuality[index].first, { selectedItem = passwordQuality[index].first }) + MyScaffold(R.string.required_password_quality, 8.dp, navCtrl) { + passwordQuality.forEach { + RadioButtonItem(it.value, selectedItem == it.key, { selectedItem = it.key }) } Spacer(Modifier.padding(vertical = 5.dp)) Button( @@ -667,7 +589,6 @@ private fun PasswordQuality() { ) { Text(stringResource(R.string.apply)) } - Spacer(Modifier.padding(vertical = 30.dp)) } } diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/Permissions.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/Permissions.kt index 22bdcae..2e4a93f 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/Permissions.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/Permissions.kt @@ -13,7 +13,6 @@ import android.os.RemoteException import android.os.UserManager import android.widget.Toast import androidx.compose.animation.AnimatedVisibility -import androidx.compose.foundation.* import androidx.compose.foundation.layout.* import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions @@ -23,7 +22,6 @@ import androidx.compose.material3.MaterialTheme.colorScheme import androidx.compose.material3.MaterialTheme.typography import androidx.compose.runtime.* import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.alpha import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.painterResource @@ -31,10 +29,6 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.unit.dp import androidx.navigation.NavHostController -import androidx.navigation.compose.NavHost -import androidx.navigation.compose.composable -import androidx.navigation.compose.currentBackStackEntryAsState -import androidx.navigation.compose.rememberNavController import com.bintianqi.owndroid.R import com.bintianqi.owndroid.backToHomeStateFlow import com.bintianqi.owndroid.ui.* @@ -44,47 +38,9 @@ import com.rosan.dhizuku.api.Dhizuku import com.rosan.dhizuku.api.DhizukuRequestPermissionListener import kotlinx.coroutines.launch -@Composable -fun DpmPermissions(navCtrl:NavHostController) { - val localNavCtrl = rememberNavController() - val backStackEntry by localNavCtrl.currentBackStackEntryAsState() - val scrollState = rememberScrollState() - Scaffold( - topBar = { - TopBar(backStackEntry,navCtrl,localNavCtrl) { - if(backStackEntry?.destination?.route=="Home"&&scrollState.maxValue > 100) { - Text( - text = stringResource(R.string.permission), - modifier = Modifier.alpha((maxOf(scrollState.value-30,0)).toFloat()/80) - ) - } - } - } - ) { - NavHost( - navController = localNavCtrl, startDestination = "Home", - enterTransition = Animations.navHostEnterTransition, - exitTransition = Animations.navHostExitTransition, - popEnterTransition = Animations.navHostPopEnterTransition, - popExitTransition = Animations.navHostPopExitTransition, - modifier = Modifier.padding(top = it.calculateTopPadding()) - ) { - composable(route = "Home") { Home(localNavCtrl,scrollState) } - composable(route = "Shizuku") { ShizukuActivate() } - composable(route = "DeviceAdmin") { DeviceAdmin() } - composable(route = "ProfileOwner") { ProfileOwner() } - composable(route = "DeviceOwner") { DeviceOwner() } - composable(route = "DeviceInfo") { DeviceInfo() } - composable(route = "LockScreenInfo") { LockScreenInfo() } - composable(route = "SupportMsg") { SupportMsg() } - composable(route = "TransformOwnership") { TransferOwnership() } - } - } -} - @SuppressLint("NewApi") @Composable -private fun Home(localNavCtrl:NavHostController,listScrollState:ScrollState) { +fun Permissions(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() @@ -95,12 +51,7 @@ private fun Home(localNavCtrl:NavHostController,listScrollState:ScrollState) { val userManager = context.getSystemService(Context.USER_SERVICE) as UserManager var dialog by remember { mutableIntStateOf(0) } val enrollmentSpecificId = if(VERSION.SDK_INT >= 31 && (deviceOwner || profileOwner)) dpm.enrollmentSpecificId else "" - Column(modifier = Modifier.fillMaxSize().verticalScroll(listScrollState)) { - Text( - text = stringResource(R.string.permission), - style = typography.headlineLarge, - modifier = Modifier.padding(top = 8.dp, bottom = 5.dp, start = 16.dp) - ) + MyScaffold(R.string.permissions, 0.dp, navCtrl) { if(!dpm.isDeviceOwnerApp(context.packageName)) { SwitchItem( R.string.dhizuku, "", null, @@ -109,43 +60,42 @@ private fun Home(localNavCtrl:NavHostController,listScrollState:ScrollState) { onClickBlank = { dialog = 4 } ) } - SubPageItem( + FunctionItem( R.string.device_admin, stringResource(if(deviceAdmin) R.string.activated else R.string.deactivated), - operation = { localNavCtrl.navigate("DeviceAdmin") } + operation = { navCtrl.navigate("DeviceAdmin") } ) if(profileOwner || !userManager.isSystemUser) { - SubPageItem( + FunctionItem( R.string.profile_owner, stringResource(if(profileOwner) R.string.activated else R.string.deactivated), - operation = { localNavCtrl.navigate("ProfileOwner") } + operation = { navCtrl.navigate("ProfileOwner") } ) } if(!profileOwner && userManager.isSystemUser) { - SubPageItem( + FunctionItem( R.string.device_owner, stringResource(if(deviceOwner) R.string.activated else R.string.deactivated), - operation = { localNavCtrl.navigate("DeviceOwner") } + operation = { navCtrl.navigate("DeviceOwner") } ) } - SubPageItem(R.string.shizuku,"") { localNavCtrl.navigate("Shizuku") } - SubPageItem(R.string.device_info, "", R.drawable.perm_device_information_fill0) { localNavCtrl.navigate("DeviceInfo") } + FunctionItem(R.string.shizuku,"") { navCtrl.navigate("Shizuku") } + FunctionItem(R.string.device_info, "", R.drawable.perm_device_information_fill0) { navCtrl.navigate("DeviceInfo") } if((VERSION.SDK_INT >= 26 && deviceOwner) || (VERSION.SDK_INT>=24 && profileOwner)) { - SubPageItem(R.string.org_name, "", R.drawable.corporate_fare_fill0) { dialog = 2 } + FunctionItem(R.string.org_name, "", R.drawable.corporate_fare_fill0) { dialog = 2 } } if(VERSION.SDK_INT >= 31 && (profileOwner || deviceOwner)) { - SubPageItem(R.string.org_id, "", R.drawable.corporate_fare_fill0) { dialog = 3 } + FunctionItem(R.string.org_id, "", R.drawable.corporate_fare_fill0) { dialog = 3 } } if(enrollmentSpecificId != "") { - SubPageItem(R.string.enrollment_specific_id, "", R.drawable.id_card_fill0) { dialog = 1 } + FunctionItem(R.string.enrollment_specific_id, "", R.drawable.id_card_fill0) { dialog = 1 } } if(VERSION.SDK_INT >= 24 && (deviceOwner || dpm.isOrgProfile(receiver))) { - SubPageItem(R.string.device_owner_lock_screen_info, "", R.drawable.screen_lock_portrait_fill0) { localNavCtrl.navigate("LockScreenInfo") } + FunctionItem(R.string.lock_screen_info, "", R.drawable.screen_lock_portrait_fill0) { navCtrl.navigate("LockScreenInfo") } } if(VERSION.SDK_INT >= 24 && deviceAdmin) { - SubPageItem(R.string.support_msg, "", R.drawable.chat_fill0) { localNavCtrl.navigate("SupportMsg") } + FunctionItem(R.string.support_messages, "", R.drawable.chat_fill0) { navCtrl.navigate("SupportMessages") } } if(VERSION.SDK_INT >= 28 && (deviceOwner || profileOwner)) { - SubPageItem(R.string.transfer_ownership, "", R.drawable.admin_panel_settings_fill0) { localNavCtrl.navigate("TransformOwnership") } + FunctionItem(R.string.transfer_ownership, "", R.drawable.admin_panel_settings_fill0) { navCtrl.navigate("TransferOwnership") } } - Spacer(Modifier.padding(vertical = 30.dp)) } if(dialog != 0) { var input by remember { mutableStateOf("") } @@ -157,7 +107,7 @@ private fun Home(localNavCtrl:NavHostController,listScrollState:ScrollState) { 2 -> R.string.org_name 3 -> R.string.org_id 4 -> R.string.dhizuku - else -> R.string.permission + else -> R.string.permissions } )) }, @@ -176,7 +126,7 @@ private fun Home(localNavCtrl:NavHostController,listScrollState:ScrollState) { 1 -> R.string.enrollment_specific_id 2 -> R.string.org_name 3 -> R.string.org_id - else -> R.string.permission + else -> R.string.permissions } )) }, @@ -237,7 +187,7 @@ private fun toggleDhizukuMode(status: Boolean, context: Context) { dhizukuErrorStatus.value = 1 return } - if(Dhizuku.isPermissionGranted()) { + if(dhizukuPermissionGranted()) { sharedPref.edit().putBoolean("dhizuku", true).apply() Dhizuku.init(context) backToHomeStateFlow.value = true @@ -260,17 +210,16 @@ private fun toggleDhizukuMode(status: Boolean, context: Context) { @SuppressLint("NewApi") @Composable -private fun LockScreenInfo() { +fun LockScreenInfo(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() val focusMgr = LocalFocusManager.current var infoText by remember { mutableStateOf(dpm.deviceOwnerLockScreenInfo?.toString() ?: "") } - Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(horizontal = 8.dp)) { - Text(text = stringResource(R.string.device_owner_lock_screen_info), style = typography.headlineLarge) + MyScaffold(R.string.lock_screen_info, 8.dp, navCtrl) { OutlinedTextField( value = infoText, - label = { Text(stringResource(R.string.device_owner_lock_screen_info)) }, + label = { Text(stringResource(R.string.lock_screen_info)) }, onValueChange = { infoText = it }, keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), keyboardActions = KeyboardActions { focusMgr.clearFocus() }, @@ -302,15 +251,13 @@ private fun LockScreenInfo() { } @Composable -private fun DeviceAdmin() { +fun DeviceAdmin(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() var deactivateDialog by remember { mutableStateOf(false) } val deviceAdmin = context.isDeviceAdmin - Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(horizontal = 8.dp)) { - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.device_admin), style = typography.headlineLarge) + MyScaffold(R.string.device_admin, 8.dp, navCtrl) { Text(text = stringResource(if(context.isDeviceAdmin) R.string.activated else R.string.deactivated), style = typography.titleLarge) Spacer(Modifier.padding(vertical = 5.dp)) AnimatedVisibility(deviceAdmin) { @@ -361,15 +308,13 @@ private fun DeviceAdmin() { } @Composable -private fun ProfileOwner() { +fun ProfileOwner(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() var deactivateDialog by remember { mutableStateOf(false) } val profileOwner = context.isProfileOwner - Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(horizontal = 8.dp)) { - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.profile_owner), style = typography.headlineLarge) + MyScaffold(R.string.profile_owner, 8.dp, navCtrl) { Text(stringResource(if(profileOwner) R.string.activated else R.string.deactivated), style = typography.titleLarge) Spacer(Modifier.padding(vertical = 5.dp)) if(VERSION.SDK_INT >= 24 && profileOwner) { @@ -416,14 +361,12 @@ private fun ProfileOwner() { } @Composable -private fun DeviceOwner() { +fun DeviceOwner(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() var deactivateDialog by remember { mutableStateOf(false) } val deviceOwner = context.isDeviceOwner - Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(horizontal = 8.dp)) { - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.device_owner), style = typography.headlineLarge) + MyScaffold(R.string.device_owner, 8.dp, navCtrl) { Text(text = stringResource(if(deviceOwner) R.string.activated else R.string.deactivated), style = typography.titleLarge) Spacer(Modifier.padding(vertical = 5.dp)) AnimatedVisibility(deviceOwner) { @@ -488,15 +431,12 @@ private fun DeviceOwner() { } @Composable -fun DeviceInfo() { +fun DeviceInfo(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() var dialog by remember { mutableIntStateOf(0) } - Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(horizontal = 8.dp)) { - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.device_info), style = typography.headlineLarge) - Spacer(Modifier.padding(vertical = 5.dp)) + MyScaffold(R.string.device_info, 8.dp, navCtrl) { if(VERSION.SDK_INT>=34 && (context.isDeviceOwner || dpm.isOrgProfile(receiver))) { CardItem(R.string.financed_device, dpm.isDeviceFinanced.yesOrNo) } @@ -532,7 +472,7 @@ fun DeviceInfo() { @SuppressLint("NewApi") @Composable -private fun SupportMsg() { +fun SupportMessages(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() @@ -543,10 +483,7 @@ private fun SupportMsg() { longMsg = dpm.getLongSupportMessage(receiver)?.toString() ?: "" } LaunchedEffect(Unit) { refreshMsg() } - Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(horizontal = 8.dp)) { - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.support_msg), style = typography.headlineLarge) - Spacer(Modifier.padding(vertical = 5.dp)) + MyScaffold(R.string.support_messages, 8.dp, navCtrl) { OutlinedTextField( value = shortMsg, label = { Text(stringResource(R.string.short_support_msg)) }, @@ -608,22 +545,18 @@ private fun SupportMsg() { } } InfoCard(R.string.info_long_support_message) - Spacer(Modifier.padding(vertical = 30.dp)) } } @SuppressLint("NewApi") @Composable -private fun TransferOwnership() { +fun TransferOwnership(navCtrl: NavHostController) { val context = LocalContext.current val focusMgr = LocalFocusManager.current var input by remember { mutableStateOf("") } val componentName = ComponentName.unflattenFromString(input) var dialog by remember { mutableStateOf(false) } - Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(horizontal = 8.dp)) { - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.transfer_ownership), style = typography.headlineLarge) - Spacer(Modifier.padding(vertical = 5.dp)) + MyScaffold(R.string.transfer_ownership, 8.dp, navCtrl) { OutlinedTextField( value = input, onValueChange = { input = it }, label = { Text(stringResource(R.string.target_component_name)) }, modifier = Modifier.fillMaxWidth(), diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/ShizukuActivate.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/Shizuku.kt similarity index 88% rename from app/src/main/java/com/bintianqi/owndroid/dpm/ShizukuActivate.kt rename to app/src/main/java/com/bintianqi/owndroid/dpm/Shizuku.kt index 8c67c72..a7c11b3 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/ShizukuActivate.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/Shizuku.kt @@ -10,14 +10,12 @@ import android.os.IBinder import android.widget.Toast import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.horizontalScroll -import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.selection.SelectionContainer -import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Button import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -34,8 +32,10 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import androidx.navigation.NavHostController import com.bintianqi.owndroid.IUserService import com.bintianqi.owndroid.R +import com.bintianqi.owndroid.ui.MyScaffold import kotlinx.coroutines.delay import kotlinx.coroutines.launch import rikka.shizuku.Shizuku @@ -43,7 +43,7 @@ import rikka.shizuku.Shizuku private var waitGrantPermission = false @Composable -fun ShizukuActivate() { +fun Shizuku(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() @@ -69,19 +69,14 @@ fun ShizukuActivate() { shizukuService.value = null userServiceControl(context, true) } - Column( - modifier = Modifier - .fillMaxSize() - .padding(horizontal = 8.dp) - .verticalScroll(rememberScrollState()), - horizontalAlignment = Alignment.CenterHorizontally - ) { - AnimatedVisibility(bindShizuku) { + MyScaffold(R.string.shizuku, 0.dp, navCtrl, false) { + AnimatedVisibility(visible = bindShizuku, modifier = Modifier.fillMaxWidth()) { Button( onClick = { userServiceControl(context, true) outputText = "" - } + }, + modifier = Modifier.wrapContentWidth(Alignment.CenterHorizontally) ) { Text(stringResource(R.string.bind_shizuku)) } @@ -100,7 +95,8 @@ fun ShizukuActivate() { coScope.launch { outputTextScrollState.animateScrollTo(0) } - } + }, + modifier = Modifier.align(Alignment.CenterHorizontally) ) { Text(text = stringResource(R.string.check_shizuku)) } @@ -112,7 +108,8 @@ fun ShizukuActivate() { outputTextScrollState.animateScrollTo(0) } }, - enabled = enabled + enabled = enabled, + modifier = Modifier.align(Alignment.CenterHorizontally) ) { Text(text = stringResource(R.string.list_owners)) } @@ -123,7 +120,8 @@ fun ShizukuActivate() { outputTextScrollState.animateScrollTo(0) } }, - enabled = enabled + enabled = enabled, + modifier = Modifier.align(Alignment.CenterHorizontally) ) { Text(text = stringResource(R.string.list_users)) } @@ -134,7 +132,8 @@ fun ShizukuActivate() { outputTextScrollState.animateScrollTo(0) } }, - enabled = enabled + enabled = enabled, + modifier = Modifier.align(Alignment.CenterHorizontally) ) { Text(text = stringResource(R.string.list_accounts)) } @@ -150,7 +149,8 @@ fun ShizukuActivate() { showDeviceAdminButton = !context.isDeviceAdmin } }, - enabled = enabled + enabled = enabled, + modifier = Modifier.align(Alignment.CenterHorizontally) ) { Text(text = stringResource(R.string.activate_device_admin)) } @@ -166,7 +166,8 @@ fun ShizukuActivate() { showDeviceOwnerButton = !context.isDeviceOwner } }, - enabled = enabled + enabled = enabled, + modifier = Modifier.align(Alignment.CenterHorizontally) ) { Text(text = stringResource(R.string.activate_device_owner)) } @@ -186,7 +187,8 @@ fun ShizukuActivate() { showOrgProfileOwnerButton = !dpm.isOrganizationOwnedDeviceWithManagedProfile } }, - enabled = enabled + enabled = enabled, + modifier = Modifier.align(Alignment.CenterHorizontally) ) { Text(text = stringResource(R.string.activate_org_profile)) } @@ -194,10 +196,8 @@ fun ShizukuActivate() { } SelectionContainer(modifier = Modifier.fillMaxWidth().horizontalScroll(outputTextScrollState)) { - Text(text = outputText, softWrap = false, modifier = Modifier.padding(4.dp)) + Text(text = outputText, softWrap = false, modifier = Modifier.padding(vertical = 9.dp, horizontal = 12.dp)) } - - Spacer(Modifier.padding(vertical = 30.dp)) } } diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/System.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/System.kt index 4a78320..2db2cd2 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/System.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/System.kt @@ -44,23 +44,19 @@ import android.os.UserManager import android.widget.Toast import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.animateContentSize -import androidx.compose.foundation.ScrollState import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement 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.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items -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.List import androidx.compose.material.icons.filled.Add @@ -72,7 +68,6 @@ import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme.colorScheme import androidx.compose.material3.MaterialTheme.typography import androidx.compose.material3.OutlinedTextField -import androidx.compose.material3.Scaffold import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.material3.TextButton @@ -90,7 +85,6 @@ 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.draw.alpha import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalFocusManager @@ -101,10 +95,6 @@ import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import androidx.core.app.NotificationCompat import androidx.navigation.NavHostController -import androidx.navigation.compose.NavHost -import androidx.navigation.compose.composable -import androidx.navigation.compose.currentBackStackEntryAsState -import androidx.navigation.compose.rememberNavController import com.bintianqi.owndroid.R import com.bintianqi.owndroid.StopLockTaskModeReceiver import com.bintianqi.owndroid.exportFile @@ -115,15 +105,14 @@ import com.bintianqi.owndroid.getFile import com.bintianqi.owndroid.prepareForNotification import com.bintianqi.owndroid.selectedPackage import com.bintianqi.owndroid.toggle -import com.bintianqi.owndroid.ui.Animations import com.bintianqi.owndroid.ui.CheckBoxItem +import com.bintianqi.owndroid.ui.FunctionItem import com.bintianqi.owndroid.ui.InfoCard import com.bintianqi.owndroid.ui.Information import com.bintianqi.owndroid.ui.ListItem +import com.bintianqi.owndroid.ui.MyScaffold import com.bintianqi.owndroid.ui.RadioButtonItem -import com.bintianqi.owndroid.ui.SubPageItem import com.bintianqi.owndroid.ui.SwitchItem -import com.bintianqi.owndroid.ui.TopBar import com.bintianqi.owndroid.uriToStream import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -137,54 +126,9 @@ import java.util.TimeZone import java.util.concurrent.Executors import kotlin.math.pow -@Composable -fun SystemManage(navCtrl: NavHostController) { - val localNavCtrl = rememberNavController() - val backStackEntry by localNavCtrl.currentBackStackEntryAsState() - val scrollState = rememberScrollState() - Scaffold( - topBar = { - TopBar(backStackEntry,navCtrl,localNavCtrl) { - if(backStackEntry?.destination?.route=="Home"&&scrollState.maxValue > 100) { - Text( - text = stringResource(R.string.system), - modifier = Modifier.alpha((maxOf(scrollState.value-30,0)).toFloat()/80) - ) - } - } - } - ) { - NavHost( - navController = localNavCtrl, startDestination = "Home", - enterTransition = Animations.navHostEnterTransition, - exitTransition = Animations.navHostExitTransition, - popEnterTransition = Animations.navHostPopEnterTransition, - popExitTransition = Animations.navHostPopExitTransition, - modifier = Modifier.padding(top = it.calculateTopPadding()) - ) { - composable(route = "Home") { Home(localNavCtrl, scrollState) } - composable(route = "Switches") { Switches() } - composable(route = "Keyguard") { Keyguard() } - composable(route = "EditTime") { EditTime() } - composable(route = "EditTimeZone") { EditTimeZone() } - composable(route = "PermissionPolicy") { PermissionPolicy() } - composable(route = "MTEPolicy") { MTEPolicy() } - composable(route = "NearbyStreamingPolicy") { NearbyStreamingPolicy() } - composable(route = "LockTaskMode") { LockTaskMode(navCtrl) } - composable(route = "CaCert") { CaCert() } - composable(route = "SecurityLogs") { SecurityLogs() } - composable(route = "DisableAccountManagement") { DisableAccountManagement() } - composable(route = "SystemUpdatePolicy") { SysUpdatePolicy() } - composable(route = "InstallSystemUpdate") { InstallSystemUpdate() } - composable(route = "WipeData") { WipeData() } - composable(route = "FRP") { FactoryResetProtection() } - } - } -} - @SuppressLint("NewApi") @Composable -private fun Home(navCtrl: NavHostController, scrollState: ScrollState) { +fun SystemManage(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() @@ -194,60 +138,54 @@ private fun Home(navCtrl: NavHostController, scrollState: ScrollState) { val deviceOwner = context.isDeviceOwner val profileOwner = context.isProfileOwner var dialog by remember { mutableIntStateOf(0) } - Column(modifier = Modifier.fillMaxSize().verticalScroll(scrollState)) { - Text( - text = stringResource(R.string.system), - style = typography.headlineLarge, - modifier = Modifier.padding(top = 8.dp, bottom = 5.dp, start = 16.dp) - ) + MyScaffold(R.string.system, 0.dp, navCtrl) { if(deviceOwner || profileOwner) { - SubPageItem(R.string.options, "", R.drawable.tune_fill0) { navCtrl.navigate("Switches") } + FunctionItem(R.string.options, "", R.drawable.tune_fill0) { navCtrl.navigate("SystemOptions") } } - SubPageItem(R.string.keyguard, "", R.drawable.screen_lock_portrait_fill0) { navCtrl.navigate("Keyguard") } + FunctionItem(R.string.keyguard, "", R.drawable.screen_lock_portrait_fill0) { navCtrl.navigate("Keyguard") } if(VERSION.SDK_INT >= 24 && deviceOwner) { - SubPageItem(R.string.reboot, "", R.drawable.restart_alt_fill0) { dialog = 1 } + FunctionItem(R.string.reboot, "", R.drawable.restart_alt_fill0) { dialog = 1 } } if(deviceOwner && ((VERSION.SDK_INT >= 28 && dpm.isAffiliatedUser) || VERSION.SDK_INT >= 24)) { - SubPageItem(R.string.bug_report, "", R.drawable.bug_report_fill0) { dialog = 2 } + FunctionItem(R.string.bug_report, "", R.drawable.bug_report_fill0) { dialog = 2 } } if(VERSION.SDK_INT >= 28 && (deviceOwner || dpm.isOrgProfile(receiver))) { - SubPageItem(R.string.change_time, "", R.drawable.schedule_fill0) { navCtrl.navigate("EditTime") } - SubPageItem(R.string.change_timezone, "", R.drawable.schedule_fill0) { navCtrl.navigate("EditTimeZone") } + FunctionItem(R.string.change_time, "", R.drawable.schedule_fill0) { navCtrl.navigate("ChangeTime") } + FunctionItem(R.string.change_timezone, "", R.drawable.schedule_fill0) { navCtrl.navigate("ChangeTimeZone") } } if(VERSION.SDK_INT >= 23 && (deviceOwner || profileOwner)) { - SubPageItem(R.string.permission_policy, "", R.drawable.key_fill0) { navCtrl.navigate("PermissionPolicy") } + FunctionItem(R.string.permission_policy, "", R.drawable.key_fill0) { navCtrl.navigate("PermissionPolicy") } } if(VERSION.SDK_INT >= 34 && deviceOwner) { - SubPageItem(R.string.mte_policy, "", R.drawable.memory_fill0) { navCtrl.navigate("MTEPolicy") } + FunctionItem(R.string.mte_policy, "", R.drawable.memory_fill0) { navCtrl.navigate("MTEPolicy") } } if(VERSION.SDK_INT >= 31 && (deviceOwner || profileOwner)) { - SubPageItem(R.string.nearby_streaming_policy, "", R.drawable.share_fill0) { navCtrl.navigate("NearbyStreamingPolicy") } + FunctionItem(R.string.nearby_streaming_policy, "", R.drawable.share_fill0) { navCtrl.navigate("NearbyStreamingPolicy") } } if(VERSION.SDK_INT >= 28 && deviceOwner) { - SubPageItem(R.string.lock_task_mode, "", R.drawable.lock_fill0) { navCtrl.navigate("LockTaskMode") } + FunctionItem(R.string.lock_task_mode, "", R.drawable.lock_fill0) { navCtrl.navigate("LockTaskMode") } } if(deviceOwner || profileOwner) { - SubPageItem(R.string.ca_cert, "", R.drawable.license_fill0) { navCtrl.navigate("CaCert") } + FunctionItem(R.string.ca_cert, "", R.drawable.license_fill0) { navCtrl.navigate("CACert") } } if(VERSION.SDK_INT >= 26 && !dhizuku && (deviceOwner || dpm.isOrgProfile(receiver))) { - SubPageItem(R.string.security_logs, "", R.drawable.description_fill0) { navCtrl.navigate("SecurityLogs") } + FunctionItem(R.string.security_logging, "", R.drawable.description_fill0) { navCtrl.navigate("SecurityLogging") } } if(deviceOwner || profileOwner) { - SubPageItem(R.string.disable_account_management, "", R.drawable.account_circle_fill0) { navCtrl.navigate("DisableAccountManagement") } + FunctionItem(R.string.disable_account_management, "", R.drawable.account_circle_fill0) { navCtrl.navigate("DisableAccountManagement") } } if(VERSION.SDK_INT >= 23 && (deviceOwner || dpm.isOrgProfile(receiver))) { - SubPageItem(R.string.system_update_policy, "", R.drawable.system_update_fill0) { navCtrl.navigate("SystemUpdatePolicy") } + FunctionItem(R.string.system_update_policy, "", R.drawable.system_update_fill0) { navCtrl.navigate("SystemUpdatePolicy") } } if(VERSION.SDK_INT >= 29 && (deviceOwner || dpm.isOrgProfile(receiver))) { - SubPageItem(R.string.install_system_update, "", R.drawable.system_update_fill0) { navCtrl.navigate("InstallSystemUpdate") } + FunctionItem(R.string.install_system_update, "", R.drawable.system_update_fill0) { navCtrl.navigate("InstallSystemUpdate") } } if(VERSION.SDK_INT >= 30 && (deviceOwner || dpm.isOrgProfile(receiver))) { - SubPageItem(R.string.frp_policy, "", R.drawable.device_reset_fill0) { navCtrl.navigate("FRP") } + FunctionItem(R.string.frp_policy, "", R.drawable.device_reset_fill0) { navCtrl.navigate("FRPPolicy") } } if(dangerousFeatures && context.isDeviceAdmin && !(VERSION.SDK_INT >= 24 && profileOwner && dpm.isManagedProfile(receiver))) { - SubPageItem(R.string.wipe_data, "", R.drawable.device_reset_fill0) { navCtrl.navigate("WipeData") } + FunctionItem(R.string.wipe_data, "", R.drawable.device_reset_fill0) { navCtrl.navigate("WipeData") } } - Spacer(Modifier.padding(vertical = 30.dp)) LaunchedEffect(Unit) { fileUriFlow.value = Uri.parse("") } } if(dialog != 0) AlertDialog( @@ -279,7 +217,7 @@ private fun Home(navCtrl: NavHostController, scrollState: ScrollState) { } @Composable -private fun Switches() { +fun SystemOptions(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() @@ -287,8 +225,7 @@ private fun Switches() { val profileOwner = context.isProfileOwner val um = context.getSystemService(Context.USER_SERVICE) as UserManager var dialog by remember { mutableIntStateOf(0) } - Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())) { - Spacer(Modifier.padding(vertical = 5.dp)) + MyScaffold(R.string.options, 0.dp, navCtrl) { if(deviceOwner || profileOwner) { SwitchItem(R.string.disable_cam,"", R.drawable.photo_camera_fill0, { dpm.getCameraDisabled(null) }, { dpm.setCameraDisabled(receiver,it) } @@ -344,7 +281,6 @@ private fun Switches() { { dpm.isUsbDataSignalingEnabled = !it }, ) } - Spacer(Modifier.padding(vertical = 30.dp)) } if(dialog != 0) AlertDialog( text = { @@ -364,17 +300,14 @@ private fun Switches() { } @Composable -private fun Keyguard() { +fun Keyguard(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() val deviceOwner = context.isDeviceOwner val profileOwner = context.isProfileOwner - Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { - Spacer(Modifier.padding(vertical = 10.dp)) + MyScaffold(R.string.keyguard, 8.dp, navCtrl) { if(VERSION.SDK_INT >= 23) { - Text(text = stringResource(R.string.keyguard), style = typography.headlineLarge) - Spacer(Modifier.padding(vertical = 5.dp)) Row( horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth() @@ -401,7 +334,7 @@ private fun Keyguard() { InfoCard(R.string.info_disable_keyguard) Spacer(Modifier.padding(vertical = 12.dp)) } - Text(text = stringResource(R.string.lock_now), style = typography.headlineLarge) + if(VERSION.SDK_INT >= 23) Text(text = stringResource(R.string.lock_now), style = typography.headlineLarge) Spacer(Modifier.padding(vertical = 2.dp)) var flag by remember { mutableIntStateOf(0) } if(VERSION.SDK_INT >= 26 && profileOwner && dpm.isManagedProfile(receiver)) { @@ -424,22 +357,18 @@ private fun Keyguard() { if(VERSION.SDK_INT >= 26 && profileOwner && dpm.isManagedProfile(receiver)) { InfoCard(R.string.info_evict_credential_encryption_key) } - Spacer(Modifier.padding(vertical = 30.dp)) } } @SuppressLint("NewApi") @Composable -private fun EditTime() { +fun ChangeTime(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() val focusMgr = LocalFocusManager.current var inputTime by remember { mutableStateOf("") } - Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.change_time), style = typography.headlineLarge) - Spacer(Modifier.padding(vertical = 5.dp)) + MyScaffold(R.string.change_time, 8.dp, navCtrl) { OutlinedTextField( value = inputTime, label = { Text(stringResource(R.string.time_unit_ms)) }, @@ -459,22 +388,17 @@ private fun EditTime() { } } } + @SuppressLint("NewApi") @Composable -private fun EditTimeZone() { +fun ChangeTimeZone(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val focusMgr = LocalFocusManager.current val receiver = context.getReceiver() var inputTimezone by remember { mutableStateOf("") } var dialog by remember { mutableStateOf(false) } - Column( - modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState()), - horizontalAlignment = Alignment.CenterHorizontally - ) { - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.change_timezone), style = typography.headlineLarge, modifier = Modifier.align(Alignment.Start)) - Spacer(Modifier.padding(vertical = 5.dp)) + MyScaffold(R.string.change_timezone, 8.dp, navCtrl) { OutlinedTextField( value = inputTimezone, label = { Text(stringResource(R.string.timezone_id)) }, @@ -533,15 +457,12 @@ private fun EditTimeZone() { @SuppressLint("NewApi") @Composable -private fun PermissionPolicy() { +fun PermissionPolicy(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() - Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { - var selectedPolicy by remember { mutableIntStateOf(dpm.getPermissionPolicy(receiver)) } - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.permission_policy), style = typography.headlineLarge) - Spacer(Modifier.padding(vertical = 5.dp)) + var selectedPolicy by remember { mutableIntStateOf(dpm.getPermissionPolicy(receiver)) } + MyScaffold(R.string.permission_policy, 8.dp, navCtrl) { RadioButtonItem(R.string.default_stringres, selectedPolicy == PERMISSION_POLICY_PROMPT, { selectedPolicy = PERMISSION_POLICY_PROMPT }) RadioButtonItem(R.string.auto_grant, selectedPolicy == PERMISSION_POLICY_AUTO_GRANT, { selectedPolicy = PERMISSION_POLICY_AUTO_GRANT }) RadioButtonItem(R.string.auto_deny, selectedPolicy == PERMISSION_POLICY_AUTO_DENY, { selectedPolicy = PERMISSION_POLICY_AUTO_DENY }) @@ -561,14 +482,11 @@ private fun PermissionPolicy() { @SuppressLint("NewApi") @Composable -private fun MTEPolicy() { +fun MTEPolicy(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() - Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.mte_policy), style = typography.headlineLarge) - Spacer(Modifier.padding(vertical = 5.dp)) - var selectedMtePolicy by remember { mutableIntStateOf(dpm.mtePolicy) } + var selectedMtePolicy by remember { mutableIntStateOf(dpm.mtePolicy) } + MyScaffold(R.string.mte_policy, 8.dp, navCtrl) { RadioButtonItem( R.string.decide_by_user, selectedMtePolicy == MTE_NOT_CONTROLLED_BY_POLICY, @@ -599,17 +517,16 @@ private fun MTEPolicy() { Text(stringResource(R.string.apply)) } InfoCard(R.string.info_mte_policy) - Spacer(Modifier.padding(vertical = 30.dp)) } } @SuppressLint("NewApi") @Composable -private fun NearbyStreamingPolicy() { +fun NearbyStreamingPolicy(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() - Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { - var appPolicy by remember { mutableIntStateOf(dpm.nearbyAppStreamingPolicy) } + var appPolicy by remember { mutableIntStateOf(dpm.nearbyAppStreamingPolicy) } + MyScaffold(R.string.nearby_streaming_policy, 8.dp, navCtrl) { Spacer(Modifier.padding(vertical = 10.dp)) Text(text = stringResource(R.string.nearby_app_streaming), style = typography.titleLarge) Spacer(Modifier.padding(vertical = 3.dp)) @@ -679,20 +596,19 @@ private fun NearbyStreamingPolicy() { Text(stringResource(R.string.apply)) } InfoCard(R.string.info_nearby_notification_streaming_policy) - Spacer(Modifier.padding(vertical = 30.dp)) } } @SuppressLint("NewApi") @Composable -private fun LockTaskMode(navCtrl: NavHostController) { +fun LockTaskMode(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() val focusMgr = LocalFocusManager.current val coroutine = rememberCoroutineScope() var appSelectorRequest by rememberSaveable { mutableIntStateOf(0) } - Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { + MyScaffold(R.string.lock_task_mode, 8.dp, navCtrl, false) { val lockTaskFeatures = remember { mutableStateListOf() } var custom by rememberSaveable { mutableStateOf(false) } val refreshFeature = { @@ -710,7 +626,7 @@ private fun LockTaskMode(navCtrl: NavHostController) { } if(calculate - 1 >= 0) { lockTaskFeatures += 1 } custom = true - }else{ + } else { custom = false } } @@ -803,7 +719,7 @@ private fun LockTaskMode(navCtrl: NavHostController) { keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }), trailingIcon = { - Icon(painter = painterResource(R.drawable.checklist_fill0), contentDescription = null, + Icon(painter = painterResource(R.drawable.list_fill0), contentDescription = null, modifier = Modifier .clip(RoundedCornerShape(50)) .clickable(onClick = { @@ -865,7 +781,7 @@ private fun LockTaskMode(navCtrl: NavHostController) { keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }), trailingIcon = { - Icon(painter = painterResource(R.drawable.checklist_fill0), contentDescription = null, + Icon(painter = painterResource(R.drawable.list_fill0), contentDescription = null, modifier = Modifier .clip(RoundedCornerShape(50)) .clickable(onClick = { @@ -915,12 +831,11 @@ private fun LockTaskMode(navCtrl: NavHostController) { Text(stringResource(R.string.start)) } InfoCard(R.string.info_start_lock_task_mode) - Spacer(Modifier.padding(vertical = 30.dp)) } } @Composable -private fun CaCert() { +fun CACert(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() @@ -941,10 +856,7 @@ private fun CaCert() { exist = dpm.hasCaCertInstalled(receiver, caCertByteArray) } } - Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.ca_cert), style = typography.headlineLarge) - Spacer(Modifier.padding(vertical = 5.dp)) + MyScaffold(R.string.ca_cert, 8.dp, navCtrl) { AnimatedVisibility(uriPath != "") { Text(text = uriPath) } @@ -1004,7 +916,7 @@ private fun CaCert() { @SuppressLint("NewApi") @Composable -private fun SecurityLogs() { +fun SecurityLogging(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() @@ -1013,10 +925,7 @@ private fun SecurityLogs() { LaunchedEffect(Unit) { fileSize = logFile.length() } - Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.security_logs), style = typography.headlineLarge) - Spacer(Modifier.padding(vertical = 5.dp)) + MyScaffold(R.string.security_logging, 8.dp, navCtrl) { SwitchItem(R.string.enable, "", null, { dpm.isSecurityLoggingEnabled(receiver) }, { dpm.setSecurityLoggingEnabled(receiver, it) }, padding = false) Text(stringResource(R.string.log_file_size_is, formatFileSize(fileSize))) Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) { @@ -1087,21 +996,18 @@ private fun SecurityLogs() { } @Composable -private fun DisableAccountManagement() { +fun DisableAccountManagement(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() val focusMgr = LocalFocusManager.current - Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(horizontal = 8.dp)) { - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.disable_account_management), style = typography.headlineLarge) + MyScaffold(R.string.disable_account_management, 8.dp, navCtrl) { val list = remember { mutableStateListOf() } fun refreshList() { list.clear() dpm.accountTypesWithManagementDisabled?.forEach { list += it } } LaunchedEffect(Unit) { refreshList() } - Spacer(Modifier.padding(vertical = 5.dp)) Column(modifier = Modifier.animateContentSize()) { if(list.isEmpty()) Text(stringResource(R.string.none)) for(i in list) { @@ -1139,7 +1045,7 @@ private fun DisableAccountManagement() { @SuppressLint("NewApi") @Composable -fun FactoryResetProtection() { +fun FRPPolicy(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val focusMgr = LocalFocusManager.current @@ -1165,12 +1071,7 @@ fun FactoryResetProtection() { } } } - Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.frp_policy), style = typography.headlineLarge) - Spacer(Modifier.padding(vertical = 3.dp)) - Text(stringResource(R.string.factory_reset_protection_policy)) - Spacer(Modifier.padding(vertical = 5.dp)) + MyScaffold(R.string.frp_policy, 8.dp, navCtrl) { Row( horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth().padding(horizontal = 6.dp, vertical = 8.dp) @@ -1231,13 +1132,12 @@ fun FactoryResetProtection() { if(unsupported) Text(stringResource(R.string.frp_policy_not_supported)) Spacer(Modifier.padding(vertical = 6.dp)) InfoCard(R.string.info_frp_policy) - Spacer(Modifier.padding(vertical = 30.dp)) } } @SuppressLint("NewApi") @Composable -private fun WipeData() { +fun WipeData(navCtrl: NavHostController) { val context = LocalContext.current val userManager = context.getSystemService(Context.USER_SERVICE) as UserManager val dpm = context.getDPM() @@ -1249,14 +1149,7 @@ private fun WipeData() { var euicc by remember { mutableStateOf(false) } var silent by remember { mutableStateOf(false) } var reason by remember { mutableStateOf("") } - Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { - Spacer(Modifier.padding(vertical = 10.dp)) - Text( - text = stringResource(R.string.wipe_data), - style = typography.headlineLarge, - modifier = Modifier.padding(6.dp),color = colorScheme.error - ) - Spacer(Modifier.padding(vertical = 5.dp)) + MyScaffold(R.string.wipe_data, 8.dp, navCtrl) { CheckBoxItem( R.string.wipe_external_storage, externalStorage, { externalStorage = it } @@ -1302,7 +1195,6 @@ private fun WipeData() { Text("WipeDevice") } } - Spacer(Modifier.padding(vertical = 30.dp)) } if(warning) { LaunchedEffect(Unit) { silent = reason == "" } @@ -1360,18 +1252,15 @@ private fun WipeData() { } @Composable -private fun SysUpdatePolicy() { +fun SystemUpdatePolicy(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() val focusMgr = LocalFocusManager.current - Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { - Spacer(Modifier.padding(vertical = 10.dp)) + MyScaffold(R.string.system_update_policy, 8.dp, navCtrl) { if(VERSION.SDK_INT >= 23) { Column { var selectedPolicy by remember { mutableStateOf(dpm.systemUpdatePolicy?.policyType) } - Text(text = stringResource(R.string.system_update_policy), style = typography.headlineLarge) - Spacer(Modifier.padding(vertical = 5.dp)) RadioButtonItem( R.string.system_update_policy_automatic, selectedPolicy == TYPE_INSTALL_AUTOMATIC, { selectedPolicy = TYPE_INSTALL_AUTOMATIC } @@ -1447,13 +1336,12 @@ private fun SysUpdatePolicy() { } } } - Spacer(Modifier.padding(vertical = 30.dp)) } } @SuppressLint("NewApi") @Composable -fun InstallSystemUpdate() { +fun InstallSystemUpdate(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() @@ -1472,10 +1360,7 @@ fun InstallSystemUpdate() { } } val uri by fileUriFlow.collectAsState() - Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.install_system_update), style = typography.headlineLarge) - Spacer(Modifier.padding(vertical = 5.dp)) + MyScaffold(R.string.install_system_update, 8.dp, navCtrl) { Button( onClick = { val intent = Intent(Intent.ACTION_GET_CONTENT) @@ -1508,10 +1393,7 @@ fun InstallSystemUpdate() { } } Spacer(Modifier.padding(vertical = 10.dp)) - Information { - Text(stringResource(R.string.auto_reboot_after_install_succeed)) - } - Spacer(Modifier.padding(vertical = 30.dp)) + InfoCard(R.string.auto_reboot_after_install_succeed) } } diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/UserRestriction.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/UserRestriction.kt index 57a50bc..b50a866 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/UserRestriction.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/UserRestriction.kt @@ -6,34 +6,20 @@ import android.os.UserManager import android.widget.Toast import androidx.annotation.DrawableRes import androidx.annotation.StringRes -import androidx.compose.foundation.ScrollState import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.MaterialTheme.typography -import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.alpha import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.navigation.NavHostController -import androidx.navigation.compose.NavHost -import androidx.navigation.compose.composable -import androidx.navigation.compose.currentBackStackEntryAsState -import androidx.navigation.compose.rememberNavController import com.bintianqi.owndroid.R -import com.bintianqi.owndroid.ui.Animations -import com.bintianqi.owndroid.ui.SubPageItem +import com.bintianqi.owndroid.ui.FunctionItem +import com.bintianqi.owndroid.ui.MyScaffold import com.bintianqi.owndroid.ui.SwitchItem -import com.bintianqi.owndroid.ui.TopBar data class Restriction( val id: String, @@ -42,92 +28,29 @@ data class Restriction( ) @Composable -fun UserRestriction(navCtrl: NavHostController) { - val localNavCtrl = rememberNavController() - val backStackEntry by localNavCtrl.currentBackStackEntryAsState() - val scrollState = rememberScrollState() - /*val titleMap = mapOf( - "Internet" to R.string.network_internet, - "Connectivity" to R.string.more_connectivity, - "Users" to R.string.users, - "Media" to R.string.media, - "Applications" to R.string.applications, - "Other" to R.string.other - )*/ - Scaffold( - topBar = { - /*TopAppBar( - title = {Text(text = stringResource(titleMap[backStackEntry?.destination?.route]?:R.string.user_restrict))}, - navigationIcon = {NavIcon{if(backStackEntry?.destination?.route=="Home"){navCtrl.navigateUp()}else{localNavCtrl.navigateUp()}}}, - colors = TopAppBarDefaults.topAppBarColors(containerColor = colorScheme.surfaceVariant) - )*/ - TopBar(backStackEntry,navCtrl,localNavCtrl){ - if(backStackEntry?.destination?.route=="Home"&&scrollState.maxValue>80){ - Text( - text = stringResource(R.string.user_restrict), - modifier = Modifier.alpha((maxOf(scrollState.value-30, 0)).toFloat() / 80) - ) - } - } - } - ) { - NavHost( - navController = localNavCtrl, startDestination = "Home", - enterTransition = Animations.navHostEnterTransition, - exitTransition = Animations.navHostExitTransition, - popEnterTransition = Animations.navHostPopEnterTransition, - popExitTransition = Animations.navHostPopExitTransition, - modifier = Modifier.padding(top = it.calculateTopPadding()) - ) { - composable(route = "Home") { Home(localNavCtrl, scrollState) } - composable(route = "Internet") { UserRestrictionPage(RestrictionData.internet()) } - composable(route = "Connectivity") { UserRestrictionPage(RestrictionData.connectivity())} - composable(route = "Applications") { UserRestrictionPage(RestrictionData.connectivity()) } - composable(route = "Users") { UserRestrictionPage(RestrictionData.user()) } - composable(route = "Media") { UserRestrictionPage(RestrictionData.media()) } - composable(route = "Other") { UserRestrictionPage(RestrictionData.other()) } - } - } -} - -@Composable -private fun Home(navCtrl:NavHostController, scrollState: ScrollState) { +fun UserRestriction(navCtrl:NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() - Column(modifier = Modifier.fillMaxSize().verticalScroll(scrollState)) { - Text( - text = stringResource(R.string.user_restrict), - style = typography.headlineLarge, - modifier = Modifier.padding(top = 8.dp, bottom = 7.dp, start = 16.dp) - ) + MyScaffold(R.string.user_restriction, 0.dp, navCtrl) { Text(text = stringResource(R.string.switch_to_disable_feature), modifier = Modifier.padding(start = 16.dp)) if(context.isProfileOwner) { Text(text = stringResource(R.string.profile_owner_is_restricted), modifier = Modifier.padding(start = 16.dp)) } if(context.isProfileOwner && (VERSION.SDK_INT < 24 || (VERSION.SDK_INT >= 24 && dpm.isManagedProfile(receiver)))) { Text(text = stringResource(R.string.some_features_invalid_in_work_profile), modifier = Modifier.padding(start = 16.dp)) } Spacer(Modifier.padding(vertical = 2.dp)) - SubPageItem(R.string.network_internet, "", R.drawable.wifi_fill0) { navCtrl.navigate("Internet") } - SubPageItem(R.string.more_connectivity, "", R.drawable.devices_other_fill0) { navCtrl.navigate("Connectivity") } - SubPageItem(R.string.applications, "", R.drawable.apps_fill0) { navCtrl.navigate("Applications") } - SubPageItem(R.string.users, "", R.drawable.account_circle_fill0) { navCtrl.navigate("Users") } - SubPageItem(R.string.media, "", R.drawable.volume_up_fill0) { navCtrl.navigate("Media") } - SubPageItem(R.string.other, "", R.drawable.more_horiz_fill0) { navCtrl.navigate("Other") } - Spacer(Modifier.padding(vertical = 30.dp)) - } -} - -@Composable -private fun UserRestrictionPage(restrictions: List) { - Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())) { - restrictions.forEach { UserRestrictionItem(it) } - Spacer(Modifier.padding(vertical = 30.dp)) + FunctionItem(R.string.network_and_internet, "", R.drawable.wifi_fill0) { navCtrl.navigate("UR-Internet") } + FunctionItem(R.string.connectivity, "", R.drawable.devices_other_fill0) { navCtrl.navigate("UR-Connectivity") } + FunctionItem(R.string.applications, "", R.drawable.apps_fill0) { navCtrl.navigate("UR-Applications") } + FunctionItem(R.string.users, "", R.drawable.account_circle_fill0) { navCtrl.navigate("UR-Users") } + FunctionItem(R.string.media, "", R.drawable.volume_up_fill0) { navCtrl.navigate("UR-Media") } + FunctionItem(R.string.other, "", R.drawable.more_horiz_fill0) { navCtrl.navigate("UR-Other") } } } @SuppressLint("NewApi") @Composable -private fun UserRestrictionItem(restriction: Restriction) { +fun UserRestrictionItem(restriction: Restriction) { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() @@ -153,7 +76,7 @@ private fun UserRestrictionItem(restriction: Restriction) { } object RestrictionData { - fun internet(): List { + val internet: List get() { val list = mutableListOf() list += Restriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS, R.string.config_mobile_network, R.drawable.signal_cellular_alt_fill0) list += Restriction(UserManager.DISALLOW_CONFIG_WIFI, R.string.config_wifi, R.drawable.wifi_fill0) @@ -179,7 +102,7 @@ object RestrictionData { list += Restriction(UserManager.DISALLOW_OUTGOING_CALLS, R.string.outgoing_calls, R.drawable.phone_forwarded_fill0) return list } - fun connectivity(): List { + val connectivity: List get() { val list = mutableListOf() if(VERSION.SDK_INT>=26) { list += Restriction(UserManager.DISALLOW_BLUETOOTH, R.string.bluetooth, R.drawable.bluetooth_fill0) @@ -193,7 +116,7 @@ object RestrictionData { if(VERSION.SDK_INT>=28) list += Restriction(UserManager.DISALLOW_PRINTING, R.string.printing, R.drawable.print_fill0) return list } - fun application(): List { + val applications: List get() { val list = mutableListOf() list += Restriction(UserManager.DISALLOW_INSTALL_APPS, R.string.install_app, R.drawable.android_fill0) if(VERSION.SDK_INT>=29) list += Restriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY, R.string.install_unknown_src_globally, R.drawable.android_fill0) @@ -203,7 +126,7 @@ object RestrictionData { if(VERSION.SDK_INT>=34) list += Restriction(UserManager.DISALLOW_CONFIG_DEFAULT_APPS, R.string.config_default_apps, R.drawable.apps_fill0) return list } - fun media(): List { + val media: List get() { val list = mutableListOf() if(VERSION.SDK_INT>=28) { list += Restriction(UserManager.DISALLOW_CONFIG_BRIGHTNESS, R.string.config_brightness, R.drawable.brightness_5_fill0) @@ -218,7 +141,7 @@ object RestrictionData { } return list } - fun user(): List { + val users: List get() { val list = mutableListOf() list += Restriction(UserManager.DISALLOW_ADD_USER, R.string.add_user, R.drawable.account_circle_fill0) list += Restriction(UserManager.DISALLOW_REMOVE_USER, R.string.remove_user, R.drawable.account_circle_fill0) @@ -231,7 +154,7 @@ object RestrictionData { } return list } - fun other(): List { + val other: List get() { val list = mutableListOf() if(VERSION.SDK_INT>=26) list += Restriction(UserManager.DISALLOW_AUTOFILL, R.string.autofill, R.drawable.password_fill0) list += Restriction(UserManager.DISALLOW_CONFIG_CREDENTIALS, R.string.config_credentials, R.drawable.android_fill0) @@ -254,14 +177,7 @@ object RestrictionData { list += Restriction(UserManager.DISALLOW_DEBUGGING_FEATURES, R.string.debug_features, R.drawable.adb_fill0) return list } - fun getAllRestrictions(): List { - val result = mutableListOf() - internet().forEach { result.add(it.id) } - connectivity().forEach { result.add(it.id) } - media().forEach { result.add(it.id) } - application().forEach { result.add(it.id) } - user().forEach { result.add(it.id) } - other().forEach { result.add(it.id) } - return result - } + fun getAllRestrictions(): List = + internet + connectivity + media + applications + users + other + } diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/Users.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/Users.kt index 6d849c3..995ad65 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/Users.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/Users.kt @@ -18,20 +18,16 @@ import androidx.annotation.StringRes import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.animateContentSize import androidx.compose.foundation.Image -import androidx.compose.foundation.ScrollState import androidx.compose.foundation.layout.Arrangement 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.padding import androidx.compose.foundation.layout.size -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.filled.Add import androidx.compose.material3.AlertDialog @@ -39,9 +35,7 @@ import androidx.compose.material3.Button import androidx.compose.material3.Card import androidx.compose.material3.Icon import androidx.compose.material3.IconButton -import androidx.compose.material3.MaterialTheme.typography import androidx.compose.material3.OutlinedTextField -import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable @@ -55,7 +49,6 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.platform.LocalContext @@ -65,106 +58,56 @@ import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import androidx.navigation.NavHostController -import androidx.navigation.compose.NavHost -import androidx.navigation.compose.composable -import androidx.navigation.compose.currentBackStackEntryAsState -import androidx.navigation.compose.rememberNavController import com.bintianqi.owndroid.R import com.bintianqi.owndroid.fileUriFlow import com.bintianqi.owndroid.getFile import com.bintianqi.owndroid.parseTimestamp import com.bintianqi.owndroid.toggle -import com.bintianqi.owndroid.ui.Animations import com.bintianqi.owndroid.ui.CardItem import com.bintianqi.owndroid.ui.CheckBoxItem +import com.bintianqi.owndroid.ui.FunctionItem import com.bintianqi.owndroid.ui.InfoCard import com.bintianqi.owndroid.ui.ListItem -import com.bintianqi.owndroid.ui.SubPageItem +import com.bintianqi.owndroid.ui.MyScaffold import com.bintianqi.owndroid.ui.SwitchItem -import com.bintianqi.owndroid.ui.TopBar import com.bintianqi.owndroid.uriToStream import com.bintianqi.owndroid.yesOrNo @Composable -fun UserManage(navCtrl: NavHostController) { - val localNavCtrl = rememberNavController() - val backStackEntry by localNavCtrl.currentBackStackEntryAsState() - val scrollState = rememberScrollState() - Scaffold( - topBar = { - TopBar(backStackEntry, navCtrl, localNavCtrl) { - if(backStackEntry?.destination?.route == "Home" && scrollState.maxValue > 100) { - Text( - text = stringResource(R.string.users), - modifier = Modifier.alpha((maxOf(scrollState.value-30, 0)).toFloat() / 80) - ) - } - } - } - ) { - NavHost( - navController = localNavCtrl, startDestination = "Home", - enterTransition = Animations.navHostEnterTransition, - exitTransition = Animations.navHostExitTransition, - popEnterTransition = Animations.navHostPopEnterTransition, - popExitTransition = Animations.navHostPopExitTransition, - modifier = Modifier.padding(top = it.calculateTopPadding()) - ) { - composable(route = "Home") { Home(localNavCtrl, scrollState) } - composable(route = "UserInfo") { CurrentUserInfo() } - composable(route = "Options") { Options() } - composable(route = "UserOperation") { UserOperation() } - composable(route = "CreateUser") { CreateUser() } - composable(route = "EditUsername") { Username() } - composable(route = "ChangeUserIcon") { UserIcon() } - composable(route = "UserSessionMessage") { UserSessionMessage() } - composable(route = "AffiliationID") { AffiliationID() } - } - } -} - -@Composable -private fun Home(navCtrl: NavHostController,scrollState: ScrollState) { +fun Users(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() val deviceOwner = context.isDeviceOwner val profileOwner = context.isProfileOwner - //var logoutDialog by remember { mutableStateOf(false) } var dialog by remember { mutableIntStateOf(0) } - Column(modifier = Modifier.fillMaxSize().verticalScroll(scrollState)) { - Text( - text = stringResource(R.string.users), - style = typography.headlineLarge, - modifier = Modifier.padding(top = 8.dp, bottom = 5.dp, start = 16.dp) - ) - SubPageItem(R.string.user_info, "", R.drawable.person_fill0) { navCtrl.navigate("UserInfo") } + MyScaffold(R.string.users, 0.dp, navCtrl) { + FunctionItem(R.string.user_info, "", R.drawable.person_fill0) { navCtrl.navigate("UserInfo") } if(deviceOwner && VERSION.SDK_INT >= 28) { - SubPageItem(R.string.secondary_users, "", R.drawable.list_fill0) { dialog = 1 } - SubPageItem(R.string.options, "", R.drawable.tune_fill0) { navCtrl.navigate("Options") } + FunctionItem(R.string.secondary_users, "", R.drawable.list_fill0) { dialog = 1 } + FunctionItem(R.string.options, "", R.drawable.tune_fill0) { navCtrl.navigate("UserOptions") } } if(deviceOwner) { - SubPageItem(R.string.user_operation, "", R.drawable.sync_alt_fill0) { navCtrl.navigate("UserOperation") } + FunctionItem(R.string.user_operation, "", R.drawable.sync_alt_fill0) { navCtrl.navigate("UserOperation") } } if(VERSION.SDK_INT >= 24 && deviceOwner) { - SubPageItem(R.string.create_user, "", R.drawable.person_add_fill0) { navCtrl.navigate("CreateUser") } + FunctionItem(R.string.create_user, "", R.drawable.person_add_fill0) { navCtrl.navigate("CreateUser") } } if(VERSION.SDK_INT >= 28 && profileOwner && dpm.isAffiliatedUser) { - SubPageItem(R.string.logout_current_user, "", R.drawable.logout_fill0) { dialog = 2 } + FunctionItem(R.string.logout_current_user, "", R.drawable.logout_fill0) { dialog = 2 } } if(deviceOwner || profileOwner) { - SubPageItem(R.string.change_username, "", R.drawable.edit_fill0) { navCtrl.navigate("EditUsername") } + FunctionItem(R.string.change_username, "", R.drawable.edit_fill0) { navCtrl.navigate("ChangeUsername") } } if(VERSION.SDK_INT >= 23 && (deviceOwner || profileOwner)) { - SubPageItem(R.string.change_user_icon, "", R.drawable.account_circle_fill0) { navCtrl.navigate("ChangeUserIcon") } + FunctionItem(R.string.change_user_icon, "", R.drawable.account_circle_fill0) { navCtrl.navigate("ChangeUserIcon") } } if(VERSION.SDK_INT >= 28 && deviceOwner) { - SubPageItem(R.string.user_session_msg, "", R.drawable.notifications_fill0) { navCtrl.navigate("UserSessionMessage") } + FunctionItem(R.string.user_session_msg, "", R.drawable.notifications_fill0) { navCtrl.navigate("UserSessionMessage") } } if(VERSION.SDK_INT >= 26 && (deviceOwner || profileOwner)) { - SubPageItem(R.string.affiliation_id, "", R.drawable.id_card_fill0) { navCtrl.navigate("AffiliationID") } + FunctionItem(R.string.affiliation_id, "", R.drawable.id_card_fill0) { navCtrl.navigate("AffiliationID") } } - Spacer(Modifier.padding(vertical = 30.dp)) LaunchedEffect(Unit) { fileUriFlow.value = Uri.parse("") } } if(dialog != 0 && VERSION.SDK_INT >= 28) AlertDialog( @@ -208,11 +151,11 @@ private fun Home(navCtrl: NavHostController,scrollState: ScrollState) { } @Composable -private fun Options() { +fun UserOptions(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() - Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())) { + MyScaffold(R.string.options, 0.dp, navCtrl) { if(VERSION.SDK_INT >= 28) { SwitchItem(R.string.enable_logout, "", null, { dpm.isLogoutEnabled }, { dpm.setLogoutEnabled(receiver, it) }) } @@ -220,17 +163,14 @@ private fun Options() { } @Composable -private fun CurrentUserInfo() { +fun CurrentUserInfo(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() val userManager = context.getSystemService(Context.USER_SERVICE) as UserManager val user = Process.myUserHandle() var infoDialog by remember { mutableIntStateOf(0) } - Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.user_info), style = typography.headlineLarge) - Spacer(Modifier.padding(vertical = 5.dp)) + MyScaffold(R.string.user_info, 8.dp, navCtrl) { if(VERSION.SDK_INT >= 24) CardItem(R.string.support_multiuser, UserManager.supportsMultipleUsers().yesOrNo) if(VERSION.SDK_INT >= 31) CardItem(R.string.headless_system_user_mode, UserManager.isHeadlessSystemUserMode().yesOrNo) { infoDialog = 1 } Spacer(Modifier.padding(vertical = 8.dp)) @@ -247,7 +187,6 @@ private fun CurrentUserInfo() { } CardItem(R.string.user_id, (Binder.getCallingUid() / 100000).toString()) CardItem(R.string.user_serial_number, userManager.getSerialNumberForUser(Process.myUserHandle()).toString()) - Spacer(Modifier.padding(vertical = 30.dp)) } if(infoDialog != 0) AlertDialog( text = { Text(stringResource(R.string.info_headless_system_user_mode)) }, @@ -261,7 +200,7 @@ private fun CurrentUserInfo() { } @Composable -private fun UserOperation() { +fun UserOperation(navCtrl: NavHostController) { val context = LocalContext.current val userManager = context.getSystemService(Context.USER_SERVICE) as UserManager val dpm = context.getDPM() @@ -287,10 +226,7 @@ private fun UserOperation() { } catch(_: Exception) { false } - Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.user_operation), style = typography.headlineLarge) - Spacer(Modifier.padding(vertical = 5.dp)) + MyScaffold(R.string.user_operation, 8.dp, navCtrl) { OutlinedTextField( value = idInput, onValueChange = { @@ -365,13 +301,12 @@ private fun UserOperation() { Text(stringResource(R.string.delete)) } InfoCard(R.string.info_user_operation) - Spacer(Modifier.padding(vertical = 30.dp)) } } @SuppressLint("NewApi") @Composable -private fun CreateUser() { +fun CreateUser(navCtrl: NavHostController) { val context = LocalContext.current val userManager = context.getSystemService(Context.USER_SERVICE) as UserManager val dpm = context.getDPM() @@ -379,10 +314,7 @@ private fun CreateUser() { val focusMgr = LocalFocusManager.current var userName by remember { mutableStateOf("") } val flags = remember { mutableStateListOf() } - Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.create_user), style = typography.headlineLarge) - Spacer(Modifier.padding(vertical = 5.dp)) + MyScaffold(R.string.create_user, 8.dp, navCtrl) { OutlinedTextField( value = userName, onValueChange = { userName= it }, @@ -423,13 +355,12 @@ private fun CreateUser() { } Spacer(Modifier.padding(vertical = 5.dp)) if(newUserHandle != null) { Text(text = stringResource(R.string.serial_number_of_new_user_is, userManager.getSerialNumberForUser(newUserHandle))) } - Spacer(Modifier.padding(vertical = 30.dp)) } } @SuppressLint("NewApi") @Composable -private fun AffiliationID() { +fun AffiliationID(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() @@ -441,10 +372,7 @@ private fun AffiliationID() { list.addAll(dpm.getAffiliationIds(receiver)) } LaunchedEffect(Unit) { refreshIds() } - Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.affiliation_id), style = typography.headlineLarge) - Spacer(Modifier.padding(vertical = 5.dp)) + MyScaffold(R.string.affiliation_id, 8.dp, navCtrl) { Column(modifier = Modifier.animateContentSize()) { if(list.isEmpty()) Text(stringResource(R.string.none)) for(i in list) { @@ -483,21 +411,17 @@ private fun AffiliationID() { Text(stringResource(R.string.apply)) } InfoCard(R.string.info_affiliated_id) - Spacer(Modifier.padding(vertical = 30.dp)) } } @Composable -private fun Username() { +fun ChangeUsername(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() val focusMgr = LocalFocusManager.current var inputUsername by remember { mutableStateOf("") } - Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.change_username), style = typography.headlineLarge) - Spacer(Modifier.padding(vertical = 5.dp)) + MyScaffold(R.string.change_username, 8.dp, navCtrl) { OutlinedTextField( value = inputUsername, onValueChange = { inputUsername= it }, @@ -527,7 +451,7 @@ private fun Username() { @SuppressLint("NewApi") @Composable -private fun UserSessionMessage() { +fun UserSessionMessage(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() @@ -539,10 +463,7 @@ private fun UserSessionMessage() { end = dpm.getEndUserSessionMessage(receiver)?.toString() ?: "" } LaunchedEffect(Unit) { refreshMsg() } - Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.user_session_msg), style = typography.headlineLarge) - Spacer(Modifier.padding(vertical = 5.dp)) + MyScaffold(R.string.user_session_msg, 8.dp, navCtrl) { OutlinedTextField( value = start, onValueChange = { start= it }, @@ -604,13 +525,12 @@ private fun UserSessionMessage() { Text(stringResource(R.string.reset)) } } - Spacer(Modifier.padding(vertical = 30.dp)) } } @SuppressLint("NewApi") @Composable -private fun UserIcon() { +fun ChangeUserIcon(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() @@ -623,10 +543,7 @@ private fun UserIcon() { bitmap = BitmapFactory.decodeStream(stream) } } - Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.change_user_icon), style = typography.headlineLarge) - Spacer(Modifier.padding(vertical = 5.dp)) + MyScaffold(R.string.change_user_icon, 8.dp, navCtrl) { CheckBoxItem(R.string.file_picker_instead_gallery, getContent, { getContent = it }) Spacer(Modifier.padding(vertical = 5.dp)) Button( diff --git a/app/src/main/java/com/bintianqi/owndroid/ui/Components.kt b/app/src/main/java/com/bintianqi/owndroid/ui/Components.kt index 5a7385a..c1e8582 100644 --- a/app/src/main/java/com/bintianqi/owndroid/ui/Components.kt +++ b/app/src/main/java/com/bintianqi/owndroid/ui/Components.kt @@ -8,8 +8,10 @@ import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.selection.SelectionContainer +import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Info import androidx.compose.material3.* @@ -18,11 +20,13 @@ import androidx.compose.material3.MaterialTheme.typography import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.navigation.NavBackStackEntry import androidx.navigation.NavHostController @@ -33,9 +37,9 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.launch @Composable -fun SubPageItem( +fun FunctionItem( @StringRes title: Int, - desc:String, + desc: String, @DrawableRes icon: Int? = null, operation: () -> Unit ) { @@ -48,8 +52,7 @@ fun SubPageItem( verticalAlignment = Alignment.CenterVertically ) { if(icon != null) Icon( - painter = painterResource(icon), - contentDescription = null, + painter = painterResource(icon), contentDescription = null, modifier = Modifier.padding(top = 1.dp, end = 20.dp).offset(x = (-2).dp) ) Column { @@ -194,25 +197,6 @@ fun SwitchItem( } } -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun TopBar( - backStackEntry: NavBackStackEntry?, - navCtrl: NavHostController, - localNavCtrl: NavHostController, - title: @Composable ()->Unit = {} -) { - TopAppBar( - title = title, - navigationIcon = { - NavIcon{ - if(backStackEntry?.destination?.route == "Home") { navCtrl.navigateUp() } else { localNavCtrl.navigateUp() } - } - }, - colors = TopAppBarDefaults.topAppBarColors(containerColor = colorScheme.background) - ) -} - @Composable fun CopyTextButton(@StringRes label: Int, content: String) { val context = LocalContext.current @@ -287,6 +271,7 @@ fun ListItem(text: String, onDelete: () -> Unit) { fun InfoCard(@StringRes strID: Int) { Column( modifier = Modifier + .fillMaxWidth() .padding(vertical = 8.dp) .clip(RoundedCornerShape(10)) .background(color = colorScheme.tertiaryContainer) @@ -296,3 +281,53 @@ fun InfoCard(@StringRes strID: Int) { Text(stringResource(strID)) } } + +@Composable +fun MyScaffold( + @StringRes title: Int, + horizonPadding: Dp, + navCtrl: NavHostController, + displayTitle: Boolean = true, + content: @Composable ColumnScope.() -> Unit +) = MyScaffold(title, horizonPadding, { navCtrl.navigateUp() }, displayTitle, content) + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun MyScaffold( + @StringRes title: Int, + horizonPadding: Dp, + onNavIconClicked: () -> Unit, + displayTitle: Boolean, + content: @Composable ColumnScope.() -> Unit +) { + val scrollState = rememberScrollState() + Scaffold( + topBar = { + TopAppBar( + title = { + Text( + text = stringResource(title), + modifier = if(displayTitle) Modifier.alpha((maxOf(scrollState.value-90,0)).toFloat()/50) else Modifier + ) + }, + navigationIcon = { NavIcon (onNavIconClicked) } + ) + } + ) { paddingValues -> + Column( + modifier = Modifier + .fillMaxSize() + .padding(paddingValues) + .padding(horizontal = horizonPadding) + .verticalScroll(scrollState) + .padding(bottom = 80.dp) + ) { + if(displayTitle) Text( + text = stringResource(title), + style = typography.headlineLarge, + modifier = Modifier.padding(start = if(horizonPadding == 0.dp) 16.dp else 0.dp,top = 10.dp, bottom = 5.dp) + ) + content() + } + } +} diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index ed5b259..eb9b342 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -91,8 +91,8 @@ Тип аккаунта Передача прав владения Имя целевого компонента - Информация на экране блокировки - Сообщение поддержки + Информация на экране блокировки + Сообщение поддержки Краткое сообщение Подробное сообщение Передать @@ -175,7 +175,7 @@ Сертификат установлен: %1$s Выберите сертификат... Удалить все пользовательские CA-сертификаты - Журналы безопасности + Security logging Журналы безопасности перед перезагрузкой Стереть данные Стереть внешнее хранилище @@ -186,7 +186,6 @@ Ваш рабочий профиль будет УДАЛЕН Статус шифрования Политика FRP - Политика защиты от сброса к заводским настройкам Политика FRP не поддерживается на этом устройстве Включить FRP Список аккаунтов: @@ -236,12 +235,11 @@ Указать порт Неверная конфигурация Исключить хосты - Журналы сети + Network logging Размер файла журнала: %1$s Удалить журналы Экспортировать журналы Пара ключей Wi-Fi - Пара ключей Предпочтительная сетевая служба Идентификатор сети Разрешить переход на соединение по умолчанию @@ -286,7 +284,6 @@ Перенести аккаунт Имя аккаунта Сохранить аккаунт - Рабочий профиль принадлежит организации: %1$s Рабочий профиль организации Пропустить шифрование Создать @@ -316,7 +313,7 @@ Постоянный VPN Включить блокировку Очистить текущую конфигурацию - Разрешение + Разрешение Область действия: рабочий профиль Информация о приложении Не установлено @@ -366,12 +363,12 @@ - Ограничения пользователя + Ограничения пользователя Владелец профиля может использовать ограниченные функции Включите переключатель, чтобы отключить эту функцию. Функции в рабочем профиле ограничены. - Сеть - Другие подключения + Сеть + Другие подключения Медиа Другое Требуется владелец устройства @@ -478,7 +475,6 @@ Изменить значок пользователя Использовать выборщик файлов вместо галереи Выберите изображение... - Неизвестный результат (возможно, ошибка) Ошибка: управляемый профиль Ошибка: текущий пользователь Сообщение о сеансе пользователя diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index bf82952..7dd3071 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -92,8 +92,8 @@ Hesap Türleri Sahipliği Devret Target component name - Ekran Kilidi Bilgisi - Destek Mesajı + Ekran Kilidi Bilgisi + Destek Mesajı Kısa Mesaj Uzun Mesaj Transfer @@ -177,7 +177,7 @@ Yüklenen sertifika: %1$s Sertifika seç... Tüm kullanıcı sertifikalarını kaldır - Güvenlik kayıtları + Security logging Yeniden başlatmadan önce güvenlik kayıtları Verileri sil Harici depolamayı sil @@ -188,7 +188,6 @@ Your work profile will be DELETED Encryption status FRP politikası - Fabrika ayarlarına sıfırlama koruma politikası FRP politikası bu cihazda desteklenmiyor FRP\'yi etkinleştir "Hesap listesi: " @@ -237,12 +236,11 @@ Specify port Invalid config Exclude hosts - Ağ kayıtları + Network logging Log file size: %1$s Delete logs Export logs Wi-Fi anahtar çifti - Anahtar çifti Tercihli ağ hizmeti Network ID Allow fallback to default connection @@ -285,7 +283,6 @@ Migrate account Account name Keep account - Kuruluşa ait iş profili: %1$s Kuruluş iş profili Şifrelemeyi atla Oluştur @@ -314,7 +311,7 @@ Her zaman açık VPN Enable lockdown Clear current config - İzin + İzin Kapsam: iş profili Uygulama bilgisi Yüklü değil @@ -365,12 +362,12 @@ Başarısız: zaman aşımı - Kullanıcı kısıtlaması + Kullanıcı kısıtlaması Profil sahibi sınırlı işlev kullanabilir Bu işlevi devre dışı bırakmak için bir anahtar açın. İş profilindeki işlevler sınırlıdır. - - Diğer bağlantı + + Diğer bağlantı Medya Diğer Cihaz sahibi gerektirir @@ -476,7 +473,6 @@ Kullanıcı simgesini değiştir Galeri yerine dosya seçici kullan Resim seç... - Bilinmeyen sonuç (başarısız olabilir) Başarısız: yönetilen profil Başarısız: mevcut kullanıcı Kullanıcı oturum mesajı diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 380ebd6..188e96f 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -87,8 +87,8 @@ 账号类型 转移所有权 目标组件名 - 锁屏提示信息 - 提供支持的消息 + 锁屏提示信息 + 提供支持的消息 提供支持的短消息 提供支持的长消息 转移 @@ -172,7 +172,7 @@ 证书已安装:%1$s 选择证书... 卸载所有用户证书 - 安全日志 + 安全日志 重启前安全日志 清除数据 清除外部存储 @@ -183,7 +183,6 @@ 你的工作资料将会被删除 加密状态 FRP策略 - 恢复出厂设置保护策略 这个设备不支持恢复出厂设置保护策略 启用FRP 账户列表: @@ -232,12 +231,11 @@ 指定端口 无效配置 排除列表 - 收集网络日志 + 网络日志 日志文件大小:%1$s 删除日志 导出日志 Wi-Fi密钥对 - 密钥对 首选网络服务 网络ID 允许回落到默认连接 @@ -280,7 +278,6 @@ 迁移账号 账号名 保留账号 - 由组织拥有的工作资料:%1$s 组织拥有的工作资料 跳过加密 创建 @@ -309,7 +306,7 @@ VPN保持打开 启用锁定 清除当前配置 - 权限 + 权限 作用域: 工作资料 应用详情 未安装 @@ -357,12 +354,12 @@ 超时 - 用户限制 + 用户限制 Profile owner无法使用部分功能 打开开关后会禁用对应的功能 工作资料中部分功能无效 - 网络和互联网 - 更多连接 + 网络和互联网 + 更多连接 媒体 其他 需要DeviceOwner @@ -468,7 +465,6 @@ 更换用户头像 使用文件选择器而不是相册 选择图片... - 未知结果(失败) 失败:受管理的资料 失败:当前用户 用户会话消息 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1a1b94e..7f2d672 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -94,8 +94,8 @@ Account type Transfer Ownership Target component name - Lockscreen info - Support Message + Lockscreen info + Support Messages Short message Long message Transfer @@ -181,7 +181,7 @@ Certificate installed: %1$s Select certificate... Uninstall all user CA certificate - Security logs + Security logging Pre-reboot security logs Wipe data Wipe external storage @@ -192,7 +192,6 @@ Your work profile will be DELETED Encryption status FRP policy - Factory reset protection policy FRP policy is not supported on this device Enable FRP "Account list: " @@ -242,12 +241,11 @@ Specify port Invalid config Exclude hosts - Network logs + Network logging Log file size: %1$s Delete logs Export logs Wi-Fi keypair - Keypair Preferential network service Network ID Allow fallback to default connection @@ -290,7 +288,6 @@ Migrate account Account name Keep account - Organization owned work profile: %1$s Organization work profile dpm mark-profile-owner-on-organization-owned-device --user %1$s com.bintianqi.owndroid/com.bintianqi.owndroid.Receiver @@ -322,7 +319,7 @@ Always-on VPN Enable lockdown Clear current config - Permission + Permissions Scope: work profile App info Not installed @@ -371,12 +368,12 @@ Fail: timeout - User restriction + User restriction Profile owner can use limited function Turn on a switch to disable that function. Functions in work profile is limited. - Network - Other connection + Network + Other connection Media Other Require device owner @@ -482,7 +479,6 @@ Change user icon Use file picker instead of gallery Select image... - Unknown result(may failed) Failed: managed profile Failed: current user User session message @@ -631,7 +627,7 @@ If a Device owner use this function, all users should be affiliated. Not all devices support pre-reboot security logs. When account management is disabled for an account type, adding or removing an account of that type will not be possible. - FRP can protect the device after untrusted factory reset (in Recovery or Fastboot).\nTo enable this feature, the device must support persistent data block service. + Factory reset protection can protect the device after untrusted factory reset (in Recovery or Fastboot).\nTo enable this feature, the device must support persistent data block service. All data of this user will be wiped, but that user won\'t be removed. Control whether the user can change networks configured by the admin.\nWhen this lockdown is enabled, the user can still configure and connect to other Wi-Fi networks, or use other Wi-Fi capabilities such as tethering. Specify the minimum security level required for Wi-Fi networks. The device may not connect to networks that do not meet the minimum security level. If the current network does not meet the minimum security level set, it will be disconnected. diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0f32f42..61fa424 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,14 +1,14 @@ [versions] -agp = "8.7.2" +agp = "8.7.3" kotlin = "2.0.21" -navigation-compose = "2.8.3" -composeBom = "2024.10.01" +navigation-compose = "2.8.4" +composeBom = "2024.11.00" accompanist-drawablepainter = "0.35.0-alpha" shizuku = "13.1.5" biometric = "1.2.0-alpha05" -fragment = "1.8.0-beta01" -dhizuku = "2.5.2" +fragment = "1.8.5" +dhizuku = "2.5.3" hiddenApiBypass = "4.3" serialization = "1.7.3"