Cherish:introduce app lock
* AppLockCredentialActivity is a stripped down version of ConfirmDeviceCredentialActivity in Settings Signed-off-by: jhonboy121 <alfredmathew05@gmail.com> Signed-off-by: Hưng Phan <phandinhhungvp2001@gmail.com>
This commit is contained in:
@@ -465,4 +465,25 @@
|
|||||||
<item>1</item>
|
<item>1</item>
|
||||||
<item>2</item>
|
<item>2</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
|
<!-- App lock timeout -->
|
||||||
|
<string-array name="app_lock_timeout_entries">
|
||||||
|
<item>5 seconds</item>
|
||||||
|
<item>10 seconds</item>
|
||||||
|
<item>30 seconds</item>
|
||||||
|
<item>1 minute</item>
|
||||||
|
<item>5 minutes</item>
|
||||||
|
<item>10 minutes</item>
|
||||||
|
<item>30 minutes</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="app_lock_timeout_values" translatable="false">
|
||||||
|
<item>5000</item>
|
||||||
|
<item>10000</item>
|
||||||
|
<item>30000</item>
|
||||||
|
<item>60000</item>
|
||||||
|
<item>300000</item>
|
||||||
|
<item>600000</item>
|
||||||
|
<item>1800000</item>
|
||||||
|
</string-array>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -697,4 +697,24 @@
|
|||||||
<string name="torch_power_button_gesture_dt_toast">Jump to camera gesture is now disabled</string>
|
<string name="torch_power_button_gesture_dt_toast">Jump to camera gesture is now disabled</string>
|
||||||
<string name="torch_power_button_gesture_lp">Long press power button</string>
|
<string name="torch_power_button_gesture_lp">Long press power button</string>
|
||||||
|
|
||||||
|
<!-- App lock -->
|
||||||
|
<string name="app_lock_title">App lock</string>
|
||||||
|
<string name="app_lock_summary_singular">1 application is protected</string>
|
||||||
|
<string name="app_lock_summary_plural"><xliff:g example="2" id="Number of applications">%1$d</xliff:g> applications are protected</string>
|
||||||
|
<string name="app_lock_authentication_dialog_title">Unlock</string>
|
||||||
|
<string name="enable_debugging">Enable debugging</string>
|
||||||
|
<string name="disable_debugging">Disable debugging</string>
|
||||||
|
<string name="app_lock_packages_title">Protected apps</string>
|
||||||
|
<string name="app_lock_packages_summary">Select the apps to protect with biometrics or device credentials</string>
|
||||||
|
<string name="app_lock_timeout_title">Auto lock timeout</string>
|
||||||
|
<string name="app_lock_timeout_summary">Duration of time after which an unlocked app in background should be locked</string>
|
||||||
|
<string name="app_lock_notifications_title">Collapse notifications</string>
|
||||||
|
<string name="app_lock_notifications_summary">
|
||||||
|
Notification content will be hidden and collapsed for selected apps when they are locked.
|
||||||
|
Heads up notifications will be automatically disabled.
|
||||||
|
</string>
|
||||||
|
<string name="app_lock_notifications_disabled_summary">Protect an application first</string>
|
||||||
|
<string name="app_lock_biometrics_allowed_title">Enable biometrics for unlocking</string>
|
||||||
|
<string name="app_lock_footer_text">Bubbles will be automatically dismissed after timeout</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
52
res/xml/app_lock_settings.xml
Normal file
52
res/xml/app_lock_settings.xml
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Copyright (C) 2022 CherishOS 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.
|
||||||
|
-->
|
||||||
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:settings="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:title="@string/app_lock_title">
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
android:key="app_lock_packages"
|
||||||
|
android:title="@string/app_lock_packages_title"
|
||||||
|
android:summary="@string/app_lock_packages_summary"
|
||||||
|
android:fragment="com.cherish.settings.security.applock.AppLockPackageListFragment" />
|
||||||
|
|
||||||
|
<ListPreference
|
||||||
|
android:key="app_lock_timeout"
|
||||||
|
android:title="@string/app_lock_timeout_title"
|
||||||
|
android:summary="@string/app_lock_timeout_summary"
|
||||||
|
android:entries="@array/app_lock_timeout_entries"
|
||||||
|
android:entryValues="@array/app_lock_timeout_values"
|
||||||
|
android:defaultValue="0"
|
||||||
|
android:persistent="false"
|
||||||
|
settings:controller="com.cherish.settings.security.applock.AppLockTimeoutPreferenceController" />
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
android:key="app_lock_notifications"
|
||||||
|
android:title="@string/app_lock_notifications_title"
|
||||||
|
android:fragment="com.cherish.settings.security.applock.AppLockNotificationListFragment"
|
||||||
|
settings:controller="com.cherish.settings.security.applock.AppLockNotificationPreferenceController" />
|
||||||
|
|
||||||
|
<SwitchPreference
|
||||||
|
android:key="app_lock_biometrics_allowed"
|
||||||
|
android:title="@string/app_lock_biometrics_allowed_title"
|
||||||
|
android:persistent="false"
|
||||||
|
settings:controller="com.cherish.settings.security.applock.AppLockBiometricPreferenceController" />
|
||||||
|
|
||||||
|
<com.android.settingslib.widget.FooterPreference
|
||||||
|
android:title="@string/app_lock_footer_text"
|
||||||
|
android:selectable="false"
|
||||||
|
settings:searchable="false" />
|
||||||
|
</PreferenceScreen>
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 cherishOS 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.cherish.settings.security.applock
|
||||||
|
|
||||||
|
import android.app.AppLockManager
|
||||||
|
import android.content.Context
|
||||||
|
import android.hardware.biometrics.BiometricManager
|
||||||
|
import android.hardware.biometrics.BiometricManager.Authenticators
|
||||||
|
|
||||||
|
import androidx.preference.Preference
|
||||||
|
import androidx.preference.SwitchPreference
|
||||||
|
|
||||||
|
import com.android.settings.core.BasePreferenceController
|
||||||
|
|
||||||
|
class AppLockBiometricPreferenceController(
|
||||||
|
context: Context,
|
||||||
|
key: String,
|
||||||
|
) : BasePreferenceController(context, key),
|
||||||
|
Preference.OnPreferenceChangeListener {
|
||||||
|
|
||||||
|
private val appLockManager = context.getSystemService(AppLockManager::class.java)
|
||||||
|
private val biometricManager = context.getSystemService(BiometricManager::class.java)
|
||||||
|
|
||||||
|
override fun getAvailabilityStatus(): Int {
|
||||||
|
val biometricsAllowed = biometricManager.canAuthenticate(
|
||||||
|
Authenticators.BIOMETRIC_STRONG) == BiometricManager.BIOMETRIC_SUCCESS
|
||||||
|
return if (biometricsAllowed)
|
||||||
|
AVAILABLE
|
||||||
|
else
|
||||||
|
UNSUPPORTED_ON_DEVICE
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun updateState(preference: Preference) {
|
||||||
|
(preference as SwitchPreference).setChecked(appLockManager.isBiometricsAllowed())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPreferenceChange(preference: Preference, newValue: Any): Boolean {
|
||||||
|
appLockManager.setBiometricsAllowed(newValue as Boolean)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,204 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 The Android Open Source Project
|
||||||
|
* 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.cherish.settings.security.applock
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.app.AppLockManager
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.hardware.biometrics.BiometricConstants
|
||||||
|
import android.hardware.biometrics.BiometricManager.Authenticators
|
||||||
|
import android.hardware.biometrics.BiometricPrompt
|
||||||
|
import android.hardware.biometrics.BiometricPrompt.AuthenticationCallback
|
||||||
|
import android.hardware.biometrics.PromptInfo
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.Looper
|
||||||
|
import android.os.UserHandle.USER_NULL
|
||||||
|
import android.os.UserManager
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.WindowManager
|
||||||
|
|
||||||
|
import androidx.fragment.app.commit
|
||||||
|
import androidx.fragment.app.FragmentActivity
|
||||||
|
|
||||||
|
import com.android.internal.widget.LockPatternUtils
|
||||||
|
import com.android.settings.R
|
||||||
|
import com.android.settings.password.BiometricFragment
|
||||||
|
import com.android.settings.password.ConfirmDeviceCredentialUtils
|
||||||
|
|
||||||
|
class AppLockCredentialActivity : FragmentActivity() {
|
||||||
|
|
||||||
|
private val handler = Handler(Looper.getMainLooper())
|
||||||
|
|
||||||
|
private lateinit var lockPatternUtils: LockPatternUtils
|
||||||
|
private lateinit var userManager: UserManager
|
||||||
|
private lateinit var appLockManager: AppLockManager
|
||||||
|
|
||||||
|
private var packageName: String? = null
|
||||||
|
private var title: String? = null
|
||||||
|
private var userId: Int = USER_NULL
|
||||||
|
private var biometricFragment: BiometricFragment? = null
|
||||||
|
private var goingToBackground = false
|
||||||
|
private var waitingForBiometricCallback = false
|
||||||
|
|
||||||
|
private val authenticationCallback = object : AuthenticationCallback() {
|
||||||
|
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
|
||||||
|
if (!goingToBackground) {
|
||||||
|
waitingForBiometricCallback = false
|
||||||
|
if (errorCode == BiometricPrompt.BIOMETRIC_ERROR_USER_CANCELED
|
||||||
|
|| errorCode == BiometricPrompt.BIOMETRIC_ERROR_CANCELED) {
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
} else if (waitingForBiometricCallback) { // goingToBackground is true
|
||||||
|
waitingForBiometricCallback = false
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
|
||||||
|
waitingForBiometricCallback = false
|
||||||
|
appLockManager.unlockPackage(packageName)
|
||||||
|
ConfirmDeviceCredentialUtils.checkForPendingIntent(this@AppLockCredentialActivity)
|
||||||
|
setResult(Activity.RESULT_OK)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAuthenticationFailed() {
|
||||||
|
waitingForBiometricCallback = false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSystemEvent(event: Int) {
|
||||||
|
if (event == BiometricConstants.BIOMETRIC_SYSTEM_EVENT_EARLY_USER_CANCEL) {
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override protected fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
window.apply {
|
||||||
|
addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
|
||||||
|
statusBarColor = Color.TRANSPARENT
|
||||||
|
}
|
||||||
|
|
||||||
|
appLockManager = getSystemService(AppLockManager::class.java)
|
||||||
|
userManager = UserManager.get(this)
|
||||||
|
lockPatternUtils = LockPatternUtils(this)
|
||||||
|
|
||||||
|
packageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME)
|
||||||
|
if (packageName == null) {
|
||||||
|
Log.e(TAG, "Failed to get package name, aborting unlock")
|
||||||
|
finish()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
title = intent.getStringExtra(AppLockManager.EXTRA_PACKAGE_LABEL)
|
||||||
|
|
||||||
|
userId = intent.getIntExtra(Intent.EXTRA_USER_ID, USER_NULL)
|
||||||
|
if (userId == USER_NULL) {
|
||||||
|
Log.e(TAG, "Invalid user id, aborting")
|
||||||
|
finish()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val biometricsAllowed = intent.getBooleanExtra(
|
||||||
|
AppLockManager.EXTRA_ALLOW_BIOMETRICS,
|
||||||
|
AppLockManager.DEFAULT_BIOMETRICS_ALLOWED
|
||||||
|
)
|
||||||
|
var authenticators = Authenticators.DEVICE_CREDENTIAL
|
||||||
|
if (biometricsAllowed) {
|
||||||
|
authenticators = authenticators or Authenticators.BIOMETRIC_STRONG
|
||||||
|
}
|
||||||
|
|
||||||
|
val promptInfo = PromptInfo().also {
|
||||||
|
it.title = getString(com.android.internal.R.string.unlock_application, title)
|
||||||
|
it.isDisallowBiometricsIfPolicyExists = true
|
||||||
|
it.authenticators = authenticators
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isBiometricAllowed()) {
|
||||||
|
// Don't need to check if biometrics / pin/pattern/pass are enrolled. It will go to
|
||||||
|
// onAuthenticationError and do the right thing automatically.
|
||||||
|
showBiometricPrompt(promptInfo)
|
||||||
|
waitingForBiometricCallback = true
|
||||||
|
} else {
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override protected fun onStart() {
|
||||||
|
super.onStart()
|
||||||
|
// Translucent activity that is "visible", so it doesn't complain about finish()
|
||||||
|
// not being called before onResume().
|
||||||
|
setVisible(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPause() {
|
||||||
|
super.onPause()
|
||||||
|
if (!isChangingConfigurations()) {
|
||||||
|
goingToBackground = true
|
||||||
|
if (!waitingForBiometricCallback) {
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
goingToBackground = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// User could be locked while Effective user is unlocked even though the effective owns the
|
||||||
|
// credential. Otherwise, biometric can't unlock fbe/keystore through
|
||||||
|
// verifyTiedProfileChallenge. In such case, we also wanna show the user message that
|
||||||
|
// biometric is disabled due to device restart.
|
||||||
|
private fun isStrongAuthRequired() =
|
||||||
|
!lockPatternUtils.isBiometricAllowedForUser(userId) ||
|
||||||
|
!userManager.isUserUnlocked(userId)
|
||||||
|
|
||||||
|
private fun isBiometricAllowed() =
|
||||||
|
!isStrongAuthRequired() && !lockPatternUtils.hasPendingEscrowToken(userId)
|
||||||
|
|
||||||
|
private fun showBiometricPrompt(promptInfo: PromptInfo) {
|
||||||
|
biometricFragment = supportFragmentManager.findFragmentByTag(TAG_BIOMETRIC_FRAGMENT)
|
||||||
|
as? BiometricFragment
|
||||||
|
var newFragment = false
|
||||||
|
if (biometricFragment == null) {
|
||||||
|
biometricFragment = BiometricFragment.newInstance(promptInfo)
|
||||||
|
newFragment = true
|
||||||
|
}
|
||||||
|
biometricFragment?.also {
|
||||||
|
it.setCallbacks({
|
||||||
|
handler.post(it)
|
||||||
|
}, authenticationCallback)
|
||||||
|
it.setUser(userId)
|
||||||
|
}
|
||||||
|
if (newFragment) {
|
||||||
|
biometricFragment?.let {
|
||||||
|
supportFragmentManager.commit {
|
||||||
|
add(it, TAG_BIOMETRIC_FRAGMENT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "AppLockCredentialActivity"
|
||||||
|
private const val TAG_BIOMETRIC_FRAGMENT = "fragment"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* 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.cherish.settings.security.applock
|
||||||
|
|
||||||
|
import android.app.AppLockManager
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
|
||||||
|
import com.android.settings.R
|
||||||
|
import com.cherish.settings.fragment.AppListFragment
|
||||||
|
|
||||||
|
class AppLockNotificationListFragment : AppListFragment() {
|
||||||
|
|
||||||
|
private lateinit var appLockManager: AppLockManager
|
||||||
|
private val lockedPackages = mutableListOf<String>()
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
appLockManager = requireContext().getSystemService(AppLockManager::class.java)
|
||||||
|
lockedPackages.addAll(appLockManager.packages)
|
||||||
|
}
|
||||||
|
|
||||||
|
override protected fun getTitle(): Int = R.string.app_lock_notifications_title
|
||||||
|
|
||||||
|
override protected fun getInitialCheckedList(): List<String> =
|
||||||
|
appLockManager.packagesWithSecureNotifications
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
setDisplayCategory(CATEGORY_BOTH)
|
||||||
|
setCustomFilter {
|
||||||
|
lockedPackages.contains(it.packageName)
|
||||||
|
}
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
}
|
||||||
|
|
||||||
|
override protected fun onAppSelected(packageName: String) {
|
||||||
|
appLockManager.setSecureNotification(packageName, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
override protected fun onAppDeselected(packageName: String) {
|
||||||
|
appLockManager.setSecureNotification(packageName, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.cherish.settings.security.applock
|
||||||
|
|
||||||
|
import android.app.AppLockManager
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.UserHandle
|
||||||
|
|
||||||
|
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.settingslib.core.lifecycle.Lifecycle
|
||||||
|
import com.android.settings.core.BasePreferenceController
|
||||||
|
|
||||||
|
class AppLockNotificationPreferenceController(
|
||||||
|
private val context: Context,
|
||||||
|
lifecycle: Lifecycle?,
|
||||||
|
) : BasePreferenceController(context, KEY),
|
||||||
|
LifecycleEventObserver {
|
||||||
|
|
||||||
|
private val appLockManager = context.getSystemService(AppLockManager::class.java)
|
||||||
|
|
||||||
|
private var preference: Preference? = null
|
||||||
|
|
||||||
|
init {
|
||||||
|
lifecycle?.addObserver(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getAvailabilityStatus() =
|
||||||
|
if (appLockManager.packages.isNotEmpty()) 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) {
|
||||||
|
if (getAvailabilityStatus() == AVAILABLE) {
|
||||||
|
preference.setEnabled(true)
|
||||||
|
preference.summary = context.getString(R.string.app_lock_notifications_summary)
|
||||||
|
} else {
|
||||||
|
preference.setEnabled(false)
|
||||||
|
preference.summary = context.getString(R.string.app_lock_notifications_disabled_summary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val KEY = "app_lock_notifications"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* 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.cherish.settings.security.applock
|
||||||
|
|
||||||
|
import android.app.AppLockManager
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
|
||||||
|
import com.android.settings.R
|
||||||
|
import com.cherish.settings.fragment.AppListFragment
|
||||||
|
|
||||||
|
class AppLockPackageListFragment : AppListFragment() {
|
||||||
|
|
||||||
|
private lateinit var appLockManager: AppLockManager
|
||||||
|
private lateinit var whiteListedPackages: Array<String>
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
appLockManager = requireContext().getSystemService(AppLockManager::class.java)
|
||||||
|
whiteListedPackages = requireContext().resources.getStringArray(
|
||||||
|
com.android.internal.R.array.config_appLockAllowedSystemApps)
|
||||||
|
}
|
||||||
|
|
||||||
|
override protected fun getTitle(): Int = R.string.app_lock_packages_title
|
||||||
|
|
||||||
|
override protected fun getInitialCheckedList(): List<String> = appLockManager.packages
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
setDisplayCategory(CATEGORY_BOTH)
|
||||||
|
setCustomFilter {
|
||||||
|
!it.applicationInfo.isSystemApp() || whiteListedPackages.contains(it.packageName)
|
||||||
|
}
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
}
|
||||||
|
|
||||||
|
override protected fun onAppSelected(packageName: String) {
|
||||||
|
appLockManager.addPackage(packageName)
|
||||||
|
}
|
||||||
|
|
||||||
|
override protected fun onAppDeselected(packageName: String) {
|
||||||
|
appLockManager.removePackage(packageName)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
/*
|
||||||
|
* 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.cherish.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 com.android.settings.R
|
||||||
|
import com.android.settings.search.BaseSearchIndexProvider
|
||||||
|
import com.android.settingslib.core.AbstractPreferenceController
|
||||||
|
import com.android.settingslib.core.lifecycle.Lifecycle
|
||||||
|
import com.android.settingslib.search.SearchIndexable
|
||||||
|
import com.android.settings.dashboard.DashboardFragment
|
||||||
|
|
||||||
|
@SearchIndexable
|
||||||
|
class AppLockSettingsFragment : DashboardFragment(),
|
||||||
|
MenuItem.OnMenuItemClickListener {
|
||||||
|
|
||||||
|
private var debugEnabled = SystemProperties.get(DEBUG_PROPERTY, null) == LEVEL_DEBUG
|
||||||
|
|
||||||
|
override protected fun getPreferenceScreenResId() = R.xml.app_lock_settings
|
||||||
|
|
||||||
|
override fun getMetricsCategory() = MetricsProto.MetricsEvent.CHERISH_SETTINGS
|
||||||
|
|
||||||
|
override protected fun getLogTag() = TAG
|
||||||
|
|
||||||
|
override protected fun createPreferenceControllers(context: Context) =
|
||||||
|
buildPreferenceControllers(context, settingsLifecycle)
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = object : BaseSearchIndexProvider(
|
||||||
|
R.xml.app_lock_settings
|
||||||
|
) {
|
||||||
|
override fun createPreferenceControllers(context: Context) =
|
||||||
|
buildPreferenceControllers(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun buildPreferenceControllers(
|
||||||
|
context: Context,
|
||||||
|
lifecycle: Lifecycle? = null
|
||||||
|
): List<AbstractPreferenceController> =
|
||||||
|
listOf(
|
||||||
|
AppLockNotificationPreferenceController(
|
||||||
|
context,
|
||||||
|
lifecycle,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
/*
|
||||||
|
* 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.cherish.settings.security.applock
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.app.AppLockManager
|
||||||
|
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.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
|
||||||
|
import com.android.settings.core.BasePreferenceController
|
||||||
|
|
||||||
|
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<Intent>?
|
||||||
|
|
||||||
|
init {
|
||||||
|
lifecycle?.addObserver(this)
|
||||||
|
securityPromptLauncher = host?.registerForActivityResult(
|
||||||
|
StartActivityForResult()
|
||||||
|
) {
|
||||||
|
if (it?.resultCode == Activity.RESULT_OK) {
|
||||||
|
SubSettingLauncher(mContext)
|
||||||
|
.setDestination(AppLockSettingsFragment::class.qualifiedName)
|
||||||
|
.setSourceMetricsCategory(host.metricsCategory)
|
||||||
|
.setTransitionType(TransitionType.TRANSITION_SLIDE)
|
||||||
|
.addFlags(
|
||||||
|
Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS or
|
||||||
|
Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
|
)
|
||||||
|
.launch()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
if (getAvailabilityStatus() == AVAILABLE) {
|
||||||
|
preference.setEnabled(true)
|
||||||
|
preference.summary = getSummaryForListSize(appLockManager.getPackages().size)
|
||||||
|
} else {
|
||||||
|
preference.setEnabled(false)
|
||||||
|
preference.summary = mContext.getString(R.string.disabled_because_no_backup_security)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getSummaryForListSize(size: Int): CharSequence? =
|
||||||
|
when {
|
||||||
|
size == 0 -> null
|
||||||
|
size == 1 -> mContext.getString(R.string.app_lock_summary_singular)
|
||||||
|
else -> mContext.getString(R.string.app_lock_summary_plural, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun handlePreferenceTreeClick(preference: Preference): Boolean {
|
||||||
|
if (preference.key == preferenceKey && securityPromptLauncher != null) {
|
||||||
|
securityPromptLauncher.launch(
|
||||||
|
ConfirmDeviceCredentialActivity.createIntent(
|
||||||
|
mContext.getString(R.string.app_lock_authentication_dialog_title),
|
||||||
|
null /* details */,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return super.handlePreferenceTreeClick(preference)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.cherish.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
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user