diff --git a/Android.bp b/Android.bp
index f3725844e00..b841a91310e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -128,6 +128,12 @@ android_library {
// Lineage dependencies
"org.lineageos.platform.internal",
"LineagePreferenceLib",
+ "androidx.fragment_fragment",
+ "androidx.fragment_fragment-ktx",
+ "androidx.preference_preference-ktx",
+ "kotlin-stdlib",
+ "kotlinx_coroutines_android",
+ "kotlinx_coroutines",
],
plugins: [
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 54d2bea4f37..7671d17a271 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -158,6 +158,9 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/values/evolution_arrays.xml b/res/values/evolution_arrays.xml
index f33d20562f3..ef0a8d9e475 100644
--- a/res/values/evolution_arrays.xml
+++ b/res/values/evolution_arrays.xml
@@ -97,4 +97,25 @@
- com.android.systemui
- com.android.shell
+
+
+
+ - @string/custom_timeout_summary_5secs
+ - @string/custom_timeout_summary_10secs
+ - @string/custom_timeout_summary_30secs
+ - @string/custom_timeout_summary_1min
+ - @string/custom_timeout_summary_5mins
+ - @string/custom_timeout_summary_10mins
+ - @string/custom_timeout_summary_30mins
+
+
+
+ - 5000
+ - 10000
+ - 30000
+ - 60000
+ - 300000
+ - 600000
+ - 1800000
+
diff --git a/res/values/evolution_strings.xml b/res/values/evolution_strings.xml
index 0e1ccec9094..09259b74c5d 100644
--- a/res/values/evolution_strings.xml
+++ b/res/values/evolution_strings.xml
@@ -208,4 +208,26 @@
Search
Search apps
+
+
+ App lock
+
+ - %1$d application is protected
+ - %1$d applications are protected
+
+ Unlock
+ Enable debugging
+ Disable debugging
+ Protected apps
+ Select the apps to protect with biometrics or device credentials
+ Auto lock timeout
+ Duration of time after which an unlocked app in background should be locked
+ Redact notifications
+ Notification content will be hidden and collapsed for selected apps when they are locked. Heads up notifications will be automatically disabled.
+ Protect an application first
+ Enable biometrics for unlocking
+ Bubbles will be automatically dismissed after timeout
+ Enable protection
+ Hide from launcher
+ Prevent this application from showing up in any launcher. Requires a launcher restart for changes to take effect.
diff --git a/res/xml/app_lock_package_config_settings.xml b/res/xml/app_lock_package_config_settings.xml
new file mode 100644
index 00000000000..81f3491f7d6
--- /dev/null
+++ b/res/xml/app_lock_package_config_settings.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/xml/app_lock_package_list_settings.xml b/res/xml/app_lock_package_list_settings.xml
new file mode 100644
index 00000000000..13994b3681f
--- /dev/null
+++ b/res/xml/app_lock_package_list_settings.xml
@@ -0,0 +1,19 @@
+
+
+
+
diff --git a/res/xml/app_lock_settings.xml b/res/xml/app_lock_settings.xml
new file mode 100644
index 00000000000..4c8afb0fb27
--- /dev/null
+++ b/res/xml/app_lock_settings.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/xml/security_dashboard_settings.xml b/res/xml/security_dashboard_settings.xml
index 0550441d3f2..203445dd690 100644
--- a/res/xml/security_dashboard_settings.xml
+++ b/res/xml/security_dashboard_settings.xml
@@ -60,6 +60,11 @@
android:title="@string/security_settings_biometric_preference_title"
android:summary="@string/summary_placeholder"
settings:keywords="@string/keywords_biometric_settings" />
+
+
+
(KEY_HEADER)
+ EntityHeaderController.newInstance(
+ requireActivity(),
+ this,
+ header?.findViewById(R.id.entity_header)
+ ).setRecyclerView(listView, settingsLifecycle)
+ .setPackageName(packageInfo.packageName)
+ .setButtonActions(
+ EntityHeaderController.ActionType.ACTION_NONE,
+ EntityHeaderController.ActionType.ACTION_NONE
+ )
+ .bindHeaderButtons()
+ .setLabel(appEntry)
+ .setIcon(appEntry)
+ .done(requireActivity(), false /* rebindActions */)
+ }
+
+ override protected fun createPreferenceControllers(
+ context: Context
+ ) : List = listOf(
+ AppLockPackageProtectionPC(context, packageInfo.packageName, lifecycleScope),
+ AppLockNotificationRedactionPC(context, packageInfo.packageName, lifecycleScope),
+ AppLockHideAppPC(context, packageInfo.packageName, lifecycleScope)
+ )
+
+ override fun getMetricsCategory(): Int = MetricsProto.MetricsEvent.EVOLVER
+
+ override protected fun getPreferenceScreenResId() = R.xml.app_lock_package_config_settings
+
+ override protected fun getLogTag() = TAG
+}
diff --git a/src/com/android/settings/security/applock/AppLockPackageListFragment.kt b/src/com/android/settings/security/applock/AppLockPackageListFragment.kt
new file mode 100644
index 00000000000..069eed51355
--- /dev/null
+++ b/src/com/android/settings/security/applock/AppLockPackageListFragment.kt
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2022 FlamingoOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.security.applock
+
+import android.app.AppLockManager
+import android.content.Context
+import android.content.pm.PackageInfo
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.PackageInfoFlags
+import android.os.Bundle
+import android.view.View
+
+import androidx.lifecycle.lifecycleScope
+import androidx.preference.Preference
+import androidx.preference.forEach
+
+import com.android.internal.logging.nano.MetricsProto
+
+import com.android.settings.R
+import com.android.settings.core.SubSettingLauncher
+import com.android.settings.dashboard.DashboardFragment
+import com.android.settingslib.PrimarySwitchPreference
+import com.android.settingslib.widget.TwoTargetPreference.ICON_SIZE_SMALL
+
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+private val TAG = AppLockPackageListFragment::class.simpleName
+internal const val PACKAGE_INFO = "package_info"
+
+class AppLockPackageListFragment : DashboardFragment() {
+
+ private lateinit var appLockManager: AppLockManager
+ private lateinit var pm: PackageManager
+ private lateinit var whiteListedPackages: Array
+
+ override fun onAttach(context: Context) {
+ super.onAttach(context)
+ appLockManager = context.getSystemService(AppLockManager::class.java)
+ pm = context.packageManager
+ whiteListedPackages = resources.getStringArray(
+ com.android.internal.R.array.config_appLockAllowedSystemApps)
+ }
+
+ override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
+ super.onCreatePreferences(savedInstanceState, rootKey)
+ lifecycleScope.launch {
+ val selectedPackages = getSelectedPackages()
+ val preferences = withContext(Dispatchers.Default) {
+ pm.getInstalledPackages(
+ PackageInfoFlags.of(PackageManager.MATCH_ALL.toLong())
+ ).filter {
+ !it.applicationInfo.isSystemApp() || whiteListedPackages.contains(it.packageName)
+ }.sortedWith { first, second ->
+ getLabel(first).compareTo(getLabel(second))
+ }
+ }.map { packageInfo ->
+ createPreference(packageInfo, selectedPackages.contains(packageInfo.packageName))
+ }
+ preferenceScreen?.let {
+ preferences.forEach { pref ->
+ it.addPreference(pref)
+ }
+ }
+ }
+ }
+
+ override fun onResume() {
+ super.onResume()
+ lifecycleScope.launch {
+ val selectedPackages = getSelectedPackages()
+ preferenceScreen?.forEach {
+ if (it is PrimarySwitchPreference) {
+ it.isChecked = selectedPackages.contains(it.key)
+ }
+ }
+ }
+ }
+
+ private suspend fun getSelectedPackages(): Set {
+ return withContext(Dispatchers.IO) {
+ appLockManager.packageData.map { it.packageName }.toSet()
+ }
+ }
+
+ private fun getLabel(packageInfo: PackageInfo) =
+ packageInfo.applicationInfo.loadLabel(pm).toString()
+
+ private fun createPreference(packageInfo: PackageInfo, isProtected: Boolean): Preference {
+ val label = getLabel(packageInfo)
+ return PrimarySwitchPreference(requireContext()).apply {
+ key = packageInfo.packageName
+ title = label
+ icon = packageInfo.applicationInfo.loadIcon(pm)
+ setIconSize(ICON_SIZE_SMALL)
+ isChecked = isProtected
+ setOnPreferenceChangeListener { _, newValue ->
+ lifecycleScope.launch(Dispatchers.IO) {
+ if (newValue as Boolean) {
+ appLockManager.addPackage(packageInfo.packageName)
+ } else {
+ appLockManager.removePackage(packageInfo.packageName)
+ }
+ }
+ return@setOnPreferenceChangeListener true
+ }
+ setOnPreferenceClickListener {
+ SubSettingLauncher(requireContext())
+ .setDestination(AppLockPackageConfigFragment::class.qualifiedName)
+ .setSourceMetricsCategory(metricsCategory)
+ .setTitleText(label)
+ .setArguments(
+ Bundle(1).apply {
+ putParcelable(PACKAGE_INFO, packageInfo)
+ }
+ )
+ .launch()
+ true
+ }
+ }
+ }
+
+ override fun getMetricsCategory(): Int = MetricsProto.MetricsEvent.EVOLVER
+
+ override protected fun getPreferenceScreenResId() = R.xml.app_lock_package_list_settings
+
+ override protected fun getLogTag() = TAG
+}
diff --git a/src/com/android/settings/security/applock/AppLockPackageProtectionPC.kt b/src/com/android/settings/security/applock/AppLockPackageProtectionPC.kt
new file mode 100644
index 00000000000..34fd2cf5b4a
--- /dev/null
+++ b/src/com/android/settings/security/applock/AppLockPackageProtectionPC.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2022 FlamingoOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.security.applock
+
+import android.app.AppLockManager
+import android.content.Context
+
+import androidx.lifecycle.lifecycleScope
+import androidx.preference.Preference
+import androidx.preference.PreferenceScreen
+
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+private const val KEY = "main_switch"
+
+class AppLockPackageProtectionPC(
+ context: Context,
+ private val packageName: String,
+ private val coroutineScope: CoroutineScope
+) : AppLockTogglePreferenceController(context, KEY) {
+
+ private val appLockManager = context.getSystemService(AppLockManager::class.java)
+ private var isProtected = false
+ private var preference: Preference? = null
+
+ init {
+ coroutineScope.launch {
+ isProtected = withContext(Dispatchers.Default) {
+ appLockManager.packageData.any {
+ it.packageName == packageName
+ }
+ }
+ preference?.let {
+ updateState(it)
+ }
+ }
+ }
+
+ override fun getAvailabilityStatus() = AVAILABLE
+
+ override fun isChecked() = isProtected
+
+ override fun setChecked(checked: Boolean): Boolean {
+ if (isProtected == checked) return false
+ isProtected = checked
+ coroutineScope.launch(Dispatchers.Default) {
+ if (isProtected) {
+ appLockManager.addPackage(packageName)
+ } else {
+ appLockManager.removePackage(packageName)
+ }
+ }
+ return true
+ }
+
+ override fun displayPreference(screen: PreferenceScreen) {
+ super.displayPreference(screen)
+ preference = screen.findPreference(preferenceKey)
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/security/applock/AppLockSettingsFragment.kt b/src/com/android/settings/security/applock/AppLockSettingsFragment.kt
new file mode 100644
index 00000000000..b3e58dab0c0
--- /dev/null
+++ b/src/com/android/settings/security/applock/AppLockSettingsFragment.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2022 FlamingoOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.security.applock
+
+import android.content.Context
+import android.os.SystemProperties
+import android.view.Menu
+import android.view.MenuInflater
+import android.view.MenuItem
+
+import com.android.internal.logging.nano.MetricsProto
+import androidx.lifecycle.lifecycleScope
+
+import com.android.settings.R
+import com.android.settings.dashboard.DashboardFragment
+import com.android.settings.search.BaseSearchIndexProvider
+import com.android.settingslib.core.AbstractPreferenceController
+import com.android.settingslib.search.SearchIndexable
+
+@SearchIndexable
+class AppLockSettingsFragment : DashboardFragment(),
+ MenuItem.OnMenuItemClickListener {
+
+ private var debugEnabled = SystemProperties.get(DEBUG_PROPERTY, null) == LEVEL_DEBUG
+ private var handledClick = false
+
+ override protected fun getPreferenceScreenResId() = R.xml.app_lock_settings
+
+ override fun getMetricsCategory() = MetricsProto.MetricsEvent.EVOLVER
+
+ override protected fun getLogTag() = TAG
+
+ override fun onCreateOptionsMenu(menu: Menu, menuInflater: MenuInflater) {
+ super.onCreateOptionsMenu(menu, menuInflater)
+ menu.add(
+ 0 /* groupId */,
+ MENU_ITEM_DEBUG_ID,
+ 0 /* order */,
+ getDebugMenuItemTitle(),
+ ).setOnMenuItemClickListener(this)
+ }
+
+ private fun getDebugMenuItemTitle(): Int =
+ if (debugEnabled) R.string.disable_debugging else R.string.enable_debugging
+
+ override fun onMenuItemClick(item: MenuItem): Boolean {
+ if (item.itemId == MENU_ITEM_DEBUG_ID) {
+ debugEnabled = !debugEnabled
+ SystemProperties.set(DEBUG_PROPERTY, if (debugEnabled) LEVEL_DEBUG else null)
+ item.setTitle(getDebugMenuItemTitle())
+ return true
+ }
+ return false
+ }
+
+ override protected fun createPreferenceControllers(
+ context: Context
+ ) : List = listOf(
+ AppLockBiometricPreferenceController(context, lifecycleScope)
+ )
+
+ companion object {
+ private const val TAG = "AppLockSettingsFragment"
+
+ private const val DEBUG_PROPERTY = "log.tag.AppLockManagerService"
+ private const val LEVEL_DEBUG = "DEBUG"
+ private const val MENU_ITEM_DEBUG_ID = 101
+
+ @JvmField
+ val SEARCH_INDEX_DATA_PROVIDER = BaseSearchIndexProvider(R.xml.app_lock_settings)
+ }
+}
diff --git a/src/com/android/settings/security/applock/AppLockSettingsPreferenceController.kt b/src/com/android/settings/security/applock/AppLockSettingsPreferenceController.kt
new file mode 100644
index 00000000000..9e3d9240480
--- /dev/null
+++ b/src/com/android/settings/security/applock/AppLockSettingsPreferenceController.kt
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2022 FlamingoOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.security.applock
+
+import android.app.Activity
+import android.app.AppLockManager
+import android.app.KeyguardManager
+import android.content.Context
+import android.content.Intent
+import android.os.UserHandle
+
+import androidx.activity.result.ActivityResultLauncher
+import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
+import androidx.lifecycle.Lifecycle.Event
+import androidx.lifecycle.LifecycleEventObserver
+import androidx.lifecycle.LifecycleOwner
+import androidx.preference.Preference
+import androidx.preference.PreferenceScreen
+
+import com.android.internal.widget.LockPatternUtils
+import com.android.settings.R
+import com.android.settings.Utils.SETTINGS_PACKAGE_NAME
+import com.android.settings.core.SubSettingLauncher
+import com.android.settings.password.ConfirmDeviceCredentialActivity
+import com.android.settings.security.SecuritySettings
+import com.android.settingslib.core.lifecycle.Lifecycle
+import com.android.settingslib.transition.SettingsTransitionHelper.TransitionType.TRANSITION_SLIDE
+import com.android.settings.core.BasePreferenceController
+
+import com.android.settings.SettingsActivity
+import com.android.settings.core.SettingsBaseActivity
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider
+
+class AppLockSettingsPreferenceController(
+ context: Context,
+ preferenceKey: String,
+ private val host: SecuritySettings?,
+ lifecycle: Lifecycle?,
+) : BasePreferenceController(context, preferenceKey),
+ LifecycleEventObserver {
+
+ private val lockPatternUtils = LockPatternUtils(context)
+ private val appLockManager = context.getSystemService(AppLockManager::class.java)
+ private var preference: Preference? = null
+ private val securityPromptLauncher: ActivityResultLauncher?
+
+ init {
+ lifecycle?.addObserver(this)
+ securityPromptLauncher = host?.registerForActivityResult(
+ StartActivityForResult()
+ ) {
+ if (it?.resultCode == Activity.RESULT_OK) {
+ val intent = SubSettingLauncher(mContext)
+ .setDestination(AppLockSettingsFragment::class.qualifiedName)
+ .setSourceMetricsCategory(host.metricsCategory)
+ .setTransitionType(TRANSITION_SLIDE)
+ .toIntent()
+ intent.setClass(mContext, AppLockSubSettings::class.java)
+ mContext.startActivity(intent)
+ }
+ }
+ }
+
+ override fun getAvailabilityStatus() =
+ if (lockPatternUtils.isSecure(UserHandle.myUserId()))
+ AVAILABLE
+ else
+ DISABLED_DEPENDENT_SETTING
+
+ override fun onStateChanged(owner: LifecycleOwner, event: Event) {
+ if (event == Event.ON_START) {
+ preference?.let {
+ updateState(it)
+ }
+ }
+ }
+
+ override fun displayPreference(screen: PreferenceScreen) {
+ super.displayPreference(screen)
+ preference = screen.findPreference(preferenceKey)
+ }
+
+ override fun updateState(preference: Preference) {
+ preference.apply {
+ if (getAvailabilityStatus() == AVAILABLE) {
+ setEnabled(true)
+ summary = getSummaryForListSize(appLockManager.packageData.size)
+ } else {
+ setEnabled(false)
+ summary = mContext.getString(R.string.disabled_because_no_backup_security)
+ }
+ }
+ }
+
+ private fun getSummaryForListSize(size: Int): CharSequence? =
+ if (size == 0) {
+ null
+ } else {
+ mContext.resources.getQuantityString(R.plurals.app_lock_summary, size, size)
+ }
+
+ override fun handlePreferenceTreeClick(preference: Preference): Boolean {
+ if (preference.key == preferenceKey && securityPromptLauncher != null) {
+ val title = mContext.getString(R.string.app_lock_authentication_dialog_title)
+ val intent = Intent().apply {
+ setClassName(SETTINGS_PACKAGE_NAME,
+ ConfirmDeviceCredentialActivity::class.qualifiedName)
+ putExtra(KeyguardManager.EXTRA_TITLE, title)
+ }
+ securityPromptLauncher.launch(intent)
+ return true
+ }
+ return super.handlePreferenceTreeClick(preference)
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/security/applock/AppLockSubSettings.kt b/src/com/android/settings/security/applock/AppLockSubSettings.kt
new file mode 100644
index 00000000000..e376f1696d0
--- /dev/null
+++ b/src/com/android/settings/security/applock/AppLockSubSettings.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2022 FlamingoOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.security.applock
+
+import com.android.settings.SettingsActivity
+
+class AppLockSubSettings : SettingsActivity() {
+
+ override protected fun isValidFragment(fragmentName: String): Boolean {
+ return AppLockSettingsFragment::class.qualifiedName == fragmentName
+ }
+}
diff --git a/src/com/android/settings/security/applock/AppLockTimeoutPreferenceController.kt b/src/com/android/settings/security/applock/AppLockTimeoutPreferenceController.kt
new file mode 100644
index 00000000000..d5036c03294
--- /dev/null
+++ b/src/com/android/settings/security/applock/AppLockTimeoutPreferenceController.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2022 FlamingoOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.security.applock
+
+import android.app.AppLockManager
+import android.content.Context
+
+import androidx.preference.ListPreference
+import androidx.preference.Preference
+
+import com.android.settings.core.BasePreferenceController
+
+class AppLockTimeoutPreferenceController(
+ context: Context,
+ key: String,
+) : BasePreferenceController(context, key),
+ Preference.OnPreferenceChangeListener {
+
+ private val appLockManager = context.getSystemService(AppLockManager::class.java)
+
+ override fun getAvailabilityStatus() = AVAILABLE
+
+ override fun updateState(preference: Preference) {
+ (preference as ListPreference).value = appLockManager.timeout.takeIf {
+ it != -1L
+ }?.toString()
+ }
+
+ override fun onPreferenceChange(preference: Preference, newValue: Any): Boolean {
+ appLockManager.timeout = (newValue as String).toLong()
+ return true
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/security/applock/AppLockTogglePreferenceController.kt b/src/com/android/settings/security/applock/AppLockTogglePreferenceController.kt
new file mode 100644
index 00000000000..3564110e43f
--- /dev/null
+++ b/src/com/android/settings/security/applock/AppLockTogglePreferenceController.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2022 FlamingoOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.security.applock;
+
+import android.content.Context
+import android.widget.Switch
+
+import androidx.preference.Preference
+import androidx.preference.PreferenceScreen
+
+import com.android.settings.R
+import com.android.settings.core.TogglePreferenceController
+import com.android.settingslib.widget.MainSwitchPreference
+import com.android.settingslib.widget.OnMainSwitchChangeListener
+
+abstract class AppLockTogglePreferenceController(
+ context: Context,
+ key: String,
+) : TogglePreferenceController(context, key),
+ OnMainSwitchChangeListener {
+
+ override fun displayPreference(screen: PreferenceScreen) {
+ super.displayPreference(screen)
+ val preference = screen.findPreference(preferenceKey) ?: return
+ if (preference is MainSwitchPreference) {
+ preference.addOnSwitchChangeListener(this)
+ }
+ }
+
+ override fun onSwitchChanged(switchView: Switch, isChecked: Boolean) {
+ setChecked(isChecked)
+ }
+
+ override fun getSliceHighlightMenuRes() = R.string.menu_key_security
+}
\ No newline at end of file