2 * Copyright (C) 2013 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.camera.app;
19 import android.accessibilityservice.AccessibilityServiceInfo;
20 import android.content.Context;
21 import android.content.res.Resources;
22 import android.graphics.Bitmap;
23 import android.graphics.Canvas;
24 import android.graphics.Matrix;
25 import android.graphics.RectF;
26 import android.graphics.SurfaceTexture;
27 import android.hardware.display.DisplayManager;
28 import android.util.CameraPerformanceTracker;
29 import android.view.GestureDetector;
30 import android.view.LayoutInflater;
31 import android.view.MotionEvent;
32 import android.view.TextureView;
33 import android.view.View;
34 import android.view.ViewConfiguration;
35 import android.view.ViewGroup;
36 import android.view.accessibility.AccessibilityManager;
37 import android.widget.FrameLayout;
39 import com.android.camera.AnimationManager;
40 import com.android.camera.ButtonManager;
41 import com.android.camera.CaptureLayoutHelper;
42 import com.android.camera.ShutterButton;
43 import com.android.camera.TextureViewHelper;
44 import com.android.camera.debug.Log;
45 import com.android.camera.filmstrip.FilmstripContentPanel;
46 import com.android.camera.hardware.HardwareSpec;
47 import com.android.camera.module.ModuleController;
48 import com.android.camera.settings.Keys;
49 import com.android.camera.settings.SettingsManager;
50 import com.android.camera.ui.AbstractTutorialOverlay;
51 import com.android.camera.ui.BottomBar;
52 import com.android.camera.ui.CaptureAnimationOverlay;
53 import com.android.camera.ui.GridLines;
54 import com.android.camera.ui.MainActivityLayout;
55 import com.android.camera.ui.ModeListView;
56 import com.android.camera.ui.ModeTransitionView;
57 import com.android.camera.ui.PreviewOverlay;
58 import com.android.camera.ui.PreviewStatusListener;
59 import com.android.camera.ui.StickyBottomCaptureLayout;
60 import com.android.camera.ui.TouchCoordinate;
61 import com.android.camera.ui.focus.FocusRing;
62 import com.android.camera.util.ApiHelper;
63 import com.android.camera.util.CameraUtil;
64 import com.android.camera.util.Gusterpolator;
65 import com.android.camera.util.PhotoSphereHelper;
66 import com.android.camera.widget.Cling;
67 import com.android.camera.widget.FilmstripLayout;
68 import com.android.camera.widget.IndicatorIconController;
69 import com.android.camera.widget.ModeOptionsOverlay;
70 import com.android.camera.widget.RoundedThumbnailView;
71 import com.android.camera2.R;
73 import java.util.List;
76 * CameraAppUI centralizes control of views shared across modules. Whereas module
77 * specific views will be handled in each Module UI. For example, we can now
78 * bring the flash animation and capture animation up from each module to app
79 * level, as these animations are largely the same for all modules.
81 * This class also serves to disambiguate touch events. It recognizes all the
82 * swipe gestures that happen on the preview by attaching a touch listener to
83 * a full-screen view on top of preview TextureView. Since CameraAppUI has knowledge
84 * of how swipe from each direction should be handled, it can then redirect these
85 * events to appropriate recipient views.
87 public class CameraAppUI implements ModeListView.ModeSwitchListener,
88 TextureView.SurfaceTextureListener,
89 ModeListView.ModeListOpenListener,
90 SettingsManager.OnSettingChangedListener,
91 ShutterButton.OnShutterButtonListener {
94 * The bottom controls on the filmstrip.
96 public static interface BottomPanel {
97 /** Values for the view state of the button. */
98 public final int VIEWER_NONE = 0;
99 public final int VIEWER_PHOTO_SPHERE = 1;
100 public final int VIEWER_REFOCUS = 2;
101 public final int VIEWER_OTHER = 3;
104 * Sets a new or replaces an existing listener for bottom control events.
106 void setListener(Listener listener);
109 * Sets cling for external viewer button.
111 void setClingForViewer(int viewerType, Cling cling);
114 * Clears cling for external viewer button.
116 void clearClingForViewer(int viewerType);
119 * Returns a cling for the specified viewer type.
121 Cling getClingForViewer(int viewerType);
124 * Set if the bottom controls are visible.
125 * @param visible {@code true} if visible.
127 void setVisible(boolean visible);
130 * @param visible Whether the button is visible.
132 void setEditButtonVisibility(boolean visible);
135 * @param enabled Whether the button is enabled.
137 void setEditEnabled(boolean enabled);
140 * Sets the visibility of the view-photosphere button.
142 * @param state one of {@link #VIEWER_NONE}, {@link #VIEWER_PHOTO_SPHERE},
143 * {@link #VIEWER_REFOCUS}.
145 void setViewerButtonVisibility(int state);
148 * @param enabled Whether the button is enabled.
150 void setViewEnabled(boolean enabled);
153 * @param enabled Whether the button is enabled.
155 void setTinyPlanetEnabled(boolean enabled);
158 * @param visible Whether the button is visible.
160 void setDeleteButtonVisibility(boolean visible);
163 * @param enabled Whether the button is enabled.
165 void setDeleteEnabled(boolean enabled);
168 * @param visible Whether the button is visible.
170 void setShareButtonVisibility(boolean visible);
173 * @param enabled Whether the button is enabled.
175 void setShareEnabled(boolean enabled);
178 * Sets the texts for progress UI.
180 * @param text The text to show.
182 void setProgressText(CharSequence text);
187 * @param progress The progress value. Should be between 0 and 100.
189 void setProgress(int progress);
192 * Replaces the progress UI with an error message.
194 void showProgressError(CharSequence message);
197 * Hide the progress error message.
199 void hideProgressError();
202 * Shows the progress.
207 * Hides the progress.
212 * Shows the controls.
217 * Hides the controls.
222 * Classes implementing this interface can listen for events on the bottom
225 public static interface Listener {
227 * Called when the user pressed the "view" button to e.g. view a photo
228 * sphere or RGBZ image.
230 public void onExternalViewer();
233 * Called when the "edit" button is pressed.
235 public void onEdit();
238 * Called when the "tiny planet" button is pressed.
240 public void onTinyPlanet();
243 * Called when the "delete" button is pressed.
245 public void onDelete();
248 * Called when the "share" button is pressed.
250 public void onShare();
253 * Called when the progress error message is clicked.
255 public void onProgressErrorClicked();
260 * BottomBarUISpec provides a structure for modules
261 * to specify their ideal bottom bar mode options layout.
263 * Once constructed by a module, this class should be
264 * treated as read only.
266 * The application then edits this spec according to
267 * hardware limitations and displays the final bottom
270 public static class BottomBarUISpec {
271 /** Mode options UI */
274 * Set true if the camera option should be enabled.
275 * If not set or false, and multiple cameras are supported,
276 * the camera option will be disabled.
278 * If multiple cameras are not supported, this preference
279 * is ignored and the camera option will not be visible.
281 public boolean enableCamera;
284 * Set true if the camera option should not be visible, regardless
285 * of hardware limitations.
287 public boolean hideCamera;
290 * Set true if the photo flash option should be enabled.
291 * If not set or false, the photo flash option will be
294 * If the hardware does not support multiple flash values,
295 * this preference is ignored and the flash option will
296 * be disabled. It will not be made invisible in order to
297 * preserve a consistent experience across devices and between
298 * front and back cameras.
300 public boolean enableFlash;
303 * Set true if the video flash option should be enabled.
304 * Same disable rules apply as the photo flash option.
306 public boolean enableTorchFlash;
309 * Set true if the HDR+ flash option should be enabled.
310 * Same disable rules apply as the photo flash option.
312 public boolean enableHdrPlusFlash;
315 * Set true if flash should not be visible, regardless of
316 * hardware limitations.
318 public boolean hideFlash;
321 * Set true if the hdr/hdr+ option should be enabled.
322 * If not set or false, the hdr/hdr+ option will be disabled.
324 * Hdr or hdr+ will be chosen based on hardware limitations,
325 * with hdr+ prefered.
327 * If hardware supports neither hdr nor hdr+, then the hdr/hdr+
328 * will not be visible.
330 public boolean enableHdr;
333 * Set true if hdr/hdr+ should not be visible, regardless of
334 * hardware limitations.
336 public boolean hideHdr;
339 * Set true if grid lines should be visible. Not setting this
340 * causes grid lines to be disabled. This option is agnostic to
343 public boolean enableGridLines;
346 * Set true if grid lines should not be visible.
348 public boolean hideGridLines;
351 * Set true if the panorama orientation option should be visible.
353 * This option is not constrained by hardware limitations.
355 public boolean enablePanoOrientation;
357 public boolean enableExposureCompensation;
362 * Set true if the intent ui cancel option should be visible.
364 public boolean showCancel;
366 * Set true if the intent ui done option should be visible.
368 public boolean showDone;
370 * Set true if the intent ui retake option should be visible.
372 public boolean showRetake;
374 * Set true if the intent ui review option should be visible.
376 public boolean showReview;
378 /** Mode options callbacks */
381 * A {@link com.android.camera.ButtonManager.ButtonCallback}
382 * that will be executed when the camera option is pressed. This
383 * callback can be null.
385 public ButtonManager.ButtonCallback cameraCallback;
388 * A {@link com.android.camera.ButtonManager.ButtonCallback}
389 * that will be executed when the flash option is pressed. This
390 * callback can be null.
392 public ButtonManager.ButtonCallback flashCallback;
395 * A {@link com.android.camera.ButtonManager.ButtonCallback}
396 * that will be executed when the hdr/hdr+ option is pressed. This
397 * callback can be null.
399 public ButtonManager.ButtonCallback hdrCallback;
402 * A {@link com.android.camera.ButtonManager.ButtonCallback}
403 * that will be executed when the grid lines option is pressed. This
404 * callback can be null.
406 public ButtonManager.ButtonCallback gridLinesCallback;
409 * A {@link com.android.camera.ButtonManager.ButtonCallback}
410 * that will execute when the panorama orientation option is pressed.
411 * This callback can be null.
413 public ButtonManager.ButtonCallback panoOrientationCallback;
415 /** Intent UI callbacks */
418 * A {@link android.view.View.OnClickListener} that will execute
419 * when the cancel option is pressed. This callback can be null.
421 public View.OnClickListener cancelCallback;
424 * A {@link android.view.View.OnClickListener} that will execute
425 * when the done option is pressed. This callback can be null.
427 public View.OnClickListener doneCallback;
430 * A {@link android.view.View.OnClickListener} that will execute
431 * when the retake option is pressed. This callback can be null.
433 public View.OnClickListener retakeCallback;
436 * A {@link android.view.View.OnClickListener} that will execute
437 * when the review option is pressed. This callback can be null.
439 public View.OnClickListener reviewCallback;
442 * A ExposureCompensationSetCallback that will execute
443 * when an expsosure button is pressed. This callback can be null.
445 public interface ExposureCompensationSetCallback {
446 public void setExposure(int value);
448 public ExposureCompensationSetCallback exposureCompensationSetCallback;
451 * Exposure compensation parameters.
453 public int minExposureCompensation;
454 public int maxExposureCompensation;
455 public float exposureCompensationStep;
458 * Whether self-timer is enabled.
460 public boolean enableSelfTimer = false;
463 * Whether the option for self-timer should show. If true and
464 * {@link #enableSelfTimer} is false, then the option should be shown
467 public boolean showSelfTimer = false;
471 private final static Log.Tag TAG = new Log.Tag("CameraAppUI");
473 private final AppController mController;
474 private final boolean mIsCaptureIntent;
475 private final AnimationManager mAnimationManager;
478 private final static int IDLE = 0;
479 private final static int SWIPE_UP = 1;
480 private final static int SWIPE_DOWN = 2;
481 private final static int SWIPE_LEFT = 3;
482 private final static int SWIPE_RIGHT = 4;
483 private boolean mSwipeEnabled = true;
485 // Shared Surface Texture properities.
486 private SurfaceTexture mSurface;
487 private int mSurfaceWidth;
488 private int mSurfaceHeight;
490 // Touch related measures:
491 private final int mSlop;
492 private final static int SWIPE_TIME_OUT_MS = 500;
494 // Mode cover states:
495 private final static int COVER_HIDDEN = 0;
496 private final static int COVER_SHOWN = 1;
497 private final static int COVER_WILL_HIDE_AT_NEXT_FRAME = 2;
498 private static final int COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE = 3;
501 * Preview down-sample rate when taking a screenshot.
503 private final static int DOWN_SAMPLE_RATE_FOR_SCREENSHOT = 2;
506 private final FrameLayout mCameraRootView;
507 private final ModeTransitionView mModeTransitionView;
508 private final MainActivityLayout mAppRootView;
509 private final ModeListView mModeListView;
510 private final FilmstripLayout mFilmstripLayout;
511 private TextureView mTextureView;
512 private FrameLayout mModuleUI;
513 private ShutterButton mShutterButton;
514 private BottomBar mBottomBar;
515 private ModeOptionsOverlay mModeOptionsOverlay;
516 private IndicatorIconController mIndicatorIconController;
517 private FocusRing mFocusRing;
518 private FrameLayout mTutorialsPlaceHolderWrapper;
519 private StickyBottomCaptureLayout mStickyBottomCaptureLayout;
520 private TextureViewHelper mTextureViewHelper;
521 private final GestureDetector mGestureDetector;
522 private DisplayManager.DisplayListener mDisplayListener;
523 private int mLastRotation;
524 private int mSwipeState = IDLE;
525 private PreviewOverlay mPreviewOverlay;
526 private GridLines mGridLines;
527 private CaptureAnimationOverlay mCaptureOverlay;
528 private PreviewStatusListener mPreviewStatusListener;
529 private int mModeCoverState = COVER_HIDDEN;
530 private final FilmstripBottomPanel mFilmstripBottomControls;
531 private final FilmstripContentPanel mFilmstripPanel;
532 private Runnable mHideCoverRunnable;
533 private final View.OnLayoutChangeListener mPreviewLayoutChangeListener
534 = new View.OnLayoutChangeListener() {
536 public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
537 int oldTop, int oldRight, int oldBottom) {
538 if (mPreviewStatusListener != null) {
539 mPreviewStatusListener.onPreviewLayoutChanged(v, left, top, right, bottom, oldLeft,
540 oldTop, oldRight, oldBottom);
544 private View mModeOptionsToggle;
545 private final RoundedThumbnailView mRoundedThumbnailView;
546 private final CaptureLayoutHelper mCaptureLayoutHelper;
547 private boolean mAccessibilityEnabled;
548 private final View mAccessibilityAffordances;
550 private boolean mDisableAllUserInteractions;
552 * Provides current preview frame and the controls/overlay from the module that
553 * are shown on top of the preview.
555 public interface CameraModuleScreenShotProvider {
557 * Returns the current preview frame down-sampled using the given down-sample
560 * @param downSampleFactor the down sample factor for down sampling the
561 * preview frame. (e.g. a down sample factor of
562 * 2 means to scale down the preview frame to 1/2
563 * the width and height.)
564 * @return down-sampled preview frame
566 public Bitmap getPreviewFrame(int downSampleFactor);
569 * @return the controls and overlays that are currently showing on top of
570 * the preview drawn into a bitmap with no scaling applied.
572 public Bitmap getPreviewOverlayAndControls();
575 * Returns a bitmap containing the current screenshot.
577 * @param previewDownSampleFactor the downsample factor applied on the
578 * preview frame when taking the screenshot
580 public Bitmap getScreenShot(int previewDownSampleFactor);
584 * This listener gets called when the size of the window (excluding the system
585 * decor such as status bar and nav bar) has changed.
587 public interface NonDecorWindowSizeChangedListener {
588 public void onNonDecorWindowSizeChanged(int width, int height, int rotation);
591 private final CameraModuleScreenShotProvider mCameraModuleScreenShotProvider =
592 new CameraModuleScreenShotProvider() {
594 public Bitmap getPreviewFrame(int downSampleFactor) {
595 if (mCameraRootView == null || mTextureView == null) {
598 // Gets the bitmap from the preview TextureView.
599 Bitmap preview = mTextureViewHelper.getPreviewBitmap(downSampleFactor);
604 public Bitmap getPreviewOverlayAndControls() {
605 Bitmap overlays = Bitmap.createBitmap(mCameraRootView.getWidth(),
606 mCameraRootView.getHeight(), Bitmap.Config.ARGB_8888);
607 Canvas canvas = new Canvas(overlays);
608 mCameraRootView.draw(canvas);
613 public Bitmap getScreenShot(int previewDownSampleFactor) {
614 Bitmap screenshot = Bitmap.createBitmap(mCameraRootView.getWidth(),
615 mCameraRootView.getHeight(), Bitmap.Config.ARGB_8888);
616 Canvas canvas = new Canvas(screenshot);
617 canvas.drawARGB(255, 0, 0, 0);
618 Bitmap preview = mTextureViewHelper.getPreviewBitmap(previewDownSampleFactor);
619 if (preview != null) {
620 canvas.drawBitmap(preview, null, mTextureViewHelper.getPreviewArea(), null);
622 Bitmap overlay = getPreviewOverlayAndControls();
623 if (overlay != null) {
624 canvas.drawBitmap(overlay, 0f, 0f, null);
630 private long mCoverHiddenTime = -1; // System time when preview cover was hidden.
632 public long getCoverHiddenTime() {
633 return mCoverHiddenTime;
637 * This resets the preview to have no applied transform matrix.
639 public void clearPreviewTransform() {
640 mTextureViewHelper.clearTransform();
643 public void updatePreviewAspectRatio(float aspectRatio) {
644 mTextureViewHelper.updateAspectRatio(aspectRatio);
648 * WAR: Reset the SurfaceTexture's default buffer size to the current view dimensions of
649 * its TextureView. This is necessary to get the expected behavior for the TextureView's
650 * HardwareLayer transform matrix (set by TextureView#setTransform) after configuring the
651 * SurfaceTexture as an output for the Camera2 API (which involves changing the default buffer
654 * b/17286155 - Tracking a fix for this in HardwareLayer.
656 public void setDefaultBufferSizeToViewDimens() {
657 if (mSurface == null || mTextureView == null) {
658 Log.w(TAG, "Could not set SurfaceTexture default buffer dimensions, not yet setup");
661 mSurface.setDefaultBufferSize(mTextureView.getWidth(), mTextureView.getHeight());
665 * Updates the preview matrix without altering it.
668 * @param aspectRatio the desired aspect ratio for the preview.
670 public void updatePreviewTransformFullscreen(Matrix matrix, float aspectRatio) {
671 mTextureViewHelper.updateTransformFullScreen(matrix, aspectRatio);
675 * @return the rect that will display the preview.
677 public RectF getFullscreenRect() {
678 return mTextureViewHelper.getFullscreenRect();
682 * This is to support modules that calculate their own transform matrix because
683 * they need to use a transform matrix to rotate the preview.
685 * @param matrix transform matrix to be set on the TextureView
687 public void updatePreviewTransform(Matrix matrix) {
688 mTextureViewHelper.updateTransform(matrix);
691 public interface AnimationFinishedListener {
692 public void onAnimationFinished(boolean success);
695 private class MyTouchListener implements View.OnTouchListener {
696 private boolean mScaleStarted = false;
698 public boolean onTouch(View v, MotionEvent event) {
699 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
700 mScaleStarted = false;
701 } else if (event.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) {
702 mScaleStarted = true;
704 return (!mScaleStarted) && mGestureDetector.onTouchEvent(event);
709 * This gesture listener finds out the direction of the scroll gestures and
710 * sends them to CameraAppUI to do further handling.
712 private class MyGestureListener extends GestureDetector.SimpleOnGestureListener {
713 private MotionEvent mDown;
716 public boolean onScroll(MotionEvent e1, MotionEvent ev, float distanceX, float distanceY) {
717 if (ev.getEventTime() - ev.getDownTime() > SWIPE_TIME_OUT_MS
718 || mSwipeState != IDLE
724 int deltaX = (int) (ev.getX() - mDown.getX());
725 int deltaY = (int) (ev.getY() - mDown.getY());
726 if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) {
727 if (Math.abs(deltaX) > mSlop || Math.abs(deltaY) > mSlop) {
728 // Calculate the direction of the swipe.
729 if (deltaX >= Math.abs(deltaY)) {
731 setSwipeState(SWIPE_RIGHT);
732 } else if (deltaX <= -Math.abs(deltaY)) {
734 setSwipeState(SWIPE_LEFT);
741 private void setSwipeState(int swipeState) {
742 mSwipeState = swipeState;
743 // Notify new swipe detected.
744 onSwipeDetected(swipeState);
748 public boolean onDown(MotionEvent ev) {
749 mDown = MotionEvent.obtain(ev);
755 public CameraAppUI(AppController controller, MainActivityLayout appRootView,
756 boolean isCaptureIntent) {
757 mSlop = ViewConfiguration.get(controller.getAndroidContext()).getScaledTouchSlop();
758 mController = controller;
759 mIsCaptureIntent = isCaptureIntent;
761 mAppRootView = appRootView;
762 mFilmstripLayout = (FilmstripLayout) appRootView.findViewById(R.id.filmstrip_layout);
763 mCameraRootView = (FrameLayout) appRootView.findViewById(R.id.camera_app_root);
764 mModeTransitionView = (ModeTransitionView)
765 mAppRootView.findViewById(R.id.mode_transition_view);
766 mFilmstripBottomControls = new FilmstripBottomPanel(controller,
767 (ViewGroup) mAppRootView.findViewById(R.id.filmstrip_bottom_panel));
768 mFilmstripPanel = (FilmstripContentPanel) mAppRootView.findViewById(R.id.filmstrip_layout);
769 mGestureDetector = new GestureDetector(controller.getAndroidContext(),
770 new MyGestureListener());
771 Resources res = controller.getAndroidContext().getResources();
772 mCaptureLayoutHelper = new CaptureLayoutHelper(
773 res.getDimensionPixelSize(R.dimen.bottom_bar_height_min),
774 res.getDimensionPixelSize(R.dimen.bottom_bar_height_max),
775 res.getDimensionPixelSize(R.dimen.bottom_bar_height_optimal));
776 mModeListView = (ModeListView) appRootView.findViewById(R.id.mode_list_layout);
777 if (mModeListView != null) {
778 mModeListView.setModeSwitchListener(this);
779 mModeListView.setModeListOpenListener(this);
780 mModeListView.setCameraModuleScreenShotProvider(mCameraModuleScreenShotProvider);
781 mModeListView.setCaptureLayoutHelper(mCaptureLayoutHelper);
782 boolean shouldShowSettingsCling = mController.getSettingsManager().getBoolean(
783 SettingsManager.SCOPE_GLOBAL,
784 Keys.KEY_SHOULD_SHOW_SETTINGS_BUTTON_CLING);
785 mModeListView.setShouldShowSettingsCling(shouldShowSettingsCling);
787 Log.e(TAG, "Cannot find mode list in the view hierarchy");
789 mAnimationManager = new AnimationManager();
790 mRoundedThumbnailView = (RoundedThumbnailView) appRootView.findViewById(R.id.rounded_thumbnail_view);
791 mRoundedThumbnailView.setOnClickListener(new View.OnClickListener() {
793 public void onClick(View v) {
794 mFilmstripLayout.showFilmstrip();
798 mAppRootView.setNonDecorWindowSizeChangedListener(mCaptureLayoutHelper);
799 initDisplayListener();
800 mAccessibilityAffordances = mAppRootView.findViewById(R.id.accessibility_affordances);
801 View modeListToggle = mAppRootView.findViewById(R.id.accessibility_mode_toggle_button);
802 modeListToggle.setOnClickListener(new View.OnClickListener() {
804 public void onClick(View view) {
808 View filmstripToggle = mAppRootView.findViewById(
809 R.id.accessibility_filmstrip_toggle_button);
810 filmstripToggle.setOnClickListener(new View.OnClickListener() {
812 public void onClick(View view) {
820 * Freeze what is currently shown on screen until the next preview frame comes
823 public void freezeScreenUntilPreviewReady() {
824 Log.v(TAG, "freezeScreenUntilPreviewReady");
825 mModeTransitionView.setupModeCover(mCameraModuleScreenShotProvider
826 .getScreenShot(DOWN_SAMPLE_RATE_FOR_SCREENSHOT));
827 mHideCoverRunnable = new Runnable() {
830 mModeTransitionView.hideImageCover();
833 mModeCoverState = COVER_SHOWN;
837 * Creates a cling for the specific viewer and links the cling to the corresponding
838 * button for layout position.
840 * @param viewerType defines which viewer the cling is for.
842 public void setupClingForViewer(int viewerType) {
843 if (viewerType == BottomPanel.VIEWER_REFOCUS) {
844 FrameLayout filmstripContent = (FrameLayout) mAppRootView
845 .findViewById(R.id.camera_filmstrip_content_layout);
846 if (filmstripContent != null) {
847 // Creates refocus cling.
848 LayoutInflater inflater = (LayoutInflater) mController.getAndroidContext()
849 .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
850 Cling refocusCling = (Cling) inflater.inflate(R.layout.cling_widget, null, false);
851 // Sets instruction text in the cling.
852 refocusCling.setText(mController.getAndroidContext().getResources()
853 .getString(R.string.cling_text_for_refocus_editor_button));
855 // Adds cling into view hierarchy.
856 int clingWidth = mController.getAndroidContext()
857 .getResources().getDimensionPixelSize(R.dimen.default_cling_width);
858 filmstripContent.addView(refocusCling, clingWidth,
859 ViewGroup.LayoutParams.WRAP_CONTENT);
860 mFilmstripBottomControls.setClingForViewer(viewerType, refocusCling);
866 * Clears the listeners for the cling and remove it from the view hierarchy.
868 * @param viewerType defines which viewer the cling is for.
870 public void clearClingForViewer(int viewerType) {
871 Cling clingToBeRemoved = mFilmstripBottomControls.getClingForViewer(viewerType);
872 if (clingToBeRemoved == null) {
873 // No cling is created for the specific viewer type.
876 mFilmstripBottomControls.clearClingForViewer(viewerType);
877 clingToBeRemoved.setVisibility(View.GONE);
878 mAppRootView.removeView(clingToBeRemoved);
882 * Enable or disable swipe gestures. We want to disable them e.g. while we
885 public void setSwipeEnabled(boolean enabled) {
886 mSwipeEnabled = enabled;
887 // TODO: This can be removed once we come up with a new design for handling swipe
888 // on shutter button and mode options. (More details: b/13751653)
889 mAppRootView.setSwipeEnabled(enabled);
892 public void onDestroy() {
893 ((DisplayManager) mController.getAndroidContext()
894 .getSystemService(Context.DISPLAY_SERVICE))
895 .unregisterDisplayListener(mDisplayListener);
899 * Initializes the display listener to listen to display changes such as
900 * 180-degree rotation change, which will not have an onConfigurationChanged
903 private void initDisplayListener() {
904 if (ApiHelper.HAS_DISPLAY_LISTENER) {
905 mLastRotation = CameraUtil.getDisplayRotation(mController.getAndroidContext());
907 mDisplayListener = new DisplayManager.DisplayListener() {
909 public void onDisplayAdded(int arg0) {
914 public void onDisplayChanged(int displayId) {
915 int rotation = CameraUtil.getDisplayRotation(
916 mController.getAndroidContext());
917 if ((rotation - mLastRotation + 360) % 360 == 180
918 && mPreviewStatusListener != null) {
919 mPreviewStatusListener.onPreviewFlipped();
920 mStickyBottomCaptureLayout.requestLayout();
921 mModeListView.requestLayout();
922 mTextureView.requestLayout();
924 mLastRotation = rotation;
928 public void onDisplayRemoved(int arg0) {
933 ((DisplayManager) mController.getAndroidContext()
934 .getSystemService(Context.DISPLAY_SERVICE))
935 .registerDisplayListener(mDisplayListener, null);
940 * Redirects touch events to appropriate recipient views based on swipe direction.
941 * More specifically, swipe up and swipe down will be handled by the view that handles
942 * mode transition; swipe left will be send to filmstrip; swipe right will be redirected
943 * to mode list in order to bring up mode list.
945 private void onSwipeDetected(int swipeState) {
946 if (swipeState == SWIPE_UP || swipeState == SWIPE_DOWN) {
947 // TODO: Polish quick switch after this release.
948 // Quick switch between modes.
949 int currentModuleIndex = mController.getCurrentModuleIndex();
950 final int moduleToTransitionTo =
951 mController.getQuickSwitchToModuleId(currentModuleIndex);
952 if (currentModuleIndex != moduleToTransitionTo) {
953 mAppRootView.redirectTouchEventsTo(mModeTransitionView);
954 int shadeColorId = R.color.mode_cover_default_color;
955 int iconRes = CameraUtil.getCameraModeCoverIconResId(moduleToTransitionTo,
956 mController.getAndroidContext());
958 AnimationFinishedListener listener = new AnimationFinishedListener() {
960 public void onAnimationFinished(boolean success) {
962 mHideCoverRunnable = new Runnable() {
965 mModeTransitionView.startPeepHoleAnimation();
968 mModeCoverState = COVER_SHOWN;
969 // Go to new module when the previous operation is successful.
970 mController.onModeSelected(moduleToTransitionTo);
975 } else if (swipeState == SWIPE_LEFT) {
976 // Pass the touch sequence to filmstrip layout.
977 mAppRootView.redirectTouchEventsTo(mFilmstripLayout);
978 } else if (swipeState == SWIPE_RIGHT) {
979 // Pass the touch to mode switcher
980 mAppRootView.redirectTouchEventsTo(mModeListView);
985 * Gets called when activity resumes in preview.
987 public void resume() {
988 // Show mode theme cover until preview is ready
989 showModeCoverUntilPreviewReady();
991 // Hide action bar first since we are in full screen mode first, and
992 // switch the system UI to lights-out mode.
993 mFilmstripPanel.hide();
995 // Show UI that is meant to only be used when spoken feedback is
997 mAccessibilityEnabled = isSpokenFeedbackAccessibilityEnabled();
998 mAccessibilityAffordances.setVisibility(mAccessibilityEnabled ? View.VISIBLE : View.GONE);
1002 * @return Whether any spoken feedback accessibility feature is currently
1005 private boolean isSpokenFeedbackAccessibilityEnabled() {
1006 AccessibilityManager accessibilityManager = (AccessibilityManager) mController
1007 .getAndroidContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
1008 List<AccessibilityServiceInfo> infos = accessibilityManager
1009 .getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_SPOKEN);
1010 return infos != null && !infos.isEmpty();
1014 * Opens the mode list (e.g. because of the menu button being pressed) and
1015 * adapts the rest of the UI.
1017 public void openModeList() {
1018 mModeOptionsOverlay.closeModeOptions();
1019 mModeListView.onMenuPressed();
1023 * A cover view showing the mode theme color and mode icon will be visible on
1024 * top of preview until preview is ready (i.e. camera preview is started and
1025 * the first frame has been received).
1027 private void showModeCoverUntilPreviewReady() {
1028 int modeId = mController.getCurrentModuleIndex();
1029 int colorId = R.color.mode_cover_default_color;;
1030 int iconId = CameraUtil.getCameraModeCoverIconResId(modeId, mController.getAndroidContext());
1031 mModeTransitionView.setupModeCover(colorId, iconId);
1032 mHideCoverRunnable = new Runnable() {
1035 mModeTransitionView.hideModeCover(null);
1036 if (!mDisableAllUserInteractions) {
1037 showShimmyDelayed();
1041 mModeCoverState = COVER_SHOWN;
1044 private void showShimmyDelayed() {
1045 if (!mIsCaptureIntent) {
1046 // Show shimmy in SHIMMY_DELAY_MS
1047 mModeListView.showModeSwitcherHint();
1051 private void hideModeCover() {
1052 if (mHideCoverRunnable != null) {
1053 mAppRootView.post(mHideCoverRunnable);
1054 mHideCoverRunnable = null;
1056 mModeCoverState = COVER_HIDDEN;
1057 if (mCoverHiddenTime < 0) {
1058 mCoverHiddenTime = System.currentTimeMillis();
1063 public void onPreviewVisiblityChanged(int visibility) {
1064 if (visibility == ModuleController.VISIBILITY_HIDDEN) {
1065 setIndicatorBottomBarWrapperVisible(false);
1066 mAccessibilityAffordances.setVisibility(View.GONE);
1068 setIndicatorBottomBarWrapperVisible(true);
1069 if (mAccessibilityEnabled) {
1070 mAccessibilityAffordances.setVisibility(View.VISIBLE);
1072 mAccessibilityAffordances.setVisibility(View.GONE);
1078 * Call to stop the preview from being rendered.
1080 public void pausePreviewRendering() {
1081 mTextureView.setVisibility(View.INVISIBLE);
1085 * Call to begin rendering the preview again.
1087 public void resumePreviewRendering() {
1088 mTextureView.setVisibility(View.VISIBLE);
1092 * Returns the transform associated with the preview view.
1094 * @param m the Matrix in which to copy the current transform.
1095 * @return The specified matrix if not null or a new Matrix instance
1098 public Matrix getPreviewTransform(Matrix m) {
1099 return mTextureView.getTransform(m);
1103 public void onOpenFullScreen() {
1108 public void onModeListOpenProgress(float progress) {
1109 progress = 1 - progress;
1110 float interpolatedProgress = Gusterpolator.INSTANCE.getInterpolation(progress);
1111 mModeOptionsToggle.setAlpha(interpolatedProgress);
1112 // Change shutter button alpha linearly based on the mode list open progress:
1113 // set the alpha to disabled alpha when list is fully open, to enabled alpha
1114 // when the list is fully closed.
1115 mShutterButton.setAlpha(progress * ShutterButton.ALPHA_WHEN_ENABLED
1116 + (1 - progress) * ShutterButton.ALPHA_WHEN_DISABLED);
1120 public void onModeListClosed() {
1121 // Make sure the alpha on mode options ellipse is reset when mode drawer
1123 mModeOptionsToggle.setAlpha(1f);
1124 mShutterButton.setAlpha(ShutterButton.ALPHA_WHEN_ENABLED);
1128 * Called when the back key is pressed.
1130 * @return Whether the UI responded to the key event.
1132 public boolean onBackPressed() {
1133 if (mFilmstripLayout.getVisibility() == View.VISIBLE) {
1134 return mFilmstripLayout.onBackPressed();
1136 return mModeListView.onBackPressed();
1141 * Sets a {@link com.android.camera.ui.PreviewStatusListener} that
1142 * listens to SurfaceTexture changes. In addition, listeners are set on
1143 * dependent app ui elements.
1145 * @param previewStatusListener the listener that gets notified when SurfaceTexture
1148 public void setPreviewStatusListener(PreviewStatusListener previewStatusListener) {
1149 mPreviewStatusListener = previewStatusListener;
1150 if (mPreviewStatusListener != null) {
1151 onPreviewListenerChanged();
1156 * When the PreviewStatusListener changes, listeners need to be
1157 * set on the following app ui elements:
1158 * {@link com.android.camera.ui.PreviewOverlay},
1159 * {@link com.android.camera.ui.BottomBar},
1160 * {@link com.android.camera.ui.IndicatorIconController}.
1162 private void onPreviewListenerChanged() {
1163 // Set a listener for recognizing preview gestures.
1164 GestureDetector.OnGestureListener gestureListener
1165 = mPreviewStatusListener.getGestureListener();
1166 if (gestureListener != null) {
1167 mPreviewOverlay.setGestureListener(gestureListener);
1169 View.OnTouchListener touchListener = mPreviewStatusListener.getTouchListener();
1170 if (touchListener != null) {
1171 mPreviewOverlay.setTouchListener(touchListener);
1174 mTextureViewHelper.setAutoAdjustTransform(
1175 mPreviewStatusListener.shouldAutoAdjustTransformMatrixOnLayout());
1179 * This method should be called in onCameraOpened. It defines CameraAppUI
1180 * specific changes that depend on the camera or camera settings.
1182 public void onChangeCamera() {
1183 ModuleController moduleController = mController.getCurrentModuleController();
1184 applyModuleSpecs(moduleController.getHardwareSpec(), moduleController.getBottomBarSpec());
1186 if (mIndicatorIconController != null) {
1187 // Sync the settings state with the indicator state.
1188 mIndicatorIconController.syncIndicators();
1193 * Adds a listener to receive callbacks when preview area changes.
1195 public void addPreviewAreaChangedListener(
1196 PreviewStatusListener.PreviewAreaChangedListener listener) {
1197 mTextureViewHelper.addPreviewAreaSizeChangedListener(listener);
1201 * Removes a listener that receives callbacks when preview area changes.
1203 public void removePreviewAreaChangedListener(
1204 PreviewStatusListener.PreviewAreaChangedListener listener) {
1205 mTextureViewHelper.removePreviewAreaSizeChangedListener(listener);
1209 * This inflates generic_module layout, which contains all the shared views across
1210 * modules. Then each module inflates their own views in the given view group. For
1211 * now, this is called every time switching from a not-yet-refactored module to a
1212 * refactored module. In the future, this should only need to be done once per app
1215 public void prepareModuleUI() {
1216 mController.getSettingsManager().addListener(this);
1217 mModuleUI = (FrameLayout) mCameraRootView.findViewById(R.id.module_layout);
1218 mTextureView = (TextureView) mCameraRootView.findViewById(R.id.preview_content);
1219 mTextureViewHelper = new TextureViewHelper(mTextureView, mCaptureLayoutHelper,
1220 mController.getCameraProvider());
1221 mTextureViewHelper.setSurfaceTextureListener(this);
1222 mTextureViewHelper.setOnLayoutChangeListener(mPreviewLayoutChangeListener);
1224 mBottomBar = (BottomBar) mCameraRootView.findViewById(R.id.bottom_bar);
1225 int unpressedColor = mController.getAndroidContext().getResources()
1226 .getColor(R.color.bottombar_unpressed);
1227 setBottomBarColor(unpressedColor);
1228 updateModeSpecificUIColors();
1230 mBottomBar.setCaptureLayoutHelper(mCaptureLayoutHelper);
1233 = (ModeOptionsOverlay) mCameraRootView.findViewById(R.id.mode_options_overlay);
1235 // Sets the visibility of the bottom bar and the mode options.
1236 resetBottomControls(mController.getCurrentModuleController(),
1237 mController.getCurrentModuleIndex());
1238 mModeOptionsOverlay.setCaptureLayoutHelper(mCaptureLayoutHelper);
1240 mShutterButton = (ShutterButton) mCameraRootView.findViewById(R.id.shutter_button);
1241 addShutterListener(mController.getCurrentModuleController());
1242 addShutterListener(mModeOptionsOverlay);
1243 addShutterListener(this);
1245 mGridLines = (GridLines) mCameraRootView.findViewById(R.id.grid_lines);
1246 mTextureViewHelper.addPreviewAreaSizeChangedListener(mGridLines);
1248 mPreviewOverlay = (PreviewOverlay) mCameraRootView.findViewById(R.id.preview_overlay);
1249 mPreviewOverlay.setOnTouchListener(new MyTouchListener());
1250 mPreviewOverlay.setOnPreviewTouchedListener(mModeOptionsOverlay);
1252 mCaptureOverlay = (CaptureAnimationOverlay)
1253 mCameraRootView.findViewById(R.id.capture_overlay);
1254 mTextureViewHelper.addPreviewAreaSizeChangedListener(mPreviewOverlay);
1255 mTextureViewHelper.addPreviewAreaSizeChangedListener(mCaptureOverlay);
1257 if (mIndicatorIconController == null) {
1258 mIndicatorIconController =
1259 new IndicatorIconController(mController, mAppRootView);
1262 mController.getButtonManager().load(mCameraRootView);
1263 mController.getButtonManager().setListener(mIndicatorIconController);
1264 mController.getSettingsManager().addListener(mIndicatorIconController);
1266 mModeOptionsToggle = mCameraRootView.findViewById(R.id.mode_options_toggle);
1267 mFocusRing = (FocusRing) mCameraRootView.findViewById(R.id.focus_ring);
1268 mTutorialsPlaceHolderWrapper = (FrameLayout) mCameraRootView
1269 .findViewById(R.id.tutorials_placeholder_wrapper);
1270 mStickyBottomCaptureLayout = (StickyBottomCaptureLayout) mAppRootView
1271 .findViewById(R.id.sticky_bottom_capture_layout);
1272 mStickyBottomCaptureLayout.setCaptureLayoutHelper(mCaptureLayoutHelper);
1274 mTextureViewHelper.addPreviewAreaSizeChangedListener(mModeListView);
1275 mTextureViewHelper.addAspectRatioChangedListener(
1276 new PreviewStatusListener.PreviewAspectRatioChangedListener() {
1278 public void onPreviewAspectRatioChanged(float aspectRatio) {
1279 mModeOptionsOverlay.requestLayout();
1280 mBottomBar.requestLayout();
1287 * Called indirectly from each module in their initialization to get a view group
1288 * to inflate the module specific views in.
1290 * @return a view group for modules to attach views to
1292 public FrameLayout getModuleRootView() {
1293 // TODO: Change it to mModuleUI when refactor is done
1294 return mCameraRootView;
1298 * Remove all the module specific views.
1300 public void clearModuleUI() {
1301 if (mModuleUI != null) {
1302 mModuleUI.removeAllViews();
1304 removeShutterListener(mController.getCurrentModuleController());
1305 mTutorialsPlaceHolderWrapper.removeAllViews();
1306 mTutorialsPlaceHolderWrapper.setVisibility(View.GONE);
1308 setShutterButtonEnabled(true);
1309 mPreviewStatusListener = null;
1310 mPreviewOverlay.reset();
1312 Log.v(TAG, "mFocusRing.stopFocusAnimations()");
1313 mFocusRing.stopFocusAnimations();
1317 * Gets called when preview is ready to start. It sets up one shot preview callback
1318 * in order to receive a callback when the preview frame is available, so that
1319 * the preview cover can be hidden to reveal preview.
1321 * An alternative for getting the timing to hide preview cover is through
1322 * {@link CameraAppUI#onSurfaceTextureUpdated(android.graphics.SurfaceTexture)},
1323 * which is less accurate but therefore is the fallback for modules that manage
1324 * their own preview callbacks (as setting one preview callback will override
1325 * any other installed preview callbacks), or use camera2 API.
1327 public void onPreviewReadyToStart() {
1328 if (mModeCoverState == COVER_SHOWN) {
1329 mModeCoverState = COVER_WILL_HIDE_AT_NEXT_FRAME;
1330 mController.setupOneShotPreviewListener();
1335 * Gets called when preview is started.
1337 public void onPreviewStarted() {
1338 Log.v(TAG, "onPreviewStarted");
1339 if (mModeCoverState == COVER_SHOWN) {
1340 mModeCoverState = COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE;
1342 enableModeOptions();
1346 * Gets notified when next preview frame comes in.
1348 public void onNewPreviewFrame() {
1349 Log.v(TAG, "onNewPreviewFrame");
1350 CameraPerformanceTracker.onEvent(CameraPerformanceTracker.FIRST_PREVIEW_FRAME);
1355 public void onShutterButtonClick() {
1357 * Set the mode options toggle unclickable, generally
1358 * throughout the app, whenever the shutter button is clicked.
1360 * This could be done in the OnShutterButtonListener of the
1361 * ModeOptionsOverlay, but since it is very important that we
1362 * can clearly see when the toggle becomes clickable again,
1363 * keep all of that logic at this level.
1365 // disableModeOptions();
1369 public void onShutterCoordinate(TouchCoordinate coord) {
1374 public void onShutterButtonFocus(boolean pressed) {
1379 public void onShutterButtonLongPressed() {
1384 * Set the mode options toggle clickable.
1386 public void enableModeOptions() {
1388 * For modules using camera 1 api, this gets called in
1389 * onSurfaceTextureUpdated whenever the preview gets stopped and
1390 * started after each capture. This also takes care of the
1391 * case where the mode options might be unclickable when we
1394 * For modules using camera 2 api, they're required to call this
1395 * method when a capture is "completed". Unfortunately this differs
1396 * per module implementation.
1398 if (!mDisableAllUserInteractions) {
1399 mModeOptionsOverlay.setToggleClickable(true);
1404 * Set the mode options toggle not clickable.
1406 public void disableModeOptions() {
1407 mModeOptionsOverlay.setToggleClickable(false);
1410 public void setDisableAllUserInteractions(boolean disable) {
1412 disableModeOptions();
1413 setShutterButtonEnabled(false);
1414 setSwipeEnabled(false);
1415 mModeListView.hideAnimated();
1417 enableModeOptions();
1418 setShutterButtonEnabled(true);
1419 setSwipeEnabled(true);
1421 mDisableAllUserInteractions = disable;
1425 public void onModeButtonPressed(int modeIndex) {
1426 // TODO: Make CameraActivity listen to ModeListView's events.
1427 int pressedModuleId = mController.getModuleId(modeIndex);
1428 int currentModuleId = mController.getCurrentModuleIndex();
1429 if (pressedModuleId != currentModuleId) {
1430 hideCaptureIndicator();
1435 * Gets called when a mode is selected from {@link com.android.camera.ui.ModeListView}
1437 * @param modeIndex mode index of the selected mode
1440 public void onModeSelected(int modeIndex) {
1441 mHideCoverRunnable = new Runnable() {
1444 mModeListView.startModeSelectionAnimation();
1447 mShutterButton.setAlpha(ShutterButton.ALPHA_WHEN_ENABLED);
1448 mModeCoverState = COVER_SHOWN;
1450 int lastIndex = mController.getCurrentModuleIndex();
1451 // Actual mode teardown / new mode initialization happens here
1452 mController.onModeSelected(modeIndex);
1453 int currentIndex = mController.getCurrentModuleIndex();
1455 if (lastIndex == currentIndex) {
1459 updateModeSpecificUIColors();
1462 private void updateModeSpecificUIColors() {
1463 setBottomBarColorsForModeIndex(mController.getCurrentModuleIndex());
1467 public void onSettingsSelected() {
1468 mController.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL,
1469 Keys.KEY_SHOULD_SHOW_SETTINGS_BUTTON_CLING, false);
1470 mModeListView.setShouldShowSettingsCling(false);
1471 mController.onSettingsSelected();
1475 public int getCurrentModeIndex() {
1476 return mController.getCurrentModuleIndex();
1479 /********************** Capture animation **********************/
1480 /* TODO: This session is subject to UX changes. In addition to the generic
1481 flash animation and post capture animation, consider designating a parameter
1482 for specifying the type of animation, as well as an animation finished listener
1483 so that modules can have more knowledge of the status of the animation. */
1486 * Starts the capture indicator pop-out animation.
1488 * @param accessibilityString An accessibility String to be announced during the peek animation.
1490 public void startCaptureIndicatorRevealAnimation(String accessibilityString) {
1491 if (mFilmstripLayout.getVisibility() == View.VISIBLE) {
1494 mRoundedThumbnailView.startRevealThumbnailAnimation(accessibilityString);
1498 * Updates the thumbnail image in the capture indicator.
1500 * @param thumbnailBitmap The thumbnail image to be shown.
1502 public void updateCaptureIndicatorThumbnail(Bitmap thumbnailBitmap, int rotation) {
1503 mRoundedThumbnailView.setThumbnail(thumbnailBitmap);
1504 mRoundedThumbnailView.setRotation(rotation);
1508 * Hides the capture indicator.
1510 public void hideCaptureIndicator() {
1511 mRoundedThumbnailView.hideThumbnail();
1515 * Starts the flash animation.
1517 public void startFlashAnimation(boolean shortFlash) {
1518 mCaptureOverlay.startFlashAnimation(shortFlash);
1522 * Cancels the pre-capture animation.
1524 public void cancelPreCaptureAnimation() {
1525 mAnimationManager.cancelAnimations();
1529 * Cancels the post-capture animation.
1531 public void cancelPostCaptureAnimation() {
1532 mAnimationManager.cancelAnimations();
1535 public FilmstripContentPanel getFilmstripContentPanel() {
1536 return mFilmstripPanel;
1540 * @return The {@link com.android.camera.app.CameraAppUI.BottomPanel} on the
1541 * bottom of the filmstrip.
1543 public BottomPanel getFilmstripBottomControls() {
1544 return mFilmstripBottomControls;
1547 public void showBottomControls() {
1548 mFilmstripBottomControls.show();
1551 public void hideBottomControls() {
1552 mFilmstripBottomControls.hide();
1556 * @param listener The listener for bottom controls.
1558 public void setFilmstripBottomControlsListener(BottomPanel.Listener listener) {
1559 mFilmstripBottomControls.setListener(listener);
1562 /***************************SurfaceTexture Api and Listener*********************************/
1565 * Return the shared surface texture.
1567 public SurfaceTexture getSurfaceTexture() {
1572 * Return the shared {@link android.graphics.SurfaceTexture}'s width.
1574 public int getSurfaceWidth() {
1575 return mSurfaceWidth;
1579 * Return the shared {@link android.graphics.SurfaceTexture}'s height.
1581 public int getSurfaceHeight() {
1582 return mSurfaceHeight;
1586 public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
1588 mSurfaceWidth = width;
1589 mSurfaceHeight = height;
1590 Log.v(TAG, "SurfaceTexture is available");
1591 if (mPreviewStatusListener != null) {
1592 mPreviewStatusListener.onSurfaceTextureAvailable(surface, width, height);
1594 enableModeOptions();
1598 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
1600 mSurfaceWidth = width;
1601 mSurfaceHeight = height;
1602 if (mPreviewStatusListener != null) {
1603 mPreviewStatusListener.onSurfaceTextureSizeChanged(surface, width, height);
1608 public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
1610 Log.v(TAG, "SurfaceTexture is destroyed");
1611 if (mPreviewStatusListener != null) {
1612 return mPreviewStatusListener.onSurfaceTextureDestroyed(surface);
1618 public void onSurfaceTextureUpdated(SurfaceTexture surface) {
1620 if (mPreviewStatusListener != null) {
1621 mPreviewStatusListener.onSurfaceTextureUpdated(surface);
1623 if (mModeCoverState == COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE) {
1624 Log.v(TAG, "hiding cover via onSurfaceTextureUpdated");
1625 CameraPerformanceTracker.onEvent(CameraPerformanceTracker.FIRST_PREVIEW_FRAME);
1630 /****************************Grid lines api ******************************/
1633 * Show a set of evenly spaced lines over the preview. The number
1634 * of lines horizontally and vertically is determined by
1635 * {@link com.android.camera.ui.GridLines}.
1637 public void showGridLines() {
1638 if (mGridLines != null) {
1639 mGridLines.setVisibility(View.VISIBLE);
1644 * Hide the set of evenly spaced grid lines overlaying the preview.
1646 public void hideGridLines() {
1647 if (mGridLines != null) {
1648 mGridLines.setVisibility(View.INVISIBLE);
1653 * Return a callback which shows or hide the preview grid lines
1654 * depending on whether the grid lines setting is set on.
1656 public ButtonManager.ButtonCallback getGridLinesCallback() {
1657 return new ButtonManager.ButtonCallback() {
1659 public void onStateChanged(int state) {
1660 if (!mController.isPaused()) {
1661 if (Keys.areGridLinesOn(mController.getSettingsManager())) {
1671 /***************************Mode options api *****************************/
1674 * Set the mode options visible.
1676 public void showModeOptions() {
1677 /* Make mode options clickable. */
1678 enableModeOptions();
1679 mModeOptionsOverlay.setVisibility(View.VISIBLE);
1683 * Set the mode options invisible. This is necessary for modes
1684 * that don't show a bottom bar for the capture UI.
1686 public void hideModeOptions() {
1687 mModeOptionsOverlay.setVisibility(View.INVISIBLE);
1690 /****************************Bottom bar api ******************************/
1693 * Sets up the bottom bar and mode options with the correct
1694 * shutter button and visibility based on the current module.
1696 public void resetBottomControls(ModuleController module, int moduleIndex) {
1697 if (areBottomControlsUsed(module)) {
1698 setBottomBarShutterIcon(moduleIndex);
1699 mCaptureLayoutHelper.setShowBottomBar(true);
1701 mCaptureLayoutHelper.setShowBottomBar(false);
1706 * Show or hide the mode options and bottom bar, based on
1707 * whether the current module is using the bottom bar. Returns
1708 * whether the mode options and bottom bar are used.
1710 private boolean areBottomControlsUsed(ModuleController module) {
1711 if (module.isUsingBottomBar()) {
1723 * Set the bottom bar visible.
1725 public void showBottomBar() {
1726 mBottomBar.setVisibility(View.VISIBLE);
1730 * Set the bottom bar invisible.
1732 public void hideBottomBar() {
1733 mBottomBar.setVisibility(View.INVISIBLE);
1737 * Sets the color of the bottom bar.
1739 public void setBottomBarColor(int colorId) {
1740 mBottomBar.setBackgroundColor(colorId);
1744 * Sets the pressed color of the bottom bar for a camera mode index.
1746 public void setBottomBarColorsForModeIndex(int index) {
1747 mBottomBar.setColorsForModeIndex(index);
1751 * Sets the shutter button icon on the bottom bar, based on
1754 public void setBottomBarShutterIcon(int modeIndex) {
1755 int shutterIconId = CameraUtil.getCameraShutterIconId(modeIndex,
1756 mController.getAndroidContext());
1757 mBottomBar.setShutterButtonIcon(shutterIconId);
1760 public void animateBottomBarToVideoStop(int shutterIconId) {
1761 mBottomBar.animateToVideoStop(shutterIconId);
1764 public void animateBottomBarToFullSize(int shutterIconId) {
1765 mBottomBar.animateToFullSize(shutterIconId);
1768 public void setShutterButtonEnabled(final boolean enabled) {
1769 if (!mDisableAllUserInteractions) {
1770 mBottomBar.post(new Runnable() {
1773 mBottomBar.setShutterButtonEnabled(enabled);
1779 public void setShutterButtonImportantToA11y(boolean important) {
1780 mBottomBar.setShutterButtonImportantToA11y(important);
1783 public boolean isShutterButtonEnabled() {
1784 return mBottomBar.isShutterButtonEnabled();
1787 public void setIndicatorBottomBarWrapperVisible(boolean visible) {
1788 mStickyBottomCaptureLayout.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
1792 * Set the visibility of the bottom bar.
1794 // TODO: needed for when panorama is managed by the generic module ui.
1795 public void setBottomBarVisible(boolean visible) {
1796 mBottomBar.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
1800 * Add a {@link #ShutterButton.OnShutterButtonListener} to the shutter button.
1802 public void addShutterListener(ShutterButton.OnShutterButtonListener listener) {
1803 mShutterButton.addOnShutterButtonListener(listener);
1807 * Remove a {@link #ShutterButton.OnShutterButtonListener} from the shutter button.
1809 public void removeShutterListener(ShutterButton.OnShutterButtonListener listener) {
1810 mShutterButton.removeOnShutterButtonListener(listener);
1814 * Performs a transition to the capture layout of the bottom bar.
1816 public void transitionToCapture() {
1817 ModuleController moduleController = mController.getCurrentModuleController();
1818 applyModuleSpecs(moduleController.getHardwareSpec(),
1819 moduleController.getBottomBarSpec());
1820 mBottomBar.transitionToCapture();
1824 * Displays the Cancel button instead of the capture button.
1826 public void transitionToCancel() {
1827 ModuleController moduleController = mController.getCurrentModuleController();
1828 applyModuleSpecs(moduleController.getHardwareSpec(),
1829 moduleController.getBottomBarSpec());
1830 mBottomBar.transitionToCancel();
1834 * Performs a transition to the global intent layout.
1836 public void transitionToIntentCaptureLayout() {
1837 ModuleController moduleController = mController.getCurrentModuleController();
1838 applyModuleSpecs(moduleController.getHardwareSpec(),
1839 moduleController.getBottomBarSpec());
1840 mBottomBar.transitionToIntentCaptureLayout();
1844 * Performs a transition to the global intent review layout.
1846 public void transitionToIntentReviewLayout() {
1847 ModuleController moduleController = mController.getCurrentModuleController();
1848 applyModuleSpecs(moduleController.getHardwareSpec(),
1849 moduleController.getBottomBarSpec());
1850 mBottomBar.transitionToIntentReviewLayout();
1854 * @return whether UI is in intent review mode
1856 public boolean isInIntentReview() {
1857 return mBottomBar.isInIntentReview();
1861 public void onSettingChanged(SettingsManager settingsManager, String key) {
1862 // Update the mode options based on the hardware spec,
1863 // when hdr changes to prevent flash from getting out of sync.
1864 if (key.equals(Keys.KEY_CAMERA_HDR)) {
1865 ModuleController moduleController = mController.getCurrentModuleController();
1866 applyModuleSpecs(moduleController.getHardwareSpec(),
1867 moduleController.getBottomBarSpec());
1872 * Applies a {@link com.android.camera.CameraAppUI.BottomBarUISpec}
1873 * to the bottom bar mode options based on limitations from a
1874 * {@link com.android.camera.hardware.HardwareSpec}.
1876 * Options not supported by the hardware are either hidden
1877 * or disabled, depending on the option.
1879 * Otherwise, the option is fully enabled and clickable.
1881 public void applyModuleSpecs(final HardwareSpec hardwareSpec,
1882 final BottomBarUISpec bottomBarSpec) {
1883 if (hardwareSpec == null || bottomBarSpec == null) {
1887 ButtonManager buttonManager = mController.getButtonManager();
1888 SettingsManager settingsManager = mController.getSettingsManager();
1890 buttonManager.setToInitialState();
1892 /** Standard mode options */
1893 if (mController.getCameraProvider().getNumberOfCameras() > 1 &&
1894 hardwareSpec.isFrontCameraSupported()) {
1895 if (bottomBarSpec.enableCamera) {
1896 buttonManager.initializeButton(ButtonManager.BUTTON_CAMERA,
1897 bottomBarSpec.cameraCallback);
1899 buttonManager.disableButton(ButtonManager.BUTTON_CAMERA);
1902 // Hide camera icon if front camera not available.
1903 buttonManager.hideButton(ButtonManager.BUTTON_CAMERA);
1906 boolean flashBackCamera = mController.getSettingsManager().getBoolean(
1907 SettingsManager.SCOPE_GLOBAL, Keys.KEY_FLASH_SUPPORTED_BACK_CAMERA);
1908 if (bottomBarSpec.hideFlash || !flashBackCamera) {
1909 // Hide both flash and torch button in flash disable logic
1910 buttonManager.hideButton(ButtonManager.BUTTON_FLASH);
1911 buttonManager.hideButton(ButtonManager.BUTTON_TORCH);
1913 if (hardwareSpec.isFlashSupported()) {
1914 if (bottomBarSpec.enableFlash) {
1915 buttonManager.initializeButton(ButtonManager.BUTTON_FLASH,
1916 bottomBarSpec.flashCallback);
1917 } else if (bottomBarSpec.enableTorchFlash) {
1918 buttonManager.initializeButton(ButtonManager.BUTTON_TORCH,
1919 bottomBarSpec.flashCallback);
1920 } else if (bottomBarSpec.enableHdrPlusFlash) {
1921 buttonManager.initializeButton(ButtonManager.BUTTON_HDR_PLUS_FLASH,
1922 bottomBarSpec.flashCallback);
1924 // Hide both flash and torch button in flash disable logic
1925 buttonManager.disableButton(ButtonManager.BUTTON_FLASH);
1926 buttonManager.disableButton(ButtonManager.BUTTON_TORCH);
1929 // Disable both flash and torch icon if not supported
1930 // by the chosen camera hardware.
1931 buttonManager.disableButton(ButtonManager.BUTTON_FLASH);
1932 buttonManager.disableButton(ButtonManager.BUTTON_TORCH);
1936 if (bottomBarSpec.hideHdr || mIsCaptureIntent) {
1937 // Force hide hdr or hdr plus icon.
1938 buttonManager.hideButton(ButtonManager.BUTTON_HDR_PLUS);
1940 if (hardwareSpec.isHdrPlusSupported()) {
1941 if (bottomBarSpec.enableHdr && Keys.isCameraBackFacing(settingsManager,
1942 mController.getModuleScope())) {
1943 buttonManager.initializeButton(ButtonManager.BUTTON_HDR_PLUS,
1944 bottomBarSpec.hdrCallback);
1946 buttonManager.disableButton(ButtonManager.BUTTON_HDR_PLUS);
1948 } else if (hardwareSpec.isHdrSupported()) {
1949 if (bottomBarSpec.enableHdr && Keys.isCameraBackFacing(settingsManager,
1950 mController.getModuleScope())) {
1951 buttonManager.initializeButton(ButtonManager.BUTTON_HDR,
1952 bottomBarSpec.hdrCallback);
1954 buttonManager.disableButton(ButtonManager.BUTTON_HDR);
1957 // Hide hdr plus or hdr icon if neither are supported.
1958 buttonManager.hideButton(ButtonManager.BUTTON_HDR_PLUS);
1962 if (bottomBarSpec.hideGridLines) {
1963 // Force hide grid lines icon.
1964 buttonManager.hideButton(ButtonManager.BUTTON_GRID_LINES);
1967 if (bottomBarSpec.enableGridLines) {
1968 buttonManager.initializeButton(ButtonManager.BUTTON_GRID_LINES,
1969 bottomBarSpec.gridLinesCallback != null ?
1970 bottomBarSpec.gridLinesCallback : getGridLinesCallback()
1973 buttonManager.disableButton(ButtonManager.BUTTON_GRID_LINES);
1978 if (bottomBarSpec.enableSelfTimer) {
1979 buttonManager.initializeButton(ButtonManager.BUTTON_COUNTDOWN, null);
1981 if (bottomBarSpec.showSelfTimer) {
1982 buttonManager.disableButton(ButtonManager.BUTTON_COUNTDOWN);
1984 buttonManager.hideButton(ButtonManager.BUTTON_COUNTDOWN);
1988 if (bottomBarSpec.enablePanoOrientation
1989 && PhotoSphereHelper.getPanoramaOrientationOptionArrayId() > 0) {
1990 buttonManager.initializePanoOrientationButtons(bottomBarSpec.panoOrientationCallback);
1993 boolean enableExposureCompensation = bottomBarSpec.enableExposureCompensation &&
1994 !(bottomBarSpec.minExposureCompensation == 0 && bottomBarSpec.maxExposureCompensation == 0) &&
1995 mController.getSettingsManager().getBoolean(SettingsManager.SCOPE_GLOBAL,
1996 Keys.KEY_EXPOSURE_COMPENSATION_ENABLED);
1997 if (enableExposureCompensation) {
1998 buttonManager.initializePushButton(ButtonManager.BUTTON_EXPOSURE_COMPENSATION, null);
1999 buttonManager.setExposureCompensationParameters(
2000 bottomBarSpec.minExposureCompensation,
2001 bottomBarSpec.maxExposureCompensation,
2002 bottomBarSpec.exposureCompensationStep);
2004 buttonManager.setExposureCompensationCallback(
2005 bottomBarSpec.exposureCompensationSetCallback);
2006 buttonManager.updateExposureButtons();
2008 buttonManager.hideButton(ButtonManager.BUTTON_EXPOSURE_COMPENSATION);
2009 buttonManager.setExposureCompensationCallback(null);
2013 if (bottomBarSpec.showCancel) {
2014 buttonManager.initializePushButton(ButtonManager.BUTTON_CANCEL,
2015 bottomBarSpec.cancelCallback);
2017 if (bottomBarSpec.showDone) {
2018 buttonManager.initializePushButton(ButtonManager.BUTTON_DONE,
2019 bottomBarSpec.doneCallback);
2021 if (bottomBarSpec.showRetake) {
2022 buttonManager.initializePushButton(ButtonManager.BUTTON_RETAKE,
2023 bottomBarSpec.retakeCallback);
2025 if (bottomBarSpec.showReview) {
2026 buttonManager.initializePushButton(ButtonManager.BUTTON_REVIEW,
2027 bottomBarSpec.reviewCallback,
2028 R.drawable.ic_play);
2033 * Shows the given tutorial on the screen.
2035 public void showTutorial(AbstractTutorialOverlay tutorial, LayoutInflater inflater) {
2036 tutorial.show(mTutorialsPlaceHolderWrapper, inflater);
2039 /***************************Filmstrip api *****************************/
2041 public void showFilmstrip() {
2042 mModeListView.onBackPressed();
2043 mFilmstripLayout.showFilmstrip();
2046 public void hideFilmstrip() {
2047 mFilmstripLayout.hideFilmstrip();