1
1
package me.lucky.sentry
2
2
3
- import android.content.pm.PackageManager
3
+ import android.content.SharedPreferences
4
4
import android.os.Build
5
5
import android.os.Bundle
6
- import android.view.View
7
6
import androidx.activity.result.contract.ActivityResultContracts
8
7
import androidx.appcompat.app.AppCompatActivity
8
+ import androidx.biometric.BiometricManager
9
+ import androidx.biometric.BiometricPrompt
10
+ import androidx.core.content.ContextCompat
9
11
import com.google.android.material.snackbar.Snackbar
10
12
11
13
import me.lucky.sentry.databinding.ActivityMainBinding
12
14
13
15
class MainActivity : AppCompatActivity () {
14
16
private lateinit var binding: ActivityMainBinding
15
- private lateinit var prefs: PreferencesProxy
17
+ private lateinit var prefs: Preferences
18
+ private lateinit var prefsdb: Preferences
16
19
private lateinit var admin: DeviceAdminManager
17
20
18
21
private val registerForDeviceAdmin =
19
22
registerForActivityResult(ActivityResultContracts .StartActivityForResult ()) {
20
- if (it.resultCode != RESULT_OK ) binding.toggle.isChecked = false else setOn()
23
+ if (it.resultCode != RESULT_OK ) setOff() else setOn()
21
24
}
22
25
26
+ private val prefsListener = SharedPreferences .OnSharedPreferenceChangeListener { _, key ->
27
+ prefs.copyTo(prefsdb, key)
28
+ }
29
+
23
30
override fun onCreate (savedInstanceState : Bundle ? ) {
24
31
super .onCreate(savedInstanceState)
25
32
binding = ActivityMainBinding .inflate(layoutInflater)
26
33
setContentView(binding.root)
27
34
init ()
35
+ if (initBiometric()) return
28
36
setup()
29
37
}
30
38
31
39
override fun onStart () {
32
40
super .onStart()
41
+ prefs.registerListener(prefsListener)
33
42
update()
34
43
}
35
44
45
+ override fun onStop () {
46
+ super .onStop()
47
+ prefs.unregisterListener(prefsListener)
48
+ }
49
+
50
+ private fun initBiometric (): Boolean {
51
+ val authenticators = BiometricManager .Authenticators .BIOMETRIC_STRONG or
52
+ BiometricManager .Authenticators .DEVICE_CREDENTIAL
53
+ when (BiometricManager
54
+ .from(this )
55
+ .canAuthenticate(authenticators))
56
+ {
57
+ BiometricManager .BIOMETRIC_SUCCESS -> {}
58
+ else -> return false
59
+ }
60
+ val executor = ContextCompat .getMainExecutor(this )
61
+ val prompt = BiometricPrompt (
62
+ this ,
63
+ executor,
64
+ object : BiometricPrompt .AuthenticationCallback ()
65
+ {
66
+ override fun onAuthenticationError (errorCode : Int , errString : CharSequence ) {
67
+ super .onAuthenticationError(errorCode, errString)
68
+ finishAndRemoveTask()
69
+ }
70
+
71
+ override fun onAuthenticationSucceeded (result : BiometricPrompt .AuthenticationResult ) {
72
+ super .onAuthenticationSucceeded(result)
73
+ setup()
74
+ }
75
+ })
76
+ prompt.authenticate(BiometricPrompt .PromptInfo .Builder ()
77
+ .setTitle(getString(R .string.biometric_title))
78
+ .setConfirmationRequired(false )
79
+ .setAllowedAuthenticators(authenticators)
80
+ .build())
81
+ return true
82
+ }
83
+
36
84
private fun init () {
37
- prefs = PreferencesProxy (this )
38
- prefs.clone()
85
+ prefs = Preferences (this )
86
+ prefsdb = Preferences (this , encrypted = false )
87
+ prefs.copyTo(prefsdb)
39
88
admin = DeviceAdminManager (this )
40
- if (prefs.isEnabled && prefs.maxFailedPasswordAttempts > 0 )
41
- try { admin.setMaximumFailedPasswordsForWipe(0 ) } catch (exc: SecurityException ) {}
42
- if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .Q &&
43
- ! packageManager.hasSystemFeature(PackageManager .FEATURE_SECURE_LOCK_SCREEN ))
44
- hideSecureLockScreenRequired()
45
- if (Build .VERSION .SDK_INT < Build .VERSION_CODES .S || ! admin.canUsbDataSignalingBeDisabled())
46
- hideUsbDataSignaling()
89
+ if (Build .VERSION .SDK_INT < Build .VERSION_CODES .S ||
90
+ ! admin.canUsbDataSignalingBeDisabled() ||
91
+ ! admin.isDeviceOwner())
92
+ disableUsbDataSignaling()
47
93
binding.apply {
48
94
maxFailedPasswordAttempts.value = prefs.maxFailedPasswordAttempts.toFloat()
49
95
usbDataSignaling.isChecked = isUsbDataSignalingEnabled()
50
96
toggle.isChecked = prefs.isEnabled
51
97
}
52
98
}
53
99
54
- private fun setup () {
55
- binding.apply {
56
- maxFailedPasswordAttempts.addOnChangeListener { _, value, _ ->
57
- prefs.maxFailedPasswordAttempts = value.toInt()
58
- }
59
- if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .S )
60
- usbDataSignaling.setOnCheckedChangeListener { _, isChecked ->
61
- try {
62
- admin.setUsbDataSignalingEnabled(isChecked)
63
- } catch (exc: Exception ) {
64
- Snackbar .make(
65
- usbDataSignaling,
66
- R .string.usb_data_signaling_change_failed_popup,
67
- Snackbar .LENGTH_SHORT ,
68
- ).show()
69
- usbDataSignaling.isChecked = ! isChecked
70
- }
100
+ private fun setup () = binding.apply {
101
+ maxFailedPasswordAttempts.addOnChangeListener { _, value, _ ->
102
+ prefs.maxFailedPasswordAttempts = value.toInt()
103
+ }
104
+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .S )
105
+ usbDataSignaling.setOnCheckedChangeListener { _, isChecked ->
106
+ try { admin.setUsbDataSignalingEnabled(isChecked) } catch (exc: Exception ) {
107
+ Snackbar .make(
108
+ usbDataSignaling,
109
+ R .string.usb_data_signaling_change_failed_popup,
110
+ Snackbar .LENGTH_SHORT ,
111
+ ).show()
112
+ usbDataSignaling.isChecked = ! isChecked
71
113
}
72
- toggle.setOnCheckedChangeListener { _, isChecked ->
73
- if (isChecked) requestAdmin() else setOff()
74
114
}
115
+ toggle.setOnCheckedChangeListener { _, isChecked ->
116
+ if (isChecked) requestAdmin() else setOff()
75
117
}
76
118
}
77
119
78
- private fun hideSecureLockScreenRequired () {
79
- binding.apply {
80
- maxFailedPasswordAttempts.visibility = View .GONE
81
- maxFailedPasswordAttemptsDescription.visibility = View .GONE
82
- space.visibility = View .GONE
83
- }
84
- }
85
-
86
- private fun hideUsbDataSignaling () {
87
- binding.apply {
88
- usbDataSignaling.visibility = View .GONE
89
- usbDataSignalingDescription.visibility = View .GONE
90
- }
91
- }
120
+ private fun disableUsbDataSignaling () { binding.usbDataSignaling.isEnabled = false }
92
121
93
122
private fun setOn () {
94
123
prefs.isEnabled = true
124
+ binding.toggle.isChecked = true
95
125
}
96
126
97
127
private fun setOff () {
98
128
prefs.isEnabled = false
99
129
try { admin.remove() } catch (exc: SecurityException ) {}
130
+ binding.toggle.isChecked = false
100
131
}
101
132
102
- private fun update () {
103
- binding.usbDataSignaling.isChecked = isUsbDataSignalingEnabled()
104
- if (prefs.isEnabled && ! admin.isActive())
105
- Snackbar .make(
106
- binding.toggle,
107
- R .string.service_unavailable_popup,
108
- Snackbar .LENGTH_SHORT ,
109
- ).show()
110
- }
133
+ private fun update () { binding.usbDataSignaling.isChecked = isUsbDataSignalingEnabled() }
111
134
112
135
private fun isUsbDataSignalingEnabled () = if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .S )
113
136
admin.isUsbDataSignalingEnabled() else true
114
137
115
138
private fun requestAdmin () = registerForDeviceAdmin.launch(admin.makeRequestIntent())
116
- }
139
+ }
0 commit comments