Home | History | Annotate | Download | only in ui
      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