Skip to content
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

feat(VideoManager): add "Continue watching" dialog #4389

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
- [3l0w](https://github.com/3l0w)
- [MajMongoose](https://github.com/majmongoose)
- [Olaren15](https://github.com/Olaren15)
- [Kinhelm](https://github.com/Kinhelm)

# Emby Contributors

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import org.jellyfin.androidtv.ui.navigation.NavigationAction
import org.jellyfin.androidtv.ui.navigation.NavigationRepository
import org.jellyfin.androidtv.ui.screensaver.InAppScreensaver
import org.jellyfin.androidtv.ui.startup.StartupActivity
import org.jellyfin.androidtv.util.WatchTracker
import org.jellyfin.androidtv.util.applyTheme
import org.jellyfin.androidtv.util.isMediaSessionKeyEvent
import org.koin.android.ext.android.inject
Expand Down Expand Up @@ -165,6 +166,7 @@ class MainActivity : FragmentActivity() {
onKeyEvent(keyCode, event) || super.onKeyUp(keyCode, event)

override fun onUserInteraction() {
WatchTracker.onUserInteraction()
super.onUserInteraction()

screensaverViewModel.notifyInteraction(false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.jellyfin.androidtv.ui.livetv.TvManager;
import org.jellyfin.androidtv.util.TimeUtils;
import org.jellyfin.androidtv.util.Utils;
import org.jellyfin.androidtv.util.WatchTracker;
import org.jellyfin.androidtv.util.apiclient.ReportingHelper;
import org.jellyfin.androidtv.util.profile.ExoPlayerProfile;
import org.jellyfin.androidtv.util.sdk.compat.JavaCompat;
Expand Down Expand Up @@ -1168,6 +1169,7 @@ public void onError() {

@Override
public void onCompletion() {
WatchTracker.INSTANCE.onEpisodeWatched();
Timber.d("On Completion fired");
itemComplete();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import org.jellyfin.androidtv.data.compat.StreamInfo;
import org.jellyfin.androidtv.preference.UserPreferences;
import org.jellyfin.androidtv.preference.constant.ZoomMode;
import org.jellyfin.androidtv.util.WatchTracker;
import org.jellyfin.sdk.api.client.ApiClient;
import org.jellyfin.sdk.model.api.MediaStream;
import org.jellyfin.sdk.model.api.MediaStreamType;
Expand Down Expand Up @@ -162,6 +163,8 @@ public void onTracksChanged(Tracks tracks) {
Timber.d("Tracks changed");
}
});

WatchTracker.INSTANCE.startWatchTime(activity, this);
}

public void subscribe(@NonNull PlaybackControllerNotifiable notifier) {
Expand Down Expand Up @@ -276,16 +279,19 @@ public void start() {
_helper.getFragment().closePlayer();
return;
}
WatchTracker.INSTANCE.startWatchTime(this.mActivity, this);
mExoPlayer.setPlayWhenReady(true);
normalWidth = mExoPlayerView.getLayoutParams().width;
normalHeight = mExoPlayerView.getLayoutParams().height;
}

public void play() {
WatchTracker.INSTANCE.startWatchTime(this.mActivity, this);
mExoPlayer.setPlayWhenReady(true);
}

public void pause() {
WatchTracker.INSTANCE.stopWatchTime();
mExoPlayer.setPlayWhenReady(false);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import org.jellyfin.androidtv.ui.playback.CustomPlaybackOverlayFragment;
import org.jellyfin.androidtv.ui.playback.PlaybackController;
import org.jellyfin.androidtv.util.Utils;
import org.jellyfin.androidtv.util.WatchTracker;
import org.jellyfin.androidtv.util.apiclient.StreamHelper;
import org.jellyfin.sdk.model.api.ChapterInfo;
import org.jellyfin.sdk.model.api.MediaSourceInfo;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package org.jellyfin.androidtv.ui.playback.overlay.action
import android.content.Context
import androidx.leanback.widget.PlaybackControlsRow
import org.jellyfin.androidtv.ui.playback.overlay.VideoPlayerAdapter
import org.jellyfin.androidtv.util.WatchTracker

class PlayPauseAction(context: Context) : PlaybackControlsRow.PlayPauseAction(context),
AndroidAction {
Expand Down
120 changes: 120 additions & 0 deletions app/src/main/java/org/jellyfin/androidtv/util/WatchTracker.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package org.jellyfin.androidtv.util

import android.app.AlertDialog
import android.content.Context
import android.os.Handler
import android.os.Looper
import org.jellyfin.androidtv.R
import org.jellyfin.androidtv.ui.playback.VideoManager
import java.util.logging.Logger

interface PromptUserCallback {
fun onResult(result: Boolean)
}

object WatchTracker {
private var episodeCount = 0
private var watchTime = 0L
private var lastUpdateTime= 0L
private var lastInteractionTime = System.currentTimeMillis()
private val watchTimeHandler = Handler(Looper.getMainLooper())
private var isPlaying: Boolean = false

private class WatchTimeUpdater(
private val context: Context,
private val videoManager: VideoManager
) : Runnable {
override fun run() {
if (isPlaying) {
val currentTime = System.currentTimeMillis()
watchTime += currentTime - lastUpdateTime
lastUpdateTime = currentTime
checkPrompt(context, videoManager)
watchTimeHandler.postDelayed(this, 1000)
}
}
}

fun onEpisodeWatched() {
Logger.getLogger(WatchTracker::class.java.name).info("Watcher onEpisodeWatched")
episodeCount++
}

fun onUserInteraction() {
Logger.getLogger(WatchTracker::class.java.name).info("Watcher onUserInteraction")
resetWatchTime()
lastInteractionTime = System.currentTimeMillis()
}

private fun checkPrompt(context: Context, videoManager: VideoManager) {
if (episodeCount == 3 || watchTime >= 90 * 60 * 1000) {
videoManager.pause()
promptUser(context, object : PromptUserCallback {
override fun onResult(result: Boolean) {
if (result) {
videoManager.play()
} else {
context.getActivity()?.finish()
}
}
})
}
}

private fun promptUser(context: Context, callback: PromptUserCallback) {
val dialog = AlertDialog.Builder(context)
.setTitle(context.getString(R.string.still_watching_title))
.setMessage(context.getString(R.string.continue_watching_message))
.setPositiveButton(R.string.lbl_yes) { dialog, _ ->
dialog.dismiss()
callback.onResult(true)
}
.setNegativeButton(R.string.lbl_no) { dialog, _ ->
dialog.dismiss()
callback.onResult(false)
}
.setCancelable(false)
.create()

dialog.show()

val negativeButton = dialog.getButton(AlertDialog.BUTTON_NEGATIVE)
val handler = Handler(Looper.getMainLooper())
val startTime = System.currentTimeMillis()
val countdownTime = 15000L // 15 seconds

handler.post(object : Runnable {
override fun run() {
val elapsedTime = System.currentTimeMillis() - startTime
val remainingTime = (countdownTime - elapsedTime) / 1000
if (remainingTime > 0) {
negativeButton.text = context.getString(R.string.no_button_text_with_time, remainingTime)
handler.postDelayed(this, 1000)
} else {
if (dialog.isShowing) {
dialog.dismiss()
callback.onResult(false)
}
}
}
})
}

fun startWatchTime(context: Context, videoManager: VideoManager) {
Logger.getLogger(WatchTracker::class.java.name).info("Start tracker")
resetWatchTime()
lastUpdateTime = System.currentTimeMillis()
watchTimeHandler.post(WatchTimeUpdater(context, videoManager))
}

fun stopWatchTime() {
isPlaying = false
watchTimeHandler.removeCallbacksAndMessages(null)
}

private fun resetWatchTime() {
watchTime = 0L
episodeCount = 0
isPlaying = true
}
}
5 changes: 4 additions & 1 deletion app/src/main/res/values-de/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,9 @@
<string name="item_deleted">%1$s gelöscht</string>
<string name="pref_screensaver">Bildschirmschoner</string>
<string name="pref_screensaver_inapp_enabled">In-App-Bildschirmschoner verwenden</string>
<string name="still_watching_title">Schaust du noch?</string>
<string name="continue_watching_message">Möchtest du weiter schauen?</string>
<string name="no_button_text_with_time">Nein (%1$d)</string>
<plurals name="seconds">
<item quantity="one">%1$s Sekunde</item>
<item quantity="other">%1$s Sekunden</item>
Expand Down Expand Up @@ -566,4 +569,4 @@
<string name="skip_forward_length">Sprungweite vorwärts</string>
<string name="runtime_hours_minutes">%1$dh %2$dm</string>
<string name="runtime_minutes">%1$dm</string>
</resources>
</resources>
5 changes: 4 additions & 1 deletion app/src/main/res/values-en-rGB/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,9 @@
<string name="item_delete_confirm_title">Delete item</string>
<string name="lbl_latest_in">Recently added in %1$s</string>
<string name="pref_screensaver_inapp_timeout">Start screensaver after</string>
<string name="still_watching_title">Are you still watching?</string>
<string name="continue_watching_message">Do you want to continue watching?</string>
<string name="no_button_text_with_time">No (%1$d)</string>
<plurals name="minutes">
<item quantity="one">%1$s minute</item>
<item quantity="other">%1$s minutes</item>
Expand Down Expand Up @@ -564,4 +567,4 @@
<string name="segment_action_ask_to_skip">Ask to skip</string>
<string name="skip_forward_length">Skip forward length</string>
<string name="preference_enable_trickplay">Enable trickplay in video player</string>
</resources>
</resources>
5 changes: 4 additions & 1 deletion app/src/main/res/values-fr/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,9 @@
<string name="pref_screensaver_inapp_timeout">Lancer l\'économiseur d\'écran après</string>
<string name="pref_screensaver">Économiseur d\'écran</string>
<string name="not_set">Non défini</string>
<string name="still_watching_title">Regardez-vous toujours ?</string>
<string name="continue_watching_message">Voulez-vous continuer à regarder ?</string>
<string name="no_button_text_with_time">Non (%1$d)</string>
<plurals name="seconds">
<item quantity="one">%1$s seconde</item>
<item quantity="many">%1$s secondes</item>
Expand Down Expand Up @@ -576,4 +579,4 @@
<string name="skip_forward_length">Longueur de l\'avance rapide</string>
<string name="runtime_hours_minutes">%1$dh %2$dm</string>
<string name="runtime_minutes">%1$dm</string>
</resources>
</resources>
5 changes: 4 additions & 1 deletion app/src/main/res/values-pl/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,9 @@
<string name="pref_screensaver_inapp_enabled_description">Pokaż wygaszacz ekranu Jellyfin podczas uruchomienia aplikacji</string>
<string name="pref_screensaver_inapp_enabled">Użyj wbudowanego wygaszacza ekranu</string>
<string name="pref_screensaver_inapp_timeout">Uruchom wygaszacz ekranu po</string>
<string name="still_watching_title">Czy nadal oglądasz?</string>
<string name="continue_watching_message">Czy chcesz kontynuować oglądanie?</string>
<string name="no_button_text_with_time">Nie (%1$d)</string>
<plurals name="seconds">
<item quantity="one">%1$s sekunda</item>
<item quantity="few">%1$s sekundy</item>
Expand Down Expand Up @@ -586,4 +589,4 @@
<string name="skip_forward_length">Długość pomijania do przodu</string>
<string name="runtime_hours_minutes">%1$dh %2$dm</string>
<string name="runtime_minutes">%1$dm</string>
</resources>
</resources>
5 changes: 4 additions & 1 deletion app/src/main/res/values-ru/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,9 @@
<string name="not_set">Не задано</string>
<string name="pref_screensaver">Заставка</string>
<string name="pref_screensaver_inapp_enabled">Использовать заставку приложения</string>
<string name="still_watching_title">Вы все еще смотрите?</string>
<string name="continue_watching_message">Вы хотите продолжить просмотр?</string>
<string name="no_button_text_with_time">Нет (%1$d)</string>
<plurals name="hours">
<item quantity="one">%1$s час</item>
<item quantity="few">%1$s часа</item>
Expand Down Expand Up @@ -584,4 +587,4 @@
<string name="clear_image_cache">Очистить кэш изображений</string>
<string name="preference_enable_trickplay">Включить trickplay при просмотре видео</string>
<string name="skip_forward_length">Длина перемотки вперед</string>
</resources>
</resources>
3 changes: 3 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,9 @@
<string name="segment_type_unknown">Unknown segments</string>
<string name="skip_forward_length">Skip forward length</string>
<string name="preference_enable_trickplay">Enable trickplay in video player</string>
<string name="still_watching_title">Are you still watching?</string>
<string name="continue_watching_message">Do you want to continue watching?</string>
<string name="no_button_text_with_time">No (%1$d)</string>
<plurals name="seconds">
<item quantity="one">%1$s second</item>
<item quantity="other">%1$s seconds</item>
Expand Down