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