blob: 752213e4b0c7e7f5fbe8f065da2e2a3280ffb262 [file] [log] [blame]
/*
* Copyright (C) 2011 The Android Open Source Project
*
* 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.android.gallery3d.app;
import com.android.gallery3d.common.Utils;
import com.android.gallery3d.R;
import android.content.Context;
import android.os.Handler;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.AnimationUtils;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
/**
* The playback controller for the Movie Player.
*/
public class MovieControllerOverlay extends FrameLayout implements
ControllerOverlay,
OnClickListener,
AnimationListener,
TimeBar.Listener {
private enum State {
PLAYING,
PAUSED,
ENDED,
ERROR,
LOADING
}
private static final float ERROR_MESSAGE_RELATIVE_PADDING = 1.0f / 6;
private Listener listener;
private final View background;
private final TimeBar timeBar;
private View mainView;
private final LinearLayout loadingView;
private final TextView errorView;
private final ImageView playPauseReplayView;
private final Handler handler;
private final Runnable startHidingRunnable;
private final Animation hideAnimation;
private State state;
private boolean hidden;
private boolean canReplay = true;
public MovieControllerOverlay(Context context) {
super(context);
state = State.LOADING;
LayoutParams wrapContent =
new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
LayoutParams matchParent =
new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
LayoutInflater inflater = LayoutInflater.from(context);
background = new View(context);
background.setBackgroundColor(context.getResources().getColor(R.color.darker_transparent));
addView(background, matchParent);
timeBar = new TimeBar(context, this);
addView(timeBar, wrapContent);
loadingView = new LinearLayout(context);
loadingView.setOrientation(LinearLayout.VERTICAL);
loadingView.setGravity(Gravity.CENTER_HORIZONTAL);
ProgressBar spinner = new ProgressBar(context);
spinner.setIndeterminate(true);
loadingView.addView(spinner, wrapContent);
addView(loadingView, wrapContent);
playPauseReplayView = new ImageView(context);
playPauseReplayView.setImageResource(R.drawable.ic_vidcontrol_play);
playPauseReplayView.setBackgroundResource(R.drawable.bg_vidcontrol);
playPauseReplayView.setScaleType(ScaleType.CENTER);
playPauseReplayView.setFocusable(true);
playPauseReplayView.setClickable(true);
playPauseReplayView.setOnClickListener(this);
addView(playPauseReplayView, wrapContent);
errorView = new TextView(context);
errorView.setGravity(Gravity.CENTER);
errorView.setBackgroundColor(0xCC000000);
errorView.setTextColor(0xFFFFFFFF);
addView(errorView, matchParent);
handler = new Handler();
startHidingRunnable = new Runnable() {
public void run() {
startHiding();
}
};
hideAnimation = AnimationUtils.loadAnimation(context, R.anim.player_out);
hideAnimation.setAnimationListener(this);
RelativeLayout.LayoutParams params =
new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
setLayoutParams(params);
hide();
}
public void setListener(Listener listener) {
this.listener = listener;
}
public void setCanReplay(boolean canReplay) {
this.canReplay = canReplay;
}
public View getView() {
return this;
}
public void showPlaying() {
state = State.PLAYING;
showMainView(playPauseReplayView);
}
public void showPaused() {
state = State.PAUSED;
showMainView(playPauseReplayView);
}
public void showEnded() {
state = State.ENDED;
showMainView(playPauseReplayView);
}
public void showLoading() {
state = State.LOADING;
showMainView(loadingView);
}
public void showErrorMessage(String message) {
state = State.ERROR;
int padding = (int) (getMeasuredWidth() * ERROR_MESSAGE_RELATIVE_PADDING);
errorView.setPadding(padding, 10, padding, 10);
errorView.setText(message);
showMainView(errorView);
}
public void resetTime() {
timeBar.resetTime();
}
public void setTimes(int currentTime, int totalTime) {
timeBar.setTime(currentTime, totalTime);
}
public void hide() {
boolean wasHidden = hidden;
hidden = true;
playPauseReplayView.setVisibility(View.INVISIBLE);
loadingView.setVisibility(View.INVISIBLE);
background.setVisibility(View.INVISIBLE);
timeBar.setVisibility(View.INVISIBLE);
setVisibility(View.INVISIBLE);
setFocusable(true);
requestFocus();
if (listener != null && wasHidden != hidden) {
listener.onHidden();
}
}
private void showMainView(View view) {
mainView = view;
errorView.setVisibility(mainView == errorView ? View.VISIBLE : View.INVISIBLE);
loadingView.setVisibility(mainView == loadingView ? View.VISIBLE : View.INVISIBLE);
playPauseReplayView.setVisibility(
mainView == playPauseReplayView ? View.VISIBLE : View.INVISIBLE);
show();
}
public void show() {
boolean wasHidden = hidden;
hidden = false;
updateViews();
setVisibility(View.VISIBLE);
setFocusable(false);
if (listener != null && wasHidden != hidden) {
listener.onShown();
}
maybeStartHiding();
}
private void maybeStartHiding() {
cancelHiding();
if (state == State.PLAYING) {
handler.postDelayed(startHidingRunnable, 2500);
}
}
private void startHiding() {
startHideAnimation(timeBar);
startHideAnimation(playPauseReplayView);
}
private void startHideAnimation(View view) {
if (view.getVisibility() == View.VISIBLE) {
view.startAnimation(hideAnimation);
}
}
private void cancelHiding() {
handler.removeCallbacks(startHidingRunnable);
background.setAnimation(null);
timeBar.setAnimation(null);
playPauseReplayView.setAnimation(null);
}
public void onAnimationStart(Animation animation) {
// Do nothing.
}
public void onAnimationRepeat(Animation animation) {
// Do nothing.
}
public void onAnimationEnd(Animation animation) {
hide();
}
public void onClick(View view) {
if (listener != null) {
if (view == playPauseReplayView) {
if (state == State.ENDED) {
if (canReplay) {
listener.onReplay();
}
} else if (state == State.PAUSED || state == State.PLAYING) {
listener.onPlayPause();
}
}
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (hidden) {
show();
}
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (super.onTouchEvent(event)) {
return true;
}
if (hidden) {
show();
return true;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
cancelHiding();
if (state == State.PLAYING || state == State.PAUSED) {
listener.onPlayPause();
}
break;
case MotionEvent.ACTION_UP:
maybeStartHiding();
break;
}
return true;
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int bw;
int bh;
int y;
int h = b - t;
int w = r - l;
boolean error = errorView.getVisibility() == View.VISIBLE;
bw = timeBar.getBarHeight();
bh = bw;
y = b - bh;
background.layout(l, y, r, b);
timeBar.layout(l, b - timeBar.getPreferredHeight(), r, b);
// Needed, otherwise the framework will not re-layout in case only the padding is changed
timeBar.requestLayout();
// play pause / next / previous buttons
int cx = l + w / 2; // center x
int playbackButtonsCenterline = t + h / 2;
bw = playPauseReplayView.getMeasuredWidth();
bh = playPauseReplayView.getMeasuredHeight();
playPauseReplayView.layout(
cx - bw / 2, playbackButtonsCenterline - bh / 2, cx + bw / 2,
playbackButtonsCenterline + bh / 2);
// Space available on each side of the error message for the next and previous buttons
int errorMessagePadding = (int) (w * ERROR_MESSAGE_RELATIVE_PADDING);
if (mainView != null) {
layoutCenteredView(mainView, l, t, r, b);
}
}
private void layoutCenteredView(View view, int l, int t, int r, int b) {
int cw = view.getMeasuredWidth();
int ch = view.getMeasuredHeight();
int cl = (r - l - cw) / 2;
int ct = (b - t - ch) / 2;
view.layout(cl, ct, cl + cw, ct + ch);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
measureChildren(widthMeasureSpec, heightMeasureSpec);
}
private void updateViews() {
if (hidden) {
return;
}
background.setVisibility(View.VISIBLE);
timeBar.setVisibility(View.VISIBLE);
playPauseReplayView.setImageResource(
state == State.PAUSED ? R.drawable.ic_vidcontrol_play :
state == State.PLAYING ? R.drawable.ic_vidcontrol_pause :
R.drawable.ic_vidcontrol_reload);
playPauseReplayView.setVisibility(
(state != State.LOADING && state != State.ERROR &&
!(state == State.ENDED && !canReplay))
? View.VISIBLE : View.GONE);
requestLayout();
}
// TimeBar listener
public void onScrubbingStart() {
cancelHiding();
listener.onSeekStart();
}
public void onScrubbingMove(int time) {
cancelHiding();
listener.onSeekMove(time);
}
public void onScrubbingEnd(int time) {
maybeStartHiding();
listener.onSeekEnd(time);
}
}