Add opt-in device credential app lock

This commit is contained in:
ajp_anton
2026-07-23 10:00:12 +00:00
parent 53cc4eb529
commit 6280c8ef80
4 changed files with 41 additions and 1 deletions
@@ -1,6 +1,9 @@
package se.ajpanton.notificationlog package se.ajpanton.notificationlog
import android.os.Bundle import android.os.Bundle
import android.hardware.biometrics.BiometricPrompt
import android.hardware.biometrics.BiometricManager.Authenticators
import android.os.CancellationSignal
import android.content.res.Configuration import android.content.res.Configuration
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
@@ -19,11 +22,13 @@ import androidx.fragment.app.commit
import com.google.android.material.navigation.NavigationView import com.google.android.material.navigation.NavigationView
import se.ajpanton.notificationlog.databinding.ActivityMainBinding import se.ajpanton.notificationlog.databinding.ActivityMainBinding
import se.ajpanton.notificationlog.settings.LoggingType import se.ajpanton.notificationlog.settings.LoggingType
import se.ajpanton.notificationlog.settings.AppLockStore
class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener { class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {
private lateinit var binding: ActivityMainBinding private lateinit var binding: ActivityMainBinding
private lateinit var drawerToggle: ActionBarDrawerToggle private lateinit var drawerToggle: ActionBarDrawerToggle
private var permanentSidebar = false private var permanentSidebar = false
private var needsUnlock = true
private var currentItemId = R.id.nav_view_logs private var currentItemId = R.id.nav_view_logs
private var drawerNavigationBasePaddingLeft: Int? = null private var drawerNavigationBasePaddingLeft: Int? = null
private var permanentNavigationBasePaddingLeft: Int? = null private var permanentNavigationBasePaddingLeft: Int? = null
@@ -77,6 +82,27 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
super.onSaveInstanceState(outState) super.onSaveInstanceState(outState)
} }
override fun onResume() {
super.onResume()
if (needsUnlock && AppLockStore(this).enabled) requestUnlock()
}
override fun onStop() {
super.onStop()
if (!isChangingConfigurations) needsUnlock = true
}
private fun requestUnlock() {
BiometricPrompt.Builder(this)
.setTitle("Unlock Notification Log")
.setAllowedAuthenticators(Authenticators.BIOMETRIC_STRONG or Authenticators.DEVICE_CREDENTIAL)
.build()
.authenticate(CancellationSignal(), mainExecutor, object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult?) { needsUnlock = false }
override fun onAuthenticationError(errorCode: Int, errString: CharSequence?) { finishAndRemoveTask() }
})
}
override fun onNavigationItemSelected(item: MenuItem): Boolean { override fun onNavigationItemSelected(item: MenuItem): Boolean {
if (item.itemId == R.id.nav_settings_header) { if (item.itemId == R.id.nav_settings_header) {
return false return false
@@ -11,6 +11,7 @@ import se.ajpanton.notificationlog.settings.LogViewSettingsStore
import se.ajpanton.notificationlog.settings.TimestampZone import se.ajpanton.notificationlog.settings.TimestampZone
import se.ajpanton.notificationlog.settings.LoggingType import se.ajpanton.notificationlog.settings.LoggingType
import se.ajpanton.notificationlog.settings.LoggingRuleStore import se.ajpanton.notificationlog.settings.LoggingRuleStore
import se.ajpanton.notificationlog.settings.AppLockStore
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
class SettingsFragment : Fragment(R.layout.fragment_settings) { class SettingsFragment : Fragment(R.layout.fragment_settings) {
@@ -46,6 +47,9 @@ class SettingsFragment : Fragment(R.layout.fragment_settings) {
} }
} }
binding!!.copyEventSettings.setOnClickListener { chooseCopySource() } binding!!.copyEventSettings.setOnClickListener { chooseCopySource() }
val lockStore = AppLockStore(requireContext())
binding!!.appLock.isChecked = lockStore.enabled
binding!!.appLock.setOnCheckedChangeListener { _, enabled -> lockStore.enabled = enabled }
} }
override fun onDestroyView() { binding = null; super.onDestroyView() } override fun onDestroyView() { binding = null; super.onDestroyView() }
@@ -0,0 +1,10 @@
package se.ajpanton.notificationlog.settings
import android.content.Context
class AppLockStore(context: Context) {
private val prefs = context.getSharedPreferences("app-lock", Context.MODE_PRIVATE)
var enabled: Boolean
get() = prefs.getBoolean("enabled", false)
set(value) = prefs.edit().putBoolean("enabled", value).apply()
}
@@ -1,2 +1,2 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"><LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="@dimen/page_padding"><TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Settings" android:textAppearance="?attr/textAppearanceHeadline5"/><TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="24dp" android:text="Show on each log line"/><LinearLayout android:id="@+id/field_toggles" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"/><TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="24dp" android:text="Timestamp timezone"/><Spinner android:id="@+id/timezone" android:layout_width="match_parent" android:layout_height="wrap_content"/><Button android:id="@+id/copy_event_settings" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="24dp" android:text="Copy settings"/></LinearLayout></ScrollView> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"><LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="@dimen/page_padding"><TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Settings" android:textAppearance="?attr/textAppearanceHeadline5"/><TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="24dp" android:text="Show on each log line"/><LinearLayout android:id="@+id/field_toggles" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"/><TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="24dp" android:text="Timestamp timezone"/><Spinner android:id="@+id/timezone" android:layout_width="match_parent" android:layout_height="wrap_content"/><com.google.android.material.switchmaterial.SwitchMaterial android:id="@+id/app_lock" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="24dp" android:text="Lock app with phone unlock"/><Button android:id="@+id/copy_event_settings" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="24dp" android:text="Copy settings"/></LinearLayout></ScrollView>