• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 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 android.animation.Animator;
20 import android.animation.Animator.AnimatorListener;
21 import android.animation.AnimatorListenerAdapter;
22 import android.app.Activity;
23 import android.content.Context;
24 import android.content.res.Configuration;
25 import android.graphics.Canvas;
26 import android.graphics.drawable.Drawable;
27 import android.util.AttributeSet;
28 import android.view.LayoutInflater;
29 import android.view.MotionEvent;
30 import android.view.View;
31 import android.view.View.OnClickListener;
32 import android.view.View.OnTouchListener;
33 import android.view.ViewGroup;
34 import android.widget.FrameLayout.LayoutParams;
35 import android.widget.LinearLayout;
36 
37 import com.android.camera.Util;
38 import com.android.gallery3d.R;
39 import com.android.gallery3d.common.ApiHelper;
40 import com.android.gallery3d.util.LightCycleHelper;
41 import com.android.gallery3d.util.UsageStatistics;
42 
43 public class CameraSwitcher extends RotateImageView
44         implements OnClickListener, OnTouchListener {
45 
46     private static final String TAG = "CAM_Switcher";
47     private static final int SWITCHER_POPUP_ANIM_DURATION = 200;
48 
49     public static final int PHOTO_MODULE_INDEX = 0;
50     public static final int VIDEO_MODULE_INDEX = 1;
51     public static final int PANORAMA_MODULE_INDEX = 2;
52     public static final int LIGHTCYCLE_MODULE_INDEX = 3;
53     private static final int[] DRAW_IDS = {
54             R.drawable.ic_switch_camera,
55             R.drawable.ic_switch_video,
56             R.drawable.ic_switch_pan,
57             R.drawable.ic_switch_photosphere
58     };
59     public interface CameraSwitchListener {
onCameraSelected(int i)60         public void onCameraSelected(int i);
onShowSwitcherPopup()61         public void onShowSwitcherPopup();
62     }
63 
64     private CameraSwitchListener mListener;
65     private int mCurrentIndex;
66     private int[] mModuleIds;
67     private int[] mDrawIds;
68     private int mItemSize;
69     private View mPopup;
70     private View mParent;
71     private boolean mShowingPopup;
72     private boolean mNeedsAnimationSetup;
73     private Drawable mIndicator;
74 
75     private float mTranslationX = 0;
76     private float mTranslationY = 0;
77 
78     private AnimatorListener mHideAnimationListener;
79     private AnimatorListener mShowAnimationListener;
80 
CameraSwitcher(Context context)81     public CameraSwitcher(Context context) {
82         super(context);
83         init(context);
84     }
85 
CameraSwitcher(Context context, AttributeSet attrs)86     public CameraSwitcher(Context context, AttributeSet attrs) {
87         super(context, attrs);
88         init(context);
89     }
90 
init(Context context)91     private void init(Context context) {
92         mItemSize = context.getResources().getDimensionPixelSize(R.dimen.switcher_size);
93         setOnClickListener(this);
94         mIndicator = context.getResources().getDrawable(R.drawable.ic_switcher_menu_indicator);
95         initializeDrawables(context);
96     }
97 
initializeDrawables(Context context)98     public void initializeDrawables(Context context) {
99         int totaldrawid = (LightCycleHelper.hasLightCycleCapture(context)
100                 ? DRAW_IDS.length : DRAW_IDS.length - 1);
101         if (!ApiHelper.HAS_OLD_PANORAMA) totaldrawid--;
102 
103         int[] drawids = new int[totaldrawid];
104         int[] moduleids = new int[totaldrawid];
105         int ix = 0;
106         for (int i = 0; i < DRAW_IDS.length; i++) {
107             if (i == PANORAMA_MODULE_INDEX && !ApiHelper.HAS_OLD_PANORAMA) {
108             continue; // not enabled, so don't add to UI
109             }
110             if (i == LIGHTCYCLE_MODULE_INDEX && !LightCycleHelper.hasLightCycleCapture(context)) {
111             continue; // not enabled, so don't add to UI
112             }
113             moduleids[ix] = i;
114             drawids[ix++] = DRAW_IDS[i];
115         }
116         setIds(moduleids, drawids);
117     }
118 
setIds(int[] moduleids, int[] drawids)119     public void setIds(int[] moduleids, int[] drawids) {
120         mDrawIds = drawids;
121         mModuleIds = moduleids;
122     }
123 
setCurrentIndex(int i)124     public void setCurrentIndex(int i) {
125         mCurrentIndex = i;
126         setImageResource(mDrawIds[i]);
127     }
128 
setSwitchListener(CameraSwitchListener l)129     public void setSwitchListener(CameraSwitchListener l) {
130         mListener = l;
131     }
132 
133     @Override
onClick(View v)134     public void onClick(View v) {
135         showSwitcher();
136         mListener.onShowSwitcherPopup();
137     }
138 
onCameraSelected(int ix)139     private void onCameraSelected(int ix) {
140         hidePopup();
141         if ((ix != mCurrentIndex) && (mListener != null)) {
142             UsageStatistics.onEvent("CameraModeSwitch", null, null);
143             UsageStatistics.setPendingTransitionCause(
144                     UsageStatistics.TRANSITION_MENU_TAP);
145             setCurrentIndex(ix);
146             mListener.onCameraSelected(mModuleIds[ix]);
147         }
148     }
149 
150     @Override
onDraw(Canvas canvas)151     protected void onDraw(Canvas canvas) {
152         super.onDraw(canvas);
153         mIndicator.setBounds(getDrawable().getBounds());
154         mIndicator.draw(canvas);
155     }
156 
initPopup()157     private void initPopup() {
158         mParent = LayoutInflater.from(getContext()).inflate(R.layout.switcher_popup,
159                 (ViewGroup) getParent());
160         LinearLayout content = (LinearLayout) mParent.findViewById(R.id.content);
161         mPopup = content;
162         // Set the gravity of the popup, so that it shows up at the right position
163         // on screen
164         LayoutParams lp = ((LayoutParams) mPopup.getLayoutParams());
165         lp.gravity = ((LayoutParams) mParent.findViewById(R.id.camera_switcher)
166                 .getLayoutParams()).gravity;
167         mPopup.setLayoutParams(lp);
168 
169         mPopup.setVisibility(View.INVISIBLE);
170         mNeedsAnimationSetup = true;
171         for (int i = mDrawIds.length - 1; i >= 0; i--) {
172             RotateImageView item = new RotateImageView(getContext());
173             item.setImageResource(mDrawIds[i]);
174             item.setBackgroundResource(R.drawable.bg_pressed);
175             final int index = i;
176             item.setOnClickListener(new OnClickListener() {
177                 @Override
178                 public void onClick(View v) {
179                     if (showsPopup()) onCameraSelected(index);
180                 }
181             });
182             switch (mDrawIds[i]) {
183                 case R.drawable.ic_switch_camera:
184                     item.setContentDescription(getContext().getResources().getString(
185                             R.string.accessibility_switch_to_camera));
186                     break;
187                 case R.drawable.ic_switch_video:
188                     item.setContentDescription(getContext().getResources().getString(
189                             R.string.accessibility_switch_to_video));
190                     break;
191                 case R.drawable.ic_switch_pan:
192                     item.setContentDescription(getContext().getResources().getString(
193                             R.string.accessibility_switch_to_panorama));
194                     break;
195                 case R.drawable.ic_switch_photosphere:
196                     item.setContentDescription(getContext().getResources().getString(
197                             R.string.accessibility_switch_to_new_panorama));
198                     break;
199                 default:
200                     break;
201             }
202             content.addView(item, new LinearLayout.LayoutParams(mItemSize, mItemSize));
203         }
204         mPopup.measure(MeasureSpec.makeMeasureSpec(mParent.getWidth(), MeasureSpec.AT_MOST),
205                 MeasureSpec.makeMeasureSpec(mParent.getHeight(), MeasureSpec.AT_MOST));
206     }
207 
showsPopup()208     public boolean showsPopup() {
209         return mShowingPopup;
210     }
211 
isInsidePopup(MotionEvent evt)212     public boolean isInsidePopup(MotionEvent evt) {
213         if (!showsPopup()) return false;
214         int topLeft[] = new int[2];
215         mPopup.getLocationOnScreen(topLeft);
216         int left = topLeft[0];
217         int top = topLeft[1];
218         int bottom = top + mPopup.getHeight();
219         int right = left + mPopup.getWidth();
220         return evt.getX() >= left && evt.getX() < right
221                 && evt.getY() >= top && evt.getY() < bottom;
222     }
223 
hidePopup()224     private void hidePopup() {
225         mShowingPopup = false;
226         setVisibility(View.VISIBLE);
227         if (mPopup != null && !animateHidePopup()) {
228             mPopup.setVisibility(View.INVISIBLE);
229         }
230         mParent.setOnTouchListener(null);
231     }
232 
233     @Override
onConfigurationChanged(Configuration config)234     public void onConfigurationChanged(Configuration config) {
235         if (showsPopup()) {
236             ((ViewGroup) mParent).removeView(mPopup);
237             mPopup = null;
238             initPopup();
239             mPopup.setVisibility(View.VISIBLE);
240         }
241     }
242 
showSwitcher()243     private void showSwitcher() {
244         mShowingPopup = true;
245         if (mPopup == null) {
246             initPopup();
247         }
248         layoutPopup();
249         mPopup.setVisibility(View.VISIBLE);
250         if (!animateShowPopup()) {
251             setVisibility(View.INVISIBLE);
252         }
253         mParent.setOnTouchListener(this);
254     }
255 
256     @Override
onTouch(View v, MotionEvent event)257     public boolean onTouch(View v, MotionEvent event) {
258         closePopup();
259         return true;
260     }
261 
closePopup()262     public void closePopup() {
263         if (showsPopup()) {
264             hidePopup();
265         }
266     }
267 
268     @Override
setOrientation(int degree, boolean animate)269     public void setOrientation(int degree, boolean animate) {
270         super.setOrientation(degree, animate);
271         ViewGroup content = (ViewGroup) mPopup;
272         if (content == null) return;
273         for (int i = 0; i < content.getChildCount(); i++) {
274             RotateImageView iv = (RotateImageView) content.getChildAt(i);
275             iv.setOrientation(degree, animate);
276         }
277     }
278 
layoutPopup()279     private void layoutPopup() {
280         int orientation = Util.getDisplayRotation((Activity) getContext());
281         int w = mPopup.getMeasuredWidth();
282         int h = mPopup.getMeasuredHeight();
283         if (orientation == 0) {
284             mPopup.layout(getRight() - w, getBottom() - h, getRight(), getBottom());
285             mTranslationX = 0;
286             mTranslationY = h / 3;
287         } else if (orientation == 90) {
288             mTranslationX = w / 3;
289             mTranslationY = - h / 3;
290             mPopup.layout(getRight() - w, getTop(), getRight(), getTop() + h);
291         } else if (orientation == 180) {
292             mTranslationX = - w / 3;
293             mTranslationY = - h / 3;
294             mPopup.layout(getLeft(), getTop(), getLeft() + w, getTop() + h);
295         } else {
296             mTranslationX = - w / 3;
297             mTranslationY = h - getHeight();
298             mPopup.layout(getLeft(), getBottom() - h, getLeft() + w, getBottom());
299         }
300     }
301 
302     @Override
onLayout(boolean changed, int left, int top, int right, int bottom)303     public void onLayout(boolean changed, int left, int top, int right, int bottom) {
304         super.onLayout(changed, left, top, right, bottom);
305         if (mPopup != null) {
306             layoutPopup();
307         }
308     }
309 
popupAnimationSetup()310     private void popupAnimationSetup() {
311         if (!ApiHelper.HAS_VIEW_PROPERTY_ANIMATOR) {
312             return;
313         }
314         layoutPopup();
315         mPopup.setScaleX(0.3f);
316         mPopup.setScaleY(0.3f);
317         mPopup.setTranslationX(mTranslationX);
318         mPopup.setTranslationY(mTranslationY);
319         mNeedsAnimationSetup = false;
320     }
321 
animateHidePopup()322     private boolean animateHidePopup() {
323         if (!ApiHelper.HAS_VIEW_PROPERTY_ANIMATOR) {
324             return false;
325         }
326         if (mHideAnimationListener == null) {
327             mHideAnimationListener = new AnimatorListenerAdapter() {
328                 @Override
329                 public void onAnimationEnd(Animator animation) {
330                     // Verify that we weren't canceled
331                     if (!showsPopup() && mPopup != null) {
332                         mPopup.setVisibility(View.INVISIBLE);
333                         ((ViewGroup) mParent).removeView(mPopup);
334                         mPopup = null;
335                     }
336                 }
337             };
338         }
339         mPopup.animate()
340                 .alpha(0f)
341                 .scaleX(0.3f).scaleY(0.3f)
342                 .translationX(mTranslationX)
343                 .translationY(mTranslationY)
344                 .setDuration(SWITCHER_POPUP_ANIM_DURATION)
345                 .setListener(mHideAnimationListener);
346         animate().alpha(1f).setDuration(SWITCHER_POPUP_ANIM_DURATION)
347                 .setListener(null);
348         return true;
349     }
350 
animateShowPopup()351     private boolean animateShowPopup() {
352         if (!ApiHelper.HAS_VIEW_PROPERTY_ANIMATOR) {
353             return false;
354         }
355         if (mNeedsAnimationSetup) {
356             popupAnimationSetup();
357         }
358         if (mShowAnimationListener == null) {
359             mShowAnimationListener = new AnimatorListenerAdapter() {
360                 @Override
361                 public void onAnimationEnd(Animator animation) {
362                     // Verify that we weren't canceled
363                     if (showsPopup()) {
364                         setVisibility(View.INVISIBLE);
365                         // request layout to make sure popup is laid out correctly on ICS
366                         mPopup.requestLayout();
367                     }
368                 }
369             };
370         }
371         mPopup.animate()
372                 .alpha(1f)
373                 .scaleX(1f).scaleY(1f)
374                 .translationX(0)
375                 .translationY(0)
376                 .setDuration(SWITCHER_POPUP_ANIM_DURATION)
377                 .setListener(null);
378         animate().alpha(0f).setDuration(SWITCHER_POPUP_ANIM_DURATION)
379                 .setListener(mShowAnimationListener);
380         return true;
381     }
382 }
383