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