Skip to content
This repository has been archived by the owner on Jul 22, 2024. It is now read-only.

Closes #3269 Adds Tooltips to the video controls #3529

Merged
merged 1 commit into from
Jun 24, 2020
Merged
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
22 changes: 10 additions & 12 deletions app/src/common/shared/org/mozilla/vrbrowser/ui/views/UIButton.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ public class UIButton extends AppCompatImageButton implements CustomUIButton {
private int mTooltipDelay;
private float mTooltipDensity;
private @LayoutRes int mTooltipLayout;
private boolean mCurvedTooltip = true;
private boolean mCurvedTooltip;
private boolean mCurvedTooltipOverridden;
private ViewUtils.TooltipPosition mTooltipPosition;
private boolean mIsPrivate;
private boolean mIsActive;
Expand Down Expand Up @@ -85,6 +86,8 @@ public UIButton(Context context, AttributeSet attrs, int defStyleAttr) {
mTooltipText = arr.getString(0);
}
mTooltipLayout = attributes.getResourceId(R.styleable.UIButton_tooltipLayout, R.layout.tooltip);
mCurvedTooltip = attributes.getBoolean(R.styleable.UIButton_tooltipCurved, false);
mCurvedTooltipOverridden = attributes.hasValue(R.styleable.UIButton_tooltipCurved);
mClipDrawable = (ClipDrawable)attributes.getDrawable(R.styleable.UIButton_clipDrawable);
mClipColor = attributes.getColor(R.styleable.UIButton_clipColor, 0);
attributes.recycle();
Expand Down Expand Up @@ -154,13 +157,6 @@ private void setTooltipTextInternal(@Nullable CharSequence tooltipText) {
super.setTooltipText(tooltipText);
}

public void setCurvedTooltip(boolean aEnabled) {
mCurvedTooltip = aEnabled;
if (mTooltipView != null) {
mTooltipView.setCurvedMode(aEnabled);
}
}

@Override
public boolean onHoverEvent(MotionEvent event) {
if (getTooltipText() != null) {
Expand Down Expand Up @@ -304,7 +300,6 @@ public void run() {
if (mTooltipView == null) {
mTooltipView = new TooltipWidget(getContext(), mTooltipLayout);
}
mTooltipView.setCurvedMode(mCurvedTooltip);
if (getTooltipText() != null) {
mTooltipView.setText(getTooltipText().toString());
}
Expand All @@ -314,6 +309,12 @@ public void run() {
UIWidget parent = ViewUtils.getParentWidget(UIButton.this);
parent.offsetDescendantRectToMyCoords(UIButton.this, offsetViewBounds);

// Use parent curved mode unless it has been overridden in the tooltip XML properties
mTooltipView.setCurvedMode(parent.getPlacement().cylinder);
if (mCurvedTooltipOverridden) {
mTooltipView.setCurvedMode(mCurvedTooltip);
}

float ratio = WidgetPlacement.viewToWidgetRatio(getContext(), parent);

mTooltipView.getPlacement().parentHandle = parent.getHandle();
Expand All @@ -323,16 +324,13 @@ public void run() {
mTooltipView.getPlacement().anchorY = 1.0f;
mTooltipView.getPlacement().parentAnchorY = 0.0f;
mTooltipView.getPlacement().translationX = (offsetViewBounds.left + UIButton.this.getWidth() / 2.0f) * ratio;
mTooltipView.getPlacement().translationY = -offsetViewBounds.top * ratio;

} else {
mTooltipView.getPlacement().anchorY = 0.0f;
mTooltipView.getPlacement().parentAnchorY = 1.0f;
mTooltipView.getPlacement().translationX = (offsetViewBounds.left + UIButton.this.getHeight() / 2.0f) * ratio;
mTooltipView.getPlacement().translationY = offsetViewBounds.top * ratio;
}

mTooltipView.setCurvedMode(false);
mTooltipView.show(UIWidget.CLEAR_FOCUS);
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,40 +7,27 @@

import android.content.Context;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.TextView;

import org.mozilla.telemetry.schedule.jobscheduler.TelemetryJobService;
import org.mozilla.vrbrowser.R;
import androidx.databinding.DataBindingUtil;

import org.mozilla.geckoview.MediaElement;
import org.mozilla.vrbrowser.R;
import org.mozilla.vrbrowser.browser.Media;
import org.mozilla.vrbrowser.browser.SettingsStore;
import org.mozilla.vrbrowser.databinding.MediaControlsBinding;
import org.mozilla.vrbrowser.ui.views.MediaSeekBar;
import org.mozilla.vrbrowser.ui.views.UIButton;
import org.mozilla.vrbrowser.ui.views.VolumeControl;
import org.mozilla.vrbrowser.ui.widgets.menus.VideoProjectionMenuWidget;

public class MediaControlsWidget extends UIWidget implements MediaElement.Delegate {

private MediaControlsBinding mBinding;
private Media mMedia;
private MediaSeekBar mSeekBar;
private VolumeControl mVolumeControl;
private UIButton mMediaPlayButton;
private UIButton mMediaSeekBackButton;
private UIButton mMediaSeekForwardButton;
private UIButton mMediaProjectionButton;
private UIButton mMediaVolumeButton;
private UIButton mMediaBackButton;
private TextView mMediaSeekLabel;
private Drawable mPlayIcon;
private Drawable mPauseIcon;
private Drawable mVolumeIcon;
private Drawable mMutedIcon;
private Runnable mBackHandler;
private boolean mPlayOnSeekEnd;
private Rect mOffsetViewBounds;
Expand All @@ -66,31 +53,21 @@ public MediaControlsWidget(Context aContext, AttributeSet aAttrs, int aDefStyle)
}

private void initialize(Context aContext) {
inflate(aContext, R.layout.media_controls, this);

mSeekBar = findViewById(R.id.mediaControlSeekBar);
mVolumeControl = findViewById(R.id.volumeControl);
mMediaPlayButton = findViewById(R.id.mediaPlayButton);
mMediaSeekBackButton = findViewById(R.id.mediaSeekBackwardButton);
mMediaSeekForwardButton = findViewById(R.id.mediaSeekForwardButton);
mMediaProjectionButton = findViewById(R.id.mediaProjectionButton);
mMediaVolumeButton = findViewById(R.id.mediaVolumeButton);
mMediaBackButton = findViewById(R.id.mediaBackButton);
mMediaSeekLabel = findViewById(R.id.mediaControlSeekLabel);
mPlayIcon = aContext.getDrawable(R.drawable.ic_icon_media_play);
mPauseIcon = aContext.getDrawable(R.drawable.ic_icon_media_pause);
mMutedIcon = aContext.getDrawable(R.drawable.ic_icon_media_volume_muted);
mVolumeIcon = aContext.getDrawable(R.drawable.ic_icon_media_volume);
LayoutInflater inflater = LayoutInflater.from(getContext());
mBinding = DataBindingUtil.inflate(inflater, R.layout.media_controls, this, true);
mBinding.setPlaying(true);
mBinding.setMuted(false);

mOffsetViewBounds = new Rect();

mVolumeCtrlRunnable = () -> {
if ((mHideVolumeSlider) && (mVolumeControl.getVisibility() == View.VISIBLE)) {
mVolumeControl.setVisibility(View.INVISIBLE);
if ((mHideVolumeSlider) && (mBinding.volumeControl.getVisibility() == View.VISIBLE)) {
mBinding.volumeControl.setVisibility(View.INVISIBLE);
stopVolumeCtrlHandler();
}
};

mMediaPlayButton.setOnClickListener(v -> {
mBinding.mediaPlayButton.setOnClickListener(v -> {
if (mMedia.isEnded()) {
mMedia.seek(0);
mMedia.play();
Expand All @@ -100,24 +77,24 @@ private void initialize(Context aContext) {
mMedia.play();
}

mMediaPlayButton.requestFocusFromTouch();
mBinding.mediaPlayButton.requestFocusFromTouch();
});

mMediaSeekBackButton.setOnClickListener(v -> {
mBinding.mediaSeekBackwardButton.setOnClickListener(v -> {
mMedia.seek(Math.max(0, mMedia.getCurrentTime() - 10.0f));
mMediaSeekBackButton.requestFocusFromTouch();
mBinding.mediaSeekBackwardButton.requestFocusFromTouch();
});

mMediaSeekForwardButton.setOnClickListener(v -> {
mBinding.mediaSeekForwardButton.setOnClickListener(v -> {
double t = mMedia.getCurrentTime() + 30;
if (mMedia.getDuration() > 0) {
t = Math.min(mMedia.getDuration(), t);
}
mMedia.seek(t);
mMediaSeekForwardButton.requestFocusFromTouch();
mBinding.mediaSeekForwardButton.requestFocusFromTouch();
});

mMediaProjectionButton.setOnClickListener(v -> {
mBinding.mediaProjectionButton.setOnClickListener(v -> {
WidgetPlacement placement = mProjectionMenu.getPlacement();
placement.parentHandle = this.getHandle();
placement.worldWidth = 0.5f;
Expand All @@ -138,30 +115,30 @@ private void initialize(Context aContext) {
mWidgetManager.updateWidget(mProjectionMenu);
});

mMediaVolumeButton.setOnClickListener(v -> {
mBinding.mediaVolumeButton.setOnClickListener(v -> {
if (mMedia.isMuted()) {
mMedia.setMuted(false);
} else {
mMedia.setMuted(true);
mVolumeControl.setVolume(0);
mBinding.volumeControl.setVolume(0);
}
mMediaVolumeButton.requestFocusFromTouch();
mBinding.mediaVolumeButton.requestFocusFromTouch();
});

mMediaBackButton.setOnClickListener(v -> {
mBinding.mediaBackButton.setOnClickListener(v -> {
if (mBackHandler != null) {
mBackHandler.run();
}
mMediaBackButton.requestFocusFromTouch();
mBinding.mediaBackButton.requestFocusFromTouch();
});

mSeekBar.setDelegate(new MediaSeekBar.Delegate() {
mBinding.mediaControlSeekBar.setDelegate(new MediaSeekBar.Delegate() {
@Override
public void onSeekDragStart() {
mPlayOnSeekEnd = mMedia.isPlaying();
mMediaSeekLabel.setVisibility(View.VISIBLE);
mBinding.mediaControlSeekLabel.setVisibility(View.VISIBLE);
mMedia.pause();
mSeekBar.requestFocusFromTouch();
mBinding.mediaControlSeekBar.requestFocusFromTouch();
}

@Override
Expand All @@ -174,42 +151,42 @@ public void onSeekDragEnd() {
if (mPlayOnSeekEnd) {
mMedia.play();
}
mMediaSeekLabel.setVisibility(View.GONE);
mBinding.mediaControlSeekLabel.setVisibility(View.GONE);
}

@Override
public void onSeekHoverStart() {
mMediaSeekLabel.setVisibility(View.VISIBLE);
mBinding.mediaControlSeekLabel.setVisibility(View.VISIBLE);
}

@Override
public void onSeekHoverEnd() {
mMediaSeekLabel.setVisibility(View.GONE);
mBinding.mediaControlSeekLabel.setVisibility(View.GONE);
}

@Override
public void onSeekPreview(String aText, double aRatio) {
mMediaSeekLabel.setText(aText);
View childView = mSeekBar.getSeekBarView();
mBinding.mediaControlSeekLabel.setText(aText);
View childView = mBinding.mediaControlSeekBar.getSeekBarView();
childView.getDrawingRect(mOffsetViewBounds);
MediaControlsWidget.this.offsetDescendantRectToMyCoords(childView, mOffsetViewBounds);

FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) mMediaSeekLabel.getLayoutParams();
params.setMarginStart(mOffsetViewBounds.left + (int) (aRatio * mOffsetViewBounds.width()) - mMediaSeekLabel.getMeasuredWidth() / 2);
mMediaSeekLabel.setLayoutParams(params);
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) mBinding.mediaControlSeekLabel.getLayoutParams();
params.setMarginStart(mOffsetViewBounds.left + (int) (aRatio * mOffsetViewBounds.width()) - mBinding.mediaControlSeekLabel.getMeasuredWidth() / 2);
mBinding.mediaControlSeekLabel.setLayoutParams(params);
}
});


mVolumeControl.setDelegate(new VolumeControl.Delegate() {
mBinding.volumeControl.setDelegate(new VolumeControl.Delegate() {

@Override
public void onVolumeChange(double aVolume) {
mMedia.setVolume(aVolume);
if (mMedia.isMuted()) {
mMedia.setMuted(false);
}
mVolumeControl.requestFocusFromTouch();
mBinding.volumeControl.requestFocusFromTouch();
}

@Override
Expand Down Expand Up @@ -237,22 +214,22 @@ public void onSeekBarActionCancelled() {

return false;
});
mMediaVolumeButton.setOnHoverListener((v, event) -> {
mBinding.mediaVolumeButton.setOnHoverListener((v, event) -> {
float startY = v.getY();
float maxY = startY + v.getHeight();
//for this we only hide on the left side of volume button or outside y area of button
if ((event.getX() <= 0) || (event.getX() >= v.getWidth()) || (!(event.getY() > startY && event.getY() < maxY))) {
mHideVolumeSlider = true;
startVolumeCtrlHandler();
} else {
mVolumeControl.setVisibility(View.VISIBLE);
mBinding.volumeControl.setVisibility(View.VISIBLE);
mHideVolumeSlider = false;
stopVolumeCtrlHandler();
}
return false;
});

mVolumeControl.setOnHoverListener((v, event) -> {
mBinding.volumeControl.setOnHoverListener((v, event) -> {
float startY = 0;
float maxY = startY + v.getHeight();
if ((event.getX() > 0 && event.getX() < v.getWidth()) && (event.getY() > startY && event.getY() < maxY)) {
Expand All @@ -273,6 +250,7 @@ else if ((event.getX() <= 0) || (event.getX() >= v.getWidth()) || (!(event.getY(
@Override
protected void initializeWidgetPlacement(WidgetPlacement aPlacement) {
Context context = getContext();
aPlacement.worldWidth = WidgetPlacement.floatDimension(getContext(), R.dimen.media_controls_world_width);
aPlacement.width = WidgetPlacement.dpDimension(context, R.dimen.media_controls_container_width);
aPlacement.height = WidgetPlacement.dpDimension(context, R.dimen.media_controls_container_height);
aPlacement.translationY = WidgetPlacement.unitFromMeters(getContext(), R.dimen.settings_world_y) -
Expand Down Expand Up @@ -311,11 +289,11 @@ public void setMedia(Media aMedia) {
}
mMedia = aMedia;
boolean enabled = mMedia != null;
mMediaPlayButton.setEnabled(enabled);
mMediaVolumeButton.setEnabled(enabled);
mMediaSeekForwardButton.setEnabled(enabled);
mMediaSeekBackButton.setEnabled(enabled);
mSeekBar.setEnabled(enabled);
mBinding.mediaPlayButton.setEnabled(enabled);
mBinding.mediaVolumeButton.setEnabled(enabled);
mBinding.mediaSeekForwardButton.setEnabled(enabled);
mBinding.mediaSeekBackwardButton.setEnabled(enabled);
mBinding.mediaControlSeekBar.setEnabled(enabled);

if (mMedia == null) {
return;
Expand All @@ -330,16 +308,16 @@ public void setMedia(Media aMedia) {
}

public void setProjectionSelectorEnabled(boolean aEnabled) {
mMediaProjectionButton.setEnabled(aEnabled);
mBinding.mediaProjectionButton.setEnabled(aEnabled);
}

// Media Element delegate
@Override
public void onPlaybackStateChange(MediaElement mediaElement, int playbackState) {
if (playbackState == MediaElement.MEDIA_STATE_PLAY) {
mMediaPlayButton.setImageDrawable(mPauseIcon);
mBinding.setPlaying(true);
} else if (playbackState == MediaElement.MEDIA_STATE_PAUSE) {
mMediaPlayButton.setImageDrawable(mPlayIcon);
mBinding.setPlaying(false);
}
}

Expand All @@ -354,36 +332,36 @@ public void onMetadataChange(MediaElement mediaElement, MediaElement.Metadata me
if (metaData == null) {
return;
}
mSeekBar.setDuration(metaData.duration);
mBinding.mediaControlSeekBar.setDuration(metaData.duration);
if (metaData.audioTrackCount == 0) {
mMediaVolumeButton.setImageDrawable(mMutedIcon);
mMediaVolumeButton.setEnabled(false);
mBinding.setMuted(true);
mBinding.mediaVolumeButton.setEnabled(false);
} else {
mMediaVolumeButton.setEnabled(true);
mBinding.mediaVolumeButton.setEnabled(true);
}
mSeekBar.setSeekable(metaData.isSeekable);
mBinding.mediaControlSeekBar.setSeekable(metaData.isSeekable);
}

@Override
public void onLoadProgress(MediaElement mediaElement, MediaElement.LoadProgressInfo progressInfo) {
if (progressInfo.buffered != null) {
mSeekBar.setBuffered(progressInfo.buffered[progressInfo.buffered.length - 1].end);
mBinding.mediaControlSeekBar.setBuffered(progressInfo.buffered[progressInfo.buffered.length - 1].end);
}
}

@Override
public void onVolumeChange(MediaElement mediaElement, double volume, boolean muted) {
if (!mMediaVolumeButton.isEnabled()) {
if (!mBinding.mediaVolumeButton.isEnabled()) {
return;
}
mMediaVolumeButton.setImageDrawable(muted ? mMutedIcon : mVolumeIcon);
mVolumeControl.setVolume(volume);
mVolumeControl.setMuted(muted);
mBinding.setMuted(muted);
mBinding.volumeControl.setVolume(volume);
mBinding.volumeControl.setMuted(muted);
}

@Override
public void onTimeChange(MediaElement mediaElement, double time) {
mSeekBar.setCurrentTime(time);
mBinding.mediaControlSeekBar.setCurrentTime(time);
}

@Override
Expand Down
Loading