From fa141adc60300d5866270566f98fcc7304811b85 Mon Sep 17 00:00:00 2001 From: damontecres <154766448+damontecres@users.noreply.github.com> Date: Sun, 7 Dec 2025 17:48:46 -0500 Subject: [PATCH 1/4] Ignore auto sign in if user has PIN (#392) If the current user has a PIN, the sign in automatically setting is ignored if the app is put in the background or restarted. Follow up to #356 & https://github.com/damontecres/Wholphin/issues/321#issuecomment-3621312496 I think the PIN protection needs some more thought overall. --- .../main/java/com/github/damontecres/wholphin/MainActivity.kt | 4 ++-- .../com/github/damontecres/wholphin/data/ServerRepository.kt | 4 +++- .../github/damontecres/wholphin/data/model/JellyfinServer.kt | 2 ++ 3 files changed, 7 insertions(+), 3 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 dd3312c8..484e1ed4 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/MainActivity.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/MainActivity.kt @@ -179,7 +179,7 @@ class MainActivity : AppCompatActivity() { } else { DisposableEffect(Unit) { onDispose { - if (!appPreferences.signInAutomatically) { + if (!appPreferences.signInAutomatically || current?.user?.hasPin == true) { serverRepository.closeSession() } } @@ -252,7 +252,7 @@ class MainActivity : AppCompatActivity() { val signInAutomatically = runBlocking { userPreferencesDataStore.data.firstOrNull()?.signInAutomatically } ?: true Timber.i("onRestart: signInAutomatically=$signInAutomatically") - if (!signInAutomatically) { + if (!signInAutomatically || serverRepository.currentUser.value?.hasPin == true) { serverRepository.closeSession() } } 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 eed4422f..5fc98724 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 @@ -109,6 +109,8 @@ class ServerRepository /** * Restores a session for the given server & user such as when the app reopens + * + * If user has a PIN, this returns false */ suspend fun restoreSession( serverId: UUID?, @@ -124,7 +126,7 @@ class ServerRepository } if (serverAndUsers != null) { val user = serverAndUsers.users.firstOrNull { it.id == userId } - if (user != null) { + if (user != null && !user.hasPin) { changeUser(serverAndUsers.server, user) return true } diff --git a/app/src/main/java/com/github/damontecres/wholphin/data/model/JellyfinServer.kt b/app/src/main/java/com/github/damontecres/wholphin/data/model/JellyfinServer.kt index 0b8a3302..d8e40779 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/data/model/JellyfinServer.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/data/model/JellyfinServer.kt @@ -52,6 +52,8 @@ data class JellyfinUser( val accessToken: String?, val pin: String? = null, ) { + val hasPin: Boolean get() = pin.isNotNullOrBlank() + override fun toString(): String = "JellyfinUser(rowId=$rowId, id=$id, name=$name, serverId=$serverId, accessToken?=${accessToken.isNotNullOrBlank()}, pin?=${pin.isNotNullOrBlank()})" } From 82927d196838e1161086cc6dece74b29b0c6a45f Mon Sep 17 00:00:00 2001 From: damontecres <154766448+damontecres@users.noreply.github.com> Date: Mon, 8 Dec 2025 10:13:51 -0500 Subject: [PATCH 2/4] Remove PIN & sign-in auto settings (#397) Unfortunately, I think the PIN and automatic sign-in features require more thought before it should be rolled out as a stable feature. This PR disables the two settings and removes them from the UI. See discussions in #321 and will reopen #268 --- .../damontecres/wholphin/MainActivity.kt | 79 +++++++++---------- .../wholphin/preferences/AppPreference.kt | 6 +- .../wholphin/ui/setup/SwitchUserContent.kt | 12 +-- 3 files changed, 50 insertions(+), 47 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 484e1ed4..ef90f38a 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/MainActivity.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/MainActivity.kt @@ -1,7 +1,6 @@ package com.github.damontecres.wholphin import android.os.Bundle -import android.view.WindowManager import androidx.activity.compose.setContent import androidx.appcompat.app.AppCompatActivity import androidx.compose.foundation.background @@ -10,7 +9,6 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.size import androidx.compose.material3.CircularProgressIndicator import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue @@ -45,7 +43,6 @@ import com.github.damontecres.wholphin.services.UpdateChecker import com.github.damontecres.wholphin.services.hilt.AuthOkHttpClient import com.github.damontecres.wholphin.ui.CoilConfig import com.github.damontecres.wholphin.ui.LocalImageUrlService -import com.github.damontecres.wholphin.ui.isNotNullOrBlank import com.github.damontecres.wholphin.ui.launchIO import com.github.damontecres.wholphin.ui.nav.ApplicationContent import com.github.damontecres.wholphin.ui.nav.Destination @@ -53,8 +50,6 @@ import com.github.damontecres.wholphin.ui.theme.WholphinTheme import com.github.damontecres.wholphin.util.DebugLogTree import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.firstOrNull -import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext import okhttp3.OkHttpClient import org.jellyfin.sdk.model.serializer.toUUIDOrNull @@ -146,16 +141,17 @@ class MainActivity : AppCompatActivity() { ) { var isRestoringSession by remember { mutableStateOf(true) } LaunchedEffect(Unit) { - if (appPreferences.signInAutomatically) { - try { - serverRepository.restoreSession( - appPreferences.currentServerId?.toUUIDOrNull(), - appPreferences.currentUserId?.toUUIDOrNull(), - ) - } catch (ex: Exception) { - Timber.e(ex, "Exception restoring session") - } + // TODO PIN-related +// if (appPreferences.signInAutomatically) { + try { + serverRepository.restoreSession( + appPreferences.currentServerId?.toUUIDOrNull(), + appPreferences.currentUserId?.toUUIDOrNull(), + ) + } catch (ex: Exception) { + Timber.e(ex, "Exception restoring session") } +// } isRestoringSession = false } val current by serverRepository.current.observeAsState() @@ -177,32 +173,34 @@ class MainActivity : AppCompatActivity() { ) } } else { - DisposableEffect(Unit) { - onDispose { - if (!appPreferences.signInAutomatically || current?.user?.hasPin == true) { - serverRepository.closeSession() - } - } - } + // TODO PIN-related +// DisposableEffect(Unit) { +// onDispose { +// if (!appPreferences.signInAutomatically || current?.user?.hasPin == true) { +// serverRepository.closeSession() +// } +// } +// } key(current?.server?.id, current?.user?.id) { - LaunchedEffect(current?.user?.pin) { - if (current?.user?.pin?.isNotNullOrBlank() == true) { - // If user has a pin, then obscure the window in previews - window?.setFlags( - WindowManager.LayoutParams.FLAG_SECURE, - WindowManager.LayoutParams.FLAG_SECURE, - ) - } else { - window?.clearFlags(WindowManager.LayoutParams.FLAG_SECURE) - } - } + // TODO PIN-related +// LaunchedEffect(current?.user?.pin) { +// if (current?.user?.pin?.isNotNullOrBlank() == true) { +// // If user has a pin, then obscure the window in previews +// window?.setFlags( +// WindowManager.LayoutParams.FLAG_SECURE, +// WindowManager.LayoutParams.FLAG_SECURE, +// ) +// } else { +// window?.clearFlags(WindowManager.LayoutParams.FLAG_SECURE) +// } +// } val initialDestination = when { current != null -> Destination.Home() - !appPreferences.signInAutomatically -> Destination.ServerList + // TODO PIN-related + // !appPreferences.signInAutomatically -> Destination.ServerList - // TODO user list? else -> Destination.ServerList } val backStack = rememberNavBackStack(initialDestination) @@ -249,11 +247,12 @@ class MainActivity : AppCompatActivity() { override fun onRestart() { super.onRestart() - val signInAutomatically = - runBlocking { userPreferencesDataStore.data.firstOrNull()?.signInAutomatically } ?: true - Timber.i("onRestart: signInAutomatically=$signInAutomatically") - if (!signInAutomatically || serverRepository.currentUser.value?.hasPin == true) { - serverRepository.closeSession() - } + // TODO PIN-related +// val signInAutomatically = +// runBlocking { userPreferencesDataStore.data.firstOrNull()?.signInAutomatically } ?: true +// Timber.i("onRestart: signInAutomatically=$signInAutomatically") +// if (!signInAutomatically || serverRepository.currentUser.value?.hasPin == true) { +// serverRepository.closeSession() +// } } } 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 4e182e0c..9a196d6b 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 @@ -793,7 +793,8 @@ val basicPreferences = title = R.string.ui_interface, preferences = listOf( - AppPreference.SignInAuto, + // TODO PIN-related + // AppPreference.SignInAuto, AppPreference.HomePageItems, AppPreference.CombineContinueNext, AppPreference.RewatchNextUp, @@ -826,7 +827,8 @@ val basicPreferences = title = R.string.profile_specific_settings, preferences = listOf( - AppPreference.RequireProfilePin, + // TODO PIN-related + // 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 e0aeb5a1..df0b79ee 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 @@ -119,11 +119,13 @@ fun SwitchUserContent( users = users, currentUser = currentUser, onSwitchUser = { user -> - if (user.pin.isNotNullOrBlank()) { - switchUserWithPin = user - } else { - viewModel.switchUser(user) - } + // TODO PIN-related +// if (user.pin.isNotNullOrBlank()) { +// switchUserWithPin = user +// } else { +// viewModel.switchUser(user) +// } + viewModel.switchUser(user) }, onAddUser = { showAddUser = true }, onRemoveUser = { user -> From 2cc3de6eff0d655454e3f224947321d3cc32e269 Mon Sep 17 00:00:00 2001 From: Damontecres Date: Mon, 8 Dec 2025 10:22:26 -0500 Subject: [PATCH 3/4] Release v0.3.5 From 25fa8a73c2c738a8dd579bca8f25eb1b35bbf38b Mon Sep 17 00:00:00 2001 From: damontecres <154766448+damontecres@users.noreply.github.com> Date: Mon, 8 Dec 2025 10:34:41 -0500 Subject: [PATCH 4/4] Readme updates for v0.3.5 Updated images and added new features to the README. --- README.md | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 8fd53cfa..fd07c32d 100644 --- a/README.md +++ b/README.md @@ -18,23 +18,34 @@ This is not a fork of the [official client](https://github.com/jellyfin/jellyfin

-0 3 0_home +0_3_5_home ## Features +### User interface + - A navigation drawer for quick access to libraries, search, and settings from almost anywhere in the app +- Option to combine Continue Watching & Next Up rows - Show Movie/TV Show titles when browsing libraries -- Play TV Show theme music, if available -- Plex inspired playback controls, such as: - - Using D-Pad left/right for seeking during playback - - Quickly access video chapters during playback - - Optionally skip back a few seconds when resuming playback -- Other (subjective) enhancements: - - Subtly show playback position along the bottom of the screen while seeking w/ D-Pad - - Force Continue Watching & Next Up TV episodes to use their Series posters +- Play theme music, if available +- Customize layout grids for libraries +- Access all your favorites quickly from the nav drawer +- Multiple app color themes + +### Playback + - Different media playback engines, including: - Default ExoPlayer/Media3 - Experimental MPV +- Plex inspired playback controls, such as: + - Using D-Pad left/right for seeking during playback + - Quickly access video chapters & queue during playback + - Optionally skip back a few seconds when resuming playback +- Live TV & DVR support +- Auto play next episodes with pass out protection +- Other (subjective) enhancements: + - Subtly show playback position along the bottom of the screen while seeking w/ D-Pad + - Force Continue Watching & Next Up TV episodes to use their Series posters ### Roadmap @@ -88,8 +99,11 @@ You can [help translate Wholphin](https://translate.codeberg.org/engage/wholphin ### Movie library browsing 0 3 0_movies +### Movie page +0_3_5_movie + ### Series page -0 3 0_series_overview +0_3_5_series ### Playlist 0 3 0_playlist