Skip to content
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
42 changes: 42 additions & 0 deletions app/src/main/java/org/schabi/newpipe/player/Player.java
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@
import org.schabi.newpipe.util.StreamTypeUtil;
import org.schabi.newpipe.util.image.CoilHelper;

import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Optional;
import java.util.stream.IntStream;
Expand Down Expand Up @@ -263,6 +264,9 @@ public final class Player implements PlaybackListener, Listener {
@NonNull
private final HistoryRecordManager recordManager;

java.util.Timer sleepTimer;
java.time.Instant sleepTimerEnd;


/*//////////////////////////////////////////////////////////////////////////
// Constructor
Expand Down Expand Up @@ -2262,6 +2266,44 @@ public void setAudioTrack(@Nullable final String audioTrackId) {
}


public void setSleepTimer(@Nullable final long sleepMinutes) {
if (sleepTimer != null) {
sleepTimer.cancel();
sleepTimer.purge();
sleepTimer = null;
}

sleepTimerEnd = java.time.Instant.now().plus(sleepMinutes, ChronoUnit.MINUTES);

sleepTimer = new java.util.Timer();
//final Player thisPlayer = this;
final java.util.TimerTask task = new java.util.TimerTask() {


@Override
public void run() {
if (java.time.Instant.now().compareTo(sleepTimerEnd) >= 0) {
UIs.call(playerUi -> playerUi.onSleepTimerUpdate(0));
cancelSleepTimer();
return;
}

final long remainingMinutes = java.time.Instant.now().until(sleepTimerEnd,
ChronoUnit.MINUTES);
UIs.call(playerUi -> playerUi.onSleepTimerUpdate(remainingMinutes + 1));
Comment on lines +2291 to +2293
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is remainingMinutes + 1 used because until floors the value? If so, add an explaining comment.

}
};
sleepTimer.schedule(task, 1000, 1000);
}

public void cancelSleepTimer() {
if (sleepTimer != null) {
sleepTimer.cancel();
sleepTimer.purge();
sleepTimer = null;
}
}

@NonNull
public Context getContext() {
return context;
Expand Down
7 changes: 7 additions & 0 deletions app/src/main/java/org/schabi/newpipe/player/ui/PlayerUi.java
Original file line number Diff line number Diff line change
Expand Up @@ -209,4 +209,11 @@ public void onPlayQueueEdited() {
*/
public void onVideoSizeChanged(@NonNull final VideoSize videoSize) {
}

/**
* @param remainingTime the remaining sleep timer time, set to 0 to pause the player and
* disable the sleep timer
*/
public void onSleepTimerUpdate(final long remainingTime) {
}
Comment on lines +213 to +218
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please ensure that the time unit is mentioned.

}
111 changes: 111 additions & 0 deletions app/src/main/java/org/schabi/newpipe/player/ui/VideoPlayerUi.java
Original file line number Diff line number Diff line change
Expand Up @@ -128,12 +128,14 @@ private enum PlayButtonAction {
private static final int POPUP_MENU_ID_AUDIO_TRACK = 70;
private static final int POPUP_MENU_ID_PLAYBACK_SPEED = 79;
private static final int POPUP_MENU_ID_CAPTION = 89;
private static final int POPUP_MENU_ID_SLEEP_TIMER = 90; // TODO is 90 still available?

protected boolean isSomePopupMenuVisible = false;
private PopupMenu qualityPopupMenu;
private PopupMenu audioTrackPopupMenu;
protected PopupMenu playbackSpeedPopupMenu;
private PopupMenu captionPopupMenu;
private PopupMenu sleepTimerPopupMenu;


/*//////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -186,6 +188,8 @@ private void initViews() {
audioTrackPopupMenu = new PopupMenu(themeWrapper, binding.audioTrackTextView);
playbackSpeedPopupMenu = new PopupMenu(context, binding.playbackSpeed);
captionPopupMenu = new PopupMenu(themeWrapper, binding.captionTextView);
sleepTimerPopupMenu = new PopupMenu(themeWrapper, binding.sleepTimer);
buildSleepTimerMenu();

binding.progressBarLoadingPanel.getIndeterminateDrawable()
.setColorFilter(new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.MULTIPLY));
Expand All @@ -204,6 +208,9 @@ protected void initListeners() {
binding.audioTrackTextView.setOnClickListener(
makeOnClickListener(this::onAudioTracksClicked));
binding.playbackSpeed.setOnClickListener(makeOnClickListener(this::onPlaybackSpeedClicked));
binding.sleepTimer.setOnClickListener(makeOnClickListener(this::onSleepTimerClicked));
binding.sleepTimerCancel.setOnClickListener(
makeOnClickListener(this::onSleepTimerCancelClicked));

binding.playbackSeekBar.setOnSeekBarChangeListener(this);
binding.captionTextView.setOnClickListener(makeOnClickListener(this::onCaptionClicked));
Expand Down Expand Up @@ -1239,6 +1246,49 @@ private void buildCaptionMenu(@NonNull final List<String> availableLanguages) {
}
}

private void buildSleepTimerMenu() {
if (sleepTimerPopupMenu == null) {
return;
}
qualityPopupMenu.getMenu().removeGroup(POPUP_MENU_ID_SLEEP_TIMER);

final Resources res = context.getResources();
sleepTimerPopupMenu.getMenu().add(POPUP_MENU_ID_SLEEP_TIMER, 0, 0,
res.getString(R.string.sleep_timer_popup_title));

final String[] descriptions = context.getResources().getStringArray(
R.array.sleep_timer_description);
final int[] values = context.getResources().getIntArray(
R.array.sleep_timer_value);
for (int i = 0; i < descriptions.length && i < values.length; i++) {
String description = "";
try {
final int hours = values[i] / 60;
final int minutes = values[i] % 60;
if (hours != 0) {
description += String.format(res.getQuantityString(R.plurals.hours, hours),
hours);
}

if (minutes != 0) {
if (hours != 0) {
description += " ";
}
description += String.format(res.getQuantityString(R.plurals.minutes, minutes),
minutes);
}
} catch (final Resources.NotFoundException ignored) {
// if this happens, the translation is missing,
// and the english string will be displayed instead
description = descriptions[i];
}
sleepTimerPopupMenu.getMenu().add(POPUP_MENU_ID_SLEEP_TIMER, i + 1, i + 1, description);
}

sleepTimerPopupMenu.setOnMenuItemClickListener(this);
sleepTimerPopupMenu.setOnDismissListener(this);
}

protected abstract void onPlaybackSpeedClicked();

private void onQualityClicked() {
Expand All @@ -1255,6 +1305,19 @@ private void onAudioTracksClicked() {
isSomePopupMenuVisible = true;
}

private void onSleepTimerClicked() {
sleepTimerPopupMenu.show();
isSomePopupMenuVisible = true;
}

private void onSleepTimerCancelClicked() {
player.cancelSleepTimer();

binding.sleepTimerCancel.setVisibility(View.INVISIBLE);
binding.sleepTimerTextView.setVisibility(View.INVISIBLE);
binding.sleepTimerTextView.setText("0:00");
}

/**
* Called when an item of the quality selector or the playback speed selector is selected.
*/
Expand All @@ -1278,8 +1341,12 @@ public boolean onMenuItemClick(@NonNull final MenuItem menuItem) {

player.setPlaybackSpeed(speed);
binding.playbackSpeed.setText(formatSpeed(speed));
} else if (menuItem.getGroupId() == POPUP_MENU_ID_SLEEP_TIMER) {
onSleepTimerItemClick(menuItem);
return true;
}


return false;
}

Expand Down Expand Up @@ -1324,6 +1391,24 @@ private void onAudioTrackItemClick(@NonNull final MenuItem menuItem) {
binding.audioTrackTextView.setText(menuItem.getTitle());
}

private void onSleepTimerItemClick(@NonNull final MenuItem menuItem) {
final int menuItemIndex = menuItem.getItemId();
if (menuItemIndex == 0) {
return;
}

final int index = menuItemIndex - 1;
final int sleepTime = context.getResources().getIntArray(R.array.sleep_timer_value)[index];
final long remainingTimeHours = sleepTime / 60;
final long remainingTimeMinutes = sleepTime % 60;
final String text = String.format("%d:%02d", remainingTimeHours, remainingTimeMinutes);

player.setSleepTimer(sleepTime);
binding.sleepTimerCancel.setVisibility(View.VISIBLE);
binding.sleepTimerTextView.setVisibility(View.VISIBLE);
binding.sleepTimerTextView.setText(text);
}

/**
* Called when some popup menu is dismissed.
*/
Expand Down Expand Up @@ -1561,6 +1646,32 @@ public void onVideoSizeChanged(@NonNull final VideoSize videoSize) {
}
//endregion

@Override
public void onSleepTimerUpdate(final long remainingTime) {
if (remainingTime == 0) {
binding.sleepTimerTextView.post(new Runnable() {
public void run() {
player.pause();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this method is just used for UI updates. The pause() call should happen inside the player.

binding.sleepTimerCancel.setVisibility(View.INVISIBLE);
binding.sleepTimerTextView.setVisibility(View.INVISIBLE);
binding.sleepTimerTextView.setText("0:00");
}
});
return;
}

final long remainingTimeHours = remainingTime / 60;
final long remainingTimeMinutes = remainingTime % 60;
final String text = String.format("%d:%02d", remainingTimeHours, remainingTimeMinutes);

// Since this callback can/will be called from a different thread, we need to run set
// the code in the UI thread
binding.sleepTimerTextView.post(new Runnable() {
public void run() {
binding.sleepTimerTextView.setText(text);
}
});
}

/*//////////////////////////////////////////////////////////////////////////
// SurfaceHolderCallback helpers
Expand Down
10 changes: 10 additions & 0 deletions app/src/main/res/drawable/ic_alarm.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="@color/defaultIconTint"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M12.5 8H11v6l4.75 2.85 0.75-1.23-4-2.37zm4.837-6.19 4.607 3.845-1.28 1.535-4.61-3.843zm-10.674 0 1.282 1.536L3.337 7.19l-1.28-1.536zM12 4a9 9 0 1 0 0.001 18.001A9 9 0 0 0 12 4zm0 16c-3.86 0-7-3.14-7-7s3.14-7 7-7 7 3.14 7 7-3.14 7-7 7z" />
</vector>
10 changes: 10 additions & 0 deletions app/src/main/res/drawable/ic_alarm_off.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="@color/defaultIconTint"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M10.04 6.29C10.66 6.11 11.32 6 12 6c3.86 0 7 3.14 7 7 0 0.68-0.11 1.34-0.29 1.96l1.56 1.56c0.47-1.08 0.73-2.27 0.73-3.52A9 9 0 0 0 8.47 4.72l1.57 1.57zm7.297-4.48 4.607 3.845-1.28 1.535-4.61-3.843zM3.02 2.1 1.61 3.51l1.37 1.37-0.92 0.77 1.28 1.54 1.06-0.88 0.8 0.8A8.964 8.964 0 0 0 3 13a9 9 0 0 0 9 9c2.25 0 4.31-0.83 5.89-2.2l2.1 2.1 1.41-1.41L3.02 2.1zM12 20c-3.86 0-7-3.14-7-7 0-1.7 0.61-3.26 1.62-4.47l9.85 9.85A6.956 6.956 0 0 1 12 20zM7.48 3.73l0.46-0.38-1.28-1.54-0.6 0.5z" />
</vector>
Loading