Skip to content

Commit 4c69ae6

Browse files
committed
feat: add close button to lock screen overlays and improve exit handling
1 parent f6ab252 commit 4c69ae6

4 files changed

Lines changed: 294 additions & 276 deletions

File tree

app/src/main/java/dev/pranav/applock/features/lockscreen/ui/LockScreenOverlayManager.kt

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,11 @@ class LockScreenOverlayManager(private val context: Context):
102102
if (lockType == PreferencesRepository.LOCK_TYPE_PATTERN) {
103103
PatternLockScreen(
104104
fromMainActivity = false,
105+
showCloseButton = true,
106+
onClose = {
107+
onExit()
108+
removeOverlay()
109+
},
105110
lockedAppName = appName,
106111
triggeringPackageName = triggeringPackageName,
107112
onPatternAttempt = onPatternAttemptCallback
@@ -110,6 +115,11 @@ class LockScreenOverlayManager(private val context: Context):
110115
PasswordOverlayScreen(
111116
showBiometricButton = appLockRepository.isBiometricAuthEnabled(),
112117
fromMainActivity = false,
118+
showCloseButton = true,
119+
onClose = {
120+
onExit()
121+
removeOverlay()
122+
},
113123
lockedAppName = appName,
114124
triggeringPackageName = triggeringPackageName,
115125
onAuthSuccess = {
@@ -142,8 +152,8 @@ class LockScreenOverlayManager(private val context: Context):
142152
WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY,
143153
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or
144154
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or
145-
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS or
146-
WindowManager.LayoutParams.FLAG_SECURE,
155+
WindowManager.LayoutParams.FLAG_SECURE or
156+
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
147157
PixelFormat.TRANSLUCENT
148158
).apply {
149159
// Respect brightness setting

app/src/main/java/dev/pranav/applock/features/lockscreen/ui/PasswordOverlayScreen.kt

Lines changed: 116 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import androidx.compose.foundation.shape.CircleShape
2626
import androidx.compose.foundation.shape.RoundedCornerShape
2727
import androidx.compose.material.icons.Icons
2828
import androidx.compose.material.icons.automirrored.rounded.KeyboardArrowRight
29+
import androidx.compose.material.icons.filled.Close
2930
import androidx.compose.material3.*
3031
import androidx.compose.runtime.*
3132
import androidx.compose.ui.Alignment
@@ -213,7 +214,9 @@ class PasswordOverlayActivity: FragmentActivity() {
213214
onAuthSuccess = {},
214215
lockedAppName = appName,
215216
triggeringPackageName = triggeringPackageNameFromIntent,
216-
onPinAttempt = onPinAttemptCallback
217+
onPinAttempt = onPinAttemptCallback,
218+
showCloseButton = true,
219+
onClose = { finish() }
217220
)
218221

219222
BackHandler { }
@@ -336,6 +339,8 @@ fun PasswordOverlayScreen(
336339
modifier: Modifier = Modifier,
337340
showBiometricButton: Boolean = false,
338341
fromMainActivity: Boolean = false,
342+
showCloseButton: Boolean = false,
343+
onClose: () -> Unit = {},
339344
onBiometricAuth: () -> Unit = {},
340345
onAuthSuccess: () -> Unit,
341346
lockedAppName: String? = null,
@@ -356,36 +361,125 @@ fun PasswordOverlayScreen(
356361
modifier = modifier.fillMaxSize(),
357362
color = MaterialTheme.colorScheme.surfaceContainer
358363
) {
359-
val passwordState = remember { mutableStateOf("") }
360-
var showError by remember { mutableStateOf(false) }
361-
val minLength = 4
364+
Box(modifier = Modifier.fillMaxSize()) {
365+
if (showCloseButton) {
366+
IconButton(
367+
onClick = onClose,
368+
modifier = Modifier
369+
.statusBarsPadding()
370+
.padding(start = 8.dp, top = 8.dp)
371+
.align(Alignment.TopStart)
372+
) {
373+
Icon(
374+
imageVector = Icons.Default.Close,
375+
contentDescription = "Close",
376+
tint = MaterialTheme.colorScheme.onSurface
377+
)
378+
}
379+
}
362380

363-
if (isLandscape) {
364-
Row(
365-
modifier = Modifier
366-
.fillMaxSize()
367-
.padding(horizontal = 16.dp, vertical = 16.dp),
368-
horizontalArrangement = Arrangement.SpaceEvenly,
369-
verticalAlignment = Alignment.CenterVertically
370-
) {
381+
val passwordState = remember { mutableStateOf("") }
382+
var showError by remember { mutableStateOf(false) }
383+
val minLength = 4
384+
385+
if (isLandscape) {
386+
Row(
387+
modifier = Modifier
388+
.fillMaxSize()
389+
.padding(horizontal = 16.dp, vertical = 16.dp),
390+
horizontalArrangement = Arrangement.SpaceEvenly,
391+
verticalAlignment = Alignment.CenterVertically
392+
) {
393+
Column(
394+
modifier = Modifier
395+
.weight(1f)
396+
.padding(end = 32.dp),
397+
horizontalAlignment = Alignment.CenterHorizontally,
398+
verticalArrangement = Arrangement.Center
399+
) {
400+
Text(
401+
text = if (!fromMainActivity && !lockedAppName.isNullOrEmpty())
402+
"Continue to $lockedAppName"
403+
else
404+
stringResource(R.string.enter_password_to_continue),
405+
style = MaterialTheme.typography.titleLarge,
406+
textAlign = TextAlign.Center
407+
)
408+
409+
// if (!fromMainActivity && !triggeringPackageName.isNullOrEmpty()) {
410+
// Spacer(modifier = Modifier.height(8.dp))
411+
// Text(
412+
// text = triggeringPackageName,
413+
// style = MaterialTheme.typography.labelSmall,
414+
// color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.6f),
415+
// textAlign = TextAlign.Center
416+
// )
417+
// }
418+
419+
Spacer(modifier = Modifier.height(16.dp))
420+
421+
PasswordIndicators(
422+
passwordLength = passwordState.value.length,
423+
)
424+
425+
if (showError) {
426+
Spacer(modifier = Modifier.height(8.dp))
427+
Text(
428+
text = stringResource(R.string.incorrect_pin_try_again),
429+
color = MaterialTheme.colorScheme.error,
430+
style = MaterialTheme.typography.bodyMedium,
431+
)
432+
}
433+
}
434+
435+
Column(
436+
horizontalAlignment = Alignment.CenterHorizontally,
437+
verticalArrangement = Arrangement.Center
438+
) {
439+
KeypadSection(
440+
passwordState = passwordState,
441+
minLength = minLength,
442+
showBiometricButton = showBiometricButton,
443+
fromMainActivity = fromMainActivity,
444+
onBiometricAuth = onBiometricAuth,
445+
onAuthSuccess = onAuthSuccess,
446+
onPinAttempt = onPinAttempt,
447+
onPasswordChange = {
448+
showError = false
449+
450+
if (appLockRepository.isAutoUnlockEnabled()) {
451+
onPinAttempt?.invoke(passwordState.value)
452+
}
453+
},
454+
onPinIncorrect = { showError = true }
455+
)
456+
}
457+
}
458+
} else {
371459
Column(
372460
modifier = Modifier
373-
.weight(1f)
374-
.padding(end = 32.dp),
461+
.fillMaxSize()
462+
.padding(vertical = if (fromMainActivity) 24.dp else 12.dp),
375463
horizontalAlignment = Alignment.CenterHorizontally,
376-
verticalArrangement = Arrangement.Center
464+
verticalArrangement = Arrangement.spacedBy(24.dp)
377465
) {
466+
// Dynamic spacer for small screens
467+
val topSpacerHeight = if (screenHeightDp < 600.dp) 12.dp else 48.dp
468+
Spacer(modifier = Modifier.height(topSpacerHeight))
469+
378470
Text(
379471
text = if (!fromMainActivity && !lockedAppName.isNullOrEmpty())
380472
"Continue to $lockedAppName"
381473
else
382474
stringResource(R.string.enter_password_to_continue),
383-
style = MaterialTheme.typography.titleLarge,
475+
style = if (!fromMainActivity && !lockedAppName.isNullOrEmpty())
476+
MaterialTheme.typography.titleLargeEmphasized
477+
else
478+
MaterialTheme.typography.headlineMediumEmphasized,
384479
textAlign = TextAlign.Center
385480
)
386481

387482
// if (!fromMainActivity && !triggeringPackageName.isNullOrEmpty()) {
388-
// Spacer(modifier = Modifier.height(8.dp))
389483
// Text(
390484
// text = triggeringPackageName,
391485
// style = MaterialTheme.typography.labelSmall,
@@ -401,19 +495,16 @@ fun PasswordOverlayScreen(
401495
)
402496

403497
if (showError) {
404-
Spacer(modifier = Modifier.height(8.dp))
405498
Text(
406499
text = stringResource(R.string.incorrect_pin_try_again),
407500
color = MaterialTheme.colorScheme.error,
408501
style = MaterialTheme.typography.bodyMedium,
502+
modifier = Modifier.padding(top = 8.dp)
409503
)
410504
}
411-
}
412505

413-
Column(
414-
horizontalAlignment = Alignment.CenterHorizontally,
415-
verticalArrangement = Arrangement.Center
416-
) {
506+
Spacer(modifier = Modifier.weight(1f))
507+
417508
KeypadSection(
418509
passwordState = passwordState,
419510
minLength = minLength,
@@ -433,74 +524,6 @@ fun PasswordOverlayScreen(
433524
)
434525
}
435526
}
436-
} else {
437-
Column(
438-
modifier = Modifier
439-
.fillMaxSize()
440-
.padding(vertical = if (fromMainActivity) 24.dp else 12.dp),
441-
horizontalAlignment = Alignment.CenterHorizontally,
442-
verticalArrangement = Arrangement.spacedBy(24.dp)
443-
) {
444-
// Dynamic spacer for small screens
445-
val topSpacerHeight = if (screenHeightDp < 600.dp) 12.dp else 48.dp
446-
Spacer(modifier = Modifier.height(topSpacerHeight))
447-
448-
Text(
449-
text = if (!fromMainActivity && !lockedAppName.isNullOrEmpty())
450-
"Continue to $lockedAppName"
451-
else
452-
stringResource(R.string.enter_password_to_continue),
453-
style = if (!fromMainActivity && !lockedAppName.isNullOrEmpty())
454-
MaterialTheme.typography.titleLargeEmphasized
455-
else
456-
MaterialTheme.typography.headlineMediumEmphasized,
457-
textAlign = TextAlign.Center
458-
)
459-
460-
// if (!fromMainActivity && !triggeringPackageName.isNullOrEmpty()) {
461-
// Text(
462-
// text = triggeringPackageName,
463-
// style = MaterialTheme.typography.labelSmall,
464-
// color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.6f),
465-
// textAlign = TextAlign.Center
466-
// )
467-
// }
468-
469-
Spacer(modifier = Modifier.height(16.dp))
470-
471-
PasswordIndicators(
472-
passwordLength = passwordState.value.length,
473-
)
474-
475-
if (showError) {
476-
Text(
477-
text = stringResource(R.string.incorrect_pin_try_again),
478-
color = MaterialTheme.colorScheme.error,
479-
style = MaterialTheme.typography.bodyMedium,
480-
modifier = Modifier.padding(top = 8.dp)
481-
)
482-
}
483-
484-
Spacer(modifier = Modifier.weight(1f))
485-
486-
KeypadSection(
487-
passwordState = passwordState,
488-
minLength = minLength,
489-
showBiometricButton = showBiometricButton,
490-
fromMainActivity = fromMainActivity,
491-
onBiometricAuth = onBiometricAuth,
492-
onAuthSuccess = onAuthSuccess,
493-
onPinAttempt = onPinAttempt,
494-
onPasswordChange = {
495-
showError = false
496-
497-
if (appLockRepository.isAutoUnlockEnabled()) {
498-
onPinAttempt?.invoke(passwordState.value)
499-
}
500-
},
501-
onPinIncorrect = { showError = true }
502-
)
503-
}
504527
}
505528
}
506529
}
@@ -516,6 +539,8 @@ fun PasswordIndicators(
516539
val screenWidth = windowInfo.containerSize.width
517540
val screenHeight = windowInfo.containerSize.height
518541
val screenWidthDp = configuration.screenWidthDp.dp
542+
val screenHeightDp = configuration.screenHeightDp.dp
543+
519544
val isLandscape = screenWidth > screenHeight
520545

521546
val indicatorSize = remember(screenWidthDp) {

0 commit comments

Comments
 (0)