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