mirror of
https://github.com/awfixers-stuff/OwnDroid.git
synced 2026-03-23 19:15:58 +00:00
Confirm changing user icon in dialog
Disable API on deactivate Fix changing theme Fix auth in app installer
This commit is contained in:
@@ -241,7 +241,8 @@ class AppInstallerViewModel(application: Application): AndroidViewModel(applicat
|
||||
val writtenPackages = MutableStateFlow(setOf<Uri>())
|
||||
val writingPackage = MutableStateFlow<Uri?>(null)
|
||||
fun startInstallationProcess(activity: FragmentActivity) {
|
||||
startAuth(activity, object : BiometricPrompt.AuthenticationCallback() {
|
||||
val sp = SharedPrefs(getApplication<Application>())
|
||||
if(sp.auth) startAuth(activity, object : BiometricPrompt.AuthenticationCallback() {
|
||||
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
|
||||
super.onAuthenticationSucceeded(result)
|
||||
startInstall()
|
||||
@@ -251,6 +252,7 @@ class AppInstallerViewModel(application: Application): AndroidViewModel(applicat
|
||||
Toast.makeText(activity, R.string.failed_to_authenticate, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
})
|
||||
else startInstall()
|
||||
}
|
||||
private fun startInstall() {
|
||||
if(installing.value) return
|
||||
|
||||
@@ -76,8 +76,6 @@ import com.bintianqi.owndroid.dpm.ChangeTime
|
||||
import com.bintianqi.owndroid.dpm.ChangeTimeScreen
|
||||
import com.bintianqi.owndroid.dpm.ChangeTimeZone
|
||||
import com.bintianqi.owndroid.dpm.ChangeTimeZoneScreen
|
||||
import com.bintianqi.owndroid.dpm.ChangeUserIcon
|
||||
import com.bintianqi.owndroid.dpm.ChangeUserIconScreen
|
||||
import com.bintianqi.owndroid.dpm.ChangeUsername
|
||||
import com.bintianqi.owndroid.dpm.ChangeUsernameScreen
|
||||
import com.bintianqi.owndroid.dpm.ContentProtectionPolicy
|
||||
@@ -255,31 +253,19 @@ class MainActivity : FragmentActivity() {
|
||||
@ExperimentalMaterial3Api
|
||||
@Composable
|
||||
fun Home(activity: FragmentActivity, vm: MyViewModel) {
|
||||
val navCtrl = rememberNavController()
|
||||
val navController = rememberNavController()
|
||||
val context = LocalContext.current
|
||||
val dpm = context.getDPM()
|
||||
val receiver = context.getReceiver()
|
||||
val sp = SharedPrefs(context)
|
||||
val focusMgr = LocalFocusManager.current
|
||||
val backToHome by backToHomeStateFlow.collectAsState()
|
||||
val lifecycleOwner = LocalLifecycleOwner.current
|
||||
LaunchedEffect(backToHome) {
|
||||
if(backToHome) { navCtrl.navigateUp(); backToHomeStateFlow.value = false }
|
||||
if(backToHome) { navController.navigateUp(); backToHomeStateFlow.value = false }
|
||||
}
|
||||
val userRestrictions by vm.userRestrictions.collectAsStateWithLifecycle()
|
||||
fun onUserRestrictionsChange(id: String, status: Boolean) {
|
||||
try {
|
||||
if(status) dpm.addUserRestriction(receiver, id)
|
||||
else dpm.clearUserRestriction(receiver, id)
|
||||
@SuppressLint("NewApi")
|
||||
vm.userRestrictions.value = dpm.getUserRestrictions(receiver)
|
||||
} catch(_: Exception) {
|
||||
context.showOperationResultToast(false)
|
||||
}
|
||||
}
|
||||
fun navigateUp() { navCtrl.navigateUp() }
|
||||
fun navigateUp() { navController.navigateUp() }
|
||||
@Suppress("NewApi") NavHost(
|
||||
navController = navCtrl,
|
||||
navController = navController,
|
||||
startDestination = Home,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
@@ -291,15 +277,15 @@ fun Home(activity: FragmentActivity, vm: MyViewModel) {
|
||||
popEnterTransition = Animations.navHostPopEnterTransition,
|
||||
popExitTransition = Animations.navHostPopExitTransition
|
||||
) {
|
||||
composable<Home> { HomeScreen { navCtrl.navigate(it) } }
|
||||
composable<Home> { HomeScreen { navController.navigate(it) } }
|
||||
|
||||
composable<Permissions> {
|
||||
PermissionsScreen(::navigateUp, { navCtrl.navigate(it) }) {
|
||||
val dest = navCtrl.graph.findNode(ShizukuScreen)!!.id
|
||||
navCtrl.navigate(dest, it)
|
||||
PermissionsScreen(::navigateUp, { navController.navigate(it) }) {
|
||||
val dest = navController.graph.findNode(ShizukuScreen)!!.id
|
||||
navController.navigate(dest, it)
|
||||
}
|
||||
}
|
||||
composable<ShizukuScreen> { ShizukuScreen(it.arguments!!, ::navigateUp) { navCtrl.navigate(it) } }
|
||||
composable<ShizukuScreen> { ShizukuScreen(it.arguments!!, ::navigateUp) { navController.navigate(it) } }
|
||||
composable<Accounts>(mapOf(serializableNavTypePair<List<Accounts.Account>>())) { AccountsScreen(it.toRoute(), ::navigateUp) }
|
||||
composable<DeviceAdmin> { DeviceAdminScreen(::navigateUp) }
|
||||
composable<ProfileOwner> { ProfileOwnerScreen(::navigateUp) }
|
||||
@@ -310,7 +296,7 @@ fun Home(activity: FragmentActivity, vm: MyViewModel) {
|
||||
composable<SupportMessage> { SupportMessageScreen(::navigateUp) }
|
||||
composable<TransferOwnership> { TransferOwnershipScreen(::navigateUp) }
|
||||
|
||||
composable<SystemManager> { SystemManagerScreen(::navigateUp) { navCtrl.navigate(it) } }
|
||||
composable<SystemManager> { SystemManagerScreen(::navigateUp) { navController.navigate(it) } }
|
||||
composable<SystemOptions> { SystemOptionsScreen(::navigateUp) }
|
||||
composable<Keyguard> { KeyguardScreen(::navigateUp) }
|
||||
composable<HardwareMonitor> { HardwareMonitorScreen(::navigateUp) }
|
||||
@@ -330,20 +316,20 @@ fun Home(activity: FragmentActivity, vm: MyViewModel) {
|
||||
composable<FrpPolicy> { FrpPolicyScreen(::navigateUp) }
|
||||
composable<WipeData> { WipeDataScreen(::navigateUp) }
|
||||
|
||||
composable<Network> { NetworkScreen(::navigateUp) { navCtrl.navigate(it) } }
|
||||
composable<Network> { NetworkScreen(::navigateUp) { navController.navigate(it) } }
|
||||
composable<WiFi> {
|
||||
WifiScreen(::navigateUp, { navCtrl.navigate(it) }) {
|
||||
val dest = navCtrl.graph.findNode(AddNetwork)!!.id
|
||||
navCtrl.navigate(dest, it)
|
||||
WifiScreen(::navigateUp, { navController.navigate(it) }) {
|
||||
val dest = navController.graph.findNode(AddNetwork)!!.id
|
||||
navController.navigate(dest, it)
|
||||
}
|
||||
}
|
||||
composable<NetworkOptions> { NetworkOptionsScreen(::navigateUp) }
|
||||
composable<AddNetwork> { AddNetworkScreen(it.arguments!!, ::navigateUp) }
|
||||
composable<WifiSecurityLevel> { WifiSecurityLevelScreen(::navigateUp) }
|
||||
composable<WifiSsidPolicyScreen> { WifiSsidPolicyScreen(::navigateUp) }
|
||||
composable<QueryNetworkStats> { NetworkStatsScreen(::navigateUp) { navCtrl.navigate(it) } }
|
||||
composable<QueryNetworkStats> { NetworkStatsScreen(::navigateUp) { navController.navigate(it) } }
|
||||
composable<NetworkStatsViewer>(mapOf(serializableNavTypePair<List<NetworkStatsViewer.Data>>())) {
|
||||
NetworkStatsViewerScreen(it.toRoute()) { navCtrl.navigateUp() }
|
||||
NetworkStatsViewerScreen(it.toRoute()) { navController.navigateUp() }
|
||||
}
|
||||
composable<PrivateDns> { PrivateDnsScreen(::navigateUp) }
|
||||
composable<AlwaysOnVpnPackage> { AlwaysOnVpnPackageScreen(::navigateUp) }
|
||||
@@ -353,7 +339,7 @@ fun Home(activity: FragmentActivity, vm: MyViewModel) {
|
||||
composable<PreferentialNetworkService> { PreferentialNetworkServiceScreen(::navigateUp) }
|
||||
composable<OverrideApn> { OverrideApnScreen(::navigateUp) }
|
||||
|
||||
composable<WorkProfile> { WorkProfileScreen(::navigateUp) { navCtrl.navigate(it) } }
|
||||
composable<WorkProfile> { WorkProfileScreen(::navigateUp) { navController.navigate(it) } }
|
||||
composable<OrganizationOwnedProfile> { OrganizationOwnedProfileScreen(::navigateUp) }
|
||||
composable<CreateWorkProfile> { CreateWorkProfileScreen(::navigateUp) }
|
||||
composable<SuspendPersonalApp> { SuspendPersonalAppScreen(::navigateUp) }
|
||||
@@ -364,27 +350,36 @@ fun Home(activity: FragmentActivity, vm: MyViewModel) {
|
||||
|
||||
composable<UserRestriction> {
|
||||
LaunchedEffect(Unit) {
|
||||
vm.userRestrictions.value = dpm.getUserRestrictions(receiver)
|
||||
vm.userRestrictions.value = context.getDPM().getUserRestrictions(receiver)
|
||||
}
|
||||
UserRestrictionScreen(::navigateUp) { title, items ->
|
||||
navCtrl.navigate(UserRestrictionOptions(title, items))
|
||||
navController.navigate(UserRestrictionOptions(title, items))
|
||||
}
|
||||
}
|
||||
composable<UserRestrictionOptions>(mapOf(serializableNavTypePair<List<Restriction>>())) {
|
||||
UserRestrictionOptionsScreen(it.toRoute(), userRestrictions, ::onUserRestrictionsChange, ::navigateUp)
|
||||
UserRestrictionOptionsScreen(it.toRoute(), userRestrictions, ::navigateUp) { id, status ->
|
||||
try {
|
||||
val dpm = context.getDPM()
|
||||
if(status) dpm.addUserRestriction(receiver, id)
|
||||
else dpm.clearUserRestriction(receiver, id)
|
||||
@SuppressLint("NewApi")
|
||||
vm.userRestrictions.value = dpm.getUserRestrictions(receiver)
|
||||
} catch(_: Exception) {
|
||||
context.showOperationResultToast(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
composable<Users> { UsersScreen(::navigateUp) { navCtrl.navigate(it) } }
|
||||
composable<Users> { UsersScreen(::navigateUp) { navController.navigate(it) } }
|
||||
composable<UserInfo> { UserInfoScreen(::navigateUp) }
|
||||
composable<UsersOptions> { UsersOptionsScreen(::navigateUp) }
|
||||
composable<UserOperation> { UserOperationScreen(::navigateUp) }
|
||||
composable<CreateUser> { CreateUserScreen(::navigateUp) }
|
||||
composable<ChangeUsername> { ChangeUsernameScreen(::navigateUp) }
|
||||
composable<ChangeUserIcon> { ChangeUserIconScreen(::navigateUp) }
|
||||
composable<UserSessionMessage> { UserSessionMessageScreen(::navigateUp) }
|
||||
composable<AffiliationId> { AffiliationIdScreen(::navigateUp) }
|
||||
|
||||
composable<Password> { PasswordScreen(::navigateUp) { navCtrl.navigate(it) } }
|
||||
composable<Password> { PasswordScreen(::navigateUp) { navController.navigate(it) } }
|
||||
composable<PasswordInfo> { PasswordInfoScreen(::navigateUp) }
|
||||
composable<ResetPasswordToken> { ResetPasswordTokenScreen(::navigateUp) }
|
||||
composable<ResetPassword> { ResetPasswordScreen(::navigateUp) }
|
||||
@@ -392,7 +387,7 @@ fun Home(activity: FragmentActivity, vm: MyViewModel) {
|
||||
composable<KeyguardDisabledFeatures> { KeyguardDisabledFeaturesScreen(::navigateUp) }
|
||||
composable<RequiredPasswordQuality> { RequiredPasswordQualityScreen(::navigateUp) }
|
||||
|
||||
composable<Settings> { SettingsScreen(::navigateUp) { navCtrl.navigate(it) } }
|
||||
composable<Settings> { SettingsScreen(::navigateUp) { navController.navigate(it) } }
|
||||
composable<SettingsOptions> { SettingsOptionsScreen(::navigateUp) }
|
||||
composable<Appearance> {
|
||||
val theme by vm.theme.collectAsStateWithLifecycle()
|
||||
@@ -409,11 +404,12 @@ fun Home(activity: FragmentActivity, vm: MyViewModel) {
|
||||
}
|
||||
DisposableEffect(lifecycleOwner) {
|
||||
val observer = LifecycleEventObserver { _, event ->
|
||||
val sp = SharedPrefs(context)
|
||||
if(
|
||||
(event == Lifecycle.Event.ON_RESUME && sp.auth && sp.lockInBackground) ||
|
||||
(event == Lifecycle.Event.ON_CREATE && sp.auth)
|
||||
) {
|
||||
navCtrl.navigate(Authenticate) { launchSingleTop = true }
|
||||
navController.navigate(Authenticate) { launchSingleTop = true }
|
||||
}
|
||||
}
|
||||
lifecycleOwner.lifecycle.addObserver(observer)
|
||||
@@ -422,6 +418,8 @@ fun Home(activity: FragmentActivity, vm: MyViewModel) {
|
||||
}
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
val dpm = context.getDPM()
|
||||
val sp = SharedPrefs(context)
|
||||
val profileNotActivated = !sp.managedProfileActivated && context.isProfileOwner && (VERSION.SDK_INT < 24 || dpm.isManagedProfile(receiver))
|
||||
if(profileNotActivated) {
|
||||
dpm.setProfileEnabled(receiver)
|
||||
|
||||
@@ -11,7 +11,6 @@ import android.os.PersistableBundle
|
||||
import android.widget.Toast
|
||||
import androidx.core.app.NotificationCompat
|
||||
import com.bintianqi.owndroid.dpm.handleNetworkLogs
|
||||
import com.bintianqi.owndroid.dpm.isDeviceAdmin
|
||||
import com.bintianqi.owndroid.dpm.isDeviceOwner
|
||||
import com.bintianqi.owndroid.dpm.isProfileOwner
|
||||
import com.bintianqi.owndroid.dpm.processSecurityLogs
|
||||
@@ -29,11 +28,12 @@ class Receiver : DeviceAdminReceiver() {
|
||||
dpm.setLockTaskPackages(receiver, arrayOf())
|
||||
dpm.setLockTaskPackages(receiver, packages)
|
||||
}
|
||||
if(!context.isDeviceOwner && !context.isProfileOwner) SharedPrefs(context).isApiEnabled = false
|
||||
}
|
||||
|
||||
override fun onEnabled(context: Context, intent: Intent) {
|
||||
super.onEnabled(context, intent)
|
||||
if(context.isDeviceAdmin || context.isProfileOwner || context.isDeviceOwner){
|
||||
if(context.isProfileOwner || context.isDeviceOwner){
|
||||
Toast.makeText(context, context.getString(R.string.onEnabled), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,8 +69,9 @@ fun SettingsOptionsScreen(onNavigateUp: () -> Unit) {
|
||||
@Serializable object Appearance
|
||||
|
||||
@Composable
|
||||
fun AppearanceScreen(onNavigateUp: () -> Unit, theme: ThemeSettings, onThemeChange: (ThemeSettings) -> Unit) {
|
||||
fun AppearanceScreen(onNavigateUp: () -> Unit, currentTheme: ThemeSettings, onThemeChange: (ThemeSettings) -> Unit) {
|
||||
var darkThemeMenu by remember { mutableStateOf(false) }
|
||||
var theme by remember { mutableStateOf(currentTheme) }
|
||||
val darkThemeTextID = when(theme.darkTheme) {
|
||||
1 -> R.string.on
|
||||
0 -> R.string.off
|
||||
@@ -78,7 +79,11 @@ fun AppearanceScreen(onNavigateUp: () -> Unit, theme: ThemeSettings, onThemeChan
|
||||
}
|
||||
MyScaffold(R.string.appearance, 0.dp, onNavigateUp) {
|
||||
if(VERSION.SDK_INT >= 31) {
|
||||
SwitchItem(R.string.material_you_color, state = theme.materialYou, onCheckedChange = { onThemeChange(theme.copy(materialYou = it)) })
|
||||
SwitchItem(
|
||||
R.string.material_you_color,
|
||||
state = theme.materialYou,
|
||||
onCheckedChange = { theme = theme.copy(materialYou = it) }
|
||||
)
|
||||
}
|
||||
Box {
|
||||
FunctionItem(R.string.dark_theme, stringResource(darkThemeTextID)) { darkThemeMenu = true }
|
||||
@@ -89,28 +94,33 @@ fun AppearanceScreen(onNavigateUp: () -> Unit, theme: ThemeSettings, onThemeChan
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(R.string.follow_system)) },
|
||||
onClick = {
|
||||
onThemeChange(theme.copy(darkTheme = -1))
|
||||
theme = theme.copy(darkTheme = -1)
|
||||
darkThemeMenu = false
|
||||
}
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(R.string.on)) },
|
||||
onClick = {
|
||||
onThemeChange(theme.copy(darkTheme = 1))
|
||||
theme = theme.copy(darkTheme = 1)
|
||||
darkThemeMenu = false
|
||||
}
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(R.string.off)) },
|
||||
onClick = {
|
||||
onThemeChange(theme.copy(darkTheme = 0))
|
||||
theme = theme.copy(darkTheme = 0)
|
||||
darkThemeMenu = false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
AnimatedVisibility(theme.darkTheme == 1 || (theme.darkTheme == -1 && isSystemInDarkTheme())) {
|
||||
SwitchItem(R.string.black_theme, state = theme.blackTheme, onCheckedChange = { onThemeChange(theme.copy(blackTheme = it)) })
|
||||
SwitchItem(R.string.black_theme, state = theme.blackTheme, onCheckedChange = { theme = theme.copy(blackTheme = it) })
|
||||
}
|
||||
AnimatedVisibility(theme != currentTheme, Modifier.fillMaxWidth().padding(8.dp)) {
|
||||
Button({onThemeChange(theme)}) {
|
||||
Text(stringResource(R.string.apply))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1598,14 +1598,12 @@ fun NetworkLoggingScreen(onNavigateUp: () -> Unit) {
|
||||
val logFile = context.filesDir.resolve("NetworkLogs.json")
|
||||
var fileSize by remember { mutableLongStateOf(0) }
|
||||
LaunchedEffect(Unit) { fileSize = logFile.length() }
|
||||
val exportNetworkLogsLauncher = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
result.data?.data?.let { uri ->
|
||||
context.contentResolver.openOutputStream(uri)?.use { outStream ->
|
||||
outStream.write("[".encodeToByteArray())
|
||||
logFile.inputStream().use { it.copyTo(outStream) }
|
||||
outStream.write("]".encodeToByteArray())
|
||||
context.showOperationResultToast(true)
|
||||
}
|
||||
val exportNetworkLogsLauncher = rememberLauncherForActivityResult(ActivityResultContracts.CreateDocument("application/json")) { uri ->
|
||||
if(uri != null) context.contentResolver.openOutputStream(uri)?.use { outStream ->
|
||||
outStream.write("[".encodeToByteArray())
|
||||
logFile.inputStream().use { it.copyTo(outStream) }
|
||||
outStream.write("]".encodeToByteArray())
|
||||
context.showOperationResultToast(true)
|
||||
}
|
||||
}
|
||||
MyScaffold(R.string.network_logging, 8.dp, onNavigateUp) {
|
||||
@@ -1619,11 +1617,7 @@ fun NetworkLoggingScreen(onNavigateUp: () -> Unit) {
|
||||
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
|
||||
Button(
|
||||
onClick = {
|
||||
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
intent.setType("application/json")
|
||||
intent.putExtra(Intent.EXTRA_TITLE, "NetworkLogs.json")
|
||||
exportNetworkLogsLauncher.launch(intent)
|
||||
exportNetworkLogsLauncher.launch("NetworkLogs.json")
|
||||
},
|
||||
enabled = fileSize > 0,
|
||||
modifier = Modifier.fillMaxWidth(0.49F)
|
||||
|
||||
@@ -1299,21 +1299,17 @@ fun SecurityLoggingScreen(onNavigateUp: () -> Unit) {
|
||||
var fileSize by remember { mutableLongStateOf(0) }
|
||||
LaunchedEffect(Unit) { fileSize = logFile.length() }
|
||||
var preRebootSecurityLogs by remember { mutableStateOf(byteArrayOf()) }
|
||||
val exportPreRebootSecurityLogs = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
result.data?.data?.let { uri ->
|
||||
context.contentResolver.openOutputStream(uri)?.use { outStream ->
|
||||
preRebootSecurityLogs.inputStream().copyTo(outStream)
|
||||
}
|
||||
val exportPreRebootSecurityLogs = rememberLauncherForActivityResult(ActivityResultContracts.CreateDocument("application/json")) { uri ->
|
||||
if(uri != null) context.contentResolver.openOutputStream(uri)?.use { outStream ->
|
||||
preRebootSecurityLogs.inputStream().copyTo(outStream)
|
||||
}
|
||||
}
|
||||
val exportSecurityLogs = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
result.data?.data?.let { uri ->
|
||||
context.contentResolver.openOutputStream(uri)?.use { outStream ->
|
||||
outStream.write("[".toByteArray())
|
||||
logFile.inputStream().use { it.copyTo(outStream) }
|
||||
outStream.write("]".toByteArray())
|
||||
context.showOperationResultToast(true)
|
||||
}
|
||||
val exportSecurityLogs = rememberLauncherForActivityResult(ActivityResultContracts.CreateDocument("application/json")) { uri ->
|
||||
if(uri != null) context.contentResolver.openOutputStream(uri)?.use { outStream ->
|
||||
outStream.write("[".toByteArray())
|
||||
logFile.inputStream().use { it.copyTo(outStream) }
|
||||
outStream.write("]".toByteArray())
|
||||
context.showOperationResultToast(true)
|
||||
}
|
||||
}
|
||||
MyScaffold(R.string.security_logging, 8.dp, onNavigateUp) {
|
||||
@@ -1326,11 +1322,7 @@ fun SecurityLoggingScreen(onNavigateUp: () -> Unit) {
|
||||
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
|
||||
Button(
|
||||
onClick = {
|
||||
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
intent.setType("application/json")
|
||||
intent.putExtra(Intent.EXTRA_TITLE, "SecurityLogs.json")
|
||||
exportSecurityLogs.launch(intent)
|
||||
exportSecurityLogs.launch("SecurityLogs.json")
|
||||
},
|
||||
enabled = fileSize > 0,
|
||||
modifier = Modifier.fillMaxWidth(0.49F)
|
||||
@@ -1362,11 +1354,7 @@ fun SecurityLoggingScreen(onNavigateUp: () -> Unit) {
|
||||
processSecurityLogs(logs, outputStream)
|
||||
outputStream.write("]".encodeToByteArray())
|
||||
preRebootSecurityLogs = outputStream.toByteArray()
|
||||
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
intent.setType("application/json")
|
||||
intent.putExtra(Intent.EXTRA_TITLE, "PreRebootSecurityLogs.json")
|
||||
exportPreRebootSecurityLogs.launch(intent)
|
||||
exportPreRebootSecurityLogs.launch("PreRebootSecurityLogs.json")
|
||||
}
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
@@ -1742,16 +1730,11 @@ fun InstallSystemUpdateScreen(onNavigateUp: () -> Unit) {
|
||||
}
|
||||
}
|
||||
var uri by remember { mutableStateOf<Uri?>(null) }
|
||||
val getFileLauncher = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
uri = it.data?.data
|
||||
}
|
||||
val getFileLauncher = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { uri = it }
|
||||
MyScaffold(R.string.install_system_update, 8.dp, onNavigateUp) {
|
||||
Button(
|
||||
onClick = {
|
||||
val intent = Intent(Intent.ACTION_GET_CONTENT)
|
||||
intent.setType("application/zip")
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
getFileLauncher.launch(intent)
|
||||
getFileLauncher.launch("application/zip")
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
|
||||
@@ -73,7 +73,7 @@ data class UserRestrictionOptions(
|
||||
@Composable
|
||||
fun UserRestrictionOptionsScreen(
|
||||
data: UserRestrictionOptions, restrictions: Bundle,
|
||||
onRestrictionChange: (String, Boolean) -> Unit, onNavigateUp: () -> Unit
|
||||
onNavigateUp: () -> Unit, onRestrictionChange: (String, Boolean) -> Unit
|
||||
) {
|
||||
MyScaffold(data.title, 0.dp, onNavigateUp, false) {
|
||||
data.items.filter { Build.VERSION.SDK_INT >= it.requiresApi }.forEach { restriction ->
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.bintianqi.owndroid.dpm
|
||||
|
||||
import android.app.admin.DevicePolicyManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.os.Binder
|
||||
@@ -10,13 +9,11 @@ import android.os.Build.VERSION
|
||||
import android.os.Process
|
||||
import android.os.UserHandle
|
||||
import android.os.UserManager
|
||||
import android.provider.MediaStore
|
||||
import android.widget.Toast
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.animateContentSize
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
@@ -36,7 +33,6 @@ import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material.icons.filled.PlayArrow
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
@@ -56,7 +52,6 @@ import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
@@ -115,7 +110,16 @@ fun UsersScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) {
|
||||
FunctionItem(R.string.change_username, icon = R.drawable.edit_fill0) { onNavigate(ChangeUsername) }
|
||||
}
|
||||
if(VERSION.SDK_INT >= 23 && (deviceOwner || profileOwner)) {
|
||||
FunctionItem(R.string.change_user_icon, icon = R.drawable.account_circle_fill0) { onNavigate(ChangeUserIcon) }
|
||||
var changeUserIconDialog by remember { mutableStateOf(false) }
|
||||
var bitmap: Bitmap? by remember { mutableStateOf(null) }
|
||||
val launcher = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) {
|
||||
if(it != null) uriToStream(context, it) { stream ->
|
||||
bitmap = BitmapFactory.decodeStream(stream)
|
||||
if(bitmap != null) changeUserIconDialog = true
|
||||
}
|
||||
}
|
||||
FunctionItem(R.string.change_user_icon, icon = R.drawable.account_circle_fill0) { launcher.launch("image/*") }
|
||||
if(changeUserIconDialog == true) ChangeUserIconDialog(bitmap!!) { changeUserIconDialog = false }
|
||||
}
|
||||
if(VERSION.SDK_INT >= 28 && deviceOwner) {
|
||||
FunctionItem(R.string.user_session_msg, icon = R.drawable.notifications_fill0) { onNavigate(UserSessionMessage) }
|
||||
@@ -568,56 +572,36 @@ fun UserSessionMessageScreen(onNavigateUp: () -> Unit) {
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable object ChangeUserIcon
|
||||
|
||||
@RequiresApi(23)
|
||||
@Composable
|
||||
fun ChangeUserIconScreen(onNavigateUp: () -> Unit) {
|
||||
private fun ChangeUserIconDialog(bitmap: Bitmap, onClose: () -> Unit) {
|
||||
val context = LocalContext.current
|
||||
val dpm = context.getDPM()
|
||||
val receiver = context.getReceiver()
|
||||
var getContent by remember { mutableStateOf(false) }
|
||||
var bitmap by remember { mutableStateOf<Bitmap?>(null) }
|
||||
val getFileLauncher = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
it.data?.data?.let {
|
||||
uriToStream(context, it) { stream ->
|
||||
bitmap = BitmapFactory.decodeStream(stream)
|
||||
AlertDialog(
|
||||
title = { Text(stringResource(R.string.change_user_icon)) },
|
||||
text = {
|
||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
|
||||
Image(
|
||||
bitmap = bitmap.asImageBitmap(), contentDescription = null,
|
||||
modifier = Modifier.size(80.dp).clip(RoundedCornerShape(50))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
MyScaffold(R.string.change_user_icon, 8.dp, onNavigateUp) {
|
||||
CheckBoxItem(R.string.file_picker_instead_gallery, getContent) { getContent = it }
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
val intent = Intent(if(getContent) Intent.ACTION_GET_CONTENT else Intent.ACTION_PICK)
|
||||
if(getContent) intent.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*")
|
||||
getFileLauncher.launch(intent)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.select_picture))
|
||||
}
|
||||
AnimatedVisibility(visible = bitmap != null, modifier = Modifier.align(Alignment.CenterHorizontally)) {
|
||||
Card(modifier = Modifier.padding(top = 8.dp)) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(10.dp)) {
|
||||
Image(
|
||||
bitmap = bitmap!!.asImageBitmap(), contentDescription = "User icon",
|
||||
modifier = Modifier.padding(end = 12.dp).size(80.dp).clip(RoundedCornerShape(50))
|
||||
)
|
||||
Button(
|
||||
onClick = {
|
||||
dpm.setUserIcon(receiver, bitmap)
|
||||
context.showOperationResultToast(true)
|
||||
}
|
||||
) {
|
||||
Text(stringResource(R.string.apply))
|
||||
}
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton({
|
||||
context.getDPM().setUserIcon(context.getReceiver(), bitmap)
|
||||
context.showOperationResultToast(true)
|
||||
onClose()
|
||||
}) {
|
||||
Text(stringResource(R.string.confirm))
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClose) {
|
||||
Text(stringResource(R.string.cancel))
|
||||
}
|
||||
},
|
||||
onDismissRequest = onClose
|
||||
)
|
||||
}
|
||||
|
||||
@StringRes
|
||||
|
||||
@@ -14,6 +14,7 @@ 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.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.ArrowDropDown
|
||||
import androidx.compose.material.icons.outlined.Info
|
||||
import androidx.compose.material3.*
|
||||
@@ -67,16 +68,10 @@ fun FunctionItem(
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun NavIcon(operation: () -> Unit) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.arrow_back_fill0),
|
||||
contentDescription = "Back arrow",
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 6.dp)
|
||||
.clip(RoundedCornerShape(50))
|
||||
.clickable(onClick = operation)
|
||||
.padding(5.dp)
|
||||
)
|
||||
fun NavIcon(onClick: () -> Unit) {
|
||||
IconButton(onClick) {
|
||||
Icon(Icons.AutoMirrored.Default.ArrowBack, null)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
||||
@@ -6,12 +6,19 @@ import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.bintianqi.owndroid.MyViewModel
|
||||
import com.bintianqi.owndroid.ThemeSettings
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
private val lightScheme = lightColorScheme(
|
||||
primary = primaryLight,
|
||||
@@ -96,28 +103,24 @@ fun OwnDroidTheme(
|
||||
) {
|
||||
val darkTheme = theme.darkTheme == 1 || (theme.darkTheme == -1 && isSystemInDarkTheme())
|
||||
val context = LocalContext.current
|
||||
var colorScheme = when {
|
||||
val colorScheme = when {
|
||||
theme.materialYou && VERSION.SDK_INT >= 31 -> {
|
||||
if(darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
|
||||
}
|
||||
darkTheme -> darkScheme
|
||||
else -> lightScheme
|
||||
}
|
||||
if(darkTheme && theme.blackTheme) {
|
||||
colorScheme = colorScheme.copy(background = Color.Black)
|
||||
}
|
||||
if(!darkTheme) {
|
||||
colorScheme = colorScheme.copy(background = colorScheme.primary.copy(alpha = 0.05f))
|
||||
}.let {
|
||||
if(darkTheme && theme.blackTheme) it.copy(background = Color.Black) else it
|
||||
}.let {
|
||||
if(!darkTheme) it.copy(background = it.primary.copy(alpha = 0.05f)) else it
|
||||
}
|
||||
val view = LocalView.current
|
||||
SideEffect {
|
||||
val window = (view.context as Activity).window
|
||||
window.statusBarColor = Color.Transparent.toArgb()
|
||||
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = !darkTheme
|
||||
}
|
||||
MaterialTheme(
|
||||
colorScheme = colorScheme,
|
||||
typography = Typography,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package com.bintianqi.owndroid.ui.theme
|
||||
|
||||
import androidx.compose.material3.Typography
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.sp
|
||||
|
||||
// Set of Material typography styles to start with
|
||||
val Typography = Typography(
|
||||
bodyLarge = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 16.sp,
|
||||
lineHeight = 24.sp,
|
||||
letterSpacing = 0.5.sp
|
||||
)
|
||||
/* Other default text styles to override
|
||||
titleLarge = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 22.sp,
|
||||
lineHeight = 28.sp,
|
||||
letterSpacing = 0.sp
|
||||
),
|
||||
labelSmall = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontSize = 11.sp,
|
||||
lineHeight = 16.sp,
|
||||
letterSpacing = 0.5.sp
|
||||
)
|
||||
*/
|
||||
)
|
||||
Reference in New Issue
Block a user