CameraAppUI.java revision 8793eff1b85bda89047316fed36cdc1161a8b811
1/*
2 * Copyright (C) 2013 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.app;
18
19import android.app.Activity;
20import android.content.Context;
21import android.graphics.Matrix;
22import android.graphics.SurfaceTexture;
23import android.hardware.display.DisplayManager;
24import android.util.Log;
25import android.view.GestureDetector;
26import android.view.LayoutInflater;
27import android.view.MotionEvent;
28import android.view.TextureView;
29import android.view.View;
30import android.view.ViewConfiguration;
31import android.view.ViewGroup;
32import android.widget.FrameLayout;
33
34import com.android.camera.AnimationManager;
35import com.android.camera.ShutterButton;
36import com.android.camera.TextureViewHelper;
37import com.android.camera.filmstrip.FilmstripContentPanel;
38import com.android.camera.ui.BottomBar;
39import com.android.camera.ui.CaptureAnimationOverlay;
40import com.android.camera.ui.MainActivityLayout;
41import com.android.camera.ui.ModeListView;
42import com.android.camera.ui.ModeTransitionView;
43import com.android.camera.ui.PreviewOverlay;
44import com.android.camera.ui.PreviewStatusListener;
45import com.android.camera.util.ApiHelper;
46import com.android.camera.util.CameraUtil;
47import com.android.camera.widget.FilmstripLayout;
48import com.android.camera.widget.IndicatorIconController;
49import com.android.camera.widget.IndicatorOverlay;
50import com.android.camera2.R;
51
52/**
53 * CameraAppUI centralizes control of views shared across modules. Whereas module
54 * specific views will be handled in each Module UI. For example, we can now
55 * bring the flash animation and capture animation up from each module to app
56 * level, as these animations are largely the same for all modules.
57 *
58 * This class also serves to disambiguate touch events. It recognizes all the
59 * swipe gestures that happen on the preview by attaching a touch listener to
60 * a full-screen view on top of preview TextureView. Since CameraAppUI has knowledge
61 * of how swipe from each direction should be handled, it can then redirect these
62 * events to appropriate recipient views.
63 */
64public class CameraAppUI implements ModeListView.ModeSwitchListener,
65        TextureView.SurfaceTextureListener, ModeListView.ModeListOpenListener {
66
67    /**
68     * The bottom controls on the filmstrip.
69     */
70    public static interface BottomControls {
71        /** Values for the view state of the button. */
72        public final int VIEW_NONE = 0;
73        public final int VIEW_PHOTO_SPHERE = 1;
74        public final int VIEW_RGBZ = 2;
75
76        /**
77         * Sets a new or replaces an existing listener for bottom control events.
78         */
79        void setListener(Listener listener);
80
81        /**
82         * Set if the bottom controls are visible.
83         * @param visible {@code true} if visible.
84         */
85        void setVisible(boolean visible);
86
87        /**
88         * @param visible Whether the button is visible.
89         */
90        void setEditButtonVisibility(boolean visible);
91
92        /**
93         * @param enabled Whether the button is enabled.
94         */
95        void setEditEnabled(boolean enabled);
96
97        /**
98         * Sets the visibility of the view-photosphere button.
99         *
100         * @param state one of {@link #VIEW_NONE}, {@link #VIEW_PHOTO_SPHERE},
101         *            {@link #VIEW_RGBZ}.
102         */
103        void setViewButtonVisibility(int state);
104
105        /**
106         * @param enabled Whether the button is enabled.
107         */
108        void setViewEnabled(boolean enabled);
109
110        /**
111         * @param visible Whether the button is visible.
112         */
113        void setTinyPlanetButtonVisibility(boolean visible);
114
115        /**
116         * @param enabled Whether the button is enabled.
117         */
118        void setTinyPlanetEnabled(boolean enabled);
119
120        /**
121         * @param visible Whether the button is visible.
122         */
123        void setDeleteButtonVisibility(boolean visible);
124
125        /**
126         * @param enabled Whether the button is enabled.
127         */
128        void setDeleteEnabled(boolean enabled);
129
130        /**
131         * @param visible Whether the button is visible.
132         */
133        void setShareButtonVisibility(boolean visible);
134
135        /**
136         * @param enabled Whether the button is enabled.
137         */
138        void setShareEnabled(boolean enabled);
139
140        /**
141         * Classes implementing this interface can listen for events on the bottom
142         * controls.
143         */
144        public static interface Listener {
145            /**
146             * Called when the user pressed the "view" button to e.g. view a photo
147             * sphere or RGBZ image.
148             */
149            public void onExternalViewer();
150
151            /**
152             * Called when the "edit" button is pressed.
153             */
154            public void onEdit();
155
156            /**
157             * Called when the "tiny planet" button is pressed.
158             */
159            public void onTinyPlanet();
160
161            /**
162             * Called when the "delete" button is pressed.
163             */
164            public void onDelete();
165
166            /**
167             * Called when the "share" button is pressed.
168             */
169            public void onShare();
170        }
171    }
172
173    private final static String TAG = "CameraAppUI";
174
175    private final AppController mController;
176    private final boolean mIsCaptureIntent;
177    private final AnimationManager mAnimationManager;
178
179    // Swipe states:
180    private final static int IDLE = 0;
181    private final static int SWIPE_UP = 1;
182    private final static int SWIPE_DOWN = 2;
183    private final static int SWIPE_LEFT = 3;
184    private final static int SWIPE_RIGHT = 4;
185    private boolean mSwipeEnabled = true;
186
187    // Touch related measures:
188    private final int mSlop;
189    private final static int SWIPE_TIME_OUT_MS = 500;
190
191    private final static int SHIMMY_DELAY_MS = 1000;
192
193    // Mode cover states:
194    private final static int COVER_HIDDEN = 0;
195    private final static int COVER_SHOWN = 1;
196    private final static int COVER_WILL_HIDE_AT_NEXT_FRAME = 2;
197    private static final int COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE = 3;
198
199    // Bottom bar capture icons
200    public static final int CAMERA_SHUTTER_ICON = 0;
201    public static final int VIDEO_SHUTTER_ICON = 1;
202    public static final int STOP_SHUTTER_ICON = 2;
203
204    // App level views:
205    private final FrameLayout mCameraRootView;
206    private final ModeTransitionView mModeTransitionView;
207    private final MainActivityLayout mAppRootView;
208    private final ModeListView mModeListView;
209    private final FilmstripLayout mFilmstripLayout;
210    private TextureView mTextureView;
211    private FrameLayout mModuleUI;
212    private BottomBar mBottomBar;
213    private IndicatorOverlay mIndicatorOverlay;
214    private boolean mShouldShowShimmy = false;
215    private IndicatorIconController mIndicatorIconController;
216
217    private TextureViewHelper mTextureViewHelper;
218    private final GestureDetector mGestureDetector;
219    private DisplayManager.DisplayListener mDisplayListener;
220    private int mLastRotation;
221    private int mSwipeState = IDLE;
222    private PreviewOverlay mPreviewOverlay;
223    private CaptureAnimationOverlay mCaptureOverlay;
224    private PreviewStatusListener mPreviewStatusListener;
225    private int mModeCoverState = COVER_HIDDEN;
226    private final FilmstripBottomControls mFilmstripBottomControls;
227    private final FilmstripContentPanel mFilmstripPanel;
228    private Runnable mHideCoverRunnable;
229    private final View.OnLayoutChangeListener mPreviewLayoutChangeListener
230            = new View.OnLayoutChangeListener() {
231        @Override
232        public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
233                int oldTop, int oldRight, int oldBottom) {
234            if (mPreviewStatusListener != null) {
235                mPreviewStatusListener.onPreviewLayoutChanged(v, left, top, right, bottom, oldLeft,
236                        oldTop, oldRight, oldBottom);
237            }
238        }
239    };
240
241    public void updatePreviewAspectRatio(float aspectRatio) {
242        mTextureViewHelper.updateAspectRatio(aspectRatio);
243    }
244
245    /**
246     * This is to support modules that calculate their own transform matrix because
247     * they need to use a transform matrix to rotate the preview.
248     *
249     * @param matrix transform matrix to be set on the TextureView
250     */
251    public void updatePreviewTransform(Matrix matrix) {
252        mTextureViewHelper.updateTransform(matrix);
253    }
254
255    public interface AnimationFinishedListener {
256        public void onAnimationFinished(boolean success);
257    }
258
259    private class MyTouchListener implements View.OnTouchListener {
260        private boolean mScaleStarted = false;
261        @Override
262        public boolean onTouch(View v, MotionEvent event) {
263            if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
264                mScaleStarted = false;
265            } else if (event.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) {
266                mScaleStarted = true;
267            }
268            return (!mScaleStarted) && mGestureDetector.onTouchEvent(event);
269        }
270    }
271
272    /**
273     * This gesture listener finds out the direction of the scroll gestures and
274     * sends them to CameraAppUI to do further handling.
275     */
276    private class MyGestureListener extends GestureDetector.SimpleOnGestureListener {
277        private MotionEvent mDown;
278
279        @Override
280        public boolean onScroll(MotionEvent e1, MotionEvent ev, float distanceX, float distanceY) {
281            if (ev.getEventTime() - ev.getDownTime() > SWIPE_TIME_OUT_MS
282                    || mSwipeState != IDLE
283                    || mIsCaptureIntent
284                    || !mSwipeEnabled) {
285                return false;
286            }
287
288            int deltaX = (int) (ev.getX() - mDown.getX());
289            int deltaY = (int) (ev.getY() - mDown.getY());
290            if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) {
291                if (Math.abs(deltaX) > mSlop || Math.abs(deltaY) > mSlop) {
292                    // Calculate the direction of the swipe.
293                    if (deltaX >= Math.abs(deltaY)) {
294                        // Swipe right.
295                        setSwipeState(SWIPE_RIGHT);
296                    } else if (deltaX <= -Math.abs(deltaY)) {
297                        // Swipe left.
298                        setSwipeState(SWIPE_LEFT);
299                    } else if (deltaY >= Math.abs(deltaX)) {
300                        // Swipe down.
301                        setSwipeState(SWIPE_DOWN);
302                    } else if (deltaY <= -Math.abs(deltaX)) {
303                        // Swipe up.
304                        setSwipeState(SWIPE_UP);
305                    }
306                }
307            }
308            return true;
309        }
310
311        private void setSwipeState(int swipeState) {
312            mSwipeState = swipeState;
313            // Notify new swipe detected.
314            onSwipeDetected(swipeState);
315        }
316
317        @Override
318        public boolean onDown(MotionEvent ev) {
319            mDown = MotionEvent.obtain(ev);
320            mSwipeState = IDLE;
321            return false;
322        }
323    }
324
325    public CameraAppUI(AppController controller, MainActivityLayout appRootView,
326            boolean isCaptureIntent) {
327        mSlop = ViewConfiguration.get(controller.getAndroidContext()).getScaledTouchSlop();
328        mController = controller;
329        mIsCaptureIntent = isCaptureIntent;
330
331        mAppRootView = appRootView;
332        mFilmstripLayout = (FilmstripLayout) appRootView.findViewById(R.id.filmstrip_layout);
333        mCameraRootView = (FrameLayout) appRootView.findViewById(R.id.camera_app_root);
334        mModeTransitionView = (ModeTransitionView)
335                mAppRootView.findViewById(R.id.mode_transition_view);
336        mFilmstripBottomControls = new FilmstripBottomControls(
337                (ViewGroup) mAppRootView.findViewById(R.id.filmstrip_bottom_controls));
338        mFilmstripPanel = (FilmstripContentPanel) mAppRootView.findViewById(R.id.filmstrip_layout);
339        mGestureDetector = new GestureDetector(controller.getAndroidContext(),
340                new MyGestureListener());
341        mModeListView = (ModeListView) appRootView.findViewById(R.id.mode_list_layout);
342        if (mModeListView != null) {
343            mModeListView.setModeSwitchListener(this);
344            mModeListView.setModeListOpenListener(this);
345        } else {
346            Log.e(TAG, "Cannot find mode list in the view hierarchy");
347        }
348        mAnimationManager = new AnimationManager();
349        initDisplayListener();
350    }
351
352    /**
353     * Enable or disable swipe gestures. We want to disable them e.g. while we
354     * record a video.
355     */
356    public void setSwipeEnabled(boolean enabled) {
357        mAppRootView.setSwipeEnabled(enabled);
358        mSwipeEnabled = enabled;
359    }
360
361    /**
362     * Initializes the display listener to listen to display changes such as
363     * 180-degree rotation change, which will not have an onConfigurationChanged
364     * callback.
365     */
366    private void initDisplayListener() {
367        if (ApiHelper.HAS_DISPLAY_LISTENER) {
368            mLastRotation = CameraUtil.getDisplayRotation(
369                    (Activity) mController.getAndroidContext());
370
371            mDisplayListener = new DisplayManager.DisplayListener() {
372                @Override
373                public void onDisplayAdded(int arg0) {
374                    // Do nothing.
375                }
376
377                @Override
378                public void onDisplayChanged(int displayId) {
379                    int rotation = CameraUtil.getDisplayRotation(
380                            (Activity) mController.getAndroidContext());
381                    if ((rotation - mLastRotation + 360) % 360 == 180) {
382                        mPreviewStatusListener.onPreviewFlipped();
383                    }
384                    mLastRotation = rotation;
385                }
386
387                @Override
388                public void onDisplayRemoved(int arg0) {
389                    // Do nothing.
390                }
391            };
392
393            ((DisplayManager) mController.getAndroidContext()
394                    .getSystemService(Context.DISPLAY_SERVICE))
395                    .registerDisplayListener(mDisplayListener, null);
396        }
397    }
398
399    /**
400     * Redirects touch events to appropriate recipient views based on swipe direction.
401     * More specifically, swipe up and swipe down will be handled by the view that handles
402     * mode transition; swipe left will be send to filmstrip; swipe right will be redirected
403     * to mode list in order to bring up mode list.
404     */
405    private void onSwipeDetected(int swipeState) {
406        if (swipeState == SWIPE_UP || swipeState == SWIPE_DOWN) {
407            // Quick switch between modes.
408            int currentModuleIndex = mController.getCurrentModuleIndex();
409            final int moduleToTransitionTo =
410                    mController.getQuickSwitchToModuleId(currentModuleIndex);
411            if (currentModuleIndex != moduleToTransitionTo) {
412                mAppRootView.redirectTouchEventsTo(mModeTransitionView);
413
414                int shadeColorId = CameraUtil.getCameraThemeColorId(moduleToTransitionTo,
415                        mController.getAndroidContext());
416                int iconRes = CameraUtil.getCameraModeIconResId(moduleToTransitionTo,
417                        mController.getAndroidContext());
418
419                AnimationFinishedListener listener = new AnimationFinishedListener() {
420                    @Override
421                    public void onAnimationFinished(boolean success) {
422                        if (success) {
423                            mHideCoverRunnable = new Runnable() {
424                                @Override
425                                public void run() {
426                                    mModeTransitionView.startPeepHoleAnimation();
427                                }
428                            };
429                            mModeCoverState = COVER_SHOWN;
430                            // Go to new module when the previous operation is successful.
431                            mController.onModeSelected(moduleToTransitionTo);
432                        }
433                    }
434                };
435                if (mSwipeState == SWIPE_UP) {
436                    mModeTransitionView.prepareToPullUpShade(shadeColorId, iconRes, listener);
437                } else {
438                    mModeTransitionView.prepareToPullDownShade(shadeColorId, iconRes, listener);
439                }
440            }
441        } else if (swipeState == SWIPE_LEFT) {
442            // Pass the touch sequence to filmstrip layout.
443            mAppRootView.redirectTouchEventsTo(mFilmstripLayout);
444        } else if (swipeState == SWIPE_RIGHT) {
445            // Pass the touch to mode switcher
446            mAppRootView.redirectTouchEventsTo(mModeListView);
447        }
448    }
449
450    /**
451     * Gets called when activity resumes in preview.
452     */
453    public void resume() {
454        if (mTextureView == null || mTextureView.getSurfaceTexture() != null) {
455            if (!mIsCaptureIntent) {
456                showShimmyDelayed();
457            }
458        } else {
459            // Show mode theme cover until preview is ready
460            showModeCoverUntilPreviewReady();
461        }
462        // Hide action bar first since we are in full screen mode first, and
463        // switch the system UI to lights-out mode.
464        mFilmstripPanel.hide();
465    }
466
467    /**
468     * A cover view showing the mode theme color and mode icon will be visible on
469     * top of preview until preview is ready (i.e. camera preview is started and
470     * the first frame has been received).
471     */
472    private void showModeCoverUntilPreviewReady() {
473        int modeId = mController.getCurrentModuleIndex();
474        int colorId = CameraUtil.getCameraThemeColorId(modeId, mController.getAndroidContext());
475        int iconId = CameraUtil.getCameraModeIconResId(modeId, mController.getAndroidContext());
476        mModeTransitionView.setupModeCover(colorId, iconId);
477        mHideCoverRunnable = new Runnable() {
478            @Override
479            public void run() {
480                mModeTransitionView.hideModeCover(new AnimationFinishedListener() {
481                    @Override
482                    public void onAnimationFinished(boolean success) {
483                        if (success) {
484                            showShimmyDelayed();
485                        }
486                    }
487                });
488            }
489        };
490        mModeCoverState = COVER_SHOWN;
491    }
492
493    private void showShimmyDelayed() {
494        if (!mIsCaptureIntent) {
495            // Show shimmy in SHIMMY_DELAY_MS
496            mShouldShowShimmy = mController.shouldShowShimmy();
497            if (mShouldShowShimmy) {
498                mModeListView.startAccordionAnimationWithDelay(SHIMMY_DELAY_MS);
499            }
500        }
501    }
502
503    private void hideModeCover() {
504        if (mHideCoverRunnable != null) {
505            mAppRootView.post(mHideCoverRunnable);
506            mHideCoverRunnable = null;
507        }
508        mModeCoverState = COVER_HIDDEN;
509    }
510
511    @Override
512    public void onOpenFullScreen() {
513        if (mShouldShowShimmy) {
514            mController.decrementShimmyPlayTimes();
515            // Sets should show shimmy flag to false for this session (i.e. until
516            // next onResume)
517            mShouldShowShimmy = false;
518        }
519    }
520
521    /**
522     * Called when the back key is pressed.
523     *
524     * @return Whether the UI responded to the key event.
525     */
526    public boolean onBackPressed() {
527        if (mFilmstripLayout.getVisibility() == View.VISIBLE) {
528            return mFilmstripLayout.onBackPressed();
529        } else {
530            return mModeListView.onBackPressed();
531        }
532    }
533
534    /**
535     * Sets a {@link com.android.camera.ui.PreviewStatusListener} that
536     * listens to SurfaceTexture changes. In addition, listeners are set on
537     * dependent app ui elements.
538     *
539     * @param previewStatusListener the listener that gets notified when SurfaceTexture
540     *                              changes
541     */
542    public void setPreviewStatusListener(PreviewStatusListener previewStatusListener) {
543        mPreviewStatusListener = previewStatusListener;
544        if (mPreviewStatusListener != null) {
545            onPreviewListenerChanged();
546        }
547    }
548
549    /**
550     * When the PreviewStatusListener changes, listeners need to be
551     * set on the following app ui elements:
552     * {@link com.android.camera.ui.PreviewOverlay},
553     * {@link com.android.camera.ui.BottomBar},
554     * {@link com.android.camera.ui.IndicatorOverlay},
555     * {@link com.android.camera.ui.IndicatorIconController}.
556     */
557    private void onPreviewListenerChanged() {
558        // Set a listener for recognizing preview gestures.
559        GestureDetector.OnGestureListener gestureListener
560            = mPreviewStatusListener.getGestureListener();
561        if (gestureListener != null) {
562            mPreviewOverlay.setGestureListener(gestureListener);
563        }
564
565        // Set a listener for resizing the bottom bar on
566        // preview size changes.
567        mTextureViewHelper.setAutoAdjustTransform(
568            mPreviewStatusListener.shouldAutoAdjustTransformMatrixOnLayout());
569        if (mPreviewStatusListener.shouldAutoAdjustBottomBar()) {
570            mBottomBar = (BottomBar) mCameraRootView.findViewById(R.id.bottom_bar);
571            mTextureViewHelper.addPreviewAreaSizeChangedListener(mBottomBar);
572        }
573
574        // Set a listener for resizing the indicator overlay on
575        // preview size changes.
576        mIndicatorOverlay = (IndicatorOverlay) mAppRootView.findViewById(
577            R.id.indicator_overlay);
578        mTextureViewHelper.addPreviewAreaSizeChangedListener(mIndicatorOverlay);
579
580        if (mIndicatorIconController == null) {
581            mIndicatorIconController =
582                new IndicatorIconController(mController, mAppRootView);
583        }
584        mController.getSettingsManager().addListener(mIndicatorIconController);
585    }
586
587    /**
588     * This method should be called in onCameraOpened.  It defines CameraAppUI
589     * specific changes that depend on the camera or camera settings.
590     */
591    public void onChangeCamera() {
592        if (mIndicatorIconController != null) {
593            // Sync the settings state with the indicator state.
594            mIndicatorIconController.syncIndicators();
595        }
596    }
597
598    /**
599     * Adds a listener to receive callbacks when preview area size changes.
600     */
601    public void addPreviewAreaSizeChangedListener(
602            PreviewStatusListener.PreviewAreaSizeChangedListener listener) {
603        mTextureViewHelper.addPreviewAreaSizeChangedListener(listener);
604    }
605
606    /**
607     * Removes a listener that receives callbacks when preview area size changes.
608     */
609    public void removePreviewAreaSizeChangedListener(
610            PreviewStatusListener.PreviewAreaSizeChangedListener listener) {
611        mTextureViewHelper.removePreviewAreaSizeChangedListener(listener);
612    }
613
614    /**
615     * This inflates generic_module layout, which contains all the shared views across
616     * modules. Then each module inflates their own views in the given view group. For
617     * now, this is called every time switching from a not-yet-refactored module to a
618     * refactored module. In the future, this should only need to be done once per app
619     * start.
620     */
621    public void prepareModuleUI() {
622        mCameraRootView.removeAllViews();
623        LayoutInflater inflater = (LayoutInflater) mController.getAndroidContext()
624                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
625        inflater.inflate(R.layout.generic_module, mCameraRootView, true);
626
627        mModuleUI = (FrameLayout) mCameraRootView.findViewById(R.id.module_layout);
628        mTextureView = (TextureView) mCameraRootView.findViewById(R.id.preview_content);
629        mTextureViewHelper = new TextureViewHelper(mTextureView);
630        mTextureViewHelper.setSurfaceTextureListener(this);
631        mTextureViewHelper.setOnLayoutChangeListener(mPreviewLayoutChangeListener);
632
633        mBottomBar = (BottomBar) mCameraRootView.findViewById(R.id.bottom_bar);
634        mBottomBar.setupToggle(mIsCaptureIntent);
635
636        mPreviewOverlay = (PreviewOverlay) mCameraRootView.findViewById(R.id.preview_overlay);
637        mPreviewOverlay.setOnTouchListener(new MyTouchListener());
638        mPreviewOverlay.setOnPreviewTouchedListener(mBottomBar);
639
640        mCaptureOverlay = (CaptureAnimationOverlay)
641                mCameraRootView.findViewById(R.id.capture_overlay);
642        mTextureViewHelper.addPreviewAreaSizeChangedListener(mPreviewOverlay);
643        mTextureViewHelper.addPreviewAreaSizeChangedListener(mCaptureOverlay);
644
645        if (mIndicatorIconController == null) {
646            mIndicatorIconController =
647                new IndicatorIconController(mController, mAppRootView);
648        }
649
650        mController.getButtonManager().load(mCameraRootView);
651        mController.getButtonManager().setListener(mIndicatorIconController);
652    }
653
654    // TODO: Remove this when refactor is done.
655    // This is here to ensure refactored modules can work with not-yet-refactored ones.
656    public void clearCameraUI() {
657        mCameraRootView.removeAllViews();
658        mModuleUI = null;
659        mTextureView = null;
660        mPreviewOverlay = null;
661        mBottomBar = null;
662        mIndicatorOverlay = null;
663        mIndicatorIconController = null;
664        setBottomBarShutterListener(null);
665    }
666
667    /**
668     * Called indirectly from each module in their initialization to get a view group
669     * to inflate the module specific views in.
670     *
671     * @return a view group for modules to attach views to
672     */
673    public FrameLayout getModuleRootView() {
674        // TODO: Change it to mModuleUI when refactor is done
675        return mCameraRootView;
676    }
677
678    /**
679     * Remove all the module specific views.
680     */
681    public void clearModuleUI() {
682        if (mModuleUI != null) {
683            mModuleUI.removeAllViews();
684        }
685        mTextureViewHelper.addPreviewAreaSizeChangedListener(null);
686
687        mPreviewStatusListener = null;
688        mPreviewOverlay.reset();
689    }
690
691    /**
692     * Gets called when preview is started.
693     */
694    public void onPreviewStarted() {
695        if (mModeCoverState == COVER_SHOWN) {
696            mModeCoverState = COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE;
697        }
698    }
699
700    /**
701     * Gets notified when next preview frame comes in.
702     */
703    public void onNewPreviewFrame() {
704        hideModeCover();
705        mModeCoverState = COVER_HIDDEN;
706    }
707
708    /**
709     * Gets called when a mode is selected from {@link com.android.camera.ui.ModeListView}
710     *
711     * @param modeIndex mode index of the selected mode
712     */
713    @Override
714    public void onModeSelected(int modeIndex) {
715        mHideCoverRunnable = new Runnable() {
716            @Override
717            public void run() {
718                mModeListView.startModeSelectionAnimation();
719            }
720        };
721        mModeCoverState = COVER_SHOWN;
722
723        int lastIndex = mController.getCurrentModuleIndex();
724        mController.onModeSelected(modeIndex);
725        int currentIndex = mController.getCurrentModuleIndex();
726
727        if (mTextureView == null) {
728            // TODO: Remove this when all the modules use TextureView
729            int temporaryDelay = 600; // ms
730            mModeListView.postDelayed(new Runnable() {
731                @Override
732                public void run() {
733                    hideModeCover();
734                }
735            }, temporaryDelay);
736        } else if (lastIndex == currentIndex) {
737            hideModeCover();
738        }
739    }
740
741    /********************** Capture animation **********************/
742    /* TODO: This session is subject to UX changes. In addition to the generic
743       flash animation and post capture animation, consider designating a parameter
744       for specifying the type of animation, as well as an animation finished listener
745       so that modules can have more knowledge of the status of the animation. */
746
747    /**
748     * Starts the pre-capture animation.
749     */
750    public void startPreCaptureAnimation() {
751        mCaptureOverlay.startFlashAnimation();
752    }
753
754    /**
755     * Cancels the pre-capture animation.
756     */
757    public void cancelPreCaptureAnimation() {
758        mAnimationManager.cancelAnimations();
759    }
760
761    /**
762     * Cancels the post-capture animation.
763     */
764    public void cancelPostCaptureAnimation() {
765        mAnimationManager.cancelAnimations();
766    }
767
768    public FilmstripContentPanel getFilmstripContentPanel() {
769        return mFilmstripPanel;
770    }
771
772    /**
773     * @return The {@link com.android.camera.app.CameraAppUI.BottomControls} on the
774     * bottom of the filmstrip.
775     */
776    public BottomControls getFilmstripBottomControls() {
777        return mFilmstripBottomControls;
778    }
779
780    /**
781     * @param listener The listener for bottom controls.
782     */
783    public void setFilmstripBottomControlsListener(BottomControls.Listener listener) {
784        mFilmstripBottomControls.setListener(listener);
785    }
786
787    /***************************SurfaceTexture Listener*********************************/
788
789    @Override
790    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
791        Log.v(TAG, "SurfaceTexture is available");
792        if (mPreviewStatusListener != null) {
793            mPreviewStatusListener.onSurfaceTextureAvailable(surface, width, height);
794        }
795    }
796
797    @Override
798    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
799        if (mPreviewStatusListener != null) {
800            mPreviewStatusListener.onSurfaceTextureSizeChanged(surface, width, height);
801        }
802    }
803
804    @Override
805    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
806        Log.v(TAG, "SurfaceTexture is destroyed");
807        if (mPreviewStatusListener != null) {
808            return mPreviewStatusListener.onSurfaceTextureDestroyed(surface);
809        }
810        return false;
811    }
812
813    @Override
814    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
815        if (mModeCoverState == COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE) {
816            hideModeCover();
817            mModeCoverState = COVER_HIDDEN;
818        }
819        if (mPreviewStatusListener != null) {
820            mPreviewStatusListener.onSurfaceTextureUpdated(surface);
821        }
822    }
823
824    /**
825     * Sets the color of the bottom bar.
826     */
827    public void setBottomBarColor(int colorId) {
828        mBottomBar.setBackgroundColor(colorId);
829    }
830
831    /**
832     * Sets the pressed color of the bottom bar.
833     */
834    public void setBottomBarPressedColor(int colorId) {
835        mBottomBar.setBackgroundPressedColor(colorId);
836    }
837
838    /**
839     * converts a icon enum to a resource id
840     */
841    private int shutterIconId(int w) {
842        int resId = 0;
843        switch(w) {
844            case VIDEO_SHUTTER_ICON:
845                resId = R.drawable.ic_video_normal;
846                break;
847            case STOP_SHUTTER_ICON:
848                resId = R.drawable.ic_stop_normal;
849                break;
850            case CAMERA_SHUTTER_ICON:
851            default:
852                resId = R.drawable.ic_camera_normal;
853        }
854        return resId;
855    }
856
857    /**
858     * Sets the shutter button icon on the bottom bar
859     */
860    public void setBottomBarShutterIcon(int w) {
861        mBottomBar.setShutterButtonIcon(shutterIconId(w));
862    }
863
864    public void animateBottomBarToCircle(int w) {
865        mBottomBar.animateToCircle(shutterIconId(w));
866    }
867
868    public void animateBottomBarToFullSize(int w) {
869        mBottomBar.animateToFullSize(shutterIconId(w));
870    }
871
872    /**
873     * Set the visibility of the bottom bar.
874     */
875    // TODO: needed for when panorama is managed by the generic module ui.
876    public void setBottomBarVisible(boolean visible) {
877        mBottomBar.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
878    }
879
880    /**
881     * If the bottom bar is visible (hence has been drawn),
882     * this sets a {@link #ShutterButton.OnShutterButtonListener}
883     * on the global shutter button,
884     */
885    public void setBottomBarShutterListener(
886            ShutterButton.OnShutterButtonListener listener) {
887        ShutterButton shutterButton
888            = (ShutterButton) mCameraRootView.findViewById(R.id.shutter_button);
889        if (shutterButton != null) {
890            shutterButton.setOnShutterButtonListener(listener);
891        }
892    }
893
894    /**
895     * Performs a transition to the global intent layout.
896     */
897    public void transitionToIntentLayout() {
898        mBottomBar.transitionToIntentLayout();
899    }
900
901    /**
902     * Performs a transition to the global intent review layout.
903     */
904    public void transitionToIntentReviewLayout() {
905        mBottomBar.transitionToIntentReviewLayout();
906    }
907}
908