Skip to content

Commit 1a2d5a7

Browse files
author
aashishksahu
authored
Merge pull request #55 from aashishksahu/issue-47
Issue 47
2 parents 200a179 + 41f1b92 commit 1a2d5a7

24 files changed

Lines changed: 373 additions & 65 deletions

File tree

app/build.gradle

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ android {
1616

1717
minSdk 29
1818
targetSdk 34
19-
versionCode 20
20-
versionName "1.4.6"
19+
versionCode 22
20+
versionName "1.4.8"
2121

2222
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
2323
}
@@ -45,9 +45,9 @@ dependencies {
4545
implementation "androidx.security:security-crypto:1.0.0"
4646

4747
// For Audio/Video
48-
implementation 'androidx.media3:media3-exoplayer:1.3.0'
49-
implementation 'androidx.media3:media3-ui:1.3.0'
50-
implementation 'androidx.media3:media3-common:1.3.0'
48+
implementation 'androidx.media3:media3-exoplayer:1.3.1'
49+
implementation 'androidx.media3:media3-ui:1.3.1'
50+
implementation 'androidx.media3:media3-common:1.3.1'
5151

5252
// For Pictures
5353
implementation 'com.github.bumptech.glide:glide:4.15.1'

app/release/app-release.apk

11.3 KB
Binary file not shown.
4.14 KB
Binary file not shown.
4.07 KB
Binary file not shown.

app/release/output-metadata.json

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,27 @@
1111
"type": "SINGLE",
1212
"filters": [],
1313
"attributes": [],
14-
"versionCode": 20,
15-
"versionName": "1.4.6",
14+
"versionCode": 22,
15+
"versionName": "1.4.8",
1616
"outputFile": "app-release.apk"
1717
}
1818
],
19-
"elementType": "File"
19+
"elementType": "File",
20+
"baselineProfiles": [
21+
{
22+
"minApi": 28,
23+
"maxApi": 30,
24+
"baselineProfiles": [
25+
"baselineProfiles/1/app-release.dm"
26+
]
27+
},
28+
{
29+
"minApi": 31,
30+
"maxApi": 2147483647,
31+
"baselineProfiles": [
32+
"baselineProfiles/0/app-release.dm"
33+
]
34+
}
35+
],
36+
"minSdkVersionForDexing": 29
2037
}

app/src/main/AndroidManifest.xml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,17 @@
5353
android:name=".main.MainActivity"
5454
android:configChanges="keyboardHidden|screenSize"
5555
android:exported="false" />
56+
57+
<provider
58+
android:name="androidx.core.content.FileProvider"
59+
android:authorities="${applicationId}.provider"
60+
android:exported="false"
61+
android:grantUriPermissions="true">
62+
<meta-data
63+
android:name="android.support.FILE_PROVIDER_PATHS"
64+
android:resource="@xml/provider_paths" />
65+
</provider>
66+
5667
</application>
5768

5869
</manifest>

app/src/main/java/org/privacymatters/safespace/AuthActivity.kt

Lines changed: 171 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,17 @@ package org.privacymatters.safespace
22

33
import android.content.Context
44
import android.content.Intent
5+
import android.content.SharedPreferences
56
import android.os.Bundle
7+
import android.os.CountDownTimer
68
import android.view.KeyEvent
79
import android.widget.EditText
810
import android.widget.ImageButton
11+
import android.widget.TextView
912
import androidx.appcompat.app.AppCompatActivity
1013
import androidx.biometric.BiometricManager
1114
import androidx.biometric.BiometricPrompt
1215
import androidx.core.content.ContextCompat
13-
import androidx.core.text.isDigitsOnly
1416
import com.google.android.material.dialog.MaterialAlertDialogBuilder
1517
import org.privacymatters.safespace.lib.utils.Constants
1618
import org.privacymatters.safespace.lib.utils.EncPref
@@ -19,6 +21,7 @@ import org.privacymatters.safespace.lib.utils.SetTheme
1921
import org.privacymatters.safespace.main.MainActivity
2022
import java.util.concurrent.Executor
2123

24+
2225
/*
2326
2427
Possible 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
}

app/src/main/java/org/privacymatters/safespace/lib/fileManager/Operations.kt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@ package org.privacymatters.safespace.lib.fileManager
22

33
import android.app.Application
44
import android.content.Context
5+
import android.content.Intent
56
import android.net.Uri
67
import android.os.FileUtils
78
import android.provider.OpenableColumns
9+
import androidx.core.content.FileProvider
810
import androidx.documentfile.provider.DocumentFile
911
import kotlinx.coroutines.Dispatchers
1012
import kotlinx.coroutines.withContext
13+
import org.privacymatters.safespace.R
1114
import org.privacymatters.safespace.lib.utils.Constants
1215
import java.io.BufferedOutputStream
1316
import java.io.File
@@ -343,6 +346,27 @@ class Operations(private val application: Application) {
343346
return 1
344347
}
345348

349+
fun shareFile(file: FileItem, internalPath: String) {
350+
val fileToShare = File(joinPath(getFilesDir(), internalPath, file.name))
351+
val fileUri = FileProvider.getUriForFile(
352+
application,
353+
application.applicationContext.packageName + ".provider",
354+
fileToShare
355+
)
356+
357+
val intent = Intent(Intent.ACTION_SEND)
358+
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
359+
intent.type = application.contentResolver.getType(fileUri)
360+
intent.putExtra(Intent.EXTRA_STREAM, fileUri)
361+
362+
val chooser = Intent.createChooser(
363+
intent,
364+
application.getString(R.string.share_chooser_title)
365+
)
366+
chooser.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
367+
application.startActivity(chooser)
368+
}
369+
346370
fun createTextNote(noteName: String): String {
347371

348372
val filePath = joinPath(getFilesDir(), getInternalPath(), noteName)

0 commit comments

Comments
 (0)