From 0f52616d210ee6c0f86c5d64986b66dcf3508024 Mon Sep 17 00:00:00 2001 From: Ray <154766448+damontecres@users.noreply.github.com> Date: Mon, 19 Jan 2026 11:39:29 -0500 Subject: [PATCH] Add user PIN protection (#719) ## Description Adds (back) the ability to set a per-user PIN. This is required to switch to the user. If the user has a PIN, this overrides and ignores the "Sign in automatically" setting for that user. The PIN consists of any combination of D-Pad directional buttons. If you forget the PIN, you can click the button to login via the server instead (ie QuickConnect or username/password) which will also remove the PIN so you can set a new one if desired. ### Related issues Closes #268 Replaces #398 Related to #321 & #356 ### Screenshots ![enter_pin Large](https://github.com/user-attachments/assets/aa25364d-1266-410d-846e-8dc32c56a794) --- .../damontecres/wholphin/MainActivity.kt | 24 +++++++++++-------- .../wholphin/data/ServerRepository.kt | 2 -- .../wholphin/preferences/AppPreference.kt | 3 +-- .../wholphin/ui/setup/SwitchUserContent.kt | 12 ++++------ 4 files changed, 20 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/com/github/damontecres/wholphin/MainActivity.kt b/app/src/main/java/com/github/damontecres/wholphin/MainActivity.kt index fb3e88ed..dace5d60 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/MainActivity.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/MainActivity.kt @@ -3,6 +3,7 @@ package com.github.damontecres.wholphin import android.content.Intent import android.content.res.Configuration import android.os.Bundle +import android.view.WindowManager import androidx.activity.compose.setContent import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity @@ -138,6 +139,16 @@ class MainActivity : AppCompatActivity() { window.attributes = attrs.apply { preferredDisplayModeId = modeId } } } + viewModel.serverRepository.currentUser.observe(this) { user -> + if (user?.hasPin == true) { + window?.setFlags( + WindowManager.LayoutParams.FLAG_SECURE, + WindowManager.LayoutParams.FLAG_SECURE, + ) + } else { + window?.clearFlags(WindowManager.LayoutParams.FLAG_SECURE) + } + } viewModel.appStart() val requestedDestination = this.intent?.let(::extractDestination) setContent { @@ -292,14 +303,6 @@ class MainActivity : AppCompatActivity() { super.onRestart() Timber.d("onRestart") viewModel.appStart() -// val signInAutomatically = -// runBlocking { userPreferencesDataStore.data.firstOrNull()?.signInAutomatically } ?: true - -// // TODO PIN-related -// // if (!signInAutomatically || serverRepository.currentUser.value?.hasPin == true) { -// if (!signInAutomatically) { -// serverRepository.closeSession() -// } } override fun onStop() { @@ -392,7 +395,7 @@ class MainActivityViewModel @Inject constructor( private val preferences: DataStore, - private val serverRepository: ServerRepository, + val serverRepository: ServerRepository, private val navigationManager: SetupNavigationManager, private val deviceProfileService: DeviceProfileService, private val backdropService: BackdropService, @@ -402,7 +405,8 @@ class MainActivityViewModel try { val prefs = preferences.data.firstOrNull() ?: AppPreferences.getDefaultInstance() - if (prefs.signInAutomatically) { + val userHasPin = serverRepository.currentUser.value?.hasPin == true + if (prefs.signInAutomatically && !userHasPin) { val current = serverRepository.restoreSession( prefs.currentServerId?.toUUIDOrNull(), diff --git a/app/src/main/java/com/github/damontecres/wholphin/data/ServerRepository.kt b/app/src/main/java/com/github/damontecres/wholphin/data/ServerRepository.kt index 1e5cade5..f66dcfca 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/data/ServerRepository.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/data/ServerRepository.kt @@ -142,8 +142,6 @@ class ServerRepository } else { val user = serverAndUsers.users.firstOrNull { it.id == userId } if (user != null) { - // TODO pin-related -// if (user != null && !user.hasPin) { return changeUser(serverAndUsers.server, user) } } diff --git a/app/src/main/java/com/github/damontecres/wholphin/preferences/AppPreference.kt b/app/src/main/java/com/github/damontecres/wholphin/preferences/AppPreference.kt index f7f7ba25..a04abc59 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/preferences/AppPreference.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/preferences/AppPreference.kt @@ -932,8 +932,7 @@ val basicPreferences = title = R.string.profile_specific_settings, preferences = listOf( - // TODO PIN-related - // AppPreference.RequireProfilePin, + AppPreference.RequireProfilePin, AppPreference.UserPinnedNavDrawerItems, ), ), diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/setup/SwitchUserContent.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/setup/SwitchUserContent.kt index b71e0af0..53afb662 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/setup/SwitchUserContent.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/setup/SwitchUserContent.kt @@ -118,13 +118,11 @@ fun SwitchUserContent( users = users, currentUser = currentUser, onSwitchUser = { user -> - // TODO PIN-related -// if (user.pin.isNotNullOrBlank()) { -// switchUserWithPin = user -// } else { -// viewModel.switchUser(user) -// } - viewModel.switchUser(user) + if (user.hasPin) { + switchUserWithPin = user + } else { + viewModel.switchUser(user) + } }, onAddUser = { showAddUser = true }, onRemoveUser = { user ->