• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
TimelineHorizontalScrollView(Context context, AttributeSet attrs, int defStyle)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 
TimelineHorizontalScrollView(Context context, AttributeSet attrs)107     public TimelineHorizontalScrollView(Context context, AttributeSet attrs) {
108         this(context, attrs, 0);
109     }
110 
TimelineHorizontalScrollView(Context context)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      */
enableUserScrolling(boolean enable)119     public void enableUserScrolling(boolean enable) {
120         mEnableUserScrolling = enable;
121     }
122 
123     @Override
onInterceptTouchEvent(MotionEvent ev)124     public boolean onInterceptTouchEvent(MotionEvent ev) {
125         mScaleDetector.onTouchEvent(ev);
126         return super.onInterceptTouchEvent(ev);
127     }
128 
129     @Override
onTouchEvent(MotionEvent ev)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      */
setScaleListener(ScaleGestureDetector.SimpleOnScaleGestureListener listener)148     public void setScaleListener(ScaleGestureDetector.SimpleOnScaleGestureListener listener) {
149         mScaleDetector = new ScaleGestureDetector(getContext(), listener);
150     }
151 
152     /**
153      * @param listener The listener
154      */
addScrollListener(ScrollViewListener listener)155     public void addScrollListener(ScrollViewListener listener) {
156         mScrollListenerList.add(listener);
157     }
158 
159     /**
160      * @param listener The listener
161      */
removeScrollListener(ScrollViewListener listener)162     public void removeScrollListener(ScrollViewListener listener) {
163         mScrollListenerList.remove(listener);
164     }
165 
166     /**
167      * @return true if scrolling is in progress
168      */
isScrolling()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      */
appScrollTo(int scrollX, boolean smooth)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      */
appScrollBy(int scrollX, boolean smooth)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
computeScroll()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
dispatchDraw(Canvas canvas)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