@@ -2,15 +2,17 @@ package org.privacymatters.safespace
22
33import android.content.Context
44import android.content.Intent
5+ import android.content.SharedPreferences
56import android.os.Bundle
7+ import android.os.CountDownTimer
68import android.view.KeyEvent
79import android.widget.EditText
810import android.widget.ImageButton
11+ import android.widget.TextView
912import androidx.appcompat.app.AppCompatActivity
1013import androidx.biometric.BiometricManager
1114import androidx.biometric.BiometricPrompt
1215import androidx.core.content.ContextCompat
13- import androidx.core.text.isDigitsOnly
1416import com.google.android.material.dialog.MaterialAlertDialogBuilder
1517import org.privacymatters.safespace.lib.utils.Constants
1618import org.privacymatters.safespace.lib.utils.EncPref
@@ -19,6 +21,7 @@ import org.privacymatters.safespace.lib.utils.SetTheme
1921import org.privacymatters.safespace.main.MainActivity
2022import java.util.concurrent.Executor
2123
24+
2225/*
2326
2427Possible authentication scenarios
@@ -38,14 +41,20 @@ class AuthActivity : AppCompatActivity() {
3841 private lateinit var promptInfo: BiometricPrompt .PromptInfo
3942 private lateinit var authTouch: ImageButton
4043 private lateinit var pinField: EditText
44+ private lateinit var sharedPref: SharedPreferences
4145 private var confirmCounter = 0
4246 private var confirmPIN = " -1"
4347 private var isHardPinSet = false
48+ private var attemptCount = 0
49+ private lateinit var loginBlockMsg: TextView
50+ private lateinit var loginBlockTimer: TextView
51+ private var loginBlockedTime: Long = Constants .DEF_NUM_FLAG
52+
4453
4554 override fun onCreate (savedInstanceState : Bundle ? ) {
4655
4756 // load theme from preferences
48- val sharedPref = getSharedPreferences(Constants .SHARED_PREF_FILE , Context .MODE_PRIVATE )
57+ sharedPref = getSharedPreferences(Constants .SHARED_PREF_FILE , Context .MODE_PRIVATE )
4958
5059 if (! sharedPref.getBoolean(Constants .USE_BIOMETRIC , false )) {
5160 biometricPossible = false
@@ -64,7 +73,21 @@ class AuthActivity : AppCompatActivity() {
6473 setContentView(R .layout.activity_auth)
6574
6675 authTouch = findViewById(R .id.fingerprint)
67- pinField = findViewById(R .id.editTextNumberPassword)
76+ pinField = findViewById(R .id.editTextPassword)
77+
78+ loginBlockMsg = findViewById(R .id.loginBlockMsg)
79+ loginBlockTimer = findViewById(R .id.loginBlockTimer)
80+ loginBlockedTime =
81+ sharedPref.getLong(Constants .TIME_TO_UNLOCK_START , Constants .DEF_NUM_FLAG )
82+
83+ // check if user is blocked from login
84+ val isLoginUnlocked = checkLoginUnlocked() // returns a pair(is blocked?, for how long)
85+
86+ // if user is blocked, start a countdown
87+ if (! isLoginUnlocked.first) {
88+ setLoginBlockMsg()
89+ countDown(isLoginUnlocked.second).start()
90+ }
6891
6992 // Root Check
7093 if (! isPhoneRooted(pinField.context)) {
@@ -87,12 +110,14 @@ class AuthActivity : AppCompatActivity() {
87110 }
88111
89112 BiometricManager .BIOMETRIC_SUCCESS -> {
90- if (isHardPinSet && biometricPossible) initiateBiometricAuthentication()
113+ if (isHardPinSet && biometricPossible && isLoginUnlocked.first ) initiateBiometricAuthentication()
91114 }
92115 }
93116
94117 authTouch.setOnClickListener {
95- if (biometricPossible && isHardPinSet) biometricPrompt.authenticate(promptInfo)
118+ if (biometricPossible && isHardPinSet) biometricPrompt.authenticate(
119+ promptInfo
120+ )
96121 }
97122
98123 }
@@ -116,23 +141,154 @@ class AuthActivity : AppCompatActivity() {
116141 }
117142
118143 private fun authenticateUsingHardPin () {
119-
120- if (pinField.text.toString().isDigitsOnly() &&
121- pinField.text.toString() == EncPref .getString(
144+ if (pinField.text.toString() == EncPref .getString(
122145 Constants .HARD_PIN ,
123146 applicationContext
124147 )
125148 ) {
149+ attemptCount = 0
150+
151+ blockBiometric(false , 0 )
152+
126153 val intent = Intent (applicationContext, MainActivity ::class .java)
127154 intent.addFlags(Intent .FLAG_ACTIVITY_CLEAR_TOP or Intent .FLAG_ACTIVITY_NEW_TASK )
128155 startActivity(intent)
129156 finish()
130157 } else {
131158 pinField.error = getString(R .string.pin_error5)
132159 pinField.setText(" " )
160+ attemptCount + = 1
161+ when (attemptCount) {
162+ 10 -> {
163+ blockLogin(300000 ) // 5 minutes
164+ countDown(300000 ).start()
165+ }
166+
167+ 12 -> {
168+ blockLogin(600000 ) // 10 minutes
169+ countDown(600000 ).start()
170+ }
171+
172+ 14 -> {
173+ blockLogin(1800000 ) // 30 minutes
174+ countDown(1800000 ).start()
175+ }
176+
177+ 16 -> {
178+ blockLogin(3600000 ) // 1 Hour
179+ countDown(3600000 ).start()
180+ }
181+
182+ 18 -> {
183+ blockLogin(10800000 ) // 3 hours
184+ countDown(10800000 ).start()
185+ }
186+
187+ 20 -> {
188+ blockLogin(86400000 ) // 1 Day
189+ countDown(86400000 ).start()
190+ attemptCount = 0
191+ }
192+ }
193+
194+ }
195+ }
196+
197+ private fun blockBiometric (flag : Boolean , blockDuration : Long ) {
198+ if (flag){
199+
200+ val biometricBackup = sharedPref.getBoolean(Constants .USE_BIOMETRIC , false )
201+
202+ sharedPref.edit()
203+ .putLong(Constants .TIME_TO_UNLOCK_START , blockDuration + System .currentTimeMillis())
204+ .putBoolean(
205+ Constants .USE_BIOMETRIC_BCKP ,
206+ biometricBackup
207+ )
208+ .putBoolean(Constants .USE_BIOMETRIC , false )
209+ .apply ()
210+ }else {
211+ val biometricRestore = sharedPref.getBoolean(Constants .USE_BIOMETRIC_BCKP , false )
212+
213+ sharedPref.edit().putBoolean(
214+ Constants .USE_BIOMETRIC ,
215+ biometricRestore
216+ ).apply ()
133217 }
134218 }
135219
220+ private fun blockLogin (blockDuration : Long ) {
221+ setLoginBlockMsg()
222+
223+ biometricPossible = false
224+
225+ blockBiometric(true , blockDuration)
226+ }
227+
228+ private fun unlockLogin () {
229+ removeLoginBlockMsg()
230+ sharedPref.edit()
231+ .putLong(Constants .TIME_TO_UNLOCK_DURATION , Constants .DEF_NUM_FLAG )
232+ .putLong(Constants .TIME_TO_UNLOCK_START , Constants .DEF_NUM_FLAG )
233+ .apply ()
234+ }
235+
236+ private fun checkLoginUnlocked (): Pair <Boolean , Long > {
237+
238+ if (System .currentTimeMillis() > loginBlockedTime) {
239+ return Pair (true , - 1 )
240+ }
241+
242+ return Pair (false , loginBlockedTime - System .currentTimeMillis())
243+ }
244+
245+ private fun countDown (millisRemaining : Long ): CountDownTimer {
246+
247+ return object : CountDownTimer (millisRemaining, 1000 ) {
248+
249+ override fun onTick (millisUntilFinished : Long ) {
250+ val minutesUntilFinished = (millisUntilFinished / 60000 )
251+ val secondsUntilFinished = (millisUntilFinished % 60000 ) / 1000
252+
253+ var remainingTime = " "
254+
255+ remainingTime + = if (minutesUntilFinished < 10 ) {
256+ " 0$minutesUntilFinished "
257+ } else {
258+ minutesUntilFinished.toString()
259+ }
260+
261+ remainingTime + = " :"
262+
263+ remainingTime + = if (secondsUntilFinished < 10 ) {
264+ " 0$secondsUntilFinished "
265+ } else {
266+ secondsUntilFinished.toString()
267+ }
268+
269+ loginBlockTimer.text = remainingTime
270+ }
271+
272+ override fun onFinish () {
273+ unlockLogin()
274+ loginBlockTimer.text = " "
275+ }
276+ }
277+
278+ }
279+
280+ private fun setLoginBlockMsg () {
281+ loginBlockMsg.text = getString(R .string.pin_error6)
282+ pinField.isEnabled = false
283+ authTouch.isEnabled = false
284+ }
285+
286+ private fun removeLoginBlockMsg () {
287+ loginBlockMsg.text = " "
288+ pinField.isEnabled = true
289+ authTouch.isEnabled = true
290+ }
291+
136292 private fun setUpHardPin () {
137293
138294 pinField.hint = getString(R .string.set_pin_text)
@@ -144,15 +300,12 @@ class AuthActivity : AppCompatActivity() {
144300 } else {
145301
146302 if (confirmCounter == 0 ) {
147- if (pinField.text.toString().isDigitsOnly()) {
148- confirmCounter + = 1
149- pinField.hint = getString(R .string.confirm_pin_text)
150- confirmPIN = pinField.text.toString()
151- pinField.error = null
152- pinField.setText(" " )
153- } else {
154- pinField.error = getString(R .string.pin_error3)
155- }
303+
304+ confirmCounter + = 1
305+ pinField.hint = getString(R .string.confirm_pin_text)
306+ confirmPIN = pinField.text.toString()
307+ pinField.error = null
308+ pinField.setText(" " )
156309
157310 } else if (confirmCounter == 1 ) {
158311 if (confirmPIN != pinField.text.toString()) {
@@ -226,4 +379,5 @@ class AuthActivity : AppCompatActivity() {
226379 }
227380 return false
228381 }
382+
229383}
0 commit comments