feat: change global & secure settings (#237)

This commit is contained in:
BinTianqi
2026-02-12 17:35:15 +08:00
parent baaf2871e7
commit ca111738a1
6 changed files with 162 additions and 4 deletions

View File

@@ -101,6 +101,8 @@ import com.bintianqi.owndroid.dpm.CrossProfileIntentFilter
import com.bintianqi.owndroid.dpm.CrossProfileIntentFilterScreen
import com.bintianqi.owndroid.dpm.CrossProfilePackages
import com.bintianqi.owndroid.dpm.CrossProfileWidgetProviders
import com.bintianqi.owndroid.dpm.DefaultInputMethod
import com.bintianqi.owndroid.dpm.DefaultInputMethodScreen
import com.bintianqi.owndroid.dpm.DelegatedAdmins
import com.bintianqi.owndroid.dpm.DelegatedAdminsScreen
import com.bintianqi.owndroid.dpm.DeleteWorkProfile
@@ -363,6 +365,10 @@ fun Home(vm: MyViewModel, onLock: () -> Unit) {
HardwareMonitorScreen(vm.hardwareProperties, vm::getHardwareProperties,
vm::setHpRefreshInterval, ::navigateUp)
}
composable<DefaultInputMethod> {
DefaultInputMethodScreen(vm::getCurrentInputMethod, vm.inputMethodList,
vm::getInputMethods, vm::setDefaultInputMethod, ::navigateUp)
}
composable<ChangeTime> { ChangeTimeScreen(vm::setTime, ::navigateUp) }
composable<ChangeTimeZone> { ChangeTimeZoneScreen(vm::setTimeZone, ::navigateUp) }
composable<AutoTimePolicy> {

View File

@@ -45,7 +45,9 @@ import android.os.Bundle
import android.os.HardwarePropertiesManager
import android.os.UserHandle
import android.os.UserManager
import android.provider.Settings
import android.telephony.data.ApnSetting
import android.view.inputmethod.InputMethodManager
import androidx.annotation.RequiresApi
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
@@ -103,10 +105,12 @@ import com.bintianqi.owndroid.dpm.activateOrgProfileCommand
import com.bintianqi.owndroid.dpm.delegatedScopesList
import com.bintianqi.owndroid.dpm.doUserOperationWithContext
import com.bintianqi.owndroid.dpm.getPackageInstaller
import com.bintianqi.owndroid.dpm.globalSettings
import com.bintianqi.owndroid.dpm.handlePrivilegeChange
import com.bintianqi.owndroid.dpm.isValidPackageName
import com.bintianqi.owndroid.dpm.parsePackageInstallerMessage
import com.bintianqi.owndroid.dpm.runtimePermissions
import com.bintianqi.owndroid.dpm.secureSettings
import com.bintianqi.owndroid.dpm.temperatureTypes
import com.rosan.dhizuku.api.Dhizuku
import com.rosan.dhizuku.api.DhizukuRequestPermissionListener
@@ -717,7 +721,9 @@ class MyViewModel(application: Application): AndroidViewModel(application) {
commonCriteriaMode = if (VERSION.SDK_INT >= 30 && (privilege.device || privilege.org))
DPM.isCommonCriteriaModeEnabled(DAR) else false,
usbSignalEnabled = if (VERSION.SDK_INT >= 31) DPM.isUsbDataSignalingEnabled else false,
canDisableUsbSignal = if (VERSION.SDK_INT >= 31) DPM.canUsbDataSignalingBeDisabled() else false
canDisableUsbSignal = if (VERSION.SDK_INT >= 31) DPM.canUsbDataSignalingBeDisabled() else false,
stayOnWhilePluggedIn = Settings.Global.getInt(
application.contentResolver, Settings.Global.STAY_ON_WHILE_PLUGGED_IN) != 0
)
}
fun setCameraDisabled(disabled: Boolean) {
@@ -782,6 +788,28 @@ class MyViewModel(application: Application): AndroidViewModel(application) {
DPM.isUsbDataSignalingEnabled = enabled
systemOptionsStatus.update { it.copy(usbSignalEnabled = DPM.isUsbDataSignalingEnabled) }
}
fun setStayOnWhilePluggedIn(status: Boolean) {
DPM.setGlobalSetting(
DAR, Settings.Global.STAY_ON_WHILE_PLUGGED_IN, if (status) "15" else "0"
)
systemOptionsStatus.update { it.copy(stayOnWhilePluggedIn = status) }
}
fun getGlobalSettings(): Map<String, Boolean> {
return globalSettings.associate {
it.setting to (Settings.Global.getInt(application.contentResolver, it.setting, 0) == 1)
}
}
fun setGlobalSetting(name: String, status: Boolean) {
DPM.setGlobalSetting(DAR, name, if (status) "1" else "0")
}
fun getSecureSettings(): Map<String, Boolean> {
return secureSettings.associate {
it.setting to (Settings.Secure.getInt(application.contentResolver, it.setting, 0) == 1)
}
}
fun setSecureSetting(name: String, status: Boolean) {
DPM.setSecureSetting(DAR, name, if (status) "1" else "0")
}
fun setKeyguardDisabled(disabled: Boolean): Boolean {
return DPM.setKeyguardDisabled(DAR, disabled)
}
@@ -816,6 +844,21 @@ class MyViewModel(application: Application): AndroidViewModel(application) {
delay(hpRefreshInterval)
}
}
fun getCurrentInputMethod(): String {
return Settings.Secure.getString(
application.contentResolver, Settings.Secure.DEFAULT_INPUT_METHOD)
}
val inputMethodList = MutableStateFlow(listOf<Pair<String, AppInfo>>())
fun getInputMethods() {
val imm = application.getSystemService(InputMethodManager::class.java)
inputMethodList.value = imm.inputMethodList.map {
it.id to getAppInfo(it.packageName)
}
}
fun setDefaultInputMethod(id: String) {
DPM.setSecureSetting(DAR, Settings.Secure.DEFAULT_INPUT_METHOD, id)
getCurrentInputMethod()
}
@RequiresApi(28)
fun setTime(time: Long, useCurrentTz: Boolean): Boolean {
val offset = if (useCurrentTz) {

View File

@@ -24,11 +24,13 @@ import android.net.Uri
import android.os.Build.VERSION
import android.os.HardwarePropertiesManager
import android.os.UserManager
import android.provider.Settings
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.RequiresApi
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
@@ -39,6 +41,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.pager.HorizontalPager
@@ -69,6 +72,7 @@ import androidx.compose.material3.MaterialTheme.colorScheme
import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.PrimaryTabRow
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Slider
import androidx.compose.material3.Tab
@@ -86,6 +90,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateMapOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
@@ -93,6 +98,7 @@ 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
@@ -120,12 +126,14 @@ import com.bintianqi.owndroid.ui.FullWidthCheckBoxItem
import com.bintianqi.owndroid.ui.FullWidthRadioButtonItem
import com.bintianqi.owndroid.ui.FunctionItem
import com.bintianqi.owndroid.ui.ListItem
import com.bintianqi.owndroid.ui.MyLazyScaffold
import com.bintianqi.owndroid.ui.MyScaffold
import com.bintianqi.owndroid.ui.MySmallTitleScaffold
import com.bintianqi.owndroid.ui.NavIcon
import com.bintianqi.owndroid.ui.Notes
import com.bintianqi.owndroid.ui.SwitchItem
import com.bintianqi.owndroid.yesOrNo
import com.google.accompanist.drawablepainter.rememberDrawablePainter
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
@@ -153,6 +161,9 @@ fun SystemManagerScreen(
FunctionItem(R.string.keyguard, icon = R.drawable.screen_lock_portrait_fill0) { onNavigate(Keyguard) }
if(VERSION.SDK_INT >= 24 && privilege.device && !privilege.dhizuku)
FunctionItem(R.string.hardware_monitor, icon = R.drawable.memory_fill0) { onNavigate(HardwareMonitor) }
FunctionItem(R.string.default_input_method, icon = R.drawable.keyboard_fill0) {
onNavigate(DefaultInputMethod)
}
if(VERSION.SDK_INT >= 24 && privilege.device) {
FunctionItem(R.string.reboot, icon = R.drawable.restart_alt_fill0) { dialog = 1 }
}
@@ -314,17 +325,41 @@ data class SystemOptionsStatus(
val btContactSharingDisabled: Boolean = false,
val commonCriteriaMode: Boolean = false,
val usbSignalEnabled: Boolean = true,
val canDisableUsbSignal: Boolean = true
val canDisableUsbSignal: Boolean = true,
val stayOnWhilePluggedIn: Boolean = false
)
@Serializable object SystemOptions
class GlobalSetting(val icon: Int, val name: Int, val setting: String) // also for secure settings
// STAY_ON_WHILE_PLUGGED_IN is set separately
val globalSettings = listOf(
GlobalSetting(R.drawable.cell_tower_fill0, R.string.data_roaming, Settings.Global.DATA_ROAMING),
GlobalSetting(R.drawable.adb_fill0, R.string.enable_adb, Settings.Global.ADB_ENABLED),
GlobalSetting(R.drawable.usb_fill0, R.string.enable_usb_mass_storage,
Settings.Global.USB_MASS_STORAGE_ENABLED),
GlobalSetting(R.drawable.wifi_password_fill0, R.string.lockdown_admin_configured_network,
Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN)
)
val secureSettings = listOf(
GlobalSetting(R.drawable.light_off_fill0, R.string.skip_first_use_hints,
Settings.Secure.SKIP_FIRST_USE_HINTS)
)
@Composable
fun SystemOptionsScreen(vm: MyViewModel, onNavigateUp: () -> Unit) {
val privilege by Privilege.status.collectAsStateWithLifecycle()
var dialog by rememberSaveable { mutableIntStateOf(0) }
val status by vm.systemOptionsStatus.collectAsStateWithLifecycle()
LaunchedEffect(Unit) { vm.getSystemOptionsStatus() }
val globalSettingsStatus = remember { mutableStateMapOf<String, Boolean>() }
val secureSettingsStatus = remember { mutableStateMapOf<String, Boolean>() }
LaunchedEffect(Unit) {
vm.getSystemOptionsStatus()
globalSettingsStatus.putAll(vm.getGlobalSettings())
secureSettingsStatus.putAll(vm.getSecureSettings())
}
MyScaffold(R.string.options, onNavigateUp, 0.dp) {
SwitchItem(R.string.disable_cam, status.cameraDisabled, vm::setCameraDisabled,
R.drawable.no_photography_fill0)
@@ -366,6 +401,20 @@ fun SystemOptionsScreen(vm: MyViewModel, onNavigateUp: () -> Unit) {
SwitchItem(R.string.enable_usb_signal, status.usbSignalEnabled,
vm::setUsbSignalEnabled, R.drawable.usb_fill0)
}
SwitchItem(R.string.stay_on_while_plugged_in, status.stayOnWhilePluggedIn,
vm::setStayOnWhilePluggedIn, R.drawable.mobile_phone_fill0)
globalSettings.forEach {
SwitchItem(it.name, globalSettingsStatus[it.setting] ?: false, { state ->
vm.setGlobalSetting(it.setting, state)
globalSettingsStatus[it.setting] = state
}, it.icon)
}
secureSettings.forEach {
SwitchItem(it.name, secureSettingsStatus[it.setting] ?: false, { state ->
vm.setSecureSetting(it.setting, state)
secureSettingsStatus[it.setting] = state
}, it.icon)
}
if (VERSION.SDK_INT < 34) {
Row(
Modifier.fillMaxWidth().padding(horizontal = HorizontalPadding),
@@ -531,6 +580,47 @@ fun HardwareMonitorScreen(
}
}
@Serializable object DefaultInputMethod
@Composable
fun DefaultInputMethodScreen(
getCurrentIm: () -> String, imListState: StateFlow<List<Pair<String, AppInfo>>>,
getImList: () -> Unit, setIm: (String) -> Unit, navigateUp: () -> Unit
) {
val context = LocalContext.current
val imList by imListState.collectAsStateWithLifecycle()
var selectedIm by remember { mutableStateOf("") }
LaunchedEffect(Unit) {
selectedIm = getCurrentIm()
getImList()
}
MyLazyScaffold(R.string.default_input_method, navigateUp) {
items(imList) { (id, info) ->
Row(
Modifier.fillMaxWidth().clickable { selectedIm = id }.padding(8.dp),
verticalAlignment = Alignment.CenterVertically
) {
RadioButton(selectedIm == id, { selectedIm = id })
Image(rememberDrawablePainter(info.icon), null, Modifier.size(40.dp))
Column(Modifier.padding(start = 8.dp)) {
Text(info.label)
Text(id, Modifier.alpha(0.7F), style = typography.bodyMedium)
}
}
}
item {
Spacer(Modifier.height(8.dp))
Button({
setIm(selectedIm)
context.showOperationResultToast(true)
}, Modifier.fillMaxWidth().padding(horizontal = HorizontalPadding)) {
Text(stringResource(R.string.apply))
}
Spacer(Modifier.height(BottomPadding))
}
}
}
@Serializable object ChangeTime
@OptIn(ExperimentalMaterial3Api::class)

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:pathData="M423.5,856.5Q400,833 400,800h160q0,33 -23.5,56.5T480,880q-33,0 -56.5,-23.5ZM480,160q-44,0 -81.5,15.5T332,218l-58,-56q41,-38 93.5,-60T480,80q125,0 212.5,87.5T780,380q0,71 -25,121.5T698,584l-56,-56q21,-23 39.5,-59t18.5,-89q0,-92 -64,-156t-156,-64ZM848,848 L791,905 526,640L330,640q-69,-41 -109.5,-110T180,380q0,-20 2.5,-39t7.5,-37L56,168l56,-56 736,736ZM354,560h92L260,374v6q0,54 24.5,101t69.5,79ZM348,462ZM482,368ZM646,680v80L320,760v-80h326Z"
android:fillColor="#000000"/>
</vector>

View File

@@ -134,6 +134,10 @@
<string name="disable_bt_contact_share">禁止蓝牙分享联系人</string>
<string name="common_criteria_mode">通用标准模式</string>
<string name="enable_usb_signal">启用USB信号</string>
<string name="stay_on_while_plugged_in">充电时保持亮屏</string>
<string name="enable_adb">启用ADB</string>
<string name="enable_usb_mass_storage">启用USB大容量存储</string>
<string name="skip_first_use_hints">跳过首次使用提示</string>
<string name="keyguard">锁屏</string>
<string name="lock_now">立即锁屏</string>
<string name="lock_screen">锁屏</string>
@@ -148,7 +152,8 @@
<string name="cpu_usages">CPU使用情况</string>
<string name="active">活动</string>
<string name="total">总计</string>
<string name="fan_speeds">Fan speeds</string>
<string name="fan_speeds">风扇速度</string>
<string name="default_input_method">默认输入法</string>
<string name="bug_report">错误报告</string>
<string name="confirm_bug_report">请求错误报告?</string>
<string name="reboot">重启</string>

View File

@@ -143,6 +143,10 @@
<string name="disable_bt_contact_share">Disable bluetooth contact sharing</string>
<string name="common_criteria_mode">Common criteria mode</string>
<string name="enable_usb_signal">Enable USB signal</string>
<string name="stay_on_while_plugged_in">Stay on while plugged in</string>
<string name="enable_adb">Enable ADB</string>
<string name="enable_usb_mass_storage">Enable USB mass storage</string>
<string name="skip_first_use_hints">Skip first use hints</string>
<string name="keyguard">Keyguard</string>
<string name="lock_now">Lock screen now</string>
<string name="lock_screen">Lock screen</string>
@@ -158,6 +162,7 @@
<string name="active">Active</string>
<string name="total">Total</string>
<string name="fan_speeds">Fan speeds</string>
<string name="default_input_method">Default input method</string>
<string name="bug_report">Bug report</string>
<string name="confirm_bug_report">Request bug report?</string>
<string name="reboot">Reboot</string>