summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorTeng-Hui Zhu <ztenghui@google.com>2012-08-24 14:50:37 -0700
committerTeng-Hui Zhu <ztenghui@google.com>2012-09-11 10:36:05 -0700
commit25930913dc7129a1e6d07a88f9b79d69029d2535 (patch)
treed8d5efe785696d0ffd4b894c5f1113fc529d0680 /src
parent1a980d945c8715de18bdbe6406599503d812185f (diff)
downloadandroid_packages_apps_Snap-25930913dc7129a1e6d07a88f9b79d69029d2535.tar.gz
android_packages_apps_Snap-25930913dc7129a1e6d07a88f9b79d69029d2535.tar.bz2
android_packages_apps_Snap-25930913dc7129a1e6d07a88f9b79d69029d2535.zip
Add the UI elements for video trim activity.
1. Refactored the classes to support both the video play and trim. Now the common code for controller overlay is in CommonControllerOverlay, which is basically the same as the original MovieControllerOverlay without animation. Because in trimming, we don't want to hide all the controller. The specific animations are implemented in the sub-classes which are TrimControllerOverlay and MovieControllerOverlay. At the same time, TrimTimeBar extended TimeBar to support the extra trimming start scrubber and end scrubber. The interface between the timebar and the controllerOverlay are kept almost the same way, except adding the trimming info when necessary. With all these, the activity of TrimVideo now relied on the TrimTimeBar and TrimControllerOverlay. Similarily, the MovieActivity relied on the TimeBar and MovieControllerOverlay. 2. Hook the TrimVideo activity with the trim menu. Note that the icons are temporary for now, still need UX input on that. bug:7093055 Change-Id: Ib9bfbc090106744a569fce4c451ddffc0a2c699b
Diffstat (limited to 'src')
-rw-r--r--src/com/android/gallery3d/app/CommonControllerOverlay.java333
-rw-r--r--src/com/android/gallery3d/app/ControllerOverlay.java5
-rw-r--r--src/com/android/gallery3d/app/MovieControllerOverlay.java294
-rw-r--r--src/com/android/gallery3d/app/MoviePlayer.java4
-rw-r--r--src/com/android/gallery3d/app/PhotoPage.java5
-rw-r--r--src/com/android/gallery3d/app/TimeBar.java393
-rw-r--r--src/com/android/gallery3d/app/TrimControllerOverlay.java104
-rw-r--r--src/com/android/gallery3d/app/TrimTimeBar.java290
-rw-r--r--src/com/android/gallery3d/app/TrimVideo.java254
-rw-r--r--src/com/android/gallery3d/ui/MenuExecutor.java4
10 files changed, 1207 insertions, 479 deletions
diff --git a/src/com/android/gallery3d/app/CommonControllerOverlay.java b/src/com/android/gallery3d/app/CommonControllerOverlay.java
new file mode 100644
index 000000000..ab43dada5
--- /dev/null
+++ b/src/com/android/gallery3d/app/CommonControllerOverlay.java
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2012 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 android.content.Context;
+import android.graphics.Rect;
+import android.view.Gravity;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.FrameLayout;
+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;
+
+import com.android.gallery3d.R;
+
+/**
+ * The common playback controller for the Movie Player or Video Trimming.
+ */
+public abstract class CommonControllerOverlay extends FrameLayout implements
+ ControllerOverlay,
+ OnClickListener,
+ TimeBar.Listener {
+
+ protected enum State {
+ PLAYING,
+ PAUSED,
+ ENDED,
+ ERROR,
+ LOADING
+ }
+
+ private static final float ERROR_MESSAGE_RELATIVE_PADDING = 1.0f / 6;
+
+ protected Listener mListener;
+
+ protected final View mBackground;
+ protected TimeBar mTimeBar;
+
+ protected View mMainView;
+ protected final LinearLayout mLoadingView;
+ protected final TextView mErrorView;
+ protected final ImageView mPlayPauseReplayView;
+
+ protected State mState;
+
+ protected boolean mCanReplay = true;
+
+ public CommonControllerOverlay(Context context) {
+ super(context);
+
+ mState = State.LOADING;
+ // TODO: Move the following layout code into xml file.
+ LayoutParams wrapContent =
+ new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+ LayoutParams matchParent =
+ new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
+
+ mBackground = new View(context);
+ mBackground.setBackgroundColor(context.getResources().getColor(R.color.darker_transparent));
+ addView(mBackground, matchParent);
+
+ // Depending on the usage, the timeBar can show a single scrubber, or
+ // multiple ones for trimming.
+ createTimeBar(context);
+ addView(mTimeBar, wrapContent);
+
+ mLoadingView = new LinearLayout(context);
+ mLoadingView.setOrientation(LinearLayout.VERTICAL);
+ mLoadingView.setGravity(Gravity.CENTER_HORIZONTAL);
+ ProgressBar spinner = new ProgressBar(context);
+ spinner.setIndeterminate(true);
+ mLoadingView.addView(spinner, wrapContent);
+ TextView loadingText = createOverlayTextView(context);
+ loadingText.setText(R.string.loading_video);
+ mLoadingView.addView(loadingText, wrapContent);
+ addView(mLoadingView, wrapContent);
+
+ mPlayPauseReplayView = new ImageView(context);
+ mPlayPauseReplayView.setImageResource(R.drawable.ic_vidcontrol_play);
+ mPlayPauseReplayView.setBackgroundResource(R.drawable.bg_vidcontrol);
+ mPlayPauseReplayView.setScaleType(ScaleType.CENTER);
+ mPlayPauseReplayView.setFocusable(true);
+ mPlayPauseReplayView.setClickable(true);
+ mPlayPauseReplayView.setOnClickListener(this);
+ addView(mPlayPauseReplayView, wrapContent);
+
+ mErrorView = createOverlayTextView(context);
+ addView(mErrorView, matchParent);
+
+ RelativeLayout.LayoutParams params =
+ new RelativeLayout.LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
+ setLayoutParams(params);
+ hide();
+ }
+
+ abstract protected void createTimeBar(Context context);
+
+ private TextView createOverlayTextView(Context context) {
+ TextView view = new TextView(context);
+ view.setGravity(Gravity.CENTER);
+ view.setTextColor(0xFFFFFFFF);
+ view.setPadding(0, 15, 0, 15);
+ return view;
+ }
+
+ @Override
+ public void setListener(Listener listener) {
+ this.mListener = listener;
+ }
+
+ @Override
+ public void setCanReplay(boolean canReplay) {
+ this.mCanReplay = canReplay;
+ }
+
+ @Override
+ public View getView() {
+ return this;
+ }
+
+ @Override
+ public void showPlaying() {
+ mState = State.PLAYING;
+ showMainView(mPlayPauseReplayView);
+ }
+
+ @Override
+ public void showPaused() {
+ mState = State.PAUSED;
+ showMainView(mPlayPauseReplayView);
+ }
+
+ @Override
+ public void showEnded() {
+ mState = State.ENDED;
+ showMainView(mPlayPauseReplayView);
+ }
+
+ @Override
+ public void showLoading() {
+ mState = State.LOADING;
+ showMainView(mLoadingView);
+ }
+
+ @Override
+ public void showErrorMessage(String message) {
+ mState = State.ERROR;
+ int padding = (int) (getMeasuredWidth() * ERROR_MESSAGE_RELATIVE_PADDING);
+ mErrorView.setPadding(
+ padding, mErrorView.getPaddingTop(), padding, mErrorView.getPaddingBottom());
+ mErrorView.setText(message);
+ showMainView(mErrorView);
+ }
+
+ @Override
+ public void setTimes(int currentTime, int totalTime,
+ int trimStartTime, int trimEndTime) {
+ mTimeBar.setTime(currentTime, totalTime, trimStartTime, trimEndTime);
+ }
+
+ public void hide() {
+ mPlayPauseReplayView.setVisibility(View.INVISIBLE);
+ mLoadingView.setVisibility(View.INVISIBLE);
+ mBackground.setVisibility(View.INVISIBLE);
+ mTimeBar.setVisibility(View.INVISIBLE);
+ setVisibility(View.INVISIBLE);
+ setFocusable(true);
+ requestFocus();
+ }
+
+ private void showMainView(View view) {
+ mMainView = view;
+ mErrorView.setVisibility(mMainView == mErrorView ? View.VISIBLE : View.INVISIBLE);
+ mLoadingView.setVisibility(mMainView == mLoadingView ? View.VISIBLE : View.INVISIBLE);
+ mPlayPauseReplayView.setVisibility(
+ mMainView == mPlayPauseReplayView ? View.VISIBLE : View.INVISIBLE);
+ show();
+ }
+
+ @Override
+ public void show() {
+ updateViews();
+ setVisibility(View.VISIBLE);
+ setFocusable(false);
+ }
+
+ @Override
+ public void onClick(View view) {
+ if (mListener != null) {
+ if (view == mPlayPauseReplayView) {
+ if (mState == State.ENDED) {
+ if (mCanReplay) {
+ mListener.onReplay();
+ }
+ } else if (mState == State.PAUSED || mState == State.PLAYING) {
+ mListener.onPlayPause();
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ return super.onKeyDown(keyCode, event);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (super.onTouchEvent(event)) {
+ return true;
+ }
+ return false;
+ }
+
+ // The paddings of 4 sides which covered by system components. E.g.
+ // +-----------------+\
+ // | Action Bar | insets.top
+ // +-----------------+/
+ // | |
+ // | Content Area | insets.right = insets.left = 0
+ // | |
+ // +-----------------+\
+ // | Navigation Bar | insets.bottom
+ // +-----------------+/
+ // Please see View.fitSystemWindows() for more details.
+ private final Rect mWindowInsets = new Rect();
+
+ @Override
+ protected boolean fitSystemWindows(Rect insets) {
+ // We don't set the paddings of this View, otherwise,
+ // the content will get cropped outside window
+ mWindowInsets.set(insets);
+ return true;
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ Rect insets = mWindowInsets;
+ int pl = insets.left; // the left paddings
+ int pr = insets.right;
+ int pt = insets.top;
+ int pb = insets.bottom;
+
+ int h = bottom - top;
+ int w = right - left;
+ boolean error = mErrorView.getVisibility() == View.VISIBLE;
+
+ int y = h - pb;
+ // Put both TimeBar and Background just above the bottom system
+ // component.
+ // But extend the background to the width of the screen, since we don't
+ // care if it will be covered by a system component and it looks better.
+ mBackground.layout(0, y - mTimeBar.getBarHeight(), w, y);
+ mTimeBar.layout(pl, y - mTimeBar.getPreferredHeight(), w - pr, y);
+
+ // Needed, otherwise the framework will not re-layout in case only the
+ // padding is changed
+ mTimeBar.requestLayout();
+
+ // Put the play/pause/next/ previous button in the center of the screen
+ layoutCenteredView(mPlayPauseReplayView, 0, 0, w, h);
+
+ if (mMainView != null) {
+ layoutCenteredView(mMainView, 0, 0, w, h);
+ }
+ }
+
+ 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);
+ }
+
+ protected void updateViews() {
+ mBackground.setVisibility(View.VISIBLE);
+ mTimeBar.setVisibility(View.VISIBLE);
+ mPlayPauseReplayView.setImageResource(
+ mState == State.PAUSED ? R.drawable.ic_vidcontrol_play :
+ mState == State.PLAYING ? R.drawable.ic_vidcontrol_pause :
+ R.drawable.ic_vidcontrol_reload);
+ mPlayPauseReplayView.setVisibility(
+ (mState != State.LOADING && mState != State.ERROR &&
+ !(mState == State.ENDED && !mCanReplay))
+ ? View.VISIBLE : View.GONE);
+ requestLayout();
+ }
+
+ // TimeBar listener
+
+ @Override
+ public void onScrubbingStart() {
+ mListener.onSeekStart();
+ }
+
+ @Override
+ public void onScrubbingMove(int time) {
+ mListener.onSeekMove(time);
+ }
+
+ @Override
+ public void onScrubbingEnd(int time, int trimStartTime, int trimEndTime) {
+ mListener.onSeekEnd(time, trimStartTime, trimEndTime);
+ }
+}
diff --git a/src/com/android/gallery3d/app/ControllerOverlay.java b/src/com/android/gallery3d/app/ControllerOverlay.java
index 03472134d..078f59e28 100644
--- a/src/com/android/gallery3d/app/ControllerOverlay.java
+++ b/src/com/android/gallery3d/app/ControllerOverlay.java
@@ -24,7 +24,7 @@ public interface ControllerOverlay {
void onPlayPause();
void onSeekStart();
void onSeekMove(int time);
- void onSeekEnd(int time);
+ void onSeekEnd(int time, int trimStartTime, int trimEndTime);
void onShown();
void onHidden();
void onReplay();
@@ -51,5 +51,6 @@ public interface ControllerOverlay {
void showErrorMessage(String message);
- void setTimes(int currentTime, int totalTime);
+ void setTimes(int currentTime, int totalTime,
+ int trimStartTime, int trimEndTime);
}
diff --git a/src/com/android/gallery3d/app/MovieControllerOverlay.java b/src/com/android/gallery3d/app/MovieControllerOverlay.java
index 4781a1b8a..f01e619c6 100644
--- a/src/com/android/gallery3d/app/MovieControllerOverlay.java
+++ b/src/com/android/gallery3d/app/MovieControllerOverlay.java
@@ -17,105 +17,30 @@
package com.android.gallery3d.app;
import android.content.Context;
-import android.graphics.Rect;
import android.os.Handler;
-import android.view.Gravity;
import android.view.KeyEvent;
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.ImageView;
-import android.widget.ImageView.ScaleType;
-import android.widget.LinearLayout;
-import android.widget.ProgressBar;
-import android.widget.RelativeLayout;
-import android.widget.TextView;
-
import com.android.gallery3d.R;
/**
* 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;
+public class MovieControllerOverlay extends CommonControllerOverlay implements
+ AnimationListener {
- private final View background;
- private final TimeBar timeBar;
-
- private View mainView;
- private final LinearLayout loadingView;
- private final TextView errorView;
- private final ImageView playPauseReplayView;
+ private boolean hidden;
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);
-
- 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);
- TextView loadingText = createOverlayTextView(context);
- loadingText.setText(R.string.loading_video);
- loadingView.addView(loadingText, 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 = createOverlayTextView(context);
- addView(errorView, matchParent);
-
handler = new Handler();
startHidingRunnable = new Runnable() {
@Override
@@ -127,123 +52,47 @@ public class MovieControllerOverlay extends FrameLayout implements
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();
}
- private TextView createOverlayTextView(Context context) {
- TextView view = new TextView(context);
- view.setGravity(Gravity.CENTER);
- view.setTextColor(0xFFFFFFFF);
- view.setPadding(0, 15, 0, 15);
- return view;
- }
-
- @Override
- public void setListener(Listener listener) {
- this.listener = listener;
- }
-
- @Override
- public void setCanReplay(boolean canReplay) {
- this.canReplay = canReplay;
- }
-
- @Override
- public View getView() {
- return this;
- }
-
- @Override
- public void showPlaying() {
- state = State.PLAYING;
- showMainView(playPauseReplayView);
- }
-
- @Override
- public void showPaused() {
- state = State.PAUSED;
- showMainView(playPauseReplayView);
- }
-
@Override
- public void showEnded() {
- state = State.ENDED;
- showMainView(playPauseReplayView);
+ protected void createTimeBar(Context context) {
+ mTimeBar = new TimeBar(context, this);
}
@Override
- public void showLoading() {
- state = State.LOADING;
- showMainView(loadingView);
- }
-
- @Override
- public void showErrorMessage(String message) {
- state = State.ERROR;
- int padding = (int) (getMeasuredWidth() * ERROR_MESSAGE_RELATIVE_PADDING);
- errorView.setPadding(
- padding, errorView.getPaddingTop(), padding, errorView.getPaddingBottom());
- errorView.setText(message);
- showMainView(errorView);
- }
-
- @Override
- 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();
+ super.hide();
+ if (mListener != null && wasHidden != hidden) {
+ mListener.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();
- }
@Override
public void show() {
boolean wasHidden = hidden;
hidden = false;
- updateViews();
- setVisibility(View.VISIBLE);
- setFocusable(false);
- if (listener != null && wasHidden != hidden) {
- listener.onShown();
+ super.show();
+ if (mListener != null && wasHidden != hidden) {
+ mListener.onShown();
}
maybeStartHiding();
}
private void maybeStartHiding() {
cancelHiding();
- if (state == State.PLAYING) {
+ if (mState == State.PLAYING) {
handler.postDelayed(startHidingRunnable, 2500);
}
}
private void startHiding() {
- startHideAnimation(background);
- startHideAnimation(timeBar);
- startHideAnimation(playPauseReplayView);
+ startHideAnimation(mBackground);
+ startHideAnimation(mTimeBar);
+ startHideAnimation(mPlayPauseReplayView);
}
private void startHideAnimation(View view) {
@@ -254,9 +103,9 @@ public class MovieControllerOverlay extends FrameLayout implements
private void cancelHiding() {
handler.removeCallbacks(startHidingRunnable);
- background.setAnimation(null);
- timeBar.setAnimation(null);
- playPauseReplayView.setAnimation(null);
+ mBackground.setAnimation(null);
+ mTimeBar.setAnimation(null);
+ mPlayPauseReplayView.setAnimation(null);
}
@Override
@@ -275,21 +124,6 @@ public class MovieControllerOverlay extends FrameLayout implements
}
@Override
- 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();
@@ -310,8 +144,8 @@ public class MovieControllerOverlay extends FrameLayout implements
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
cancelHiding();
- if (state == State.PLAYING || state == State.PAUSED) {
- listener.onPlayPause();
+ if (mState == State.PLAYING || mState == State.PAUSED) {
+ mListener.onPlayPause();
}
break;
case MotionEvent.ACTION_UP:
@@ -321,88 +155,12 @@ public class MovieControllerOverlay extends FrameLayout implements
return true;
}
- // The paddings of 4 sides which covered by system components. E.g.
- // +-----------------+\
- // | Action Bar | insets.top
- // +-----------------+/
- // | |
- // | Content Area | insets.right = insets.left = 0
- // | |
- // +-----------------+\
- // | Navigation Bar | insets.bottom
- // +-----------------+/
- // Please see View.fitSystemWindows() for more details.
- private final Rect mWindowInsets = new Rect();
-
- @Override
- protected boolean fitSystemWindows(Rect insets) {
- // We don't set the paddings of this View, otherwise,
- // the content will get cropped outside window
- mWindowInsets.set(insets);
- return true;
- }
-
@Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- Rect insets = mWindowInsets;
- int pl = insets.left; // the left paddings
- int pr = insets.right;
- int pt = insets.top;
- int pb = insets.bottom;
-
- int h = bottom - top;
- int w = right - left;
- boolean error = errorView.getVisibility() == View.VISIBLE;
-
- int y = h - pb;
- // Put both TimeBar and Background just above the bottom system
- // component.
- // But extend the background to the width of the screen, since we don't
- // care if it will be covered by a system component and it looks better.
- background.layout(0, y - timeBar.getBarHeight(), w, y);
- timeBar.layout(pl, y - timeBar.getPreferredHeight(), w - pr, y);
-
- // Needed, otherwise the framework will not re-layout in case only the
- // padding is changed
- timeBar.requestLayout();
-
- // Put the play/pause/next/ previous button in the center of the screen
- layoutCenteredView(playPauseReplayView, 0, 0, w, h);
-
- if (mainView != null) {
- layoutCenteredView(mainView, 0, 0, w, h);
- }
- }
-
- 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() {
+ protected 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();
+ super.updateViews();
}
// TimeBar listener
@@ -410,18 +168,18 @@ public class MovieControllerOverlay extends FrameLayout implements
@Override
public void onScrubbingStart() {
cancelHiding();
- listener.onSeekStart();
+ super.onScrubbingStart();
}
@Override
public void onScrubbingMove(int time) {
cancelHiding();
- listener.onSeekMove(time);
+ super.onScrubbingMove(time);
}
@Override
- public void onScrubbingEnd(int time) {
+ public void onScrubbingEnd(int time, int trimStartTime, int trimEndTime) {
maybeStartHiding();
- listener.onSeekEnd(time);
+ super.onScrubbingEnd(time, trimStartTime, trimEndTime);
}
}
diff --git a/src/com/android/gallery3d/app/MoviePlayer.java b/src/com/android/gallery3d/app/MoviePlayer.java
index 0cf361c71..9d229f088 100644
--- a/src/com/android/gallery3d/app/MoviePlayer.java
+++ b/src/com/android/gallery3d/app/MoviePlayer.java
@@ -304,7 +304,7 @@ public class MoviePlayer implements
}
int position = mVideoView.getCurrentPosition();
int duration = mVideoView.getDuration();
- mController.setTimes(position, duration);
+ mController.setTimes(position, duration, 0, 0);
return position;
}
@@ -376,7 +376,7 @@ public class MoviePlayer implements
}
@Override
- public void onSeekEnd(int time) {
+ public void onSeekEnd(int time, int start, int end) {
mDragging = false;
mVideoView.seekTo(time);
setProgress();
diff --git a/src/com/android/gallery3d/app/PhotoPage.java b/src/com/android/gallery3d/app/PhotoPage.java
index 45709955e..6b7459c8d 100644
--- a/src/com/android/gallery3d/app/PhotoPage.java
+++ b/src/com/android/gallery3d/app/PhotoPage.java
@@ -94,6 +94,7 @@ public class PhotoPage extends ActivityState implements
private static final int REQUEST_CROP_PICASA = 3;
private static final int REQUEST_EDIT = 4;
private static final int REQUEST_PLAY_VIDEO = 5;
+ private static final int REQUEST_TRIM = 6;
public static final String KEY_MEDIA_SET_PATH = "media-set-path";
public static final String KEY_MEDIA_ITEM_PATH = "media-item-path";
@@ -711,7 +712,9 @@ public class PhotoPage extends ActivityState implements
return true;
}
case R.id.action_trim: {
- // TODO: Add trimming activity here.
+ Intent intent = new Intent(mActivity, TrimVideo.class);
+ intent.setData(manager.getContentUri(path));
+ mActivity.startActivityForResult(intent, REQUEST_TRIM);
return true;
}
case R.id.action_edit: {
diff --git a/src/com/android/gallery3d/app/TimeBar.java b/src/com/android/gallery3d/app/TimeBar.java
index f52879732..b7ffbb0de 100644
--- a/src/com/android/gallery3d/app/TimeBar.java
+++ b/src/com/android/gallery3d/app/TimeBar.java
@@ -30,250 +30,233 @@ import com.android.gallery3d.R;
import com.android.gallery3d.common.Utils;
/**
- * The time bar view, which includes the current and total time, the progress bar,
- * and the scrubber.
+ * The time bar view, which includes the current and total time, the progress
+ * bar, and the scrubber.
*/
public class TimeBar extends View {
- public interface Listener {
- void onScrubbingStart();
- void onScrubbingMove(int time);
- void onScrubbingEnd(int time);
- }
+ public interface Listener {
+ void onScrubbingStart();
- // Padding around the scrubber to increase its touch target
- private static final int SCRUBBER_PADDING_IN_DP = 10;
+ void onScrubbingMove(int time);
- // The total padding, top plus bottom
- private static final int V_PADDING_IN_DP = 30;
+ void onScrubbingEnd(int time, int start, int end);
+ }
- private static final int TEXT_SIZE_IN_DP = 14;
+ // Padding around the scrubber to increase its touch target
+ private static final int SCRUBBER_PADDING_IN_DP = 10;
- private final Listener listener;
+ // The total padding, top plus bottom
+ private static final int V_PADDING_IN_DP = 30;
- // the bars we use for displaying the progress
- private final Rect progressBar;
- private final Rect playedBar;
+ private static final int TEXT_SIZE_IN_DP = 14;
- private final Paint progressPaint;
- private final Paint playedPaint;
- private final Paint timeTextPaint;
+ protected final Listener mListener;
- private final Bitmap scrubber;
- private final int scrubberPadding; // adds some touch tolerance around the scrubber
+ // the bars we use for displaying the progress
+ protected final Rect mProgressBar;
+ protected final Rect mPlayedBar;
- private int scrubberLeft;
- private int scrubberTop;
- private int scrubberCorrection;
- private boolean scrubbing;
- private boolean showTimes;
- private boolean showScrubber;
+ private final Paint mProgressPaint;
+ private final Paint mPlayedPaint;
+ private final Paint mTimeTextPaint;
- private int totalTime;
- private int currentTime;
+ protected final Bitmap mScrubber;
+ protected int mScrubberPadding; // adds some touch tolerance around the
+ // scrubber
- private final Rect timeBounds;
+ protected int mScrubberLeft;
+ protected int mScrubberTop;
+ protected int mScrubberCorrection;
+ protected boolean mScrubbing;
+ protected boolean mShowTimes;
+ protected boolean mShowScrubber;
- private int vPaddingInPx;
+ protected int mTotalTime;
+ protected int mCurrentTime;
- public TimeBar(Context context, Listener listener) {
- super(context);
- this.listener = Utils.checkNotNull(listener);
+ protected final Rect mTimeBounds;
- showTimes = true;
- showScrubber = true;
+ protected int mVPaddingInPx;
- progressBar = new Rect();
- playedBar = new Rect();
+ public TimeBar(Context context, Listener listener) {
+ super(context);
+ mListener = Utils.checkNotNull(listener);
- progressPaint = new Paint();
- progressPaint.setColor(0xFF808080);
- playedPaint = new Paint();
- playedPaint.setColor(0xFFFFFFFF);
+ mShowTimes = true;
+ mShowScrubber = true;
- DisplayMetrics metrics = context.getResources().getDisplayMetrics();
- float textSizeInPx = metrics.density * TEXT_SIZE_IN_DP;
- timeTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- timeTextPaint.setColor(0xFFCECECE);
- timeTextPaint.setTextSize(textSizeInPx);
- timeTextPaint.setTextAlign(Paint.Align.CENTER);
+ mProgressBar = new Rect();
+ mPlayedBar = new Rect();
- timeBounds = new Rect();
- timeTextPaint.getTextBounds("0:00:00", 0, 7, timeBounds);
+ mProgressPaint = new Paint();
+ mProgressPaint.setColor(0xFF808080);
+ mPlayedPaint = new Paint();
+ mPlayedPaint.setColor(0xFFFFFFFF);
- scrubber = BitmapFactory.decodeResource(getResources(), R.drawable.scrubber_knob);
- scrubberPadding = (int) (metrics.density * SCRUBBER_PADDING_IN_DP);
+ DisplayMetrics metrics = context.getResources().getDisplayMetrics();
+ float textSizeInPx = metrics.density * TEXT_SIZE_IN_DP;
+ mTimeTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mTimeTextPaint.setColor(0xFFCECECE);
+ mTimeTextPaint.setTextSize(textSizeInPx);
+ mTimeTextPaint.setTextAlign(Paint.Align.CENTER);
- vPaddingInPx = (int) (metrics.density * V_PADDING_IN_DP);
- }
+ mTimeBounds = new Rect();
+ mTimeTextPaint.getTextBounds("0:00:00", 0, 7, mTimeBounds);
- private void update() {
- playedBar.set(progressBar);
+ mScrubber = BitmapFactory.decodeResource(getResources(), R.drawable.scrubber_knob);
+ mScrubberPadding = (int) (metrics.density * SCRUBBER_PADDING_IN_DP);
- if (totalTime > 0) {
- playedBar.right =
- playedBar.left + (int) ((progressBar.width() * (long) currentTime) / totalTime);
- } else {
- playedBar.right = progressBar.left;
+ mVPaddingInPx = (int) (metrics.density * V_PADDING_IN_DP);
}
- if (!scrubbing) {
- scrubberLeft = playedBar.right - scrubber.getWidth() / 2;
+ private void update() {
+ mPlayedBar.set(mProgressBar);
+
+ if (mTotalTime > 0) {
+ mPlayedBar.right =
+ mPlayedBar.left + (int) ((mProgressBar.width() * (long) mCurrentTime) / mTotalTime);
+ } else {
+ mPlayedBar.right = mProgressBar.left;
+ }
+
+ if (!mScrubbing) {
+ mScrubberLeft = mPlayedBar.right - mScrubber.getWidth() / 2;
+ }
+ invalidate();
}
- invalidate();
- }
-
- /**
- * @return the preferred height of this view, including invisible padding
- */
- public int getPreferredHeight() {
- return timeBounds.height() + vPaddingInPx + scrubberPadding;
- }
-
- /**
- * @return the height of the time bar, excluding invisible padding
- */
- public int getBarHeight() {
- return timeBounds.height() + vPaddingInPx;
- }
-
- public void setTime(int currentTime, int totalTime) {
- if (this.currentTime == currentTime && this.totalTime == totalTime) {
- return;
+
+ /**
+ * @return the preferred height of this view, including invisible padding
+ */
+ public int getPreferredHeight() {
+ return mTimeBounds.height() + mVPaddingInPx + mScrubberPadding;
}
- this.currentTime = currentTime;
- this.totalTime = totalTime;
- update();
- }
-
- public void setShowTimes(boolean showTimes) {
- this.showTimes = showTimes;
- requestLayout();
- }
-
- public void resetTime() {
- setTime(0, 0);
- }
-
- public void setShowScrubber(boolean showScrubber) {
- this.showScrubber = showScrubber;
- if (!showScrubber && scrubbing) {
- listener.onScrubbingEnd(getScrubberTime());
- scrubbing = false;
+
+ /**
+ * @return the height of the time bar, excluding invisible padding
+ */
+ public int getBarHeight() {
+ return mTimeBounds.height() + mVPaddingInPx;
}
- requestLayout();
- }
-
- private boolean inScrubber(float x, float y) {
- int scrubberRight = scrubberLeft + scrubber.getWidth();
- int scrubberBottom = scrubberTop + scrubber.getHeight();
- return scrubberLeft - scrubberPadding < x && x < scrubberRight + scrubberPadding
- && scrubberTop - scrubberPadding < y && y < scrubberBottom + scrubberPadding;
- }
-
- private void clampScrubber() {
- int half = scrubber.getWidth() / 2;
- int max = progressBar.right - half;
- int min = progressBar.left - half;
- scrubberLeft = Math.min(max, Math.max(min, scrubberLeft));
- }
-
- private int getScrubberTime() {
- return (int) ((long) (scrubberLeft + scrubber.getWidth() / 2 - progressBar.left)
- * totalTime / progressBar.width());
- }
-
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- int w = r - l;
- int h = b - t;
- if (!showTimes && !showScrubber) {
- progressBar.set(0, 0, w, h);
- } else {
- int margin = scrubber.getWidth() / 3;
- if (showTimes) {
- margin += timeBounds.width();
- }
- int progressY = (h + scrubberPadding) / 2;
- scrubberTop = progressY - scrubber.getHeight() / 2 + 1;
- progressBar.set(
- getPaddingLeft() + margin, progressY,
- w - getPaddingRight() - margin, progressY + 4);
+
+ public void setTime(int currentTime, int totalTime,
+ int trimStartTime, int trimEndTime) {
+ if (mCurrentTime == currentTime && mTotalTime == totalTime) {
+ return;
+ }
+ mCurrentTime = currentTime;
+ mTotalTime = totalTime;
+ update();
}
- update();
- }
- @Override
- public void draw(Canvas canvas) {
- super.draw(canvas);
+ private boolean inScrubber(float x, float y) {
+ int scrubberRight = mScrubberLeft + mScrubber.getWidth();
+ int scrubberBottom = mScrubberTop + mScrubber.getHeight();
+ return mScrubberLeft - mScrubberPadding < x && x < scrubberRight + mScrubberPadding
+ && mScrubberTop - mScrubberPadding < y && y < scrubberBottom + mScrubberPadding;
+ }
- // draw progress bars
- canvas.drawRect(progressBar, progressPaint);
- canvas.drawRect(playedBar, playedPaint);
+ protected void clampScrubber() {
+ int half = mScrubber.getWidth() / 2;
+ int max = mProgressBar.right - half;
+ int min = mProgressBar.left - half;
+ mScrubberLeft = Math.min(max, Math.max(min, mScrubberLeft));
+ }
- // draw scrubber and timers
- if (showScrubber) {
- canvas.drawBitmap(scrubber, scrubberLeft, scrubberTop, null);
+ protected int getScrubberTime() {
+ return (int) ((long) (mScrubberLeft + mScrubber.getWidth() / 2 - mProgressBar.left)
+ * mTotalTime / mProgressBar.width());
}
- if (showTimes) {
- canvas.drawText(
- stringForTime(currentTime),
- timeBounds.width() / 2 + getPaddingLeft(),
- timeBounds.height() + vPaddingInPx / 2 + scrubberPadding + 1,
- timeTextPaint);
- canvas.drawText(
- stringForTime(totalTime),
- getWidth() - getPaddingRight() - timeBounds.width() / 2,
- timeBounds.height() + vPaddingInPx / 2 + scrubberPadding + 1,
- timeTextPaint);
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ int w = r - l;
+ int h = b - t;
+ if (!mShowTimes && !mShowScrubber) {
+ mProgressBar.set(0, 0, w, h);
+ } else {
+ int margin = mScrubber.getWidth() / 3;
+ if (mShowTimes) {
+ margin += mTimeBounds.width();
+ }
+ int progressY = (h + mScrubberPadding) / 2;
+ mScrubberTop = progressY - mScrubber.getHeight() / 2 + 1;
+ mProgressBar.set(
+ getPaddingLeft() + margin, progressY,
+ w - getPaddingRight() - margin, progressY + 4);
+ }
+ update();
}
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
-
- if (showScrubber) {
- int x = (int) event.getX();
- int y = (int) event.getY();
-
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN: {
- scrubberCorrection = inScrubber(x, y)
- ? x - scrubberLeft
- : scrubber.getWidth() / 2;
- scrubbing = true;
- listener.onScrubbingStart();
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ // draw progress bars
+ canvas.drawRect(mProgressBar, mProgressPaint);
+ canvas.drawRect(mPlayedBar, mPlayedPaint);
+
+ // draw scrubber and timers
+ if (mShowScrubber) {
+ canvas.drawBitmap(mScrubber, mScrubberLeft, mScrubberTop, null);
}
- // fall-through
- case MotionEvent.ACTION_MOVE: {
- scrubberLeft = x - scrubberCorrection;
- clampScrubber();
- currentTime = getScrubberTime();
- listener.onScrubbingMove(currentTime);
- invalidate();
- return true;
+ if (mShowTimes) {
+ canvas.drawText(
+ stringForTime(mCurrentTime),
+ mTimeBounds.width() / 2 + getPaddingLeft(),
+ mTimeBounds.height() + mVPaddingInPx / 2 + mScrubberPadding + 1,
+ mTimeTextPaint);
+ canvas.drawText(
+ stringForTime(mTotalTime),
+ getWidth() - getPaddingRight() - mTimeBounds.width() / 2,
+ mTimeBounds.height() + mVPaddingInPx / 2 + mScrubberPadding + 1,
+ mTimeTextPaint);
}
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_UP: {
- listener.onScrubbingEnd(getScrubberTime());
- scrubbing = false;
- return true;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (mShowScrubber) {
+ int x = (int) event.getX();
+ int y = (int) event.getY();
+
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN: {
+ mScrubberCorrection = inScrubber(x, y)
+ ? x - mScrubberLeft
+ : mScrubber.getWidth() / 2;
+ mScrubbing = true;
+ mListener.onScrubbingStart();
+ }
+ // fall-through
+ case MotionEvent.ACTION_MOVE: {
+ mScrubberLeft = x - mScrubberCorrection;
+ clampScrubber();
+ mCurrentTime = getScrubberTime();
+ mListener.onScrubbingMove(mCurrentTime);
+ invalidate();
+ return true;
+ }
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP: {
+ mListener.onScrubbingEnd(getScrubberTime(), 0, 0);
+ mScrubbing = false;
+ return true;
+ }
+ }
}
- }
+ return false;
}
- return false;
- }
-
- private String stringForTime(long millis) {
- int totalSeconds = (int) millis / 1000;
- int seconds = totalSeconds % 60;
- int minutes = (totalSeconds / 60) % 60;
- int hours = totalSeconds / 3600;
- if (hours > 0) {
- return String.format("%d:%02d:%02d", hours, minutes, seconds).toString();
- } else {
- return String.format("%02d:%02d", minutes, seconds).toString();
+
+ protected String stringForTime(long millis) {
+ int totalSeconds = (int) millis / 1000;
+ int seconds = totalSeconds % 60;
+ int minutes = (totalSeconds / 60) % 60;
+ int hours = totalSeconds / 3600;
+ if (hours > 0) {
+ return String.format("%d:%02d:%02d", hours, minutes, seconds).toString();
+ } else {
+ return String.format("%02d:%02d", minutes, seconds).toString();
+ }
}
- }
}
diff --git a/src/com/android/gallery3d/app/TrimControllerOverlay.java b/src/com/android/gallery3d/app/TrimControllerOverlay.java
new file mode 100644
index 000000000..9127ad159
--- /dev/null
+++ b/src/com/android/gallery3d/app/TrimControllerOverlay.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2012 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 android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.view.MotionEvent;
+import android.view.View;
+
+/**
+ * The controller for the Trimming Video.
+ */
+public class TrimControllerOverlay extends CommonControllerOverlay {
+
+ public TrimControllerOverlay(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void createTimeBar(Context context) {
+ mTimeBar = new TrimTimeBar(context, this);
+ }
+
+ private void hidePlayButtonIfPlaying() {
+ if (mState == State.PLAYING) {
+ mPlayPauseReplayView.setVisibility(View.INVISIBLE);
+ }
+ mPlayPauseReplayView.setAlpha(1f);
+ }
+
+ @Override
+ public void showPlaying() {
+ super.showPlaying();
+
+ // Add animation to hide the play button while playing.
+ ObjectAnimator anim = ObjectAnimator.ofFloat(mPlayPauseReplayView, "alpha", 1f, 0f);
+ anim.setDuration(200);
+ anim.start();
+ anim.addListener(new AnimatorListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ hidePlayButtonIfPlaying();
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ hidePlayButtonIfPlaying();
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ }
+ });
+ }
+
+ @Override
+ public void setTimes(int currentTime, int totalTime, int trimStartTime, int trimEndTime) {
+ mTimeBar.setTime(currentTime, totalTime, trimStartTime, trimEndTime);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (super.onTouchEvent(event)) {
+ return true;
+ }
+
+ // The special thing here is that the State.ENDED include both cases of
+ // the video completed and current == trimEnd. Both request a replay.
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ if (mState == State.PLAYING || mState == State.PAUSED) {
+ mListener.onPlayPause();
+ } else if (mState == State.ENDED) {
+ if (mCanReplay) {
+ mListener.onReplay();
+ }
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ break;
+ }
+ return true;
+ }
+}
diff --git a/src/com/android/gallery3d/app/TrimTimeBar.java b/src/com/android/gallery3d/app/TrimTimeBar.java
new file mode 100644
index 000000000..f1878bb8a
--- /dev/null
+++ b/src/com/android/gallery3d/app/TrimTimeBar.java
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2012 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 android.content.Context;
+import android.graphics.Canvas;
+import android.view.MotionEvent;
+
+/**
+ * The trim time bar view, which includes the current and total time, the progress
+ * bar, and the scrubbers for current time, start and end time for trimming.
+ */
+public class TrimTimeBar extends TimeBar {
+
+ public static final int SCRUBBER_NONE = 0;
+ public static final int SCRUBBER_START = 1;
+ public static final int SCRUBBER_CURRENT = 2;
+ public static final int SCRUBBER_END = 3;
+
+ private int mPressedThumb = SCRUBBER_NONE;
+
+ // On touch event, the setting order is Scrubber Position -> Time ->
+ // PlayedBar. At the setTimes(), activity can update the Time directly, then
+ // PlayedBar will be updated too.
+ private int mTrimStartScrubberLeft;
+ private int mTrimEndScrubberLeft;
+
+ private int mTrimStartScrubberTop;
+ private int mTrimEndScrubberTop;
+
+ private int mTrimStartTime;
+ private int mTrimEndTime;
+
+ public TrimTimeBar(Context context, Listener listener) {
+ super(context, listener);
+
+ mTrimStartTime = 0;
+ mTrimEndTime = 0;
+ mTrimStartScrubberLeft = 0;
+ mTrimEndScrubberLeft = 0;
+ mTrimStartScrubberTop = 0;
+ mTrimEndScrubberTop = 0;
+
+ // Increase the size of this trimTimeBar.
+ mScrubberPadding = mScrubberPadding * 2;
+ mVPaddingInPx = mVPaddingInPx * 2;
+ }
+
+ private int calculatePlayedBarBoundary(int time) {
+ return mProgressBar.left + (int) ((mProgressBar.width() * (long) time) / mTotalTime);
+ }
+
+ // Based on all the time info (current, total, trimStart, trimEnd), we
+ // decide the playedBar size.
+ private void updatePlayedBarAndScrubberFromTime() {
+ // According to the Time, update the Played Bar
+ mPlayedBar.set(mProgressBar);
+ if (mTotalTime > 0) {
+ // set playedBar according to the trim time.
+ mPlayedBar.left = calculatePlayedBarBoundary(mTrimStartTime);
+ mPlayedBar.right = calculatePlayedBarBoundary(mCurrentTime);
+ if (!mScrubbing) {
+ mScrubberLeft = mPlayedBar.right - mScrubber.getWidth() / 2;
+ mTrimStartScrubberLeft = mPlayedBar.left - mScrubber.getWidth() / 2;
+ mTrimEndScrubberLeft = calculatePlayedBarBoundary(mTrimEndTime)
+ - mScrubber.getWidth() / 2;
+ }
+ } else {
+ // If the video is not prepared, just show the scrubber at the end
+ // of progressBar
+ mPlayedBar.right = mProgressBar.left;
+ mScrubberLeft = mProgressBar.left - mScrubber.getWidth() / 2;
+ mTrimStartScrubberLeft = mProgressBar.left - mScrubber.getWidth() / 2;
+ mTrimEndScrubberLeft = mProgressBar.right - mScrubber.getWidth() / 2;
+ }
+ }
+
+ private void initTrimTimeIfNeeded() {
+ if (mTotalTime > 0 && mTrimEndTime == 0) {
+ mTrimEndTime = mTotalTime;
+ }
+ }
+
+ private void update() {
+ initTrimTimeIfNeeded();
+ updatePlayedBarAndScrubberFromTime();
+ invalidate();
+ }
+
+ @Override
+ public void setTime(int currentTime, int totalTime,
+ int trimStartTime, int trimEndTime) {
+ if (mCurrentTime == currentTime && mTotalTime == totalTime
+ && mTrimStartTime == trimStartTime && mTrimEndTime == trimEndTime) {
+ return;
+ }
+ mCurrentTime = currentTime;
+ mTotalTime = totalTime;
+ mTrimStartTime = trimStartTime;
+ mTrimEndTime = trimEndTime;
+ update();
+ }
+
+ private int whichScrubber(float x, float y) {
+ if (inScrubber(mTrimStartScrubberLeft, mTrimStartScrubberTop, x, y)) {
+ return SCRUBBER_START;
+ } else if (inScrubber(mTrimEndScrubberLeft, mTrimEndScrubberTop, x, y)) {
+ return SCRUBBER_END;
+ } else if (inScrubber(mScrubberLeft, mScrubberTop, x, y)) {
+ return SCRUBBER_CURRENT;
+ }
+ return SCRUBBER_NONE;
+ }
+
+ private boolean inScrubber(int startX, int startY, float x, float y) {
+ int scrubberRight = startX + mScrubber.getWidth();
+ int scrubberBottom = startY + mScrubber.getHeight();
+ return startX - mScrubberPadding < x && x < scrubberRight + mScrubberPadding
+ && startY - mScrubberPadding < y && y < scrubberBottom + mScrubberPadding;
+ }
+
+ private int clampScrubber(int scrubberX) {
+ int half = mScrubber.getWidth() / 2;
+ int max = mProgressBar.right - half;
+ int min = mProgressBar.left - half;
+ return Math.min(max, Math.max(min, scrubberX));
+ }
+
+ private int getScrubberTime(int scrubberX) {
+ return (int) ((long) (scrubberX + mScrubber.getWidth() / 2 - mProgressBar.left)
+ * mTotalTime / mProgressBar.width());
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ int w = r - l;
+ int h = b - t;
+ if (!mShowTimes && !mShowScrubber) {
+ mProgressBar.set(0, 0, w, h);
+ } else {
+ int margin = mScrubber.getWidth() / 3;
+ if (mShowTimes) {
+ margin += mTimeBounds.width();
+ }
+ int progressY = (h + mScrubberPadding) / 2;
+ int scrubberY = progressY - mScrubber.getHeight() / 2 + 1;
+ mScrubberTop = scrubberY;
+ mTrimStartScrubberTop = scrubberY - mScrubber.getHeight() / 2;
+ mTrimEndScrubberTop = scrubberY + mScrubber.getHeight() / 2;
+ mProgressBar.set(
+ getPaddingLeft() + margin, progressY,
+ w - getPaddingRight() - margin, progressY + 4);
+ }
+ update();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ // draw extra scrubbers
+ if (mShowScrubber) {
+ canvas.drawBitmap(mScrubber, mTrimStartScrubberLeft, mTrimStartScrubberTop, null);
+ canvas.drawBitmap(mScrubber, mTrimEndScrubberLeft, mTrimEndScrubberTop, null);
+ }
+ }
+
+ private void updateTimeFromPos() {
+ mCurrentTime = getScrubberTime(mScrubberLeft);
+ mTrimStartTime = getScrubberTime(mTrimStartScrubberLeft);
+ mTrimEndTime = getScrubberTime(mTrimEndScrubberLeft);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (mShowScrubber) {
+ int x = (int) event.getX();
+ int y = (int) event.getY();
+
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mPressedThumb = whichScrubber(x, y);
+ switch (mPressedThumb) {
+ case SCRUBBER_NONE:
+ break;
+ case SCRUBBER_CURRENT:
+ mScrubbing = true;
+ mScrubberCorrection = x - mScrubberLeft;
+ break;
+ case SCRUBBER_START:
+ mScrubbing = true;
+ mScrubberCorrection = x - mTrimStartScrubberLeft;
+ break;
+ case SCRUBBER_END:
+ mScrubbing = true;
+ mScrubberCorrection = x - mTrimEndScrubberLeft;
+ break;
+ }
+ if (mScrubbing == true) {
+ mListener.onScrubbingStart();
+ return true;
+ }
+ break;
+ case MotionEvent.ACTION_MOVE:
+ if (mScrubbing) {
+ int seekToPos = -1;
+ switch (mPressedThumb) {
+ case SCRUBBER_CURRENT:
+ mScrubberLeft = x - mScrubberCorrection;
+ // Limit current within (start, end)
+ if (mScrubberLeft <= mTrimStartScrubberLeft) {
+ mScrubberLeft = mTrimStartScrubberLeft;
+ } else if (mScrubberLeft >= mTrimEndScrubberLeft) {
+ mScrubberLeft = mTrimEndScrubberLeft;
+ }
+ mScrubberLeft = clampScrubber(mScrubberLeft);
+ seekToPos = mScrubberLeft;
+ break;
+ case SCRUBBER_START:
+ mTrimStartScrubberLeft = x - mScrubberCorrection;
+ // Limit start <= end
+ if (mTrimStartScrubberLeft > mTrimEndScrubberLeft) {
+ mTrimStartScrubberLeft = mTrimEndScrubberLeft;
+ }
+ seekToPos = mTrimStartScrubberLeft;
+ mTrimStartScrubberLeft = clampScrubber(mTrimStartScrubberLeft);
+ break;
+ case SCRUBBER_END:
+ mTrimEndScrubberLeft = x - mScrubberCorrection;
+ // Limit end >= start
+ if (mTrimEndScrubberLeft < mTrimStartScrubberLeft) {
+ mTrimEndScrubberLeft = mTrimStartScrubberLeft;
+ }
+ seekToPos = mTrimEndScrubberLeft;
+ mTrimEndScrubberLeft = clampScrubber(mTrimEndScrubberLeft);
+ break;
+ }
+ updateTimeFromPos();
+ updatePlayedBarAndScrubberFromTime();
+ if (seekToPos != -1) {
+ mListener.onScrubbingMove(getScrubberTime(seekToPos));
+ }
+ invalidate();
+ return true;
+ }
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ if (mScrubbing) {
+ int seekToPos = 0;
+ switch (mPressedThumb) {
+ case SCRUBBER_CURRENT:
+ seekToPos = mScrubberLeft;
+ break;
+ case SCRUBBER_START:
+ seekToPos = mTrimStartScrubberLeft;
+ mScrubberLeft = mTrimStartScrubberLeft;
+ break;
+ case SCRUBBER_END:
+ seekToPos = mTrimEndScrubberLeft;
+ mScrubberLeft = mTrimEndScrubberLeft;
+ break;
+ }
+ updateTimeFromPos();
+ mListener.onScrubbingEnd(getScrubberTime(seekToPos),
+ getScrubberTime(mTrimStartScrubberLeft),
+ getScrubberTime(mTrimEndScrubberLeft));
+ mScrubbing = false;
+ mPressedThumb = SCRUBBER_NONE;
+ return true;
+ }
+ break;
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/com/android/gallery3d/app/TrimVideo.java b/src/com/android/gallery3d/app/TrimVideo.java
new file mode 100644
index 000000000..4fb255729
--- /dev/null
+++ b/src/com/android/gallery3d/app/TrimVideo.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2012 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 android.app.ActionBar;
+import android.app.Activity;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.Intent;
+import android.media.MediaPlayer;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Toast;
+import android.widget.VideoView;
+
+import com.android.gallery3d.R;
+
+
+public class TrimVideo extends Activity implements
+ MediaPlayer.OnErrorListener,
+ MediaPlayer.OnCompletionListener,
+ ControllerOverlay.Listener {
+
+ private VideoView mVideoView;
+ private TrimControllerOverlay mController;
+ private Context mContext;
+ private Uri mUri;
+ private final Handler mHandler = new Handler();
+ public static final String TRIM_ACTION = "com.android.camera.action.TRIM";
+
+ public ProgressDialog mProgress;
+
+ private int mTrimStartTime = 0;
+ private int mTrimEndTime = 0;
+ private int mVideoPosition = 0;
+ public static final String KEY_TRIM_START = "trim_start";
+ public static final String KEY_TRIM_END = "trim_end";
+ public static final String KEY_VIDEO_POSITION = "video_pos";
+ private boolean mHasPaused = false;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ mContext = getApplicationContext();
+ super.onCreate(savedInstanceState);
+
+ ActionBar actionBar = getActionBar();
+ actionBar.setDisplayHomeAsUpEnabled(true);
+
+ Intent intent = getIntent();
+ mUri = intent.getData();
+
+ setContentView(R.layout.trim_view);
+ View rootView = findViewById(R.id.trim_view_root);
+
+ mVideoView = (VideoView) rootView.findViewById(R.id.surface_view);
+
+ mController = new TrimControllerOverlay(mContext);
+ ((ViewGroup)rootView).addView(mController.getView());
+ mController.setListener(this);
+ mController.setCanReplay(true);
+
+ mVideoView.setOnErrorListener(this);
+ mVideoView.setOnCompletionListener(this);
+ mVideoView.setVideoURI(mUri);
+
+ playVideo();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (mHasPaused) {
+ mVideoView.seekTo(mVideoPosition);
+ mVideoView.resume();
+ mHasPaused = false;
+ }
+ mHandler.post(mProgressChecker);
+ }
+
+ @Override
+ public void onPause() {
+ mHasPaused = true;
+ mHandler.removeCallbacksAndMessages(null);
+ mVideoPosition = mVideoView.getCurrentPosition();
+ mVideoView.suspend();
+ super.onPause();
+ }
+
+ @Override
+ public void onDestroy() {
+ mVideoView.stopPlayback();
+ super.onDestroy();
+ }
+
+ private final Runnable mProgressChecker = new Runnable() {
+ @Override
+ public void run() {
+ int pos = setProgress();
+ mHandler.postDelayed(mProgressChecker, 200 - (pos % 200));
+ }
+ };
+
+ @Override
+ public void onSaveInstanceState(Bundle savedInstanceState) {
+ savedInstanceState.putInt(KEY_TRIM_START, mTrimStartTime);
+ savedInstanceState.putInt(KEY_TRIM_END, mTrimEndTime);
+ savedInstanceState.putInt(KEY_VIDEO_POSITION, mVideoPosition);
+ super.onSaveInstanceState(savedInstanceState);
+ }
+
+ @Override
+ public void onRestoreInstanceState(Bundle savedInstanceState) {
+ super.onRestoreInstanceState(savedInstanceState);
+ mTrimStartTime = savedInstanceState.getInt(KEY_TRIM_START, 0);
+ mTrimEndTime = savedInstanceState.getInt(KEY_TRIM_END, 0);
+ mVideoPosition = savedInstanceState.getInt(KEY_VIDEO_POSITION, 0);
+ }
+
+ // This updates the time bar display (if necessary). It is called by
+ // mProgressChecker and also from places where the time bar needs
+ // to be updated immediately.
+ private int setProgress() {
+ mVideoPosition = mVideoView.getCurrentPosition();
+ // If the video position is smaller than the starting point of trimming,
+ // correct it.
+ if (mVideoPosition < mTrimStartTime) {
+ mVideoView.seekTo(mTrimStartTime);
+ mVideoPosition = mTrimStartTime;
+ }
+ // If the position is bigger than the end point of trimming, show the
+ // replay button and pause.
+ if (mVideoPosition >= mTrimEndTime && mTrimEndTime > 0) {
+ if (mVideoPosition > mTrimEndTime) {
+ mVideoView.seekTo(mTrimEndTime);
+ mVideoPosition = mTrimEndTime;
+ }
+ mController.showEnded();
+ mVideoView.pause();
+ }
+
+ int duration = mVideoView.getDuration();
+ if (duration > 0 && mTrimEndTime == 0) {
+ mTrimEndTime = duration;
+ }
+ mController.setTimes(mVideoPosition, duration, mTrimStartTime, mTrimEndTime);
+ return mVideoPosition;
+ }
+
+ private void playVideo() {
+ mVideoView.start();
+ mController.showPlaying();
+ setProgress();
+ }
+
+ private void pauseVideo() {
+ mVideoView.pause();
+ mController.showPaused();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.trim, menu);
+ return true;
+ };
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ int id = item.getItemId();
+ if (id == android.R.id.home) {
+ finish();
+ return true;
+ } else if (id == R.id.action_trim_video) {
+ // TODO: Add the new MediaMuxer API to support the trimming.
+ Toast.makeText(getApplicationContext(),
+ "Trimming will be implemented soon!", Toast.LENGTH_SHORT).show();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void onPlayPause() {
+ if (mVideoView.isPlaying()) {
+ pauseVideo();
+ } else {
+ playVideo();
+ }
+ }
+
+ @Override
+ public void onSeekStart() {
+ pauseVideo();
+ }
+
+ @Override
+ public void onSeekMove(int time) {
+ mVideoView.seekTo(time);
+ }
+
+ @Override
+ public void onSeekEnd(int time, int start, int end) {
+ mVideoView.seekTo(time);
+ mTrimStartTime = start;
+ mTrimEndTime = end;
+ setProgress();
+ }
+
+ @Override
+ public void onShown() {
+ }
+
+
+ @Override
+ public void onHidden() {
+ }
+
+ @Override
+ public void onReplay() {
+ mVideoView.seekTo(mTrimStartTime);
+ playVideo();
+ }
+
+ @Override
+ public void onCompletion(MediaPlayer mp) {
+ mController.showEnded();
+ }
+
+ @Override
+ public boolean onError(MediaPlayer mp, int what, int extra) {
+ return false;
+ }
+}
diff --git a/src/com/android/gallery3d/ui/MenuExecutor.java b/src/com/android/gallery3d/ui/MenuExecutor.java
index 5e76fe6ad..38d2cc4c8 100644
--- a/src/com/android/gallery3d/ui/MenuExecutor.java
+++ b/src/com/android/gallery3d/ui/MenuExecutor.java
@@ -32,6 +32,7 @@ import com.actionbarsherlock.view.MenuItem;
import com.android.gallery3d.R;
import com.android.gallery3d.app.AbstractGalleryActivity;
import com.android.gallery3d.app.CropImage;
+import com.android.gallery3d.common.ApiHelper;
import com.android.gallery3d.common.Utils;
import com.android.gallery3d.data.DataManager;
import com.android.gallery3d.data.MediaItem;
@@ -159,7 +160,8 @@ public class MenuExecutor {
boolean supportDelete = (supported & MediaObject.SUPPORT_DELETE) != 0;
boolean supportRotate = (supported & MediaObject.SUPPORT_ROTATE) != 0;
boolean supportCrop = (supported & MediaObject.SUPPORT_CROP) != 0;
- boolean supportTrim = (supported & MediaObject.SUPPORT_TRIM) != 0;
+ boolean supportTrim = (supported & MediaObject.SUPPORT_TRIM) != 0
+ && ApiHelper.HAS_MEDIA_MUXER;
boolean supportShare = (supported & MediaObject.SUPPORT_SHARE) != 0;
boolean supportSetAs = (supported & MediaObject.SUPPORT_SETAS) != 0;
boolean supportShowOnMap = (supported & MediaObject.SUPPORT_SHOW_ON_MAP) != 0;