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

Add playback speed function and UI #97

Open
wants to merge 30 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
46a59e9
Add a setting ImageButton. Add a onClick callback method. Add a Playb…
dahis39 May 22, 2019
3c9c382
Move to a better location
dahis39 May 22, 2019
e82468c
Add playback speed enum and strings
dahis39 May 22, 2019
bbed653
Rename method. Bring the method call all the way up into activity layer.
dahis39 May 23, 2019
8e83027
Get all the wiring going correctly.
dahis39 May 23, 2019
e50ebf5
Get nested alert dialop going
dahis39 May 23, 2019
db6eb61
Clean ups
dahis39 May 23, 2019
dd5f566
Add a dismiss method
dahis39 May 24, 2019
7f2d11c
Get playback menu title to includes current speed.
dahis39 May 24, 2019
9db6ee3
Make method more general. Add TODO
dahis39 May 24, 2019
fc71163
Code clean up and small refacotring
dahis39 May 24, 2019
faa7c07
Fix margin setting
dahis39 May 24, 2019
5c77a00
Use proper images
dahis39 May 24, 2019
658343b
Better naming
dahis39 May 24, 2019
990719e
Remove my no longer needed TODO and commented out code
dahis39 May 24, 2019
59f6995
Add remove spacing
dahis39 May 24, 2019
4678b09
Unify naming convention
dahis39 May 24, 2019
f93b1d9
Remove unused import
dahis39 May 24, 2019
0b79b7a
Use a list that shows current selected item
dahis39 May 24, 2019
18b74cc
Not all titles will be dynamic
dahis39 May 24, 2019
1aea9e9
Write title strings in xml. Use the shorthanded getString method.
dahis39 May 24, 2019
2983ff9
Move buildOption method after inject context
dahis39 May 24, 2019
8c4176e
Move buildOptions all the way out
dahis39 May 24, 2019
e6f367e
Avoid duplicated initialization.
dahis39 May 24, 2019
f2852a4
Pass in activity instead of context. Make dialog not trigger system n…
dahis39 May 24, 2019
eed9635
Keep the navigation bar not appearing when show a alert dialog.
dahis39 May 24, 2019
db33c62
Use newer style for list toArray.
dahis39 May 24, 2019
6821d97
Use single method.
dahis39 May 24, 2019
3aeee98
Better comment
dahis39 May 24, 2019
c5b0571
Specify the activity requirement for SettingMenu since the activity n…
dahis39 May 24, 2019
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
8 changes: 8 additions & 0 deletions app/src/main/java/com/tubitv/media/demo/di/FSMModuleReal.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import com.tubitv.media.models.CuePointsRetriever;
import com.tubitv.media.models.MediaModel;
import com.tubitv.media.models.VpaidClient;
import com.tubitv.media.utilities.PlaybackSettingMenu;

import dagger.Module;
import dagger.Provides;
import java.util.ArrayList;
Expand Down Expand Up @@ -206,4 +208,10 @@ public void run() {
VpaidClient provideVpaidClient(FsmPlayer player) {
return new TubiVPAID(webView, new Handler(Looper.getMainLooper()), player);
}

@ActicityScope
@Provides
PlaybackSettingMenu providePlaybackSettingMenu() {
return new PlaybackSettingMenu();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import com.tubitv.media.models.MediaModel;
import com.tubitv.media.models.VpaidClient;
import com.tubitv.media.utilities.ExoPlayerLogger;
import com.tubitv.media.utilities.PlaybackSettingMenu;
import com.tubitv.media.utilities.PlayerDeviceUtils;
import com.tubitv.media.utilities.Utils;
import com.tubitv.media.views.UIControllerView;
Expand Down Expand Up @@ -71,6 +72,9 @@ public class DoubleViewTubiPlayerActivity extends TubiPlayerActivity implements
PlayerAdLogicController playerComponentController;
@Inject
VpaidClient vpaidClient;
@Inject
PlaybackSettingMenu playbackSettingMenu;

private DefaultTrackSelector trackSelector_ad;

protected AdRetriever getAdRetriever() {
Expand Down Expand Up @@ -239,6 +243,10 @@ public void prepareFSM() {
} else {
fsmPlayer.transit(Input.INITIALIZE);
}

playbackSettingMenu.setContentPlayer(mMoviePlayer);
playbackSettingMenu.setActivity(this);
playbackSettingMenu.buildSettingMenuOptions();
}

@Override
Expand Down Expand Up @@ -332,6 +340,11 @@ public void onPlayToggle(@Nullable MediaModel mediaModel, boolean playing) {
// ExoPlayerLogger.v(TAG, mediaModel.getMediaName() + ": " + mediaModel.toString() + " onPlayToggle :");
}

@Override
public void onPlaybackSettingClick() {
playbackSettingMenu.show();
}

@Override
public void onLearnMoreClick(@NonNull MediaModel mediaModel) {
// ExoPlayerLogger.v(TAG, mediaModel.getMediaName() + ": " + mediaModel.toString() + " onLearnMoreClick :" + mediaModel.getClickThroughUrl());
Expand Down
10 changes: 10 additions & 0 deletions lib/src/main/java/com/tubitv/media/bindings/UserController.java
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,16 @@ public void clickCurrentAd() {
mPlaybackActionCallback.onLearnMoreClick(mMediaModel);
}

@Override
public void clickPlaybackSetting() {
if (mPlaybackActionCallback == null) {
ExoPlayerLogger.w(TAG, "clickPlaybackSetting params is null");
return;
}

mPlaybackActionCallback.onPlaybackSettingClick();
}

@Override
public String getCurrentVideoName() {
return videoName.get();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import com.tubitv.media.models.CuePointsRetriever;
import com.tubitv.media.models.MediaModel;
import com.tubitv.media.models.VpaidClient;
import com.tubitv.media.utilities.PlaybackSettingMenu;

import dagger.Module;
import dagger.Provides;
import java.util.ArrayList;
Expand Down Expand Up @@ -160,4 +162,10 @@ public String getVastXml() {
}
};
}

@ActicityScope
@Provides
PlaybackSettingMenu providePlaybackSettingMenu() {
return new PlaybackSettingMenu();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ public interface PlaybackActionCallback {

void onLearnMoreClick(@NonNull MediaModel mediaModel);

void onPlaybackSettingClick();

void onSubtitles(@Nullable MediaModel mediaModel, boolean enabled);

void onQuality(@Nullable MediaModel mediaModel);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ public interface TubiPlaybackControlInterface {

void clickCurrentAd();

void clickPlaybackSetting();

//display control
String getCurrentVideoName();

Expand Down
69 changes: 69 additions & 0 deletions lib/src/main/java/com/tubitv/media/models/PlaybackSpeed.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.tubitv.media.models;

import android.content.Context;
import android.support.annotation.NonNull;

import com.tubitv.media.R;

import java.util.ArrayList;
import java.util.Arrays;

/**
* Playback Speed in percent.
* e.g.
* 1.0f = normal speed
* 0.75f = three-quarter of normal speed
* 2.0f = double normal speed
*/
public enum PlaybackSpeed {

// Ordering here will effect ordering in UI
A_QUARTER(R.string.playback_speed_a_quarter, 0.25f),
A_HALF(R.string.playback_speed_a_half, 0.5f),
THREE_QUARTER(R.string.playback_speed_three_quarter, 0.75f),
NORMAL(R.string.playback_speed_normal, 1f),
ONE_AND_A_QUARTER(R.string.playback_speed_one_and_a_quarter, 1.25f),
ONE_AND_A_HALF(R.string.playback_speed_one_and_a_half, 1.5f),
ONE_AND_THREE_QUARTER(R.string.playback_speed_one_and_three_quarter, 1.75f),
TWO(R.string.playback_speed_two, 2f);

private static final float EPSILON = 0.01f;

private final int stringResourceId;
private final float speedValue;

PlaybackSpeed(int stringResourceId, float speedValue) {
this.stringResourceId = stringResourceId;
this.speedValue = speedValue;
}

public int getStringResourceId() {
return stringResourceId;
}

public String getText(Context context) {
return context.getString(stringResourceId);
}

public static PlaybackSpeed getPlaybackSpeedBySpeedValue(Float speedValue) {
for (PlaybackSpeed playbackSpeed : PlaybackSpeed.getAllPlaybackSpeedEnums()) {
if(Math.abs(playbackSpeed.speedValue - speedValue) < EPSILON) {
return playbackSpeed;
}
}
return null;
}

public static int getPlaybackSpeedPositionBySpeedValue(Float speedValue) {
PlaybackSpeed targetPlaybackSpeed = getPlaybackSpeedBySpeedValue(speedValue);
return getAllPlaybackSpeedEnums().indexOf(targetPlaybackSpeed);
}

public float getSpeedValue() {
return speedValue;
}

public static ArrayList<PlaybackSpeed> getAllPlaybackSpeedEnums() {
return new ArrayList<>(Arrays.asList(PlaybackSpeed.class.getEnumConstants()));
}
}
185 changes: 185 additions & 0 deletions lib/src/main/java/com/tubitv/media/utilities/PlaybackSettingMenu.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
package com.tubitv.media.utilities;

import android.content.DialogInterface;
import android.support.annotation.NonNull;
import android.support.v7.app.AlertDialog;
import android.view.Gravity;
import android.view.Window;
import android.view.WindowManager;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.tubitv.media.R;
import com.tubitv.media.activities.TubiPlayerActivity;
import com.tubitv.media.models.PlaybackSpeed;

import java.util.ArrayList;

/**
* A nested playback setting menu using AlertDialog.
*/
public class PlaybackSettingMenu {

private SimpleExoPlayer contentPlayer;

private TubiPlayerActivity activity;

private AlertDialog mainDialog;

private ArrayList<MenuOption> menuOptions;

public PlaybackSettingMenu() {
}

public PlaybackSettingMenu(@NonNull SimpleExoPlayer contentPlayer, @NonNull TubiPlayerActivity playerActivity) {
this.contentPlayer = contentPlayer;
this.activity = playerActivity;
}

public void setContentPlayer(@NonNull SimpleExoPlayer contentPlayer) {
this.contentPlayer = contentPlayer;
}

public void setActivity(@NonNull TubiPlayerActivity activity) {
this.activity = activity;
}

public void buildSettingMenuOptions() {
menuOptions = new ArrayList<>();

// Option can be separately injected from root activity if needed.
// It requires dependencies: activityContext & contentSimpleExoPlayer.
MenuOption playbackSpeedOption = new MenuOption(activity.getString(
R.string.playback_setting_speed_title), new MenuOptionCallback() {
@Override
public void onClick() {
ArrayList<String> playbackSpeedTexts = new ArrayList<>();
ArrayList<Float> playbackSpeedValues = new ArrayList<>();

for (PlaybackSpeed playbackSpeed : PlaybackSpeed.getAllPlaybackSpeedEnums()) {
playbackSpeedTexts.add(playbackSpeed.getText(activity));
playbackSpeedValues.add(playbackSpeed.getSpeedValue());
}

String[] speedOptionTextArray = playbackSpeedTexts.toArray(new String[0]);
int currentSpeedPosition = PlaybackSpeed.getPlaybackSpeedPositionBySpeedValue(
contentPlayer.getPlaybackParameters().speed);

AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setSingleChoiceItems(
speedOptionTextArray,
currentSpeedPosition, // When is -1, none will be selected.
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int i) {
PlaybackParameters originParameters = contentPlayer.getPlaybackParameters();
PlaybackParameters updatedSpeedParameters = new PlaybackParameters(
playbackSpeedValues.get(i),
originParameters.pitch, // Keeping old values
originParameters.skipSilence
);

contentPlayer.setPlaybackParameters(updatedSpeedParameters);
dialog.dismiss();
}
});

AlertDialog chooseSpeedDialog = builder.create();
setAlertDialogGravityBottomCenter(chooseSpeedDialog);
alertDialogImmersiveShow(chooseSpeedDialog);
}

@Override
public String getTitle(String defaultTitle) {
// Dynamic title
Float currentSpeedValue = contentPlayer.getPlaybackParameters().speed;
PlaybackSpeed currentPlaybackSpeed = PlaybackSpeed.getPlaybackSpeedBySpeedValue(currentSpeedValue);
if (currentPlaybackSpeed != null) {
defaultTitle = defaultTitle + " - " + currentPlaybackSpeed.getText(activity);
}
return defaultTitle;
}
});

menuOptions.add(playbackSpeedOption);
}

public void show() {
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setTitle(activity.getString(R.string.playback_setting_title));

String[] settingOptionTitles = new String[menuOptions.size()];
for (int i = 0; i < menuOptions.size(); i++) {
settingOptionTitles[i] = menuOptions.get(i).getTitle();
}

builder.setItems(settingOptionTitles, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int i) {
menuOptions.get(i).onClick();
dialog.dismiss();
}
});

mainDialog = builder.create();
setAlertDialogGravityBottomCenter(mainDialog);
alertDialogImmersiveShow(mainDialog);
}

public void dismiss() {
if (mainDialog != null) {
mainDialog.dismiss();
}
}

private void setAlertDialogGravityBottomCenter(AlertDialog alertDialog) {
alertDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
WindowManager.LayoutParams layoutParams = alertDialog.getWindow().getAttributes();
if (layoutParams != null) {
layoutParams.gravity = Gravity.BOTTOM | Gravity.CENTER;
}
}

/*
* This method is to keep the system navigation bar not appearing after dialog calling .show().
*
* More on this problem:
* https://stackoverflow.com/questions/22794049/how-do-i-maintain-the-immersive-mode-in-dialogs
*/
private void alertDialogImmersiveShow(AlertDialog alertDialog) {
// Set dialog not-focusable, to avoid popping the nav bar.
alertDialog.getWindow().setFlags(
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);

alertDialog.show();

// Copy the hosted activity systemUI setting.
alertDialog.getWindow().getDecorView().setSystemUiVisibility(
activity.getWindow().getDecorView().getSystemUiVisibility());
// Clear the not-focusable, which let the dialog re-gain focus.
alertDialog.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
}

interface MenuOptionCallback {
void onClick();
String getTitle(String defaultTitle);
}

class MenuOption {
private String title;
private MenuOptionCallback callback;

MenuOption(String title, MenuOptionCallback callback) {
this.title = title;
this.callback = callback;
}

void onClick() {
callback.onClick();
}

String getTitle() {
return callback.getTitle(title);
}
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading