Java tutorial
/* * Copyright 2016 eneim@Eneim Labs, nam@ene.im * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package im.ene.lab.toro.ext.layeredvideo; import android.animation.Animator; import android.annotation.TargetApi; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.pm.ActivityInfo; import android.graphics.Color; import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Handler; import android.os.Message; import android.support.v4.content.ContextCompat; import android.support.v7.widget.Toolbar; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.SeekBar; import android.widget.TextView; import im.ene.lab.toro.ext.R; import im.ene.lab.toro.player.internal.PlayerControlCallback; import im.ene.lab.toro.player.util.PlayerUtil; import im.ene.lab.toro.player.widget.ObservablePlayerControl; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Formatter; import java.util.List; import java.util.Locale; /** * A {@link Layer} that creates a customizable view for controlling * video playback. * * <p>The view consists of: * * <p> 1) a top chrome which contains a logo, title, and set of action buttons. * * <p> 2) a bottom chrome which contains a seek bar, fullscreen button, and text views indicating * the current time and total duration of the video. * * <p> 3) a translucent middle section which displays a pause/play button. * * <p>The view appears when the container containing the {@link PlaybackControlLayer} is tapped. It * automatically disappears after a given time. * * <p>The view can be customized by: * * <p> 1) Setting the color of the top chrome, bottom chrome, and background - this is called * the chrome tint color. * * <p> 2) Setting the color of the text - this is called the text color. * * <p> 3) Setting the color of the buttons and seek bar - this is called the control tint color. * * <p> 4) Setting the logo image displayed in the left of the top chrome. * * <p> 5) Setting the title of the video displayed in the left of the top chrome * (and to the right of the logo). * * <p> 6) Adding an action button by providing an image, a content description, and a click * handler. * If * there is enough room, the action buttons will be displayed on the right of the top chrome. If * there is NOT enough room, an overflow button will be displayed. When the overflow button is * clicked, a dialog box listing the content descriptions for the action buttons is displayed. The * action is then triggered by selecting it from the dialog box. * * <p>The view is defined in the layout file: res/layout/playback_control_layer.xml. */ @TargetApi(Build.VERSION_CODES.JELLY_BEAN) public class PlaybackControlLayer implements Layer, PlayerControlCallback, Focusable { /** * In order to imbue the {@link PlaybackControlLayer} with the ability make the player * fullscreen, * a {@link FullscreenCallback} must be assigned to it. The * {@link FullscreenCallback} implementation is responsible for * hiding/showing the other views on the screen when the player enters/leaves fullscreen * mode. */ public interface FullscreenCallback { /** * When triggered, the activity should hide any additional views. */ void onGoToFullscreen(); /** * When triggered, the activity should show any views that were hidden when the player * went to fullscreen. */ void onReturnFromFullscreen(); } /** * The {@link PlayCallback} implementation will be called when the player * plays the video (e.g. to request IMA ads) upon user taps on the play button. */ public interface PlayCallback { void onPlay(); } public interface SettingsCallback { void onSettings(); } /** * Message handler which allows us to send delayed messages to the {@link PlaybackControlLayer} * This is useful for fading out the view after a certain time. */ private static class MessageHandler extends Handler { /** * A reference to the {@link PlaybackControlLayer} that we are handling messages for. */ private final WeakReference<PlaybackControlLayer> playbackControlLayer; /** * @param playbackControlLayer The {@link PlaybackControlLayer} we should handle messages for. */ private MessageHandler(PlaybackControlLayer playbackControlLayer) { this.playbackControlLayer = new WeakReference<>(playbackControlLayer); } /** * Receives either a {@link PlaybackControlLayer#FADE_OUT} message (which hides the playback * control layer) or a {@link PlaybackControlLayer#SHOW_PROGRESS} message (which updates the * seek bar to reflect the progress in the video). * * @param msg Either a {@link PlaybackControlLayer#FADE_OUT} or * {@link PlaybackControlLayer#SHOW_PROGRESS} message. */ @Override public void handleMessage(Message msg) { PlaybackControlLayer layer = playbackControlLayer.get(); if (layer == null || layer.getLayerManager().getControl() == null) { return; } int pos; switch (msg.what) { case FADE_OUT: layer.hide(); break; case SHOW_PROGRESS: pos = layer.updateProgress(); if (!layer.isSeekBarDragging && layer.isVisible // && layer.getLayerManager().getControl().isPlaying()) { msg = obtainMessage(SHOW_PROGRESS); sendMessageDelayed(msg, 1000 - (pos % 1000)); } break; default: break; } } } /** * The chrome (the top chrome, bottom chrome, and background) is by default a slightly * transparent black. */ public static final int DEFAULT_CHROME_COLOR = Color.argb(128, 0, 0, 0); /** * By default, there is no tint to the controls. */ public static final int DEFAULT_CONTROL_TINT_COLOR = Color.TRANSPARENT; /** * By default, the text is white. */ public static final int DEFAULT_TEXT_COLOR = Color.WHITE; /** * When the playback controls are shown, hide them after DEFAULT_TIMEOUT_MS milliseconds. */ private static final int DEFAULT_TIMEOUT_MS = 2000; /** * When the controls are hidden, they fade out in FADE_OUT_DURATION_MS milliseconds. */ private static final int FADE_OUT_DURATION_MS = 400; /** * Used by the {@link MessageHandler} to indicate that media controls should fade out. */ private static final int FADE_OUT = 1; /** * Used by the {@link MessageHandler} to indicate that media controls should update progress bar. */ private static final int SHOW_PROGRESS = 2; /** * List of image buttons which are displayed in the right side of the top chrome. */ private List<ImageButton> actionButtons; /** * Whether the playback control layer is visible. */ private boolean isVisible; /** * Whether the playback control layer is currently in the process of fading out. */ private boolean isFadingOut; /** * Whether the user can drag the seek bar thumb to seek. */ private boolean canSeek; /** * <p> Derived from the Color class (ex. {@link Color#RED}), the chrome consists of three * views, which are tinted with the chrome color. * * <p> The views are: * * <p> 1) The top chrome which contains the logo, title, and action buttons. * * <p> 2) The bottom chrome which contains the play/pause button, seek bar, and fullscreen * buttons. * * <p> 3) The translucent middle section of the PlaybackControlLayer. * * <p> The chromeColor changes the color of each of these elements. */ private int chromeColor; /** * Derived from the {@link Color} class (ex {@link Color#RED}), this is the color of the * play/pause button, fullscreen button, seek bar, and action buttons. */ private int controlColor; /** * Derived from the {@link Color} class (ex {@link Color#RED}), this is the color of the text * views. */ private int textColor; /** * Derived from the {@link Color} class (ex {@link Color#RED}), this is the color of the seekbar * track and thumb. */ private int seekBarColor; /** * Displays the elapsed time into video. */ private TextView currentTime; /** * Displays the duration of the video. */ private TextView endTime; /** * Makes player enter or leave fullscreen. This button is not displayed unless there is a * {@link FullscreenCallback} associated with this object. */ private ImageButton fullscreenButton; /** * This callback is triggered when going to fullscreen and returning from fullscreen. */ private FullscreenCallback fullscreenCallback; private PlayCallback playCallback; private SettingsCallback settingsCallback; /** * The message handler which deals with displaying progress and fading out the media controls * We use it so that we can make the view fade out after a timeout (by sending a delayed * message). */ private Handler handler = new MessageHandler(this); /** * Whether the player is currently in fullscreen mode. */ private boolean isFullscreen; /** * Whether the seekBar is currently being dragged. */ private boolean isSeekBarDragging; /** * The {@link LayerManager} which is responsible for adding this layer to the container and * displaying it on top of the video player. */ private LayerManager layerManager; /** * The drawable that will be displayed in the {@link PlaybackControlLayer#logoImageView}. */ private Drawable logoDrawable; /** * Displayed in the left of the top chrome - shows a logo. This is optional; if no image * is provided, then no logo will be displayed. */ private ImageView logoImageView; /** * This is the layout of the container before fullscreen mode has been entered. * When we leave fullscreen mode, we restore the layout of the container to this layout. */ private ViewGroup.LayoutParams originalContainerLayoutParams; /** * {@link Toolbar} contains action buttons and title. */ private Toolbar actionToolbar; /** * Contains the actions buttons (displayed in right of the top chrome). */ private LinearLayout actionButtonsContainer; /** * Displays the play icon when the video is playing, or the pause icon when the video is playing. */ private ImageButton pausePlayButton; /** * Displays a track and a thumb which can be used to seek to different time points in the video. */ private SeekBar seekBar; /** * Whether the play button has been pressed and the video should be playing. * We include this variable because the video may pause when buffering must occur. Although * the video will usually resume automatically when the buffering is complete, there are * instances * (i.e. ad playback), where it will not resume automatically. So, if we detect that the video is * paused after buffering and should be playing, we can resume it programmatically. */ private boolean shouldBePlaying; /** * Encodes the HH:MM:SS or MM:SS time format. */ private StringBuilder timeFormat; /** * Formats times to HH:MM:SS or MM:SS form. */ private Formatter timeFormatter; /** * Contains the logo, video title, and other actions button. It can be tinted with a color for * branding. */ private RelativeLayout topChrome; /** * This is the root view which contains all other views that make up the playback control layer. * It can be tinted by setting the chrome color. */ private FrameLayout playbackControlRootView; /** * Contains the seek bar, current time, end time, and fullscreen button. The background can * be tinted with a color for branding. */ private LinearLayout bottomChrome; /** * The title displayed in the {@link PlaybackControlLayer#actionToolbar}. */ private String videoTitle; ///** // * Video title displayed in the left of the top chrome. // */ //private TextView videoTitleView; /** * The view created by this {@link PlaybackControlLayer} */ private FrameLayout view; /** * Saved orientation for coming back from fullscreen. */ private int savedOrientation; public PlaybackControlLayer(String videoTitle) { this(videoTitle, null); } public PlaybackControlLayer(String videoTitle, FullscreenCallback fullscreenCallback) { this.videoTitle = videoTitle; this.canSeek = true; this.fullscreenCallback = fullscreenCallback; this.shouldBePlaying = false; this.actionButtons = new ArrayList<>(); } /** * Creates a button to put in the set of action buttons at the right of the top chrome. * * @param activity The activity that contains the video player. * @param icon The image of the action (ex. trash can). * @param contentDescription The text description this action. This is used in case the * action buttons do not fit in the video player. If so, an overflow * button will appear and, when clicked, it will display a list of the * content descriptions for each action. * @param onClickListener The handler for when the action is triggered. */ public void addActionButton(Activity activity, Drawable icon, String contentDescription, View.OnClickListener onClickListener) { ImageButton button = new ImageButton(activity); button.setContentDescription(contentDescription); button.setImageDrawable(icon); button.setOnClickListener(onClickListener); FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); int margin = activity.getResources().getDisplayMetrics().densityDpi * 5; layoutParams.setMargins(margin, 0, margin, 0); button.setBackgroundColor(Color.TRANSPARENT); button.setLayoutParams(layoutParams); isFullscreen = false; actionButtons.add(button); if (playbackControlRootView != null) { updateActionButtons(); updateColors(); } } @Override public FrameLayout createView(LayerManager layerManager) { this.layerManager = layerManager; view = (FrameLayout) LayoutInflater.from(layerManager.getContainer().getContext()) .inflate(R.layout.tr_ext_playback_control_layer, layerManager.getContainer(), false); setupView(); originalContainerLayoutParams = layerManager.getContainer().getLayoutParams(); layerManager.getControl().addCallback(this); savedOrientation = layerManager.getActivity().getResources().getConfiguration().orientation; textColor = DEFAULT_TEXT_COLOR; chromeColor = DEFAULT_CHROME_COLOR; controlColor = DEFAULT_CONTROL_TINT_COLOR; // Since the seek bar doesn't use image assets, we can't use TRANSPARENT as the default tint // because that would make it invisible, so instead we use the default text tint (White). seekBarColor = DEFAULT_TEXT_COLOR; if (logoDrawable != null) { logoImageView.setImageDrawable(logoDrawable); } layerManager.getContainer().setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (isVisible) { hide(); } else { show(); } } }); // Make the view hidden initially. It will be made visible again in the show(timeout) method. playbackControlRootView.setVisibility(View.INVISIBLE); return view; } /** * Hides the seek bar thumb and prevents the user from seeking to different time points in the * video. */ public void disableSeeking() { this.canSeek = false; if (playbackControlRootView != null) { updateColors(); } } /** * Fullscreen mode will rotate to landscape mode, hide the action bar, hide the navigation bar, * hide the system tray, and make the video player take up the full size of the display. * The developer who is using this function must ensure the following: * * <p>1) Inside the android manifest, the activity that uses the video player has the attribute * android:configChanges="orientation". * * <p>2) Other views in the activity (or fragment) are * hidden (or made visible) when this method is called. */ public void doToggleFullscreen() { // If there is no callback for handling fullscreen, don't do anything. if (fullscreenCallback == null) { return; } ObservablePlayerControl playerControl = layerManager.getControl(); if (playerControl == null) { return; } Activity activity = layerManager.getActivity(); FrameLayout container = layerManager.getContainer(); if (isFullscreen) { activity.setRequestedOrientation(savedOrientation); // Make the status bar and navigation bar visible again. activity.getWindow().getDecorView().setSystemUiVisibility(0); container.setLayoutParams(originalContainerLayoutParams); fullscreenButton.setImageResource(R.drawable.toro_ext_ic_fullscreen_enter); fullscreenCallback.onReturnFromFullscreen(); isFullscreen = false; } else { savedOrientation = activity.getResources().getConfiguration().orientation; activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); activity.getWindow().getDecorView().setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); container.setLayoutParams(PlayerUtil.getLayoutParamsBasedOnParent(container, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); fullscreenButton.setImageResource(R.drawable.toro_ext_ic_fullscreen_exit); fullscreenCallback.onGoToFullscreen(); isFullscreen = true; } } /** * Makes the seek bar thumb visible and allows the user to seek to different time points in the * video. */ public void enableSeeking() { this.canSeek = true; if (playbackControlRootView != null) { updateColors(); } } /** * Returns the {@link LayerManager} which is responsible for * displaying this layer's view. */ public LayerManager getLayerManager() { return layerManager; } /** * Fades the playback control layer out and then removes it from the {@link * LayerManager}'s * container. */ public void hide() { if (isFadingOut) { return; } final FrameLayout container = layerManager.getContainer(); if (container == null) { return; } if (isVisible) { isFadingOut = true; playbackControlRootView.animate().alpha(0.0f).setDuration(FADE_OUT_DURATION_MS) .setListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { isFadingOut = false; playbackControlRootView.setVisibility(View.INVISIBLE); container.removeView(view); handler.removeMessages(SHOW_PROGRESS); isVisible = false; } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); } } /** * Add the playback control layer back to the container. * The playback controls disappear after timeout milliseconds. * * @param timeout Hide the view after timeout milliseconds. If timeout == 0, then the playback * controls will not disappear unless their container is tapped again. */ public void show(int timeout) { if (!isVisible && layerManager.getContainer() != null) { playbackControlRootView.setAlpha(1.0f); // Make the view visible. playbackControlRootView.setVisibility(View.VISIBLE); updateProgress(); // Add the view to the container again. // 1. Get old params FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) view.getLayoutParams(); // 2. Remove from container layerManager.getContainer().removeView(view); // 3. Re-add layerManager.getContainer().addView(view, layoutParams); setupView(); isVisible = true; } updatePlayPauseButton(); handler.sendEmptyMessage(SHOW_PROGRESS); Message msg = handler.obtainMessage(FADE_OUT); handler.removeMessages(FADE_OUT); if (timeout > 0) { handler.sendMessageDelayed(msg, timeout); } } /** * Add the playback control layer back to the container. It will disappear when the user taps * the screen. */ public void show() { show(DEFAULT_TIMEOUT_MS); } /** * Hides the top chrome (which displays the logo, title, and action buttons). */ public void hideTopChrome() { topChrome.setVisibility(View.GONE); } /** * Shows the top chrome (which displays the logo, title, and action buttons). */ public void showTopChrome() { topChrome.setVisibility(View.VISIBLE); updateActionButtons(); updateColors(); } /** * Returns whether the player is currently in fullscreen mode. */ public boolean isFullscreen() { return isFullscreen; } /** * Make the player enter or leave fullscreen mode. * * @param shouldBeFullscreen If true, the player is put into fullscreen mode. If false, the * player * leaves fullscreen mode. */ public void setFullscreen(boolean shouldBeFullscreen) { if (shouldBeFullscreen != isFullscreen) { doToggleFullscreen(); } } @Override public void onLayerDisplayed(LayerManager layerManager) { } /** * Updates the play/pause button to the play icon. */ @Override public void onPause() { updatePlayPauseButton(); } /** * Updates the play/pause button to the pause icon. */ @Override public void onPlay() { updatePlayPauseButton(); if (playCallback != null) { playCallback.onPlay(); } } /** * Sets the color of the top chrome, bottom chrome, and background. * * @param color a color derived from the @{link Color} class (ex. {@link Color#RED}). */ public void setChromeColor(int color) { chromeColor = color; if (playbackControlRootView != null) { updateColors(); } } /** * Sets the color of the buttons and seek bar. * * @param color a color derived from the @{link Color} class (ex. {@link Color#RED}). */ public void setControlColor(int color) { this.controlColor = color; if (playbackControlRootView != null) { updateColors(); updateActionButtons(); } } /** * Sets the color of the seekbar. * * @param color a color derived from the @{link Color} class (ex. {@link Color#RED}). */ public void setSeekBarColor(int color) { this.seekBarColor = color; if (playbackControlRootView != null) { updateColors(); } } /** * Sets the color of the text views * * @param color a color derived from the @{link Color} class (ex. {@link Color#RED}). */ public void setTextColor(int color) { this.textColor = color; if (playbackControlRootView != null) { updateColors(); } } /** * Set the callback which will be called when the player enters and leaves fullscreen mode. * * @param fullscreenCallback The callback should hide other views in the activity when the player * enters fullscreen mode and show other views when the player leaves * fullscreen mode. */ public void setFullscreenCallback(FullscreenCallback fullscreenCallback) { this.fullscreenCallback = fullscreenCallback; if (fullscreenButton != null && fullscreenCallback != null) { fullscreenButton.setVisibility(View.VISIBLE); } else if (fullscreenButton != null && fullscreenCallback == null) { fullscreenButton.setVisibility(View.INVISIBLE); } } /** * Set the logo with appears in the left of the top chrome. * * @param logo The drawable which will be the logo. */ public void setLogoImageView(Drawable logo) { logoDrawable = logo; if (logoImageView != null) { logoImageView.setImageDrawable(logo); } } /** * Play or pause the player. * * @param shouldPlay If true, then the player starts playing. If false, the player pauses. */ public void setPlayPause(boolean shouldPlay) { ObservablePlayerControl playerControl = layerManager.getControl(); if (playerControl == null) { return; } if (shouldPlay) { playerControl.start(); } else { playerControl.pause(); } updatePlayPauseButton(); } /** * Set the title of the video in the left of the top chrome (to the right of the logo). * * @param title The video title. If it is too long, it will be ellipsized. */ public void setVideoTitle(String title) { videoTitle = title; if (actionToolbar != null) { actionToolbar.setTitle(title); } } /** * Perform binding to UI, setup of event handlers and initialization of values. */ private void setupView() { if (actionToolbar == null) { actionToolbar = (Toolbar) view.findViewById(R.id.playback_toolbar); actionToolbar.inflateMenu(R.menu.playback_actions); actionToolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { if (settingsCallback != null) { settingsCallback.onSettings(); return true; } return false; } }); } // Bind fields to UI elements. pausePlayButton = (ImageButton) view.findViewById(R.id.pause); fullscreenButton = (ImageButton) view.findViewById((R.id.fullscreen)); seekBar = (SeekBar) view.findViewById(R.id.media_controller_progress); endTime = (TextView) view.findViewById(R.id.time_duration); currentTime = (TextView) view.findViewById(R.id.time_current); logoImageView = (ImageView) view.findViewById(R.id.logo_image); playbackControlRootView = (FrameLayout) view.findViewById(R.id.middle_section); topChrome = (RelativeLayout) view.findViewById(R.id.top_chrome); bottomChrome = (LinearLayout) view.findViewById(R.id.bottom_chrome); actionButtonsContainer = (LinearLayout) view.findViewById(R.id.actions_container); // The play button should toggle play/pause when the play/pause button is clicked. pausePlayButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { togglePlayPause(); show(DEFAULT_TIMEOUT_MS); } }); if (fullscreenCallback == null) { fullscreenButton.setVisibility(View.INVISIBLE); } // Go into fullscreen when the fullscreen button is clicked. fullscreenButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { doToggleFullscreen(); show(DEFAULT_TIMEOUT_MS); updateActionButtons(); updateColors(); } }); seekBar.setMax(1000); seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromuser) { if (!fromuser || !canSeek) { // Ignore programmatic changes to seek bar position. // Ignore changes to seek bar position is seeking is not enabled. return; } ObservablePlayerControl playerControl = layerManager.getControl(); long duration = playerControl.getDuration(); long newPosition = (duration * progress) / 1000L; playerControl.seekTo((int) newPosition); if (currentTime != null) { currentTime.setText(formatTimeString((int) newPosition)); } } @Override public void onStartTrackingTouch(SeekBar seekBar) { show(0); isSeekBarDragging = true; handler.removeMessages(SHOW_PROGRESS); } @Override public void onStopTrackingTouch(SeekBar seekBar) { isSeekBarDragging = false; updateProgress(); updatePlayPauseButton(); show(DEFAULT_TIMEOUT_MS); handler.sendEmptyMessage(SHOW_PROGRESS); } }); actionToolbar.setTitle(videoTitle); timeFormat = new StringBuilder(); timeFormatter = new Formatter(timeFormat, Locale.getDefault()); } /** * Returns whether the player should be playing (based on whether the user has * tapped pause or play). This can be used by other classes to look at the playback control * layer's play/pause state and force the player to play or pause accordingly. */ public boolean shouldBePlaying() { return shouldBePlaying; } /** * Format the milliseconds to HH:MM:SS or MM:SS format. */ public String formatTimeString(int timeMs) { int totalSeconds = timeMs / 1000; int seconds = totalSeconds % 60; int minutes = (totalSeconds / 60) % 60; int hours = totalSeconds / 3600; timeFormat.setLength(0); if (hours > 0) { return timeFormatter.format("%d:%02d:%02d", hours, minutes, seconds).toString(); } else { return timeFormatter.format("%02d:%02d", minutes, seconds).toString(); } } /** * If the player is paused, play it and if the player is playing, pause it. */ public void togglePlayPause() { this.shouldBePlaying = !layerManager.getControl().isPlaying(); setPlayPause(shouldBePlaying); } /** * The action buttons are displayed in the top right of the video player. If the player is in * portrait mode, then display an overflow button which displays a dialog window containing the * possible actions. If the player is in landscape, then display the images for the actions in * the * top right of the video player. */ public void updateActionButtons() { actionButtonsContainer.removeAllViews(); if (isFullscreen) { for (ImageButton imageButton : actionButtons) { actionButtonsContainer.addView(imageButton); } } else { ImageButton overflowButton = new ImageButton(layerManager.getContainer().getContext()); overflowButton.setContentDescription(layerManager.getActivity().getString(R.string.overflow)); overflowButton.setImageDrawable(ContextCompat.getDrawable(layerManager.getContainer().getContext(), R.drawable.ic_action_overflow)); AlertDialog.Builder builder = new AlertDialog.Builder(layerManager.getActivity()); builder.setTitle(layerManager.getActivity().getString(R.string.select_an_action)); final CharSequence[] actions = new CharSequence[actionButtons.size()]; for (int i = 0; i < actionButtons.size(); i++) { actions[i] = actionButtons.get(i).getContentDescription(); } builder.setItems(actions, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { actionButtons.get(i).performClick(); } }); final AlertDialog alertDialog = builder.create(); overflowButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { alertDialog.show(); } }); FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); // 5dp int margin = 5 * layerManager.getActivity().getResources().getDisplayMetrics().densityDpi; layoutParams.setMargins(margin, 0, margin, 0); overflowButton.setBackgroundColor(Color.TRANSPARENT); overflowButton.setLayoutParams(layoutParams); overflowButton.setColorFilter(controlColor); actionButtonsContainer.addView(overflowButton); } } /** * Ensure that the chrome, control, and text colors displayed on the screen are correct. */ /** * @hide */ public void updateColors() { currentTime.setTextColor(textColor); endTime.setTextColor(textColor); actionToolbar.setTitleTextColor(textColor); fullscreenButton.setColorFilter(controlColor); pausePlayButton.setColorFilter(controlColor); seekBar.getProgressDrawable().setColorFilter(seekBarColor, PorterDuff.Mode.SRC_ATOP); seekBar.getThumb().setColorFilter(seekBarColor, PorterDuff.Mode.SRC_ATOP); // Hide the thumb drawable if the SeekBar is disabled if (canSeek) { seekBar.getThumb().mutate().setAlpha(255); } else { seekBar.getThumb().mutate().setAlpha(0); } for (ImageButton imageButton : actionButtons) { imageButton.setColorFilter(controlColor); } topChrome.setBackgroundColor(chromeColor); } /** * Change the icon of the play/pause button to indicate play or pause based on the state of the * video player. */ public void updatePlayPauseButton() { ObservablePlayerControl playerControl = layerManager.getControl(); if (view == null || pausePlayButton == null || playerControl == null) { return; } if (playerControl.isPlaying()) { pausePlayButton.setImageResource(R.drawable.toro_ext_ic_pause); } else { pausePlayButton.setImageResource(R.drawable.toro_ext_ic_play_arrow); } } /** * Adjust the position of the action bar to reflect the progress of the video. */ private int updateProgress() { ObservablePlayerControl playerControl = layerManager.getControl(); if (playerControl == null || isSeekBarDragging) { return 0; } int position = playerControl.getCurrentPosition(); int duration = playerControl.getDuration(); if (seekBar != null) { if (duration > 0) { long pos = 1000L * position / duration; seekBar.setProgress((int) pos); } int percent = playerControl.getBufferPercentage(); seekBar.setSecondaryProgress(percent * 10); } if (endTime != null) { endTime.setText(formatTimeString(duration)); } if (currentTime != null) { currentTime.setText(formatTimeString(position)); } return position; } @Override public void onWindowFocusChanged(boolean hasFocus) { if (hasFocus && isFullscreen) { // TODO fix this for API 16 ~ 18 layerManager.getActivity().getWindow().getDecorView().setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); } } /** * Set play callback */ public void setPlayCallback(PlayCallback playCallback) { this.playCallback = playCallback; } public void setSettingsCallback(SettingsCallback callback) { this.settingsCallback = callback; } }