-
-
Notifications
You must be signed in to change notification settings - Fork 611
Add still watching feature #4509
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
I have this like 80% done I would say, but I have hit a roadblock I could use some help with. I have the correct logic in place that will display the are you still watching screen in the correct scenario, but I can't get the actual fragment for this screen to appear. Changing the |
99ad911
to
38ba082
Compare
This feature might not be accepted as I think it would be preferred for this to be in the jellyfin server. So that it can be implemented by all the different client devices |
What part are you envisioning is at the server level? The logic to decide if the still watching screen should be displayed? There still would need to be changes in each of the consuming clients to display the client specific implementation of the screen and client specific implementations to notify the server if there has been any any device specific inputs to reset the counter. |
Yes the logic should be decided on the server once that is implemented it can be adopted for different devices/apps to give a consistent system. @nielsvanvelzen would probably be the guy to answer this. But as far as I can tell device specific features are unlikely to get implemented. |
@Dnkhatri I don't think that's right - this specific feature resets the timer every time the android tv remote is used, which is not an event the server will ever be aware of. This feature definitely should be implemented on the client. |
We have discussed it on element/discord and the consensus is that there is no reason for this to be server side at all, except for user config settings to customize the conditions to trigger the screen. So this will largely stay as is |
@johnpc I hadn't seen that PR thanks for sharing. It does look like it hasn't been worked on in 2 months though, idk if the contributor is still working on it and just hasn't pushed their changes or what. That PR does look like it is doing what I was hoping to accomplish. I am still not done with this one, I am having issues with fragment management to actually get the screen to appear. From my testing, it does still properly track the condition to display the still watching screen, but displaying the fragment itself is proving to be more troublesome than I thought. If you experienced other issues I would be interested to know what those issues were |
a288436
to
428aa96
Compare
A lot of work was done here. This was based off the work @kinhelm did in PR #4389. If/when this merges, they should also get credit for it as well. I did add 2 temp settings for this feature to make it easier to test. 1 to test the episode count condition, and 1 to test the min watch time condition. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feature is awesome - I hope it gets included in the next release.
app/src/main/java/org/jellyfin/androidtv/ui/playback/WatchTrackerViewModel.kt
Outdated
Show resolved
Hide resolved
app/src/main/java/org/jellyfin/androidtv/ui/playback/stillwatching/StillWatchingButtonsView.kt
Outdated
Show resolved
Hide resolved
One other thing I noticed - the default focus is on "No". I don't think that makes sense. The default focus should be on "Yes" so if I grab my remote and tap it, the show continues. Right now if I grab the remote and tap it, it closes the player. |
Another thing I noticed - if I choose "Yes" once, it never prompts again. I would expect that after the time elapses again, the popup would reappear. |
app/src/main/java/org/jellyfin/androidtv/ui/playback/CustomPlaybackOverlayFragment.java
Outdated
Show resolved
Hide resolved
app/src/main/java/org/jellyfin/androidtv/ui/playback/CustomPlaybackOverlayFragment.java
Outdated
Show resolved
Hide resolved
app/src/main/java/org/jellyfin/androidtv/ui/browsing/MainActivity.kt
Outdated
Show resolved
Hide resolved
app/src/main/java/org/jellyfin/androidtv/ui/playback/PlaybackController.java
Outdated
Show resolved
Hide resolved
app/src/main/java/org/jellyfin/androidtv/ui/playback/PlaybackController.java
Outdated
Show resolved
Hide resolved
app/src/main/java/org/jellyfin/androidtv/ui/playback/WatchTrackerViewModel.kt
Outdated
Show resolved
Hide resolved
app/src/main/java/org/jellyfin/androidtv/ui/playback/WatchTrackerViewModel.kt
Outdated
Show resolved
Hide resolved
app/src/main/java/org/jellyfin/androidtv/ui/playback/stillwatching/StillWatchingButtonsView.kt
Outdated
Show resolved
Hide resolved
app/src/main/java/org/jellyfin/androidtv/ui/playback/stillwatching/StillWatchingFragment.kt
Outdated
Show resolved
Hide resolved
app/src/main/java/org/jellyfin/androidtv/ui/playback/stillwatching/StillWatchingStates.kt
Outdated
Show resolved
Hide resolved
428aa96
to
94979fb
Compare
@johnpc Pushed again and addressed everything to the best of my ability. Now I will explain the logic behind the 2 requirements. Episode Requirement: We need to ensure we are above the min minute threshold in order to say we have met the episode requirement. Imagine we are watching episodes that are 10 minutes long, Within 30 minutes, we would hit the condition for the default setting to show the screen, even though the min number of minutes is 90. So if we had normal to longer episodes (45 minutes - 1 hr +), this condition will likely get tripped Time Requirement: There needs to be another condition that handles if you are watching a lot of short episodes. That is what this is for. The logic has been changed so that watchTime must be greater than or equal to the min minute threshold of your user setting AND your episode count must be greater than the number from your user setting. This ensures we capture short media to still display the screen at an appropriate interval. Hope this makes it make more sense |
fun onEpisodeWatched() { | ||
Timber.i("Watcher onEpisodeWatched") | ||
if (!itemWasInterrupted) episodeCount++ | ||
itemWasInterrupted = false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, this seems to affect whether I touch the remote at all during an episode. So if I touch the remote at the beginning of an episode (to set subtitles or see how long it is or something), it essentially doesn't count toward the still watching feature
val currentEpisodeProgress = videoManager.currentPosition.toFloat() / videoManager.duration.toFloat() | ||
val minMinutesInMs = stillWatchingSetting.minMinutes * MILLIS_PER_MIN | ||
|
||
// At episode count, your watch time is above min minute threshold, and you are at least 10% of the way through the next episode |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I saw your explanation
Episode Requirement: We need to ensure we are above the min minute threshold in order to say we have met the episode requirement. Imagine we are watching episodes that are 10 minutes long, Within 30 minutes, we would hit the condition for the default setting to show the screen, even though the min number of minutes is 90. So if we had normal to longer episodes (45 minutes - 1 hr +), this condition will likely get tripped
The code for this is
val episodeRequirementMet = episodeCount == stillWatchingSetting.episodeCount - 1 && watchTime >= minMinutesInMs && currentEpisodeProgress >= 0.1
The 10% of currentEpisodeProgress still throws me off. and I don't think the episodeCount == stillWatchingSetting.episodeCount - 1
makes sense either. Can you clarify what we're going for as it related to the code?
app/src/main/java/org/jellyfin/androidtv/ui/playback/WatchTrackerViewModel.kt
Outdated
Show resolved
Hide resolved
Also just a reminder, my suggestions come from an interest in your feature, a love of jellyfin and a desire to build great software, but I am not a maintainer just a guy so that's all they are is suggestions. :D |
app/src/main/java/org/jellyfin/androidtv/ui/playback/WatchTrackerViewModel.kt
Fixed
Show resolved
Hide resolved
app/src/main/java/org/jellyfin/androidtv/ui/playback/WatchTrackerViewModel.kt
Fixed
Show fixed
Hide fixed
app/src/main/java/org/jellyfin/androidtv/ui/playback/WatchTrackerViewModel.kt
Fixed
Show fixed
Hide fixed
94979fb
to
3859afb
Compare
At this point, I am going to wait for a maintainer to give thoughts on my implementation before working on this more. Hopefully this can get finalized soon and I can start working on making a matching implementation for web |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We already have interaction tracking for our screensaver so I'd rather add a new "InteractionTracker" that will be used by both the screensaver and this feature.
We will not be adding popups during video playback, that's an anti-pattern. Instead, this feature should be implemented in the next-up screen: when a video item ends and we want to know if a user is still watching it will show a different screen that will behave similarly to the popup you currently have.
All new UI must be written in Compose.
@nielsvanvelzen almost done with the changes you requested, but a design question. Thoughts on the screen looking nearly identical to the "Next Up" screen, except the words "Are you still watching?" along with the buttons are in the center of the screen? I thought about still having the next up info in the bottom right corner, just minus the buttons. Or did you envision this screen would look literally EXACTLY the same as the Next Up screen, just with the verbiage describing the next episode removed with just the "Are you still watching?" question with action buttons? Edit: I did just push up everything. Only thing left is just finalizing the visual design, but the buttons work, the screen displays only at the end of an episode, I combined the viewmodel for the screensaver and this feature, updated all the references for both features, and the user settings still work |
3859afb
to
c682a55
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Finally had some time to look at this PR. Started out with testing it out and here are my findings from that:
- When the title of the next playing episode is too long the thumbnail is incorrectly positioned
- The buttons don't have the most intuitive labels, I'd say maybe use "Continue watching" and "Exit" instead of "yes" and "no".
- The preference title should not use word casing and there is no need for the "settings" suffix. Maybe call it "Show inactivity timeout popup" or something like that
- While useful for testing, I don't think the 2eps/1min or 5eps/0.5min options should be kept
- I find the default option should be to disable this behavior
app/src/main/java/org/jellyfin/androidtv/preference/UserPreferences.kt
Outdated
Show resolved
Hide resolved
app/src/main/java/org/jellyfin/androidtv/preference/constant/StillWatchingBehavior.kt
Outdated
Show resolved
Hide resolved
app/src/main/java/org/jellyfin/androidtv/ui/InteractionTrackerViewModel.kt
Show resolved
Hide resolved
app/src/main/java/org/jellyfin/androidtv/ui/InteractionTrackerViewModel.kt
Outdated
Show resolved
Hide resolved
app/src/main/java/org/jellyfin/androidtv/ui/itemdetail/FullDetailsFragment.java
Show resolved
Hide resolved
app/src/main/java/org/jellyfin/androidtv/ui/playback/CustomPlaybackOverlayFragment.java
Outdated
Show resolved
Hide resolved
app/src/main/java/org/jellyfin/androidtv/ui/playback/PlaybackController.java
Show resolved
Hide resolved
app/src/main/java/org/jellyfin/androidtv/ui/playback/VideoManager.java
Outdated
Show resolved
Hide resolved
app/src/main/java/org/jellyfin/androidtv/ui/playback/stillwatching/StillWatchingFragment.kt
Outdated
Show resolved
Hide resolved
app/src/main/java/org/jellyfin/androidtv/ui/InteractionTrackerViewModel.kt
Outdated
Show resolved
Hide resolved
90c2a62
to
e59bd3d
Compare
@nielsvanvelzen had time to go back and address all your comments. Let me know if there's any additional changes you want. ALso tried testing your long title issue and I couldn't repro. If the title is long, it just turns into an ellipsis (the same as the Next Up screen) and the thumbnail looks correct. Maybe you had a weird build or something or maybe its something specific about the title you were seeing? |
e59bd3d
to
55baf9f
Compare
app/src/main/java/org/jellyfin/androidtv/ui/playback/CustomPlaybackOverlayFragment.java
Outdated
Show resolved
Hide resolved
app/src/main/java/org/jellyfin/androidtv/ui/playback/VideoManager.java
Outdated
Show resolved
Hide resolved
app/src/main/java/org/jellyfin/androidtv/ui/playback/stillwatching/StillWatchingStates.kt
Outdated
Show resolved
Hide resolved
55baf9f
to
65855b4
Compare
# Conflicts: # app/src/main/java/org/jellyfin/androidtv/ui/playback/VideoManager.java
65855b4
to
3456ceb
Compare
@nielsvanvelzen all comments have been addressed |
app/src/main/java/org/jellyfin/androidtv/ui/InteractionTrackerViewModel.kt
Fixed
Show fixed
Hide fixed
@coderabbitai full review |
✅ Actions performedFull review triggered. |
📝 WalkthroughWalkthroughThis update introduces a "Still Watching" feature to the Android TV app, replacing the previous screensaver logic. It adds new ViewModels, UI fragments, data models, and user preferences to manage and display inactivity prompts during media playback. The codebase is refactored to use the new Changes
Sequence Diagram(s)"Still Watching" Flow During PlaybacksequenceDiagram
participant User
participant MainActivity
participant InteractionTrackerViewModel
participant PlaybackController
participant StillWatchingViewModel
participant StillWatchingFragment
User->>MainActivity: Starts playback
MainActivity->>PlaybackController: Initialize playback
PlaybackController->>InteractionTrackerViewModel: notifyStart(item)
loop Watching episodes
PlaybackController->>InteractionTrackerViewModel: onEpisodeWatched()
InteractionTrackerViewModel-->>PlaybackController: getShowStillWatching()
alt Threshold reached and enabled
PlaybackController->>CustomPlaybackOverlayFragment: showStillWatching(itemId)
CustomPlaybackOverlayFragment->>StillWatchingFragment: Navigate to StillWatchingFragment(itemId)
StillWatchingFragment->>StillWatchingViewModel: setItemId(itemId)
StillWatchingViewModel->>StillWatchingFragment: Provide item data and state
User->>StillWatchingFragment: Choose "Continue Watching" or "Exit"
alt Continue Watching
StillWatchingFragment->>StillWatchingViewModel: stillWatching()
StillWatchingFragment->>PlaybackController: Resume playback
else Exit
StillWatchingFragment->>StillWatchingViewModel: close()
StillWatchingFragment->>PlaybackController: End playback
end
else Continue playback
end
end
Interaction and Screensaver ManagementsequenceDiagram
participant User
participant MainActivity
participant InteractionTrackerViewModel
participant InAppScreensaver
User->>MainActivity: Key/Touch event
MainActivity->>InteractionTrackerViewModel: notifyInteraction(canCancel, userInitiated)
InteractionTrackerViewModel->>InAppScreensaver: Update visible state
InAppScreensaver->>User: Show/hide screensaver UI
These diagrams illustrate the main flows for the new "Still Watching" feature and the refactored interaction/screen management logic. ✨ Finishing Touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
♻️ Duplicate comments (8)
app/src/main/java/org/jellyfin/androidtv/ui/itemdetail/FullDetailsFragment.java (1)
1224-1224
: Verify session tracking timing.A past review comment raised a valid concern about calling
notifyStartSession
from here rather than from the actual video player. This could lead to session tracking inaccuracies if playback fails to start after this point or if the user cancels before the video player launches.app/src/main/res/values/strings.xml (1)
1-592
: Verify usage of test strings mentioned in past reviews.The past review comments mention unused test strings (
lbl_still_watching_test_episode_count
andlbl_still_watching_test_min_minutes
). Please verify these are actually being used in the implementation or remove them if they're no longer needed.#!/bin/bash # Search for usage of the test strings mentioned in past reviews echo "Searching for test string usage..." rg -n "lbl_still_watching_test_episode_count|lbl_still_watching_test_min_minutes"app/src/main/java/org/jellyfin/androidtv/ui/playback/CustomPlaybackOverlayFragment.java (3)
222-226
: Remove controller caching - conflicts with previous review guidance.This change goes against the explicit guidance from nielsvanvelzen in previous reviews: "We explicitly use a holder-class because the actual playback controller can change. So while it's fine to store the container in a variable you should NOT store the playback controller itself."
Revert to accessing the controller through the container each time:
- PlaybackController playbackController = playbackControllerContainer.getValue().getPlaybackController(); - - if (playbackController != null) { - playbackController.init(new VideoManager(requireActivity(), view, helper), this); + if (playbackControllerContainer.getValue().getPlaybackController() != null) { + playbackControllerContainer.getValue().getPlaybackController().init(new VideoManager(requireActivity(), view, helper), this); }
442-461
: Remove controller caching - conflicts with previous review guidance.Same issue as above - this caching approach was explicitly discouraged in previous reviews.
Revert to accessing through the container:
- PlaybackController playbackController = playbackControllerContainer.getValue().getPlaybackController(); - - if (playbackController != null) { - if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) { - playbackController.play(0); - return true; - } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) { - playbackController.pause(); - return true; - } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) { - playbackController.playPause(); - return true; - } else if (keyCode == KeyEvent.KEYCODE_MEDIA_FAST_FORWARD || keyCode == KeyEvent.KEYCODE_BUTTON_R1 || keyCode == KeyEvent.KEYCODE_BUTTON_R2) { - playbackController.fastForward(); - return true; - } else if (keyCode == KeyEvent.KEYCODE_MEDIA_REWIND || keyCode == KeyEvent.KEYCODE_BUTTON_L1 || keyCode == KeyEvent.KEYCODE_BUTTON_L2) { - playbackController.rewind(); - return true; - } + if (playbackControllerContainer.getValue().getPlaybackController() != null) { + if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) { + playbackControllerContainer.getValue().getPlaybackController().play(0); + return true; + } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) { + playbackControllerContainer.getValue().getPlaybackController().pause(); + return true; + } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) { + playbackControllerContainer.getValue().getPlaybackController().playPause(); + return true; + } else if (keyCode == KeyEvent.KEYCODE_MEDIA_FAST_FORWARD || keyCode == KeyEvent.KEYCODE_BUTTON_R1 || keyCode == KeyEvent.KEYCODE_BUTTON_R2) { + playbackControllerContainer.getValue().getPlaybackController().fastForward(); + return true; + } else if (keyCode == KeyEvent.KEYCODE_MEDIA_REWIND || keyCode == KeyEvent.KEYCODE_BUTTON_L1 || keyCode == KeyEvent.KEYCODE_BUTTON_L2) { + playbackControllerContainer.getValue().getPlaybackController().rewind(); + return true; + } }
666-666
: Remove controller caching.Another instance of the same caching issue.
- if (playbackControllerContainer.getValue().getPlaybackController() == null || !playbackControllerContainer.getValue().getPlaybackController().hasFragment()) { + PlaybackController controller = playbackControllerContainer.getValue().getPlaybackController(); + if (controller == null || !controller.hasFragment()) {app/src/main/java/org/jellyfin/androidtv/ui/playback/PlaybackController.java (1)
1182-1182
: Proper episode completion trackingThe
onEpisodeWatched
call correctly tracks episode completion before processing the next item, ensuring accurate counting for the still watching feature.app/src/main/java/org/jellyfin/androidtv/ui/InteractionTrackerViewModel.kt (1)
27-28
: Add empty line after group commentGroup name comments should have an empty line following them for better readability.
// Screensaver vars + private var timer: Job? = null
app/src/main/java/org/jellyfin/androidtv/ui/playback/stillwatching/StillWatchingStates.kt (1)
5-13
: Consider moving getSetting logic to the enum classBased on past review feedback, consider moving this mapping logic to the
StillWatchingPresetConfigs
enum itself as a method, which would eliminate the need for the when statement and make the code more object-oriented.This would allow
StillWatchingPresetConfigs.DEFAULT.toStates()
instead ofStillWatchingStates.getSetting(StillWatchingPresetConfigs.DEFAULT)
.
🧹 Nitpick comments (4)
app/src/main/java/org/jellyfin/androidtv/ui/navigation/Destinations.kt (1)
3-3
: Remove unused import.The
bundleOf
import appears to be unused in this file and should be removed to keep the imports clean.-import androidx.core.os.bundleOf
app/src/main/java/org/jellyfin/androidtv/ui/playback/stillwatching/StillWatchingStates.kt (1)
3-3
: Use a more specific type forminMinutes
.The
minMinutes
property uses theNumber
type, which is abstract and not commonly used for such values. Consider usingInt
orLong
for better type safety and clarity.-data class StillWatchingStates(val enabled: Boolean, val episodeCount: Int, val minMinutes: Number) { +data class StillWatchingStates(val enabled: Boolean, val episodeCount: Int, val minMinutes: Int) {app/src/main/java/org/jellyfin/androidtv/ui/InteractionTrackerViewModel.kt (1)
125-127
: Simplify complex condition for better readabilityThe condition is complex with 4 parts. Consider extracting it to a well-named method to improve readability and maintainability.
+ private fun shouldHideScreensaver(canCancel: Boolean): Boolean { + return _screensaverVisible.value && (canCancel || !inAppEnabled || activityPaused) + } + fun notifyInteraction(canCancel: Boolean, userInitiated: Boolean) { // ... existing code ... // Hide screensaver when interacted with allowed cancellation or when disabled - if (_screensaverVisible.value && (canCancel || !inAppEnabled || activityPaused)) { + if (shouldHideScreensaver(canCancel)) { _screensaverVisible.value = false }app/src/main/java/org/jellyfin/androidtv/preference/constant/StillWatchingBehavior.kt (1)
15-15
: Fix indentation formattingThere's a formatting inconsistency with the leading whitespace on this line.
- */ + */
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (22)
app/src/main/java/org/jellyfin/androidtv/di/AppModule.kt
(3 hunks)app/src/main/java/org/jellyfin/androidtv/preference/UserPreferences.kt
(2 hunks)app/src/main/java/org/jellyfin/androidtv/preference/constant/StillWatchingBehavior.kt
(1 hunks)app/src/main/java/org/jellyfin/androidtv/ui/InteractionTrackerViewModel.kt
(1 hunks)app/src/main/java/org/jellyfin/androidtv/ui/ScreensaverViewModel.kt
(0 hunks)app/src/main/java/org/jellyfin/androidtv/ui/browsing/MainActivity.kt
(11 hunks)app/src/main/java/org/jellyfin/androidtv/ui/itemdetail/FullDetailsFragment.java
(3 hunks)app/src/main/java/org/jellyfin/androidtv/ui/navigation/Destinations.kt
(3 hunks)app/src/main/java/org/jellyfin/androidtv/ui/picture/PictureViewerFragment.kt
(3 hunks)app/src/main/java/org/jellyfin/androidtv/ui/playback/CustomPlaybackOverlayFragment.java
(4 hunks)app/src/main/java/org/jellyfin/androidtv/ui/playback/PlaybackController.java
(9 hunks)app/src/main/java/org/jellyfin/androidtv/ui/playback/PlaybackOverlayFragmentHelper.kt
(1 hunks)app/src/main/java/org/jellyfin/androidtv/ui/playback/rewrite/PlaybackRewriteFragment.kt
(3 hunks)app/src/main/java/org/jellyfin/androidtv/ui/playback/stillwatching/StillWatchingFragment.kt
(1 hunks)app/src/main/java/org/jellyfin/androidtv/ui/playback/stillwatching/StillWatchingItemData.kt
(1 hunks)app/src/main/java/org/jellyfin/androidtv/ui/playback/stillwatching/StillWatchingPresetConfigs.kt
(1 hunks)app/src/main/java/org/jellyfin/androidtv/ui/playback/stillwatching/StillWatchingState.kt
(1 hunks)app/src/main/java/org/jellyfin/androidtv/ui/playback/stillwatching/StillWatchingStates.kt
(1 hunks)app/src/main/java/org/jellyfin/androidtv/ui/playback/stillwatching/StillWatchingViewModel.kt
(1 hunks)app/src/main/java/org/jellyfin/androidtv/ui/preference/screen/PlaybackPreferencesScreen.kt
(2 hunks)app/src/main/java/org/jellyfin/androidtv/ui/screensaver/InAppScreensaver.kt
(2 hunks)app/src/main/res/values/strings.xml
(4 hunks)
💤 Files with no reviewable changes (1)
- app/src/main/java/org/jellyfin/androidtv/ui/ScreensaverViewModel.kt
🧰 Additional context used
🪛 GitHub Check: detekt
app/src/main/java/org/jellyfin/androidtv/ui/playback/stillwatching/StillWatchingFragment.kt
[warning] 130-130: One method should have one responsibility. Long methods tend to handle many things at once. Prefer smaller methods to make them easier to understand.
The function StillWatchingOverlay is too long (93). The maximum length is 60.
app/src/main/java/org/jellyfin/androidtv/ui/InteractionTrackerViewModel.kt
[warning] 94-94: The caught exception is swallowed. The original exception could be lost.
The caught exception is swallowed. The original exception could be lost.
[warning] 118-118: Complex conditions should be simplified and extracted into well-named methods if necessary.
This condition is too complex (4). Defined complexity threshold for conditions is set to '4'
🪛 GitHub Check: Android Lint
app/src/main/java/org/jellyfin/androidtv/ui/playback/stillwatching/StillWatchingViewModel.kt
[warning] 22-22: Static Field Leaks
This field leaks a context object
🪛 detekt (1.23.8)
app/src/main/java/org/jellyfin/androidtv/ui/InteractionTrackerViewModel.kt
[warning] 94-94: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
🔇 Additional comments (36)
app/src/main/java/org/jellyfin/androidtv/ui/preference/screen/PlaybackPreferencesScreen.kt (1)
67-71
: Well-structured preference integration following established patterns.The implementation correctly follows the existing code patterns for enum preferences, including proper binding and conditional visibility based on media queuing being enabled.
app/src/main/java/org/jellyfin/androidtv/ui/playback/stillwatching/StillWatchingPresetConfigs.kt (1)
3-9
: Clean enum implementation with appropriate preset values.The enum provides a good range of preset configurations from SHORT to DISABLED, enabling flexible user customization of the still watching behavior.
app/src/main/java/org/jellyfin/androidtv/ui/playback/stillwatching/StillWatchingState.kt (1)
3-8
: Well-defined state enum for UI state management.The enum values logically represent the different states of the still watching UI flow, providing clear state transitions for the feature.
app/src/main/java/org/jellyfin/androidtv/ui/navigation/Destinations.kt (1)
146-148
: Navigation destination correctly implemented.The reuse of
NextUpFragment.ARGUMENT_ITEM_ID
is appropriate since both features work with the same item data type, maintaining consistency in the navigation pattern.app/src/main/java/org/jellyfin/androidtv/ui/itemdetail/FullDetailsFragment.java (1)
154-154
: Dependency injection correctly implemented.The lazy injection of
InteractionTrackerViewModel
follows the established pattern used throughout the class.app/src/main/java/org/jellyfin/androidtv/preference/UserPreferences.kt (1)
13-13
: LGTM! Preference implementation follows established patterns.The new
stillWatchingBehavior
preference correctly follows the naming convention (nopref_
prefix as mentioned in past reviews) and uses an appropriate default value ofDISABLED
. The implementation is consistent with other enum preferences in the file.Also applies to: 91-94
app/src/main/java/org/jellyfin/androidtv/ui/picture/PictureViewerFragment.kt (1)
23-23
: LGTM! Clean migration from ScreensaverViewModel to InteractionTrackerViewModel.The changes correctly migrate from the old
ScreensaverViewModel
to the newInteractionTrackerViewModel
while preserving the existing lifecycle lock functionality. The renaming is consistent and follows the pattern used across other files in this PR.Also applies to: 48-48, 80-80
app/src/main/java/org/jellyfin/androidtv/di/AppModule.kt (1)
29-29
: LGTM! Dependency injection setup is correctly implemented.The new ViewModels are properly registered with appropriate scopes:
InteractionTrackerViewModel
as a singleton (suitable for tracking user interactions across the app)StillWatchingViewModel
as a view model (suitable for UI state management)Both ViewModels have the correct dependency injection parameters and follow established Koin patterns.
Also applies to: 36-36, 110-110, 125-125
app/src/main/res/values/strings.xml (1)
44-44
: LGTM! String resources appropriately support the still watching feature.The new string resources provide comprehensive text support for the still watching feature including preference titles, timeout options, and UI labels. All strings appear relevant and well-named.
Also applies to: 261-261, 365-368, 550-550
app/src/main/java/org/jellyfin/androidtv/ui/playback/PlaybackOverlayFragmentHelper.kt (1)
3-3
: LGTM! Consistent ViewModel migration maintains existing functionality.The migration from
ScreensaverViewModel
toInteractionTrackerViewModel
is correctly implemented, preserving the existing screensaver lock mechanism while following the consistent pattern established across other files in this PR.Also applies to: 9-9, 14-14
app/src/main/java/org/jellyfin/androidtv/ui/playback/rewrite/PlaybackRewriteFragment.kt (4)
15-15
: LGTM: Import statement updated correctly.The import change from
ScreensaverViewModel
toInteractionTrackerViewModel
is consistent with the broader refactoring across the app.
40-40
: LGTM: Property declaration updated correctly.The property rename and type change are consistent with the ViewModel refactoring while maintaining the same injection pattern.
70-70
: ```shell
#!/bin/bashSearch for InteractionTrackerViewModel class definition
rg --color never -n 'class InteractionTrackerViewModel' -C3
Search for addLifecycleLock method signature
rg --color never -n 'fun addLifecycleLock' -C3
--- `15-15`: **LGTM: Clean migration to InteractionTrackerViewModel** The refactoring from `ScreensaverViewModel` to `InteractionTrackerViewModel` is implemented correctly with proper import updates, property renaming, and preserved lifecycle lock management logic. Also applies to: 40-40, 70-70 </details> <details> <summary>app/src/main/java/org/jellyfin/androidtv/ui/screensaver/InAppScreensaver.kt (4)</summary> `19-19`: **LGTM: Import statement updated consistently.** The import change aligns with the broader ViewModel refactoring across the codebase. --- `24-25`: **LGTM: ViewModel usage updated correctly.** The ViewModel instantiation and state collection have been updated consistently with the refactoring while maintaining the same usage patterns. --- `40-40`: To locate the method definition, let’s search for its declaration across the repo: ```shell #!/bin/bash # Find the notifyInteraction method signature rg "fun notifyInteraction" -n
19-19
: LGTM: Successful migration with enhanced interaction trackingThe transition to
InteractionTrackerViewModel
maintains the existing screensaver functionality while introducing more granular interaction tracking through thenotifyInteraction
method'scanCancel
anduserInitiated
parameters.Also applies to: 24-25, 40-40
app/src/main/java/org/jellyfin/androidtv/ui/playback/stillwatching/StillWatchingItemData.kt (2)
7-13
: LGTM: Well-structured data class.The
StillWatchingItemData
class is well-designed with appropriate property types:
BaseItemDto
for the core item dataUUID
for unique identification- Optional
JellyfinImage
properties for UI display- Clean, focused data structure without unnecessary complexity
7-13
: LGTM: Well-designed data class for still watching featureThe
StillWatchingItemData
class provides a clean, well-typed container for the still watching prompt with appropriate nullable image properties and comprehensive item information.app/src/main/java/org/jellyfin/androidtv/ui/playback/stillwatching/StillWatchingStates.kt (1)
5-13
: Well-designed configuration mapping that addresses previous concerns.The
getSetting
method effectively addresses past review concerns:
Long episodes concern: The dual-threshold approach (episode count OR minutes) ensures the prompt triggers appropriately for both short and long content. For example, a single 150+ minute movie would trigger the LONG preset's time threshold even without multiple episodes.
Enum usage: The method properly leverages the
StillWatchingPresetConfigs
enum with exhaustive when-expression mapping.The configuration values appear well-balanced for different viewing patterns, from SHORT (2 episodes/60min) to VERY_LONG (8 episodes/240min).
app/src/main/java/org/jellyfin/androidtv/preference/constant/StillWatchingBehavior.kt (2)
6-29
: Excellent enum implementation with clear documentation.The
StillWatchingBehavior
enum is well-structured and addresses previous feedback:
Clear behavior description: Each enum value documents the exact thresholds (episodes and minutes) rather than referencing external services, addressing the past comment about describing actual functionality.
Comprehensive options: Provides a good range from SHORT (2 episodes/60min) to VERY_LONG (8 episodes/240min), plus a DISABLED option for users who prefer not to use the feature.
Proper implementation: Correctly implements
PreferenceEnum
with appropriate string resource references.The documentation is factual, specific, and user-friendly.
6-29
: LGTM: Well-documented enum with clear behavior descriptionsThe enum provides a comprehensive range of still watching behaviors with clear documentation that describes the actual functionality rather than external references. The implementation properly follows the PreferenceEnum pattern with appropriate string resource usage.
app/src/main/java/org/jellyfin/androidtv/ui/playback/CustomPlaybackOverlayFragment.java (1)
1325-1330
: LGTM: Well-implemented navigation method.The
showStillWatching
method follows the same pattern asshowNextUp
with proper navigation guard using thenavigating
flag.app/src/main/java/org/jellyfin/androidtv/ui/playback/stillwatching/StillWatchingViewModel.kt (1)
26-66
: LGTM: Well-structured ViewModel implementation.The ViewModel properly uses StateFlow for reactive UI updates, handles coroutines correctly with viewModelScope, and follows good practices with IO dispatcher for network calls. The use of itemImages extension properties aligns with recent framework changes.
app/src/main/java/org/jellyfin/androidtv/ui/browsing/MainActivity.kt (5)
25-25
: LGTM: Correct import update for ViewModel replacement.The import has been properly updated to use the new
InteractionTrackerViewModel
.
41-41
: LGTM: ViewModel injection updated correctly.The ViewModel injection has been properly updated to use
InteractionTrackerViewModel
.
59-74
: LGTM: Method calls updated with appropriate parameters.The method calls have been correctly updated to match the new API. The parameter usage is appropriate:
keepScreenOn
flow access is correctnotifyInteraction(canCancel = false, userInitiated = false)
for navigation actions is logical
89-90
: LGTM: Activity lifecycle handling preserved.The activity pause/resume handling has been correctly updated to use the new ViewModel while preserving the same logic.
Also applies to: 106-107
121-121
: LGTM: Interaction tracking calls properly updated.All interaction tracking calls have been consistently updated with appropriate parameter values:
- User-initiated interactions correctly set
userInitiated = true
- System interactions correctly set
userInitiated = false
canCancel
parameter is appropriately set based on the event typeAlso applies to: 140-142, 161-161, 167-169, 179-181, 190-192
app/src/main/java/org/jellyfin/androidtv/ui/playback/stillwatching/StillWatchingFragment.kt (3)
61-61
: LGTM: Proper use of Duration API.Good use of
Duration.Companion.seconds
instead of hardcoded milliseconds, addressing previous feedback about using proper duration types.
63-128
: LGTM: Well-structured Compose screen implementation.The screen properly manages state, handles navigation actions, and follows good Compose patterns with proper focus management and background handling.
242-259
: LGTM: Clean fragment implementation.The fragment properly handles argument extraction and integrates well with the Compose UI system.
app/src/main/java/org/jellyfin/androidtv/ui/playback/PlaybackController.java (3)
802-804
: Good formatting improvementAdding braces to the if statement improves code consistency and prevents potential bugs.
1096-1111
: Well-implemented still watching integrationThe logic correctly integrates the still watching feature with the existing next up functionality. The precedence given to "Still Watching" over "Next Up" when both are enabled is a reasonable design choice that prioritizes user engagement checks.
1132-1132
: Correct placement of interaction trackingThe
notifyStart
call is properly placed after the playback state is set to PLAYING, ensuring accurate tracking of playback sessions.
app/src/main/java/org/jellyfin/androidtv/ui/playback/stillwatching/StillWatchingViewModel.kt
Show resolved
Hide resolved
app/src/main/java/org/jellyfin/androidtv/ui/playback/stillwatching/StillWatchingFragment.kt
Outdated
Show resolved
Hide resolved
app/src/main/java/org/jellyfin/androidtv/ui/InteractionTrackerViewModel.kt
Show resolved
Hide resolved
app/src/main/java/org/jellyfin/androidtv/ui/InteractionTrackerViewModel.kt
Outdated
Show resolved
Hide resolved
app/src/main/java/org/jellyfin/androidtv/ui/playback/stillwatching/StillWatchingStates.kt
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could probably find more nitpicks but overall these changes are good and the functionality works as expected. I've made a few final small changes in the last commits.
Thanks for your work on this!
Changes
Monitor how many episodes have been watched in a row with no remote interaction. Default behavior is if 3 episodes have been watched, or there was 90 minutes of watch time, with no interaction, a timer starts when watching the 4th episode. If timer runs out with no interaction, "Are you still watching prompt" appears. Another countdown starts and a progress bar fills the "No" button. When countdown ends, and a choice hasn't been selected playback ends. There are a series of user customizable options to configure the conditions the screen will trigger, or you can disable it all together.
Issues
Adds a feature that has been requested for 5 years now
Also requested in #1327
Summary by CodeRabbit
New Features
Improvements
Bug Fixes