diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 991bd5f..d7a8975 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,6 +1,8 @@ + + @@ -61,6 +63,11 @@ android:label="@string/package_chooser" android:exported="false" android:launchMode="singleInstance"/> + = 25) { + val sm = context.getSystemService(ShortcutManager::class.java) + val lockIntent = Intent("com.bintianqi.owndroid.action.LOCK") + .setComponent(ComponentName(context, ShortcutsReceiverActivity::class.java)) + val shortcut = ShortcutInfo.Builder(context, "LockScreen") + .setShortLabel(context.getString(R.string.lock_now)) + .setIcon(Icon.createWithBitmap(context.getDrawable(R.drawable.screen_lock_portrait_fill0)?.toBitmap())) + .setIntent(lockIntent) + sm.addDynamicShortcuts(listOf(shortcut.build())) + } } } @@ -49,6 +63,10 @@ class Receiver : DeviceAdminReceiver() { super.onDisabled(context, intent) Toast.makeText(context, R.string.onDisabled, Toast.LENGTH_SHORT).show() SharedPrefs(context).isDefaultAffiliationIdSet = false + if(VERSION.SDK_INT >= 25) { + val sm = context.getSystemService(ShortcutManager::class.java) + sm.removeDynamicShortcuts(listOf("LockScreen")) + } } override fun onProfileProvisioningComplete(context: Context, intent: Intent) { diff --git a/app/src/main/java/com/bintianqi/owndroid/SharedPrefs.kt b/app/src/main/java/com/bintianqi/owndroid/SharedPrefs.kt index b16d4b8..0e9a32b 100644 --- a/app/src/main/java/com/bintianqi/owndroid/SharedPrefs.kt +++ b/app/src/main/java/com/bintianqi/owndroid/SharedPrefs.kt @@ -20,7 +20,7 @@ class SharedPrefs(context: Context) { var blackTheme by BooleanSharedPref("theme.black") var lockPassword by StringSharedPref("lock.password") var biometricsUnlock by BooleanSharedPref("lock.biometrics") - var applicationsListView by BooleanSharedPref("applications.list_view") + var applicationsListView by BooleanSharedPref("applications.list_view", true) } private class BooleanSharedPref(val key: String, val defValue: Boolean = false): ReadWriteProperty { diff --git a/app/src/main/java/com/bintianqi/owndroid/ShortcutsReceiverActivity.kt b/app/src/main/java/com/bintianqi/owndroid/ShortcutsReceiverActivity.kt new file mode 100644 index 0000000..807b2ac --- /dev/null +++ b/app/src/main/java/com/bintianqi/owndroid/ShortcutsReceiverActivity.kt @@ -0,0 +1,17 @@ +package com.bintianqi.owndroid + +import android.app.Activity +import android.os.Bundle +import com.bintianqi.owndroid.dpm.getDPM + +class ShortcutsReceiverActivity: Activity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + try { + if(intent.action == "com.bintianqi.owndroid.action.LOCK") { + getDPM().lockNow() + } + } catch(_: Exception) {} + finish() + } +} \ No newline at end of file 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 996e4f4..1f429f7 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/Network.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/Network.kt @@ -88,7 +88,6 @@ import androidx.compose.material3.MaterialTheme.typography import androidx.compose.material3.MenuAnchorType import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Scaffold -import androidx.compose.material3.ScrollableTabRow import androidx.compose.material3.SegmentedButton import androidx.compose.material3.SegmentedButtonDefaults import androidx.compose.material3.SingleChoiceSegmentedButtonRow @@ -133,7 +132,6 @@ import com.bintianqi.owndroid.SharedPrefs import com.bintianqi.owndroid.formatDate import com.bintianqi.owndroid.formatFileSize import com.bintianqi.owndroid.humanReadableDate -import com.bintianqi.owndroid.humanReadableDateTime import com.bintianqi.owndroid.showOperationResultToast import com.bintianqi.owndroid.ui.CheckBoxItem import com.bintianqi.owndroid.ui.ErrorDialog 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 5987c7a..59b450b 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/Password.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/Password.kt @@ -69,13 +69,13 @@ import com.bintianqi.owndroid.HorizontalPadding import com.bintianqi.owndroid.R import com.bintianqi.owndroid.SharedPrefs import com.bintianqi.owndroid.showOperationResultToast -import com.bintianqi.owndroid.ui.CardItem import com.bintianqi.owndroid.ui.CheckBoxItem import com.bintianqi.owndroid.ui.FullWidthCheckBoxItem import com.bintianqi.owndroid.ui.FullWidthRadioButtonItem import com.bintianqi.owndroid.ui.FunctionItem -import com.bintianqi.owndroid.ui.Notes +import com.bintianqi.owndroid.ui.InfoItem import com.bintianqi.owndroid.ui.MyScaffold +import com.bintianqi.owndroid.ui.Notes import com.bintianqi.owndroid.ui.RadioButtonItem import com.bintianqi.owndroid.yesOrNo import kotlinx.serialization.Serializable @@ -220,7 +220,7 @@ fun PasswordInfoScreen(onNavigateUp: () -> Unit) { val deviceOwner = context.isDeviceOwner val profileOwner = context.isProfileOwner var dialog by remember { mutableIntStateOf(0) } // 0:none, 1:password complexity - MyScaffold(R.string.password_info, onNavigateUp) { + MyScaffold(R.string.password_info, onNavigateUp, 0.dp) { if(VERSION.SDK_INT >= 29) { val text = when(dpm.passwordComplexity) { PASSWORD_COMPLEXITY_NONE -> R.string.none @@ -229,13 +229,13 @@ fun PasswordInfoScreen(onNavigateUp: () -> Unit) { PASSWORD_COMPLEXITY_HIGH -> R.string.high else -> R.string.unknown } - CardItem(R.string.current_password_complexity, text) { dialog = 1 } + InfoItem(R.string.current_password_complexity, text, true) { dialog = 1 } } if(deviceOwner || profileOwner) { - CardItem(R.string.password_sufficient, dpm.isActivePasswordSufficient.yesOrNo) + InfoItem(R.string.password_sufficient, dpm.isActivePasswordSufficient.yesOrNo) } if(VERSION.SDK_INT >= 28 && profileOwner && dpm.isManagedProfile(receiver)) { - CardItem(R.string.unified_password, dpm.isUsingUnifiedPassword(receiver).yesOrNo) + InfoItem(R.string.unified_password, dpm.isUsingUnifiedPassword(receiver).yesOrNo) } } if(dialog != 0) AlertDialog( 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 0e88921..3b217ba 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/Permissions.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/Permissions.kt @@ -646,13 +646,13 @@ fun DeviceInfoScreen(onNavigateUp: () -> Unit) { val dpm = context.getDPM() val receiver = context.getReceiver() var dialog by remember { mutableIntStateOf(0) } - MyScaffold(R.string.device_info, onNavigateUp) { + MyScaffold(R.string.device_info, onNavigateUp, 0.dp) { if(VERSION.SDK_INT>=34 && (context.isDeviceOwner || dpm.isOrgProfile(receiver))) { - CardItem(R.string.financed_device, dpm.isDeviceFinanced.yesOrNo) + InfoItem(R.string.financed_device, dpm.isDeviceFinanced.yesOrNo) } if(VERSION.SDK_INT >= 33) { val dpmRole = dpm.devicePolicyManagementRoleHolderPackage - CardItem(R.string.dpmrh, if(dpmRole == null) stringResource(R.string.none) else dpmRole) + InfoItem(R.string.dpmrh, if(dpmRole == null) stringResource(R.string.none) else dpmRole) } val encryptionStatus = mutableMapOf( DevicePolicyManager.ENCRYPTION_STATUS_INACTIVE to R.string.es_inactive, @@ -661,16 +661,16 @@ fun DeviceInfoScreen(onNavigateUp: () -> Unit) { ) if(VERSION.SDK_INT >= 23) { encryptionStatus[DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY] = R.string.es_active_default_key } if(VERSION.SDK_INT >= 24) { encryptionStatus[DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER] = R.string.es_active_per_user } - CardItem(R.string.encryption_status, encryptionStatus[dpm.storageEncryptionStatus] ?: R.string.unknown) + InfoItem(R.string.encryption_status, encryptionStatus[dpm.storageEncryptionStatus] ?: R.string.unknown) if(VERSION.SDK_INT >= 28) { - CardItem(R.string.support_device_id_attestation, dpm.isDeviceIdAttestationSupported.yesOrNo) { dialog = 1 } + InfoItem(R.string.support_device_id_attestation, dpm.isDeviceIdAttestationSupported.yesOrNo, true) { dialog = 1 } } if (VERSION.SDK_INT >= 30) { - CardItem(R.string.support_unique_device_attestation, dpm.isUniqueDeviceAttestationSupported.yesOrNo) { dialog = 2 } + InfoItem(R.string.support_unique_device_attestation, dpm.isUniqueDeviceAttestationSupported.yesOrNo, true) { dialog = 2 } } val adminList = dpm.activeAdmins if(adminList != null) { - CardItem(R.string.activated_device_admin, adminList.map { it.flattenToShortString() }.joinToString("\n")) + InfoItem(R.string.activated_device_admin, adminList.map { it.flattenToShortString() }.joinToString("\n")) } } if(dialog != 0) AlertDialog( 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 bc26bba..be03ff3 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/Users.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/Users.kt @@ -67,7 +67,7 @@ import androidx.compose.ui.window.DialogProperties import com.bintianqi.owndroid.R import com.bintianqi.owndroid.parseTimestamp import com.bintianqi.owndroid.showOperationResultToast -import com.bintianqi.owndroid.ui.CardItem +import com.bintianqi.owndroid.ui.InfoItem import com.bintianqi.owndroid.ui.CheckBoxItem import com.bintianqi.owndroid.ui.FunctionItem import com.bintianqi.owndroid.ui.Notes @@ -192,25 +192,25 @@ fun UserInfoScreen(onNavigateUp: () -> Unit) { val userManager = context.getSystemService(Context.USER_SERVICE) as UserManager val user = Process.myUserHandle() var infoDialog by remember { mutableIntStateOf(0) } - MyScaffold(R.string.user_info, onNavigateUp) { - 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 } + MyScaffold(R.string.user_info, onNavigateUp, 0.dp) { + if(VERSION.SDK_INT >= 24) InfoItem(R.string.support_multiuser, UserManager.supportsMultipleUsers().yesOrNo) + if(VERSION.SDK_INT >= 31) InfoItem(R.string.headless_system_user_mode, UserManager.isHeadlessSystemUserMode().yesOrNo, true) { infoDialog = 1 } Spacer(Modifier.padding(vertical = 8.dp)) - if(VERSION.SDK_INT >= 23) CardItem(R.string.system_user, userManager.isSystemUser.yesOrNo) - if(VERSION.SDK_INT >= 34) CardItem(R.string.admin_user, userManager.isAdminUser.yesOrNo) - if(VERSION.SDK_INT >= 25) CardItem(R.string.demo_user, userManager.isDemoUser.yesOrNo) + if(VERSION.SDK_INT >= 23) InfoItem(R.string.system_user, userManager.isSystemUser.yesOrNo) + if(VERSION.SDK_INT >= 34) InfoItem(R.string.admin_user, userManager.isAdminUser.yesOrNo) + if(VERSION.SDK_INT >= 25) InfoItem(R.string.demo_user, userManager.isDemoUser.yesOrNo) if(VERSION.SDK_INT >= 26) userManager.getUserCreationTime(user).let { - if(it != 0L) CardItem(R.string.creation_time, parseTimestamp(it)) + if(it != 0L) InfoItem(R.string.creation_time, parseTimestamp(it)) } if (VERSION.SDK_INT >= 28) { - CardItem(R.string.logout_enabled, dpm.isLogoutEnabled.yesOrNo) + InfoItem(R.string.logout_enabled, dpm.isLogoutEnabled.yesOrNo) if(context.isDeviceOwner || context.isProfileOwner) { - CardItem(R.string.ephemeral_user, dpm.isEphemeralUser(receiver).yesOrNo) + InfoItem(R.string.ephemeral_user, dpm.isEphemeralUser(receiver).yesOrNo) } - CardItem(R.string.affiliated_user, dpm.isAffiliatedUser.yesOrNo) + InfoItem(R.string.affiliated_user, dpm.isAffiliatedUser.yesOrNo) } - CardItem(R.string.user_id, (Binder.getCallingUid() / 100000).toString()) - CardItem(R.string.user_serial_number, userManager.getSerialNumberForUser(Process.myUserHandle()).toString()) + InfoItem(R.string.user_id, (Binder.getCallingUid() / 100000).toString()) + InfoItem(R.string.user_serial_number, userManager.getSerialNumberForUser(Process.myUserHandle()).toString()) } if(infoDialog != 0) AlertDialog( text = { Text(stringResource(R.string.info_headless_system_user_mode)) }, diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/WorkProfile.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/WorkProfile.kt index 7f440d1..560783f 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/WorkProfile.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/WorkProfile.kt @@ -55,12 +55,12 @@ import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import com.bintianqi.owndroid.R import com.bintianqi.owndroid.showOperationResultToast -import com.bintianqi.owndroid.ui.CardItem import com.bintianqi.owndroid.ui.CheckBoxItem import com.bintianqi.owndroid.ui.CopyTextButton import com.bintianqi.owndroid.ui.FunctionItem -import com.bintianqi.owndroid.ui.Notes +import com.bintianqi.owndroid.ui.InfoItem import com.bintianqi.owndroid.ui.MyScaffold +import com.bintianqi.owndroid.ui.Notes import com.bintianqi.owndroid.ui.SwitchItem import com.bintianqi.owndroid.yesOrNo import kotlinx.serialization.Serializable @@ -172,7 +172,7 @@ fun OrganizationOwnedProfileScreen(onNavigateUp: () -> Unit) { val context = LocalContext.current val dpm = context.getDPM() MyScaffold(R.string.org_owned_work_profile, onNavigateUp) { - CardItem(R.string.org_owned_work_profile, dpm.isOrganizationOwnedDeviceWithManagedProfile.yesOrNo) + InfoItem(R.string.org_owned_work_profile, dpm.isOrganizationOwnedDeviceWithManagedProfile.yesOrNo) Spacer(Modifier.padding(vertical = 5.dp)) if(!dpm.isOrganizationOwnedDeviceWithManagedProfile) { SelectionContainer { 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 016efdc..b6ec15c 100644 --- a/app/src/main/java/com/bintianqi/owndroid/ui/Components.kt +++ b/app/src/main/java/com/bintianqi/owndroid/ui/Components.kt @@ -13,7 +13,6 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack @@ -25,6 +24,7 @@ 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.draw.rotate import androidx.compose.ui.input.nestedscroll.nestedScroll @@ -33,6 +33,7 @@ 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 com.bintianqi.owndroid.HorizontalPadding import com.bintianqi.owndroid.R import com.bintianqi.owndroid.writeClipBoard import com.bintianqi.owndroid.zhCN @@ -236,28 +237,20 @@ fun CopyTextButton(@StringRes label: Int, content: String) { } @Composable -fun CardItem(@StringRes title: Int, @StringRes text: Int, onClickInfo: (() -> Unit)? = null) { - CardItem(title, stringResource(text), onClickInfo) -} +fun InfoItem(title: Int, text: Int, withInfo: Boolean = false, onClick: () -> Unit = {}) = + InfoItem(title, stringResource(text), withInfo, onClick) @Composable -fun CardItem(@StringRes title: Int, text: String, onClickInfo: (() -> Unit)? = null) { - Card(modifier = Modifier.fillMaxWidth().padding(vertical = 6.dp)) { - Row( - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.fillMaxWidth() - ) { - Column(modifier = Modifier.fillMaxWidth(0.85F)) { - Text(text = stringResource(title), style = typography.titleLarge, modifier = Modifier.padding(start = 8.dp, top = 6.dp)) - SelectionContainer { - Text(text = text, modifier = Modifier.padding(start = 8.dp, bottom = 6.dp)) - } - } - if(onClickInfo != null) IconButton(onClick = onClickInfo) { - Icon(imageVector = Icons.Outlined.Info, contentDescription = null) - } +fun InfoItem(title: Int, text: String, withInfo: Boolean = false, onClick: () -> Unit = {}) { + Row( + Modifier.fillMaxWidth().padding(vertical = 6.dp).padding(start = HorizontalPadding, end = 8.dp), + Arrangement.SpaceBetween, Alignment.CenterVertically + ) { + Column { + Text(stringResource(title), style = typography.titleLarge) + Text(text, Modifier.alpha(0.8F)) } + if(withInfo) IconButton(onClick) { Icon(Icons.Outlined.Info, null) } } }