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
17package com.android.camera.ui;
18
19import android.animation.Animator;
20import android.animation.Animator.AnimatorListener;
21import android.animation.AnimatorListenerAdapter;
22import android.app.Activity;
23import android.content.Context;
24import android.content.res.Configuration;
25import android.graphics.Canvas;
26import android.graphics.drawable.Drawable;
27import android.util.AttributeSet;
28import android.view.LayoutInflater;
29import android.view.MotionEvent;
30import android.view.View;
31import android.view.View.OnClickListener;
32import android.view.View.OnTouchListener;
33import android.view.ViewGroup;
34import android.widget.FrameLayout.LayoutParams;
35import android.widget.LinearLayout;
36
37import com.android.camera.util.CameraUtil;
38import com.android.camera.util.GcamHelper;
39import com.android.camera.util.PhotoSphereHelper;
40import com.android.camera.util.UsageStatistics;
41import com.android.camera2.R;
42
43public 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