summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--gallerycommon/src/com/android/gallery3d/common/ApiHelper.java3
-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
11 files changed, 1210 insertions, 479 deletions
diff --git a/gallerycommon/src/com/android/gallery3d/common/ApiHelper.java b/gallerycommon/src/com/android/gallery3d/common/ApiHelper.java
index 68aa50dda..20d7e1dc6 100644
--- a/gallerycommon/src/com/android/gallery3d/common/ApiHelper.java
+++ b/gallerycommon/src/com/android/gallery3d/common/ApiHelper.java
@@ -160,6 +160,9 @@ public class ApiHelper {
public static final boolean CAN_USE_FLAG_SECURE =
Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB;
+ public static final boolean HAS_MEDIA_MUXER =
+ Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1;
+
public static int getIntFieldIfExists(Class<?> klass, String fieldName,
Class<?> obj, int defaultVal) {
try {
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;