/*
* Copyright (C) 2016 Brian Wernick
*
* 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 com.devbrackets.android.exomedia.ui.widget;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.support.annotation.IntRange;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import com.devbrackets.android.exomedia.R;
import com.devbrackets.android.exomedia.util.TimeFormatUtil;
import com.devbrackets.android.exomedia.ui.animation.BottomViewHideShowAnimation;
import com.devbrackets.android.exomedia.ui.animation.TopViewHideShowAnimation;
import java.util.LinkedList;
import java.util.List;
/**
* Provides playback controls for the {@link VideoView} on Mobile
* (Phone, Tablet, etc.) devices.
*/
@SuppressWarnings("unused")
public class VideoControlsMobile extends VideoControls {
protected SeekBar seekBar;
protected LinearLayout extraViewsContainer;
protected boolean userInteracting = false;
public VideoControlsMobile(Context context) {
super(context);
}
public VideoControlsMobile(Context context, AttributeSet attrs) {
super(context, attrs);
}
public VideoControlsMobile(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public VideoControlsMobile(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
protected int getLayoutResource() {
return R.layout.exomedia_default_controls_mobile;
}
@Override
public void setPosition(@IntRange(from = 0) long position) {
currentTimeTextView.setText(TimeFormatUtil.formatMs(position));
seekBar.setProgress((int) position);
}
@Override
public void setDuration(@IntRange(from = 0) long duration) {
if (duration != seekBar.getMax()) {
endTimeTextView.setText(TimeFormatUtil.formatMs(duration));
seekBar.setMax((int) duration);
}
}
@Override
public void updateProgress(@IntRange(from = 0) long position, @IntRange(from = 0) long duration, @IntRange(from = 0, to = 100) int bufferPercent) {
if (!userInteracting) {
seekBar.setSecondaryProgress((int) (seekBar.getMax() * ((float)bufferPercent / 100)));
seekBar.setProgress((int) position);
currentTimeTextView.setText(TimeFormatUtil.formatMs(position));
}
}
@Override
protected void retrieveViews() {
super.retrieveViews();
seekBar = (SeekBar) findViewById(R.id.exomedia_controls_video_seek);
extraViewsContainer = (LinearLayout) findViewById(R.id.exomedia_controls_extra_container);
}
@Override
protected void registerListeners() {
super.registerListeners();
seekBar.setOnSeekBarChangeListener(new SeekBarChanged());
}
@Override
public void addExtraView(@NonNull View view) {
extraViewsContainer.addView(view);
}
@Override
public void removeExtraView(@NonNull View view) {
extraViewsContainer.removeView(view);
}
@NonNull
@Override
public List<View> getExtraViews() {
int childCount = extraViewsContainer.getChildCount();
if (childCount <= 0) {
return super.getExtraViews();
}
//Retrieves the layouts children
List<View> children = new LinkedList<>();
for (int i = 0; i < childCount; i++) {
children.add(extraViewsContainer.getChildAt(i));
}
return children;
}
@Override
public void hideDelayed(long delay) {
hideDelay = delay;
if (delay < 0 || !canViewHide || isLoading) {
return;
}
//If the user is interacting with controls we don't want to start the delayed hide yet
if (!userInteracting) {
visibilityHandler.postDelayed(new Runnable() {
@Override
public void run() {
animateVisibility(false);
}
}, delay);
}
}
@Override
protected void animateVisibility(boolean toVisible) {
if (isVisible == toVisible) {
return;
}
if (!hideEmptyTextContainer || !isTextContainerEmpty()) {
textContainer.startAnimation(new TopViewHideShowAnimation(textContainer, toVisible, CONTROL_VISIBILITY_ANIMATION_LENGTH));
}
if (!isLoading) {
controlsContainer.startAnimation(new BottomViewHideShowAnimation(controlsContainer, toVisible, CONTROL_VISIBILITY_ANIMATION_LENGTH));
}
isVisible = toVisible;
onVisibilityChanged();
}
@Override
protected void updateTextContainerVisibility() {
if (!isVisible) {
return;
}
boolean emptyText = isTextContainerEmpty();
if (hideEmptyTextContainer && emptyText && textContainer.getVisibility() == VISIBLE) {
textContainer.clearAnimation();
textContainer.startAnimation(new TopViewHideShowAnimation(textContainer, false, CONTROL_VISIBILITY_ANIMATION_LENGTH));
} else if ((!hideEmptyTextContainer || !emptyText) && textContainer.getVisibility() != VISIBLE) {
textContainer.clearAnimation();
textContainer.startAnimation(new TopViewHideShowAnimation(textContainer, true, CONTROL_VISIBILITY_ANIMATION_LENGTH));
}
}
@Override
public void showLoading(boolean initialLoad) {
if (isLoading) {
return;
}
isLoading = true;
loadingProgressBar.setVisibility(View.VISIBLE);
if (initialLoad) {
controlsContainer.setVisibility(View.GONE);
} else {
playPauseButton.setEnabled(false);
previousButton.setEnabled(false);
nextButton.setEnabled(false);
}
show();
}
@Override
public void finishLoading() {
if (!isLoading) {
return;
}
isLoading = false;
loadingProgressBar.setVisibility(View.GONE);
controlsContainer.setVisibility(View.VISIBLE);
playPauseButton.setEnabled(true);
previousButton.setEnabled(enabledViews.get(R.id.exomedia_controls_previous_btn, true));
nextButton.setEnabled(enabledViews.get(R.id.exomedia_controls_next_btn, true));
updatePlaybackState(videoView != null && videoView.isPlaying());
}
/**
* Listens to the seek bar change events and correctly handles the changes
*/
protected class SeekBarChanged implements SeekBar.OnSeekBarChangeListener {
private long seekToTime;
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (!fromUser) {
return;
}
seekToTime = progress;
if (currentTimeTextView != null) {
currentTimeTextView.setText(TimeFormatUtil.formatMs(seekToTime));
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
userInteracting = true;
if (seekListener == null || !seekListener.onSeekStarted()) {
internalListener.onSeekStarted();
}
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
userInteracting = false;
if (seekListener == null || !seekListener.onSeekEnded(seekToTime)) {
internalListener.onSeekEnded(seekToTime);
}
}
}
}