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