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

View File

@@ -45,7 +45,9 @@ import android.os.Bundle
import android.os.HardwarePropertiesManager import android.os.HardwarePropertiesManager
import android.os.UserHandle import android.os.UserHandle
import android.os.UserManager import android.os.UserManager
import android.provider.Settings
import android.telephony.data.ApnSetting import android.telephony.data.ApnSetting
import android.view.inputmethod.InputMethodManager
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb 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.delegatedScopesList
import com.bintianqi.owndroid.dpm.doUserOperationWithContext import com.bintianqi.owndroid.dpm.doUserOperationWithContext
import com.bintianqi.owndroid.dpm.getPackageInstaller import com.bintianqi.owndroid.dpm.getPackageInstaller
import com.bintianqi.owndroid.dpm.globalSettings
import com.bintianqi.owndroid.dpm.handlePrivilegeChange import com.bintianqi.owndroid.dpm.handlePrivilegeChange
import com.bintianqi.owndroid.dpm.isValidPackageName import com.bintianqi.owndroid.dpm.isValidPackageName
import com.bintianqi.owndroid.dpm.parsePackageInstallerMessage import com.bintianqi.owndroid.dpm.parsePackageInstallerMessage
import com.bintianqi.owndroid.dpm.runtimePermissions import com.bintianqi.owndroid.dpm.runtimePermissions
import com.bintianqi.owndroid.dpm.secureSettings
import com.bintianqi.owndroid.dpm.temperatureTypes import com.bintianqi.owndroid.dpm.temperatureTypes
import com.rosan.dhizuku.api.Dhizuku import com.rosan.dhizuku.api.Dhizuku
import com.rosan.dhizuku.api.DhizukuRequestPermissionListener 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)) commonCriteriaMode = if (VERSION.SDK_INT >= 30 && (privilege.device || privilege.org))
DPM.isCommonCriteriaModeEnabled(DAR) else false, DPM.isCommonCriteriaModeEnabled(DAR) else false,
usbSignalEnabled = if (VERSION.SDK_INT >= 31) DPM.isUsbDataSignalingEnabled 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) { fun setCameraDisabled(disabled: Boolean) {
@@ -782,6 +788,28 @@ class MyViewModel(application: Application): AndroidViewModel(application) {
DPM.isUsbDataSignalingEnabled = enabled DPM.isUsbDataSignalingEnabled = enabled
systemOptionsStatus.update { it.copy(usbSignalEnabled = DPM.isUsbDataSignalingEnabled) } 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 { fun setKeyguardDisabled(disabled: Boolean): Boolean {
return DPM.setKeyguardDisabled(DAR, disabled) return DPM.setKeyguardDisabled(DAR, disabled)
} }
@@ -816,6 +844,21 @@ class MyViewModel(application: Application): AndroidViewModel(application) {
delay(hpRefreshInterval) 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) @RequiresApi(28)
fun setTime(time: Long, useCurrentTz: Boolean): Boolean { fun setTime(time: Long, useCurrentTz: Boolean): Boolean {
val offset = if (useCurrentTz) { val offset = if (useCurrentTz) {

View File

@@ -24,11 +24,13 @@ import android.net.Uri
import android.os.Build.VERSION import android.os.Build.VERSION
import android.os.HardwarePropertiesManager import android.os.HardwarePropertiesManager
import android.os.UserManager import android.os.UserManager
import android.provider.Settings
import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateContentSize import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement 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.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.pager.HorizontalPager 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.MaterialTheme.typography
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.PrimaryTabRow import androidx.compose.material3.PrimaryTabRow
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.Slider import androidx.compose.material3.Slider
import androidx.compose.material3.Tab import androidx.compose.material3.Tab
@@ -86,6 +90,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateMapOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
@@ -93,6 +98,7 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager 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.FullWidthRadioButtonItem
import com.bintianqi.owndroid.ui.FunctionItem import com.bintianqi.owndroid.ui.FunctionItem
import com.bintianqi.owndroid.ui.ListItem import com.bintianqi.owndroid.ui.ListItem
import com.bintianqi.owndroid.ui.MyLazyScaffold
import com.bintianqi.owndroid.ui.MyScaffold import com.bintianqi.owndroid.ui.MyScaffold
import com.bintianqi.owndroid.ui.MySmallTitleScaffold import com.bintianqi.owndroid.ui.MySmallTitleScaffold
import com.bintianqi.owndroid.ui.NavIcon import com.bintianqi.owndroid.ui.NavIcon
import com.bintianqi.owndroid.ui.Notes import com.bintianqi.owndroid.ui.Notes
import com.bintianqi.owndroid.ui.SwitchItem import com.bintianqi.owndroid.ui.SwitchItem
import com.bintianqi.owndroid.yesOrNo import com.bintianqi.owndroid.yesOrNo
import com.google.accompanist.drawablepainter.rememberDrawablePainter
import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
@@ -153,6 +161,9 @@ fun SystemManagerScreen(
FunctionItem(R.string.keyguard, icon = R.drawable.screen_lock_portrait_fill0) { onNavigate(Keyguard) } FunctionItem(R.string.keyguard, icon = R.drawable.screen_lock_portrait_fill0) { onNavigate(Keyguard) }
if(VERSION.SDK_INT >= 24 && privilege.device && !privilege.dhizuku) if(VERSION.SDK_INT >= 24 && privilege.device && !privilege.dhizuku)
FunctionItem(R.string.hardware_monitor, icon = R.drawable.memory_fill0) { onNavigate(HardwareMonitor) } 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) { if(VERSION.SDK_INT >= 24 && privilege.device) {
FunctionItem(R.string.reboot, icon = R.drawable.restart_alt_fill0) { dialog = 1 } FunctionItem(R.string.reboot, icon = R.drawable.restart_alt_fill0) { dialog = 1 }
} }
@@ -314,17 +325,41 @@ data class SystemOptionsStatus(
val btContactSharingDisabled: Boolean = false, val btContactSharingDisabled: Boolean = false,
val commonCriteriaMode: Boolean = false, val commonCriteriaMode: Boolean = false,
val usbSignalEnabled: Boolean = true, val usbSignalEnabled: Boolean = true,
val canDisableUsbSignal: Boolean = true val canDisableUsbSignal: Boolean = true,
val stayOnWhilePluggedIn: Boolean = false
) )
@Serializable object SystemOptions @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 @Composable
fun SystemOptionsScreen(vm: MyViewModel, onNavigateUp: () -> Unit) { fun SystemOptionsScreen(vm: MyViewModel, onNavigateUp: () -> Unit) {
val privilege by Privilege.status.collectAsStateWithLifecycle() val privilege by Privilege.status.collectAsStateWithLifecycle()
var dialog by rememberSaveable { mutableIntStateOf(0) } var dialog by rememberSaveable { mutableIntStateOf(0) }
val status by vm.systemOptionsStatus.collectAsStateWithLifecycle() 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) { MyScaffold(R.string.options, onNavigateUp, 0.dp) {
SwitchItem(R.string.disable_cam, status.cameraDisabled, vm::setCameraDisabled, SwitchItem(R.string.disable_cam, status.cameraDisabled, vm::setCameraDisabled,
R.drawable.no_photography_fill0) R.drawable.no_photography_fill0)
@@ -366,6 +401,20 @@ fun SystemOptionsScreen(vm: MyViewModel, onNavigateUp: () -> Unit) {
SwitchItem(R.string.enable_usb_signal, status.usbSignalEnabled, SwitchItem(R.string.enable_usb_signal, status.usbSignalEnabled,
vm::setUsbSignalEnabled, R.drawable.usb_fill0) 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) { if (VERSION.SDK_INT < 34) {
Row( Row(
Modifier.fillMaxWidth().padding(horizontal = HorizontalPadding), 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 @Serializable object ChangeTime
@OptIn(ExperimentalMaterial3Api::class) @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="disable_bt_contact_share">禁止蓝牙分享联系人</string>
<string name="common_criteria_mode">通用标准模式</string> <string name="common_criteria_mode">通用标准模式</string>
<string name="enable_usb_signal">启用USB信号</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="keyguard">锁屏</string>
<string name="lock_now">立即锁屏</string> <string name="lock_now">立即锁屏</string>
<string name="lock_screen">锁屏</string> <string name="lock_screen">锁屏</string>
@@ -148,7 +152,8 @@
<string name="cpu_usages">CPU使用情况</string> <string name="cpu_usages">CPU使用情况</string>
<string name="active">活动</string> <string name="active">活动</string>
<string name="total">总计</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="bug_report">错误报告</string>
<string name="confirm_bug_report">请求错误报告?</string> <string name="confirm_bug_report">请求错误报告?</string>
<string name="reboot">重启</string> <string name="reboot">重启</string>

View File

@@ -143,6 +143,10 @@
<string name="disable_bt_contact_share">Disable bluetooth contact sharing</string> <string name="disable_bt_contact_share">Disable bluetooth contact sharing</string>
<string name="common_criteria_mode">Common criteria mode</string> <string name="common_criteria_mode">Common criteria mode</string>
<string name="enable_usb_signal">Enable USB signal</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="keyguard">Keyguard</string>
<string name="lock_now">Lock screen now</string> <string name="lock_now">Lock screen now</string>
<string name="lock_screen">Lock screen</string> <string name="lock_screen">Lock screen</string>
@@ -158,6 +162,7 @@
<string name="active">Active</string> <string name="active">Active</string>
<string name="total">Total</string> <string name="total">Total</string>
<string name="fan_speeds">Fan speeds</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="bug_report">Bug report</string>
<string name="confirm_bug_report">Request bug report?</string> <string name="confirm_bug_report">Request bug report?</string>
<string name="reboot">Reboot</string> <string name="reboot">Reboot</string>