/* * 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); } } } }