Home | History | Annotate | Download | only in widgets
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.videoeditor.widgets;
     18 
     19 import com.android.videoeditor.service.ApiService;
     20 import com.android.videoeditor.service.MovieTransition;
     21 import com.android.videoeditor.R;
     22 
     23 import android.content.Context;
     24 import android.content.res.Resources;
     25 import android.graphics.Bitmap;
     26 import android.graphics.Canvas;
     27 import android.graphics.Color;
     28 import android.graphics.Paint;
     29 import android.graphics.Rect;
     30 import android.util.AttributeSet;
     31 import android.util.DisplayMetrics;
     32 import android.util.Log;
     33 import android.view.Display;
     34 import android.view.GestureDetector;
     35 import android.view.MotionEvent;
     36 import android.view.View;
     37 import android.view.WindowManager;
     38 import android.widget.ImageView;
     39 
     40 /**
     41  * Transition view. This class assumes transition is always put on a MediaLinearLayout and is
     42  * wrapped with a timeline scroll view.
     43  */
     44 public class TransitionView extends ImageView {
     45     // Logging
     46     private static final String TAG = "TransitionView";
     47 
     48     // Instance variables
     49     private final GestureDetector mSimpleGestureDetector;
     50     private final ScrollViewListener mScrollListener;
     51     private final Rect mGeneratingTransitionProgressDestRect;
     52     private final Paint mSeparatorPaint;
     53     private boolean mIsScrolling;
     54     // Convenient handle to the parent timeline scroll view.
     55     private TimelineHorizontalScrollView mScrollView;
     56     // Convenient handle to the parent timeline linear layout.
     57     private MediaLinearLayout mTimeline;
     58     private int mScrollX;
     59     private int mScreenWidth;
     60     private String mProjectPath;
     61     private Bitmap[] mBitmaps;
     62     private ItemSimpleGestureListener mGestureListener;
     63     private int mGeneratingTransitionProgress;
     64     private boolean mIsPlaying;
     65 
     66     public TransitionView(Context context, AttributeSet attrs, int defStyle) {
     67         super(context, attrs, defStyle);
     68 
     69         // Setup the gesture listener
     70         mSimpleGestureDetector = new GestureDetector(context,
     71                 new GestureDetector.SimpleOnGestureListener() {
     72                     @Override
     73                     public boolean onSingleTapConfirmed(MotionEvent e) {
     74                         if (mGestureListener != null) {
     75                             return mGestureListener.onSingleTapConfirmed(TransitionView.this, -1,
     76                                     e);
     77                         } else {
     78                             return false;
     79                         }
     80                     }
     81 
     82                     @Override
     83                     public void onLongPress(MotionEvent e) {
     84                         if (mGestureListener != null) {
     85                             mGestureListener.onLongPress(TransitionView.this, e);
     86                         }
     87                     }
     88                 });
     89 
     90         mScrollListener = new ScrollViewListener() {
     91             @Override
     92             public void onScrollBegin(View view, int scrollX, int scrollY, boolean appScroll) {
     93                 mIsScrolling = true;
     94             }
     95 
     96             @Override
     97             public void onScrollProgress(View view, int scrollX, int scrollY, boolean appScroll) {
     98                 invalidate();
     99             }
    100 
    101             @Override
    102             public void onScrollEnd(View view, int scrollX, int scrollY, boolean appScroll) {
    103                 mIsScrolling = false;
    104                 mScrollX = scrollX;
    105 
    106                 if (requestThumbnails()) {
    107                     invalidate();
    108                 }
    109             }
    110         };
    111 
    112         final Resources resources = getResources();
    113         // Prepare the bitmap rectangles
    114         final ProgressBar progressBar = ProgressBar.getProgressBar(context);
    115         final int layoutHeight = (int)(resources.getDimension(R.dimen.media_layout_height) -
    116                 resources.getDimension(R.dimen.media_layout_padding) -
    117                 (2 * resources.getDimension(R.dimen.timelime_transition_vertical_inset)));
    118         mGeneratingTransitionProgressDestRect = new Rect(getPaddingLeft(),
    119                 layoutHeight - progressBar.getHeight() - getPaddingBottom(), 0,
    120                 layoutHeight - getPaddingBottom());
    121 
    122         // Initialize the progress value
    123         mGeneratingTransitionProgress = -1;
    124 
    125         // Get the screen width
    126         final Display display = ((WindowManager) getContext().getSystemService(
    127                 Context.WINDOW_SERVICE)).getDefaultDisplay();
    128         final DisplayMetrics metrics = new DisplayMetrics();
    129         display.getMetrics(metrics);
    130         mScreenWidth = metrics.widthPixels;
    131 
    132         // Prepare the separator paint
    133         mSeparatorPaint = new Paint();
    134         mSeparatorPaint.setColor(Color.BLACK);
    135         mSeparatorPaint.setStrokeWidth(2);
    136     }
    137 
    138     public TransitionView(Context context, AttributeSet attrs) {
    139         this(context, attrs, 0);
    140     }
    141 
    142     public TransitionView(Context context) {
    143         this(context, null, 0);
    144     }
    145 
    146     @Override
    147     protected void onAttachedToWindow() {
    148         // Add the horizontal scroll view listener
    149         mScrollView = (TimelineHorizontalScrollView) getRootView().findViewById(
    150                 R.id.timeline_scroller);
    151         mScrollView.addScrollListener(mScrollListener);
    152         mScrollX = mScrollView.getScrollX();
    153 
    154         mTimeline = (MediaLinearLayout) getRootView().findViewById(R.id.timeline_media);
    155     }
    156 
    157     @Override
    158     protected void onDetachedFromWindow() {
    159         // Remove the horizontal scroll listener
    160         mScrollView.removeScrollListener(mScrollListener);
    161 
    162         // Release the current set of bitmaps
    163         if (mBitmaps != null) {
    164             for (int i = 0; i < mBitmaps.length; i++) {
    165                 if (mBitmaps[i] != null) {
    166                     mBitmaps[i].recycle();
    167                 }
    168             }
    169 
    170             mBitmaps = null;
    171         }
    172     }
    173 
    174     /**
    175      * @param projectPath The project path
    176      */
    177     public void setProjectPath(String projectPath) {
    178         mProjectPath = projectPath;
    179     }
    180 
    181     /**
    182      * @param listener The gesture listener
    183      */
    184     public void setGestureListener(ItemSimpleGestureListener listener) {
    185         mGestureListener = listener;
    186     }
    187 
    188     /**
    189      * Resets the transition generation progress.
    190      */
    191     public void resetGeneratingTransitionProgress() {
    192         setGeneratingTransitionProgress(-1);
    193     }
    194 
    195     /**
    196      * Sets the transition generation progress.
    197      */
    198     public void setGeneratingTransitionProgress(int progress) {
    199         if (progress == 100) {
    200             // Request the preview bitmaps
    201             requestThumbnails();
    202             mGeneratingTransitionProgress = -1;
    203         } else {
    204             mGeneratingTransitionProgress = progress;
    205         }
    206 
    207         invalidate();
    208     }
    209 
    210     /**
    211      * @return true if generation is in progress
    212      */
    213     public boolean isGeneratingTransition() {
    214         return (mGeneratingTransitionProgress >= 0);
    215     }
    216 
    217     /**
    218      * A view enters or exits the playback mode
    219      *
    220      * @param playback true if playback is in progress
    221      */
    222     public void setPlaybackMode(boolean playback) {
    223         mIsPlaying = playback;
    224     }
    225 
    226     /**
    227      * @param bitmaps The bitmaps array
    228      *
    229      * @return true if the bitmaps were used
    230      */
    231     public boolean setBitmaps(Bitmap[] bitmaps) {
    232         if (mGeneratingTransitionProgress >= 0) {
    233             return false;
    234         }
    235 
    236         // Release the current set of bitmaps
    237         if (mBitmaps != null) {
    238             for (int i = 0; i < mBitmaps.length; i++) {
    239                 if (mBitmaps[i] != null) {
    240                     mBitmaps[i].recycle();
    241                 }
    242             }
    243         }
    244 
    245         mBitmaps = bitmaps;
    246         invalidate();
    247 
    248         return true;
    249     }
    250 
    251     @Override
    252     protected void onDraw(Canvas canvas) {
    253         super.onDraw(canvas);
    254 
    255         // If the view is too small, don't draw anything.
    256         if (getWidth() <= getPaddingLeft() + getPaddingRight()) {
    257             return;
    258         }
    259 
    260         if (mGeneratingTransitionProgress >= 0) {
    261             ProgressBar.getProgressBar(getContext()).draw(canvas, mGeneratingTransitionProgress,
    262                     mGeneratingTransitionProgressDestRect, getPaddingLeft(),
    263                     getWidth() - getPaddingRight());
    264         } else if (mBitmaps != null) {
    265             final int halfWidth = getWidth() / 2;
    266             // Draw the bitmaps
    267             // Draw the left side of the transition
    268             canvas.save();
    269             canvas.clipRect(getPaddingLeft(), getPaddingTop(), halfWidth,
    270                     getHeight() - getPaddingBottom());
    271 
    272             if (mBitmaps[0] != null) {
    273                 canvas.drawBitmap(mBitmaps[0], getPaddingLeft(), getPaddingTop(), null);
    274             } else {
    275                 canvas.drawColor(Color.BLACK);
    276             }
    277             canvas.restore();
    278 
    279             // Draw the right side of the transition
    280             canvas.save();
    281             canvas.clipRect(halfWidth, getPaddingTop(),
    282                     getWidth() - getPaddingRight(), getHeight() - getPaddingBottom());
    283             if (mBitmaps[1] != null) {
    284                 canvas.drawBitmap(mBitmaps[1],
    285                         getWidth() - getPaddingRight() - mBitmaps[1].getWidth(), getPaddingTop(),
    286                         null);
    287             } else {
    288                 canvas.drawColor(Color.BLACK);
    289             }
    290             canvas.restore();
    291 
    292             canvas.drawLine(halfWidth, getPaddingTop(), halfWidth,
    293                     getHeight() - getPaddingBottom(), mSeparatorPaint);
    294 
    295             // Dim myself if some view on the timeline is selected but not me
    296             // by drawing a transparent black overlay.
    297             if (!isSelected() && mTimeline.hasItemSelected()) {
    298                 final Paint paint = new Paint();
    299                 paint.setColor(Color.BLACK);
    300                 paint.setAlpha(192);
    301                 canvas.drawPaint(paint);
    302             }
    303         } else if (mIsPlaying) { // Playing
    304         } else if (mIsScrolling) { // Scrolling
    305         } else { // Not scrolling and not playing
    306             requestThumbnails();
    307         }
    308     }
    309 
    310     @Override
    311     public boolean onTouchEvent(MotionEvent ev) {
    312         // Let the gesture detector inspect all events.
    313         mSimpleGestureDetector.onTouchEvent(ev);
    314         return super.onTouchEvent(ev);
    315     }
    316 
    317     /**
    318      * Request thumbnails if necessary
    319      *
    320      * @return true if the bitmaps already exist
    321      */
    322     private boolean requestThumbnails() {
    323         // Check if we already have the bitmaps
    324         if (mBitmaps != null) {
    325             return true;
    326         }
    327 
    328         // Do not request thumbnails during playback
    329         if (mIsScrolling) {
    330             return false;
    331         }
    332 
    333         final MovieTransition transition = (MovieTransition)getTag();
    334         // Check if we already requested the thumbnails
    335         if (ApiService.isTransitionThumbnailsPending(mProjectPath, transition.getId())) {
    336             return false;
    337         }
    338 
    339         final int start = getLeft() + getPaddingLeft() - mScrollX;
    340         final int end = getRight() - getPaddingRight() - mScrollX;
    341 
    342         if (start >= mScreenWidth || end < 0 || start == end) {
    343             if (Log.isLoggable(TAG, Log.VERBOSE)) {
    344                 Log.v(TAG, "Transition view is off screen: " + transition.getId() +
    345                         ", from: " + start + " to " + end);
    346             }
    347 
    348             // Release the current set of bitmaps
    349             if (mBitmaps != null) {
    350                 for (int i = 0; i < mBitmaps.length; i++) {
    351                     if (mBitmaps[i] != null) {
    352                         mBitmaps[i].recycle();
    353                     }
    354                 }
    355 
    356                 mBitmaps = null;
    357             }
    358 
    359             return false;
    360         }
    361 
    362         // Compute the thumbnail width
    363         final int thumbnailHeight = getHeight() - getPaddingTop() - getPaddingBottom();
    364         // Request the thumbnails
    365         ApiService.getTransitionThumbnails(getContext(), mProjectPath, transition.getId(),
    366                 thumbnailHeight);
    367 
    368         return false;
    369     }
    370 }
    371