1 /*
2  * Copyright (C) 2011 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.camera.ui;
18 
19 import com.android.camera.R;
20 
21 import android.content.Context;
22 import android.os.Handler;
23 import android.util.AttributeSet;
24 import android.view.View;
25 import android.widget.ImageView;
26 import android.widget.RelativeLayout;
27 
28 /**
29  * A view that contains camera zoom control which could adjust the zoom in/out
30  * if the camera supports zooming.
31  */
32 public abstract class ZoomControl extends RelativeLayout implements Rotatable {
33     // The states of zoom button.
34     public static final int ZOOM_IN = 0;
35     public static final int ZOOM_OUT = 1;
36     public static final int ZOOM_STOP = 2;
37 
38     private static final String TAG = "ZoomControl";
39     private static final int ZOOMING_INTERVAL = 1000; // milliseconds
40 
41     protected ImageView mZoomIn;
42     protected ImageView mZoomOut;
43     protected ImageView mZoomSlider;
44     protected int mOrientation;
45     private Handler mHandler;
46 
47     public interface OnZoomChangedListener {
onZoomValueChanged(int index)48         void onZoomValueChanged(int index);  // only for immediate zoom
onZoomStateChanged(int state)49         void onZoomStateChanged(int state);  // only for smooth zoom
50     }
51 
52     // The interface OnZoomIndexChangedListener is used to inform the
53     // ZoomIndexBar about the zoom index change. The index position is between
54     // 0 (the index is zero) and 1.0 (the index is mZoomMax).
55     public interface OnZoomIndexChangedListener {
onZoomIndexChanged(double indexPosition)56         void onZoomIndexChanged(double indexPosition);
57     }
58 
59     protected int mZoomMax, mZoomIndex;
60     private boolean mSmoothZoomSupported;
61     private OnZoomChangedListener mListener;
62     private OnZoomIndexChangedListener mIndexListener;
63 
64     protected OnIndicatorEventListener mOnIndicatorEventListener;
65     private int mState;
66     private int mStep;
67 
68     protected final Runnable mRunnable = new Runnable() {
69         public void run() {
70             performZoom(mState, false);
71         }
72     };
73 
ZoomControl(Context context, AttributeSet attrs)74     public ZoomControl(Context context, AttributeSet attrs) {
75         super(context, attrs);
76         mZoomIn = addImageView(context, R.drawable.ic_zoom_in);
77         mZoomSlider = addImageView(context, R.drawable.ic_zoom_slider);
78         mZoomOut = addImageView(context, R.drawable.ic_zoom_out);
79         mHandler = new Handler();
80     }
81 
startZoomControl()82     public void startZoomControl() {
83         mZoomSlider.setPressed(true);
84         setZoomIndex(mZoomIndex); // Update the zoom index bar.
85     }
86 
addImageView(Context context, int iconResourceId)87     protected ImageView addImageView(Context context, int iconResourceId) {
88         ImageView image = new RotateImageView(context);
89         image.setImageResource(iconResourceId);
90         if (iconResourceId == R.drawable.ic_zoom_slider) {
91             image.setContentDescription(getResources().getString(
92                     R.string.accessibility_zoom_control));
93         } else {
94             image.setContentDescription(getResources().getString(
95                     R.string.empty));
96         }
97         addView(image);
98         return image;
99     }
100 
closeZoomControl()101     public void closeZoomControl() {
102         mZoomSlider.setPressed(false);
103         stopZooming();
104         if (!mSmoothZoomSupported) mHandler.removeCallbacks(mRunnable);
105         if (mOnIndicatorEventListener != null) {
106             mOnIndicatorEventListener.onIndicatorEvent(
107                     OnIndicatorEventListener.EVENT_LEAVE_ZOOM_CONTROL);
108         }
109     }
110 
setZoomMax(int zoomMax)111     public void setZoomMax(int zoomMax) {
112         mZoomMax = zoomMax;
113 
114         // Layout should be requested as the maximum zoom level is the key to
115         // show the correct zoom slider position.
116         requestLayout();
117     }
118 
setOnZoomChangeListener(OnZoomChangedListener listener)119     public void setOnZoomChangeListener(OnZoomChangedListener listener) {
120         mListener = listener;
121     }
122 
setOnIndicatorEventListener(OnIndicatorEventListener listener)123     public void setOnIndicatorEventListener(OnIndicatorEventListener listener) {
124         mOnIndicatorEventListener = listener;
125     }
126 
setZoomIndex(int index)127     public void setZoomIndex(int index) {
128         if (index < 0 || index > mZoomMax) {
129             throw new IllegalArgumentException("Invalid zoom value:" + index);
130         }
131         mZoomIndex = index;
132         invalidate();
133     }
134 
setSmoothZoomSupported(boolean smoothZoomSupported)135     public void setSmoothZoomSupported(boolean smoothZoomSupported) {
136         mSmoothZoomSupported = smoothZoomSupported;
137     }
138 
zoomIn()139     private boolean zoomIn() {
140         return (mZoomIndex == mZoomMax) ? false : changeZoomIndex(mZoomIndex + mStep);
141     }
142 
zoomOut()143     private boolean zoomOut() {
144         return (mZoomIndex == 0) ? false : changeZoomIndex(mZoomIndex - mStep);
145     }
146 
setZoomStep(int step)147     protected void setZoomStep(int step) {
148         mStep = step;
149     }
150 
stopZooming()151     private void stopZooming() {
152         if (mSmoothZoomSupported) {
153             if (mListener != null) mListener.onZoomStateChanged(ZOOM_STOP);
154         }
155     }
156 
157     // Called from ZoomControlWheel to change the zoom level.
158     // TODO: merge the zoom control for both platforms.
performZoom(int state)159     protected void performZoom(int state) {
160         performZoom(state, true);
161     }
162 
performZoom(int state, boolean fromUser)163     private void performZoom(int state, boolean fromUser) {
164         if ((mState == state) && fromUser) return;
165         if (fromUser) mHandler.removeCallbacks(mRunnable);
166         mState = state;
167         switch (state) {
168             case ZOOM_IN:
169                 zoomIn();
170                 break;
171             case ZOOM_OUT:
172                 zoomOut();
173                 break;
174             case ZOOM_STOP:
175                 stopZooming();
176                 break;
177         }
178         if (!mSmoothZoomSupported) {
179             // Repeat the zoom action on tablet as the user is still holding
180             // the zoom slider.
181             mHandler.postDelayed(mRunnable, ZOOMING_INTERVAL / mZoomMax);
182         }
183     }
184 
185     // Called from ZoomControlBar to change the zoom level.
performZoom(double zoomPercentage)186     protected void performZoom(double zoomPercentage) {
187         int index = (int) (mZoomMax * zoomPercentage);
188         if (mZoomIndex == index) return;
189         changeZoomIndex(index);
190    }
191 
changeZoomIndex(int index)192     private boolean changeZoomIndex(int index) {
193         if (mListener != null) {
194             if (mSmoothZoomSupported) {
195                 int zoomType = (index < mZoomIndex) ? ZOOM_OUT : ZOOM_IN;
196                 if (((zoomType == ZOOM_IN) && (mZoomIndex != mZoomMax)) ||
197                         ((zoomType == ZOOM_OUT) && (mZoomIndex != 0))) {
198                     mListener.onZoomStateChanged(zoomType);
199                 }
200             } else {
201                 if (index > mZoomMax) index = mZoomMax;
202                 if (index < 0) index = 0;
203                 mListener.onZoomValueChanged(index);
204                 mZoomIndex = index;
205             }
206         }
207         return true;
208     }
209 
setOrientation(int orientation)210     public void setOrientation(int orientation) {
211         mOrientation = orientation;
212         int count = getChildCount();
213         for (int i = 0 ; i < count ; ++i) {
214             View view = getChildAt(i);
215             if (view instanceof RotateImageView) {
216                 ((RotateImageView) view).setOrientation(orientation);
217             }
218         }
219     }
220 
221     @Override
setActivated(boolean activated)222     public void setActivated(boolean activated) {
223         super.setActivated(activated);
224         mZoomIn.setActivated(activated);
225         mZoomOut.setActivated(activated);
226     }
227 }
228