/* * Copyright (C) 2010 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.example.android.videoeditor.widgets; import com.example.android.videoeditor.service.ApiService; import com.example.android.videoeditor.service.MovieTransition; import com.example.android.videoeditor.R; import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; import android.view.Display; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; import android.view.WindowManager; import android.widget.ImageView; /** * Transition view. This class assumes transition is always put on a MediaLinearLayout and is * wrapped with a timeline scroll view. */ public class TransitionView extends ImageView { // Logging private static final String TAG = "TransitionView"; // Instance variables private final GestureDetector mSimpleGestureDetector; private final ScrollViewListener mScrollListener; private final Rect mGeneratingTransitionProgressDestRect; private final Paint mSeparatorPaint; private boolean mIsScrolling; // Convenient handle to the parent timeline scroll view. private TimelineHorizontalScrollView mScrollView; // Convenient handle to the parent timeline linear layout. private MediaLinearLayout mTimeline; private int mScrollX; private int mScreenWidth; private String mProjectPath; private Bitmap[] mBitmaps; private ItemSimpleGestureListener mGestureListener; private int mGeneratingTransitionProgress; private boolean mIsPlaying; public TransitionView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // Setup the gesture listener mSimpleGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() { @Override public boolean onSingleTapConfirmed(MotionEvent e) { if (mGestureListener != null) { return mGestureListener.onSingleTapConfirmed(TransitionView.this, -1, e); } else { return false; } } @Override public void onLongPress(MotionEvent e) { if (mGestureListener != null) { mGestureListener.onLongPress(TransitionView.this, e); } } }); mScrollListener = new ScrollViewListener() { @Override public void onScrollBegin(View view, int scrollX, int scrollY, boolean appScroll) { mIsScrolling = true; } @Override public void onScrollProgress(View view, int scrollX, int scrollY, boolean appScroll) { invalidate(); } @Override public void onScrollEnd(View view, int scrollX, int scrollY, boolean appScroll) { mIsScrolling = false; mScrollX = scrollX; if (requestThumbnails()) { invalidate(); } } }; final Resources resources = getResources(); // Prepare the bitmap rectangles final ProgressBar progressBar = ProgressBar.getProgressBar(context); final int layoutHeight = (int)(resources.getDimension(R.dimen.media_layout_height) - resources.getDimension(R.dimen.media_layout_padding) - (2 * resources.getDimension(R.dimen.timelime_transition_vertical_inset))); mGeneratingTransitionProgressDestRect = new Rect(getPaddingLeft(), layoutHeight - progressBar.getHeight() - getPaddingBottom(), 0, layoutHeight - getPaddingBottom()); // Initialize the progress value mGeneratingTransitionProgress = -1; // Get the screen width final Display display = ((WindowManager) getContext().getSystemService( Context.WINDOW_SERVICE)).getDefaultDisplay(); final DisplayMetrics metrics = new DisplayMetrics(); display.getMetrics(metrics); mScreenWidth = metrics.widthPixels; // Prepare the separator paint mSeparatorPaint = new Paint(); mSeparatorPaint.setColor(Color.BLACK); mSeparatorPaint.setStrokeWidth(2); } public TransitionView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public TransitionView(Context context) { this(context, null, 0); } @Override protected void onAttachedToWindow() { // Add the horizontal scroll view listener mScrollView = (TimelineHorizontalScrollView) getRootView().findViewById( R.id.timeline_scroller); mScrollView.addScrollListener(mScrollListener); mScrollX = mScrollView.getScrollX(); mTimeline = (MediaLinearLayout) getRootView().findViewById(R.id.timeline_media); } @Override protected void onDetachedFromWindow() { // Remove the horizontal scroll listener mScrollView.removeScrollListener(mScrollListener); // Release the current set of bitmaps if (mBitmaps != null) { for (int i = 0; i < mBitmaps.length; i++) { if (mBitmaps[i] != null) { mBitmaps[i].recycle(); } } mBitmaps = null; } } /** * @param projectPath The project path */ public void setProjectPath(String projectPath) { mProjectPath = projectPath; } /** * @param listener The gesture listener */ public void setGestureListener(ItemSimpleGestureListener listener) { mGestureListener = listener; } /** * Resets the transition generation progress. */ public void resetGeneratingTransitionProgress() { setGeneratingTransitionProgress(-1); } /** * Sets the transition generation progress. */ public void setGeneratingTransitionProgress(int progress) { if (progress == 100) { // Request the preview bitmaps requestThumbnails(); mGeneratingTransitionProgress = -1; } else { mGeneratingTransitionProgress = progress; } invalidate(); } /** * @return true if generation is in progress */ public boolean isGeneratingTransition() { return (mGeneratingTransitionProgress >= 0); } /** * A view enters or exits the playback mode * * @param playback true if playback is in progress */ public void setPlaybackMode(boolean playback) { mIsPlaying = playback; } /** * @param bitmaps The bitmaps array * * @return true if the bitmaps were used */ public boolean setBitmaps(Bitmap[] bitmaps) { if (mGeneratingTransitionProgress >= 0) { return false; } // Release the current set of bitmaps if (mBitmaps != null) { for (int i = 0; i < mBitmaps.length; i++) { if (mBitmaps[i] != null) { mBitmaps[i].recycle(); } } } mBitmaps = bitmaps; invalidate(); return true; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // If the view is too small, don't draw anything. if (getWidth() <= getPaddingLeft() + getPaddingRight()) { return; } if (mGeneratingTransitionProgress >= 0) { ProgressBar.getProgressBar(getContext()).draw(canvas, mGeneratingTransitionProgress, mGeneratingTransitionProgressDestRect, getPaddingLeft(), getWidth() - getPaddingRight()); } else if (mBitmaps != null) { final int halfWidth = getWidth() / 2; // Draw the bitmaps // Draw the left side of the transition canvas.save(); canvas.clipRect(getPaddingLeft(), getPaddingTop(), halfWidth, getHeight() - getPaddingBottom()); if (mBitmaps[0] != null) { canvas.drawBitmap(mBitmaps[0], getPaddingLeft(), getPaddingTop(), null); } else { canvas.drawColor(Color.BLACK); } canvas.restore(); // Draw the right side of the transition canvas.save(); canvas.clipRect(halfWidth, getPaddingTop(), getWidth() - getPaddingRight(), getHeight() - getPaddingBottom()); if (mBitmaps[1] != null) { canvas.drawBitmap(mBitmaps[1], getWidth() - getPaddingRight() - mBitmaps[1].getWidth(), getPaddingTop(), null); } else { canvas.drawColor(Color.BLACK); } canvas.restore(); canvas.drawLine(halfWidth, getPaddingTop(), halfWidth, getHeight() - getPaddingBottom(), mSeparatorPaint); // Dim myself if some view on the timeline is selected but not me // by drawing a transparent black overlay. if (!isSelected() && mTimeline.hasItemSelected()) { final Paint paint = new Paint(); paint.setColor(Color.BLACK); paint.setAlpha(192); canvas.drawPaint(paint); } } else if (mIsPlaying) { // Playing } else if (mIsScrolling) { // Scrolling } else { // Not scrolling and not playing requestThumbnails(); } } @Override public boolean onTouchEvent(MotionEvent ev) { // Let the gesture detector inspect all events. mSimpleGestureDetector.onTouchEvent(ev); return super.onTouchEvent(ev); } /** * Request thumbnails if necessary * * @return true if the bitmaps already exist */ private boolean requestThumbnails() { // Check if we already have the bitmaps if (mBitmaps != null) { return true; } // Do not request thumbnails during playback if (mIsScrolling) { return false; } final MovieTransition transition = (MovieTransition)getTag(); // Check if we already requested the thumbnails if (ApiService.isTransitionThumbnailsPending(mProjectPath, transition.getId())) { return false; } final int start = getLeft() + getPaddingLeft() - mScrollX; final int end = getRight() - getPaddingRight() - mScrollX; if (start >= mScreenWidth || end < 0 || start == end) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "Transition view is off screen: " + transition.getId() + ", from: " + start + " to " + end); } // Release the current set of bitmaps if (mBitmaps != null) { for (int i = 0; i < mBitmaps.length; i++) { if (mBitmaps[i] != null) { mBitmaps[i].recycle(); } } mBitmaps = null; } return false; } // Compute the thumbnail width final int thumbnailHeight = getHeight() - getPaddingTop() - getPaddingBottom(); // Request the thumbnails ApiService.getTransitionThumbnails(getContext(), mProjectPath, transition.getId(), thumbnailHeight); return false; } }