• 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 com.android.videoeditor.R;
20 
21 import android.content.Context;
22 import android.graphics.Canvas;
23 import android.graphics.drawable.Drawable;
24 import android.util.AttributeSet;
25 import android.view.MotionEvent;
26 import android.view.View;
27 
28 /**
29  * The zoom control
30  */
31 public class ZoomControl extends View {
32 
33     private static final double MAX_ANGLE = Math.PI / 3;
34     private static final double THUMB_RADIUS_CONTAINER_SIZE_RATIO = 0.432;
35     private static final double THUMB_INTERNAL_RADIUS_CONTAINER_SIZE_RATIO = 0.24;
36 
37     // Instance variables
38     private final Drawable mThumb;
39     private double mRadius;
40     private double mInternalRadius;
41     private int mMaxProgress, mProgress;
42     private OnZoomChangeListener mListener;
43     private int mThumbX, mThumbY;
44     private double mInterval;
45 
46     /**
47      * The zoom change listener
48      */
49     public interface OnZoomChangeListener {
50         /**
51          * The progress value has changed
52          *
53          * @param progress The progress value
54          * @param fromUser true if the user is changing the zoom
55          */
onProgressChanged(int progress, boolean fromUser)56         public void onProgressChanged(int progress, boolean fromUser);
57     }
58 
ZoomControl(Context context, AttributeSet attrs, int defStyle)59     public ZoomControl(Context context, AttributeSet attrs, int defStyle) {
60         super(context, attrs, defStyle);
61 
62         // Set the default maximum progress
63         mMaxProgress = 100;
64         computeInterval();
65 
66         // Load the thumb selector
67         mThumb = context.getResources().getDrawable(R.drawable.zoom_thumb_selector);
68     }
69 
70     @Override
onLayout(boolean changed, int left, int top, int right, int bottom)71     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
72       super.onLayout(changed, left, top, right, bottom);
73       double width = right - left;
74       mRadius = width * THUMB_RADIUS_CONTAINER_SIZE_RATIO;
75       mInternalRadius = width * THUMB_INTERNAL_RADIUS_CONTAINER_SIZE_RATIO;
76     }
77 
ZoomControl(Context context, AttributeSet attrs)78     public ZoomControl(Context context, AttributeSet attrs) {
79         this(context, attrs, 0);
80     }
81 
ZoomControl(Context context)82     public ZoomControl(Context context) {
83         this(context, null, 0);
84     }
85 
86     @Override
refreshDrawableState()87     public void refreshDrawableState() {
88         mThumb.setState(isPressed() ? PRESSED_WINDOW_FOCUSED_STATE_SET : ENABLED_STATE_SET);
89         invalidate();
90     }
91 
92     /**
93      * @param max The maximum value
94      */
setMax(int max)95     public void setMax(int max) {
96         mMaxProgress = max;
97         computeInterval();
98     }
99 
100     /**
101      * @param progress The progress
102      */
setProgress(int progress)103     public void setProgress(int progress) {
104         mProgress = progress;
105 
106         progressToPosition();
107         invalidate();
108     }
109 
110     /**
111      * @param listener The listener
112      */
setOnZoomChangeListener(OnZoomChangeListener listener)113     public void setOnZoomChangeListener(OnZoomChangeListener listener) {
114         mListener = listener;
115     }
116 
117     @Override
onDraw(Canvas canvas)118     protected void onDraw(Canvas canvas) {
119         super.onDraw(canvas);
120 
121         if (mThumbX == 0 && mThumbY == 0) {
122             progressToPosition();
123         }
124 
125         final int halfWidth = mThumb.getIntrinsicWidth() / 2;
126         final int halfHeight = mThumb.getIntrinsicHeight() / 2;
127         mThumb.setBounds(mThumbX - halfWidth, mThumbY - halfHeight, mThumbX + halfWidth,
128                 mThumbY + halfHeight);
129         mThumb.setAlpha(isEnabled() ? 255 : 100);
130         mThumb.draw(canvas);
131     }
132 
133     @Override
onTouchEvent(MotionEvent ev)134     public boolean onTouchEvent(MotionEvent ev) {
135         super.onTouchEvent(ev);
136         switch (ev.getAction()) {
137             case MotionEvent.ACTION_DOWN: {
138                 if (isEnabled()) {
139                     getParent().requestDisallowInterceptTouchEvent(true);
140                 }
141                 break;
142             }
143 
144             case MotionEvent.ACTION_MOVE: {
145                 if (isEnabled()) {
146                     final float x = ev.getX() - (getWidth() / 2);
147                     final float y = -(ev.getY() - (getHeight() / 2));
148                     final double alpha = Math.atan((double)y / (double)x);
149 
150                     if (!checkHit(x, y, alpha)) {
151                         return true;
152                     }
153 
154                     final int progress;
155                     if (x >= 0 && y >= 0) {
156                         mThumbX = (int)((mRadius * Math.cos(alpha)) + (getWidth() / 2));
157                         mThumbY = (int)((getHeight() / 2) - (mRadius * Math.sin(alpha)));
158                         progress = (int)((mMaxProgress / 2) - (alpha / mInterval));
159                     } else if (x >= 0 && y <= 0) {
160                         mThumbX = (int)((mRadius * Math.cos(alpha)) + (getWidth() / 2));
161                         mThumbY = (int)((getHeight() / 2) - (mRadius * Math.sin(alpha)));
162                         progress = (int)((mMaxProgress / 2) - (alpha / mInterval));
163                     } else if (x <= 0 && y >= 0) {
164                         mThumbX = (int)((getWidth() / 2) - (mRadius * Math.cos(alpha)));
165                         mThumbY = (int)((getHeight() / 2) + (mRadius * Math.sin(alpha)));
166                         progress = -(int)(((alpha + MAX_ANGLE) / mInterval));
167                     } else {
168                         mThumbX = (int)((getWidth() / 2) - (mRadius * Math.cos(alpha)));
169                         mThumbY = (int)((getHeight() / 2) + (mRadius * Math.sin(alpha)));
170                         progress = (int)(mMaxProgress - ((alpha - MAX_ANGLE) / mInterval));
171                     }
172 
173                     invalidate();
174 
175                     if (mListener != null) {
176                         if (progress != mProgress) {
177                             mProgress = progress;
178                             mListener.onProgressChanged(mProgress, true);
179                         }
180                     }
181                 }
182                 break;
183             }
184 
185             case MotionEvent.ACTION_CANCEL:
186             case MotionEvent.ACTION_UP: {
187                 break;
188             }
189 
190             default: {
191                 break;
192             }
193         }
194 
195         return true;
196     }
197 
198     /**
199      * Check if the user is touching the correct area
200      *
201      * @param x The horizontal coordinate
202      * @param y The vertical coordinate
203      * @param alpha The angle
204      * @return true if there is a hit in the allowed area
205      */
checkHit(float x, float y, double alpha)206     private boolean checkHit(float x, float y, double alpha) {
207         final double radius = Math.sqrt((x * x) + (y * y));
208         if (radius < mInternalRadius) {
209             return false;
210         }
211 
212         if (x >= 0) {
213             return true;
214         } else if (y >= 0) {
215             if ((alpha >= -(Math.PI / 2)) && (alpha <= -MAX_ANGLE)) {
216                 return true;
217             }
218         } else {
219             if ((alpha >= MAX_ANGLE) && (alpha <= (Math.PI / 2))) {
220                 return true;
221             }
222         }
223 
224         return false;
225     }
226 
227     /**
228      * Compute the position of the thumb based on the progress values
229      */
progressToPosition()230     private void progressToPosition() {
231         if (getWidth() == 0) { // Layout is not yet complete
232             return;
233         }
234 
235         final double beta;
236         if (mProgress <= mMaxProgress / 2) {
237             beta = ((mMaxProgress / 2) - mProgress) * mInterval;
238         } else {
239             beta = ((mMaxProgress - mProgress) * mInterval) + Math.PI + MAX_ANGLE;
240         }
241 
242         final double alpha;
243         if (beta >= 0 && beta <= Math.PI / 2) {
244             alpha = beta;
245             mThumbX = (int)((mRadius * Math.cos(alpha)) + (getWidth() / 2));
246             mThumbY = (int)((getHeight() / 2) - (mRadius * Math.sin(alpha)));
247         } else if (beta > Math.PI / 2 && beta < (Math.PI / 2) + MAX_ANGLE) {
248             alpha = beta - Math.PI;
249             mThumbX = (int)((getWidth() / 2) - (mRadius * Math.cos(alpha)));
250             mThumbY = (int)((getHeight() / 2) + (mRadius * Math.sin(alpha)));
251         } else if (beta <= 2 * Math.PI && beta > (3 * Math.PI) / 2) {
252             alpha = beta - (2 * Math.PI);
253             mThumbX = (int)((mRadius * Math.cos(alpha)) + (getWidth() / 2));
254             mThumbY = (int)((getHeight() / 2) - (mRadius * Math.sin(alpha)));
255         } else {
256             alpha = beta - Math.PI;
257             mThumbX = (int)((getWidth() / 2) - (mRadius * Math.cos(alpha)));
258             mThumbY = (int)((getHeight() / 2) + (mRadius * Math.sin(alpha)));
259         }
260     }
261 
262     /**
263      * Compute the radians interval between progress values
264      */
computeInterval()265     private void computeInterval() {
266         mInterval = (Math.PI - MAX_ANGLE) / (mMaxProgress / 2);
267     }
268 }
269