Skip to content

Commit

Permalink
UI component improvements
Browse files Browse the repository at this point in the history
- Make sure no events are posted on PlaybackControlView
  if it's not attached to a window. This can cause leaks.
  The target hide time is recorded if necessary and
  processed when the view is re-attached.
- Deduplicated PlaybackControlView.VisibilityListener
  invocations.
- Fixed timeouts to be more intuitive (I think).
- Fixed initial visibility of PlaybackControlView when
  used as part of SimpleExoPlayerView.
- Made some more attributes configurable from layout xml.

Issue: #1908
Issue: #1919
Issue: #1923

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=135679988
  • Loading branch information
ojw28 committed Oct 11, 2016
1 parent 97020e0 commit 91f8328
Show file tree
Hide file tree
Showing 3 changed files with 196 additions and 73 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
package com.google.android.exoplayer2.ui;

import android.content.Context;
import android.content.res.TypedArray;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.LayoutInflater;
Expand Down Expand Up @@ -52,7 +54,7 @@ public interface VisibilityListener {

public static final int DEFAULT_FAST_FORWARD_MS = 15000;
public static final int DEFAULT_REWIND_MS = 5000;
public static final int DEFAULT_SHOW_DURATION_MS = 5000;
public static final int DEFAULT_SHOW_TIMEOUT_MS = 5000;

private static final int PROGRESS_BAR_MAX = 1000;
private static final long MAX_POSITION_FOR_SEEK_TO_PREVIOUS = 3000;
Expand All @@ -74,9 +76,10 @@ public interface VisibilityListener {
private VisibilityListener visibilityListener;

private boolean dragging;
private int rewindMs = DEFAULT_REWIND_MS;
private int fastForwardMs = DEFAULT_FAST_FORWARD_MS;
private int showDurationMs = DEFAULT_SHOW_DURATION_MS;
private int rewindMs;
private int fastForwardMs;
private int showTimeoutMs;
private long hideAtMs;

private final Runnable updateProgressAction = new Runnable() {
@Override
Expand All @@ -103,6 +106,22 @@ public PlaybackControlView(Context context, AttributeSet attrs) {
public PlaybackControlView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);

rewindMs = DEFAULT_REWIND_MS;
fastForwardMs = DEFAULT_FAST_FORWARD_MS;
showTimeoutMs = DEFAULT_SHOW_TIMEOUT_MS;
if (attrs != null) {
TypedArray a = context.getTheme().obtainStyledAttributes(attrs,
R.styleable.PlaybackControlView, 0, 0);
try {
rewindMs = a.getInt(R.styleable.PlaybackControlView_rewind_increment, rewindMs);
fastForwardMs = a.getInt(R.styleable.PlaybackControlView_fastforward_increment,
fastForwardMs);
showTimeoutMs = a.getInt(R.styleable.PlaybackControlView_show_timeout, showTimeoutMs);
} finally {
a.recycle();
}
}

currentWindow = new Timeline.Window();
formatBuilder = new StringBuilder();
formatter = new Formatter(formatBuilder, Locale.getDefault());
Expand All @@ -124,7 +143,6 @@ public PlaybackControlView(Context context, AttributeSet attrs, int defStyleAttr
rewindButton.setOnClickListener(componentListener);
fastForwardButton = findViewById(R.id.ffwd);
fastForwardButton.setOnClickListener(componentListener);
updateAll();
}

/**
Expand Down Expand Up @@ -169,6 +187,7 @@ public void setVisibilityListener(VisibilityListener listener) {
*/
public void setRewindIncrementMs(int rewindMs) {
this.rewindMs = rewindMs;
updateNavigation();
}

/**
Expand All @@ -178,51 +197,60 @@ public void setRewindIncrementMs(int rewindMs) {
*/
public void setFastForwardIncrementMs(int fastForwardMs) {
this.fastForwardMs = fastForwardMs;
updateNavigation();
}

/**
* Sets the duration to show the playback control in milliseconds.
* Returns the playback controls timeout. The playback controls are automatically hidden after
* this duration of time has elapsed without user input.
*
* @param showDurationMs The duration in milliseconds.
* @return The duration in milliseconds. A non-positive value indicates that the controls will
* remain visible indefinitely.
*/
public void setShowDurationMs(int showDurationMs) {
this.showDurationMs = showDurationMs;
public int getShowTimeoutMs() {
return showTimeoutMs;
}

/**
* Shows the controller for the duration last passed to {@link #setShowDurationMs(int)}, or for
* {@link #DEFAULT_SHOW_DURATION_MS} if {@link #setShowDurationMs(int)} has not been called.
* Sets the playback controls timeout. The playback controls are automatically hidden after this
* duration of time has elapsed without user input.
*
* @param showTimeoutMs The duration in milliseconds. A non-positive value will cause the controls
* to remain visible indefinitely.
*/
public void show() {
show(showDurationMs);
public void setShowTimeoutMs(int showTimeoutMs) {
this.showTimeoutMs = showTimeoutMs;
}

/**
* Shows the controller for the {@code durationMs}. If {@code durationMs} is 0 the controller is
* shown until {@link #hide()} is called.
*
* @param durationMs The duration in milliseconds.
* Shows the playback controls. If {@link #getShowTimeoutMs()} is positive then the controls will
* be automatically hidden after this duration of time has elapsed without user input.
*/
public void show(int durationMs) {
setVisibility(VISIBLE);
if (visibilityListener != null) {
visibilityListener.onVisibilityChange(getVisibility());
public void show() {
if (!isVisible()) {
setVisibility(VISIBLE);
if (visibilityListener != null) {
visibilityListener.onVisibilityChange(getVisibility());
}
updateAll();
}
updateAll();
showDurationMs = durationMs;
hideDeferred();
// Call hideAfterTimeout even if already visible to reset the timeout.
hideAfterTimeout();
}

/**
* Hides the controller.
*/
public void hide() {
setVisibility(GONE);
if (visibilityListener != null) {
visibilityListener.onVisibilityChange(getVisibility());
if (isVisible()) {
setVisibility(GONE);
if (visibilityListener != null) {
visibilityListener.onVisibilityChange(getVisibility());
}
removeCallbacks(updateProgressAction);
removeCallbacks(hideAction);
hideAtMs = C.TIME_UNSET;
}
removeCallbacks(updateProgressAction);
removeCallbacks(hideAction);
}

/**
Expand All @@ -232,10 +260,15 @@ public boolean isVisible() {
return getVisibility() == VISIBLE;
}

private void hideDeferred() {
private void hideAfterTimeout() {
removeCallbacks(hideAction);
if (showDurationMs > 0) {
postDelayed(hideAction, showDurationMs);
if (showTimeoutMs > 0) {
hideAtMs = SystemClock.uptimeMillis() + showTimeoutMs;
if (isAttachedToWindow()) {
postDelayed(hideAction, showTimeoutMs);
}
} else {
hideAtMs = C.TIME_UNSET;
}
}

Expand All @@ -246,7 +279,7 @@ private void updateAll() {
}

private void updatePlayPauseButton() {
if (!isVisible()) {
if (!isVisible() || !isAttachedToWindow()) {
return;
}
boolean playing = player != null && player.getPlayWhenReady();
Expand All @@ -258,7 +291,7 @@ private void updatePlayPauseButton() {
}

private void updateNavigation() {
if (!isVisible()) {
if (!isVisible() || !isAttachedToWindow()) {
return;
}
Timeline currentTimeline = player != null ? player.getCurrentTimeline() : null;
Expand All @@ -276,13 +309,13 @@ private void updateNavigation() {
}
setButtonEnabled(enablePrevious , previousButton);
setButtonEnabled(enableNext, nextButton);
setButtonEnabled(isSeekable, fastForwardButton);
setButtonEnabled(isSeekable, rewindButton);
setButtonEnabled(fastForwardMs > 0 && isSeekable, fastForwardButton);
setButtonEnabled(rewindMs > 0 && isSeekable, rewindButton);
progressBar.setEnabled(isSeekable);
}

private void updateProgress() {
if (!isVisible()) {
if (!isVisible() || !isAttachedToWindow()) {
return;
}
long duration = player == null ? 0 : player.getDuration();
Expand Down Expand Up @@ -377,13 +410,40 @@ private void next() {
}

private void rewind() {
if (rewindMs <= 0) {
return;
}
player.seekTo(Math.max(player.getCurrentPosition() - rewindMs, 0));
}

private void fastForward() {
if (fastForwardMs <= 0) {
return;
}
player.seekTo(Math.min(player.getCurrentPosition() + fastForwardMs, player.getDuration()));
}

@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
if (hideAtMs != C.TIME_UNSET) {
long delayMs = hideAtMs - SystemClock.uptimeMillis();
if (delayMs <= 0) {
hide();
} else {
postDelayed(hideAction, delayMs);
}
}
updateAll();
}

@Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
removeCallbacks(updateProgressAction);
removeCallbacks(hideAction);
}

@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (player == null || event.getAction() != KeyEvent.ACTION_DOWN) {
Expand Down Expand Up @@ -440,7 +500,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
public void onStopTrackingTouch(SeekBar seekBar) {
dragging = false;
player.seekTo(positionValue(seekBar.getProgress()));
hideDeferred();
hideAfterTimeout();
}

@Override
Expand Down Expand Up @@ -485,7 +545,7 @@ public void onClick(View view) {
} else if (playButton == view) {
player.setPlayWhenReady(!player.getPlayWhenReady());
}
hideDeferred();
hideAfterTimeout();
}

}
Expand Down
Loading

1 comment on commit 91f8328

@nightwolf738
Copy link

Choose a reason for hiding this comment

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

When can we have this fix from maven repo for gradle? @ojw28

Please sign in to comment.