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 java.util.ArrayList;
     20 import java.util.List;
     21 
     22 import com.android.videoeditor.R;
     23 
     24 import android.app.Activity;
     25 import android.content.Context;
     26 import android.content.res.Resources;
     27 import android.graphics.Canvas;
     28 import android.graphics.drawable.Drawable;
     29 import android.os.Handler;
     30 import android.os.SystemClock;
     31 import android.util.AttributeSet;
     32 import android.view.Display;
     33 import android.view.MotionEvent;
     34 import android.view.ScaleGestureDetector;
     35 import android.widget.HorizontalScrollView;
     36 
     37 /**
     38  * The timeline scroll view
     39  */
     40 public class TimelineHorizontalScrollView extends HorizontalScrollView {
     41     public final static int PLAYHEAD_NORMAL = 1;
     42     public final static int PLAYHEAD_MOVE_OK = 2;
     43     public final static int PLAYHEAD_MOVE_NOT_OK = 3;
     44 
     45     // Instance variables
     46     private final List<ScrollViewListener> mScrollListenerList;
     47     private final Handler mHandler;
     48     private final int mPlayheadMarginTop;
     49     private final int mPlayheadMarginTopOk;
     50     private final int mPlayheadMarginTopNotOk;
     51     private final int mPlayheadMarginBottom;
     52     private final Drawable mNormalPlayheadDrawable;
     53     private final Drawable mMoveOkPlayheadDrawable;
     54     private final Drawable mMoveNotOkPlayheadDrawable;
     55     private final int mHalfParentWidth;
     56     private ScaleGestureDetector mScaleDetector;
     57     private int mLastScrollX;
     58     private boolean mIsScrolling;
     59     private boolean mAppScroll;
     60     private boolean mEnableUserScrolling;
     61 
     62     // The runnable which executes when the scrolling ends
     63     private Runnable mScrollEndedRunnable = new Runnable() {
     64         @Override
     65         public void run() {
     66             mIsScrolling = false;
     67 
     68             for (ScrollViewListener listener : mScrollListenerList) {
     69                 listener.onScrollEnd(TimelineHorizontalScrollView.this, getScrollX(),
     70                         getScrollY(), mAppScroll);
     71             }
     72 
     73             mAppScroll = false;
     74         }
     75     };
     76 
     77     public TimelineHorizontalScrollView(Context context, AttributeSet attrs, int defStyle) {
     78         super(context, attrs, defStyle);
     79 
     80         mEnableUserScrolling = true;
     81         mScrollListenerList = new ArrayList<ScrollViewListener>();
     82         mHandler = new Handler();
     83 
     84         // Compute half the width of the screen (and therefore the parent view)
     85         final Display display = ((Activity)context).getWindowManager().getDefaultDisplay();
     86         mHalfParentWidth = display.getWidth() / 2;
     87 
     88         // This value is shared by all children. It represents the width of
     89         // the left empty view.
     90         setTag(R.id.left_view_width, mHalfParentWidth);
     91         setTag(R.id.playhead_offset, -1);
     92         setTag(R.id.playhead_type, PLAYHEAD_NORMAL);
     93 
     94         final Resources resources = context.getResources();
     95 
     96         // Get the playhead margins
     97         mPlayheadMarginTop = (int)resources.getDimension(R.dimen.playhead_margin_top);
     98         mPlayheadMarginBottom = (int)resources.getDimension(R.dimen.playhead_margin_bottom);
     99         mPlayheadMarginTopOk = (int)resources.getDimension(R.dimen.playhead_margin_top_ok);
    100         mPlayheadMarginTopNotOk = (int)resources.getDimension(R.dimen.playhead_margin_top_not_ok);
    101 
    102         // Prepare the playhead drawable
    103         mNormalPlayheadDrawable = resources.getDrawable(R.drawable.ic_playhead);
    104         mMoveOkPlayheadDrawable = resources.getDrawable(R.drawable.playhead_move_ok);
    105         mMoveNotOkPlayheadDrawable = resources.getDrawable(R.drawable.playhead_move_not_ok);
    106     }
    107 
    108     public TimelineHorizontalScrollView(Context context, AttributeSet attrs) {
    109         this(context, attrs, 0);
    110     }
    111 
    112     public TimelineHorizontalScrollView(Context context) {
    113         this(context, null, 0);
    114     }
    115 
    116     /**
    117      * Invoked to enable/disable user scrolling (as opposed to programmatic scrolling)
    118      * @param enable true to enable user scrolling
    119      */
    120     public void enableUserScrolling(boolean enable) {
    121         mEnableUserScrolling = enable;
    122     }
    123 
    124     @Override
    125     public boolean onInterceptTouchEvent(MotionEvent ev) {
    126         mScaleDetector.onTouchEvent(ev);
    127         return mScaleDetector.isInProgress() || super.onInterceptTouchEvent(ev);
    128     }
    129 
    130     @Override
    131     public boolean onTouchEvent(MotionEvent ev) {
    132         if (mEnableUserScrolling) {
    133             mScaleDetector.onTouchEvent(ev);
    134 
    135             if (!mScaleDetector.isInProgress()) {
    136                 return super.onTouchEvent(ev);
    137             } else {
    138                 return true;
    139             }
    140         } else {
    141             if (mScaleDetector.isInProgress()) {
    142                 final MotionEvent cancelEvent = MotionEvent.obtain(SystemClock.uptimeMillis(),
    143                         SystemClock.uptimeMillis(), MotionEvent.ACTION_CANCEL, 0, 0, 0);
    144                 mScaleDetector.onTouchEvent(cancelEvent);
    145                 cancelEvent.recycle();
    146             }
    147             return true;
    148         }
    149     }
    150 
    151     /**
    152      * @param listener The scale listener
    153      */
    154     public void setScaleListener(ScaleGestureDetector.SimpleOnScaleGestureListener listener) {
    155         mScaleDetector = new ScaleGestureDetector(getContext(), listener);
    156     }
    157 
    158     /**
    159      * @param listener The listener
    160      */
    161     public void addScrollListener(ScrollViewListener listener) {
    162         mScrollListenerList.add(listener);
    163     }
    164 
    165     /**
    166      * @param listener The listener
    167      */
    168     public void removeScrollListener(ScrollViewListener listener) {
    169         mScrollListenerList.remove(listener);
    170     }
    171 
    172     /**
    173      * @return true if scrolling is in progress
    174      */
    175     public boolean isScrolling() {
    176         return mIsScrolling;
    177     }
    178 
    179     /**
    180      * The app wants to scroll (as opposed to the user)
    181      *
    182      * @param scrollX Horizontal scroll position
    183      * @param smooth true to scroll smoothly
    184      */
    185     public void appScrollTo(int scrollX, boolean smooth) {
    186         if (getScrollX() == scrollX) {
    187             return;
    188         }
    189 
    190         mAppScroll = true;
    191 
    192         if (smooth) {
    193             smoothScrollTo(scrollX, 0);
    194         } else {
    195             scrollTo(scrollX, 0);
    196         }
    197     }
    198 
    199     /**
    200      * The app wants to scroll (as opposed to the user)
    201      *
    202      * @param scrollX Horizontal scroll offset
    203      * @param smooth true to scroll smoothly
    204      */
    205     public void appScrollBy(int scrollX, boolean smooth) {
    206         mAppScroll = true;
    207 
    208         if (smooth) {
    209             smoothScrollBy(scrollX, 0);
    210         } else {
    211             scrollBy(scrollX, 0);
    212         }
    213     }
    214 
    215     @Override
    216     public void computeScroll() {
    217         super.computeScroll();
    218 
    219         final int scrollX = getScrollX();
    220         if (mLastScrollX != scrollX) {
    221             mLastScrollX = scrollX;
    222 
    223             // Cancel the previous event
    224             mHandler.removeCallbacks(mScrollEndedRunnable);
    225 
    226             // Post a new event
    227             mHandler.postDelayed(mScrollEndedRunnable, 300);
    228 
    229             final int scrollY = getScrollY();
    230             if (mIsScrolling) {
    231                 for (ScrollViewListener listener : mScrollListenerList) {
    232                     listener.onScrollProgress(this, scrollX, scrollY, mAppScroll);
    233                 }
    234             } else {
    235                 mIsScrolling = true;
    236 
    237                 for (ScrollViewListener listener : mScrollListenerList) {
    238                     listener.onScrollBegin(this, scrollX, scrollY, mAppScroll);
    239                 }
    240             }
    241         }
    242     }
    243 
    244     @Override
    245     protected void dispatchDraw(Canvas canvas) {
    246         super.dispatchDraw(canvas);
    247 
    248         final int playheadOffset = (Integer)getTag(R.id.playhead_offset);
    249         final int startX;
    250         if (playheadOffset < 0) {
    251             // Draw the playhead in the middle of the screen
    252             startX = mHalfParentWidth + getScrollX();
    253         } else {
    254             // Draw the playhead at the specified position (during trimming)
    255             startX = playheadOffset;
    256         }
    257 
    258         final int playheadType = (Integer)getTag(R.id.playhead_type);
    259         final int halfPlayheadWidth = mNormalPlayheadDrawable.getIntrinsicWidth() / 2;
    260         switch (playheadType) {
    261             case PLAYHEAD_NORMAL: {
    262                 // Draw the normal playhead
    263                 mNormalPlayheadDrawable.setBounds(
    264                         startX - halfPlayheadWidth,
    265                         mPlayheadMarginTop,
    266                         startX + halfPlayheadWidth,
    267                         getHeight() - mPlayheadMarginBottom);
    268                 mNormalPlayheadDrawable.draw(canvas);
    269                 break;
    270             }
    271 
    272             case PLAYHEAD_MOVE_OK: {
    273                 // Draw the move playhead
    274                 mMoveOkPlayheadDrawable.setBounds(
    275                         startX - halfPlayheadWidth,
    276                         mPlayheadMarginTopOk,
    277                         startX + halfPlayheadWidth,
    278                         mPlayheadMarginTopOk + mMoveOkPlayheadDrawable.getIntrinsicHeight());
    279                 mMoveOkPlayheadDrawable.draw(canvas);
    280                 break;
    281             }
    282 
    283             case PLAYHEAD_MOVE_NOT_OK: {
    284                 // Draw the move playhead
    285                 mMoveNotOkPlayheadDrawable.setBounds(
    286                         startX - halfPlayheadWidth,
    287                         mPlayheadMarginTopNotOk,
    288                         startX + halfPlayheadWidth,
    289                         mPlayheadMarginTopNotOk + mMoveNotOkPlayheadDrawable.getIntrinsicHeight());
    290                 mMoveNotOkPlayheadDrawable.draw(canvas);
    291                 break;
    292             }
    293 
    294             default: {
    295                 break;
    296             }
    297         }
    298     }
    299 }
    300