Lock screen shortcut

This commit is contained in:
BinTianqi
2025-03-28 09:18:54 +08:00
parent 1212a40e7a
commit 5f75f9c847
10 changed files with 85 additions and 52 deletions

View File

@@ -6,12 +6,16 @@ import android.app.admin.DeviceAdminReceiver
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.ShortcutInfo
import android.content.pm.ShortcutManager
import android.graphics.drawable.Icon
import android.os.Build.VERSION
import android.os.PersistableBundle
import android.os.UserHandle
import android.os.UserManager
import android.widget.Toast
import androidx.core.app.NotificationCompat
import androidx.core.graphics.drawable.toBitmap
import com.bintianqi.owndroid.dpm.handleNetworkLogs
import com.bintianqi.owndroid.dpm.isDeviceOwner
import com.bintianqi.owndroid.dpm.isProfileOwner
@@ -42,6 +46,16 @@ class Receiver : DeviceAdminReceiver() {
if(context.isProfileOwner || context.isDeviceOwner){
setDefaultAffiliationID(context)
Toast.makeText(context, context.getString(R.string.onEnabled), Toast.LENGTH_SHORT).show()
if(VERSION.SDK_INT >= 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) {

View File

@@ -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<SharedPrefs, Boolean> {

View File

@@ -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()
}
}

View File

@@ -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

View File

@@ -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(

View File

@@ -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(

View File

@@ -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)) },

View File

@@ -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 {

View File

@@ -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) }
}
}