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.res.Resources;
21 import android.graphics.Bitmap;
22 import android.graphics.Canvas;
23 import android.graphics.Matrix;
24 import android.graphics.RectF;
25 import android.graphics.SurfaceTexture;
26 import android.hardware.display.DisplayManager;
27 import android.util.CameraPerformanceTracker;
28 import android.view.GestureDetector;
29 import android.view.LayoutInflater;
30 import android.view.MotionEvent;
31 import android.view.TextureView;
32 import android.view.View;
33 import android.view.ViewConfiguration;
34 import android.view.ViewGroup;
35 import android.view.accessibility.AccessibilityManager;
36 import android.widget.FrameLayout;
38 import com.android.camera.AnimationManager;
39 import com.android.camera.ButtonManager;
40 import com.android.camera.CaptureLayoutHelper;
41 import com.android.camera.ShutterButton;
42 import com.android.camera.TextureViewHelper;
43 import com.android.camera.debug.Log;
44 import com.android.camera.filmstrip.FilmstripContentPanel;
45 import com.android.camera.hardware.HardwareSpec;
46 import com.android.camera.module.ModuleController;
47 import com.android.camera.settings.Keys;
48 import com.android.camera.settings.SettingsManager;
49 import com.android.camera.ui.AbstractTutorialOverlay;
50 import com.android.camera.ui.BottomBar;
51 import com.android.camera.ui.CaptureAnimationOverlay;
52 import com.android.camera.ui.GridLines;
53 import com.android.camera.ui.MainActivityLayout;
54 import com.android.camera.ui.ModeListView;
55 import com.android.camera.ui.ModeTransitionView;
56 import com.android.camera.ui.PreviewOverlay;
57 import com.android.camera.ui.PreviewStatusListener;
58 import com.android.camera.ui.StickyBottomCaptureLayout;
59 import com.android.camera.ui.TouchCoordinate;
60 import com.android.camera.ui.focus.FocusRing;
61 import com.android.camera.util.AndroidServices;
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 = AndroidServices.instance().provideLayoutInflater();
849 Cling refocusCling = (Cling) inflater.inflate(R.layout.cling_widget, null, false);
850 // Sets instruction text in the cling.
851 refocusCling.setText(mController.getAndroidContext().getResources()
852 .getString(R.string.cling_text_for_refocus_editor_button));
854 // Adds cling into view hierarchy.
855 int clingWidth = mController.getAndroidContext()
856 .getResources().getDimensionPixelSize(R.dimen.default_cling_width);
857 filmstripContent.addView(refocusCling, clingWidth,
858 ViewGroup.LayoutParams.WRAP_CONTENT);
859 mFilmstripBottomControls.setClingForViewer(viewerType, refocusCling);
865 * Clears the listeners for the cling and remove it from the view hierarchy.
867 * @param viewerType defines which viewer the cling is for.
869 public void clearClingForViewer(int viewerType) {
870 Cling clingToBeRemoved = mFilmstripBottomControls.getClingForViewer(viewerType);
871 if (clingToBeRemoved == null) {
872 // No cling is created for the specific viewer type.
875 mFilmstripBottomControls.clearClingForViewer(viewerType);
876 clingToBeRemoved.setVisibility(View.GONE);
877 mAppRootView.removeView(clingToBeRemoved);
881 * Enable or disable swipe gestures. We want to disable them e.g. while we
884 public void setSwipeEnabled(boolean enabled) {
885 mSwipeEnabled = enabled;
886 // TODO: This can be removed once we come up with a new design for handling swipe
887 // on shutter button and mode options. (More details: b/13751653)
888 mAppRootView.setSwipeEnabled(enabled);
891 public void onDestroy() {
892 AndroidServices.instance().provideDisplayManager()
893 .unregisterDisplayListener(mDisplayListener);
897 * Initializes the display listener to listen to display changes such as
898 * 180-degree rotation change, which will not have an onConfigurationChanged
901 private void initDisplayListener() {
902 if (ApiHelper.HAS_DISPLAY_LISTENER) {
903 mLastRotation = CameraUtil.getDisplayRotation();
905 mDisplayListener = new DisplayManager.DisplayListener() {
907 public void onDisplayAdded(int arg0) {
912 public void onDisplayChanged(int displayId) {
913 int rotation = CameraUtil.getDisplayRotation(
915 if ((rotation - mLastRotation + 360) % 360 == 180
916 && mPreviewStatusListener != null) {
917 mPreviewStatusListener.onPreviewFlipped();
918 mStickyBottomCaptureLayout.requestLayout();
919 mModeListView.requestLayout();
920 mTextureView.requestLayout();
922 mLastRotation = rotation;
926 public void onDisplayRemoved(int arg0) {
931 AndroidServices.instance().provideDisplayManager()
932 .registerDisplayListener(mDisplayListener, null);
937 * Redirects touch events to appropriate recipient views based on swipe direction.
938 * More specifically, swipe up and swipe down will be handled by the view that handles
939 * mode transition; swipe left will be send to filmstrip; swipe right will be redirected
940 * to mode list in order to bring up mode list.
942 private void onSwipeDetected(int swipeState) {
943 if (swipeState == SWIPE_UP || swipeState == SWIPE_DOWN) {
944 // TODO: Polish quick switch after this release.
945 // Quick switch between modes.
946 int currentModuleIndex = mController.getCurrentModuleIndex();
947 final int moduleToTransitionTo =
948 mController.getQuickSwitchToModuleId(currentModuleIndex);
949 if (currentModuleIndex != moduleToTransitionTo) {
950 mAppRootView.redirectTouchEventsTo(mModeTransitionView);
951 int shadeColorId = R.color.mode_cover_default_color;
952 int iconRes = CameraUtil.getCameraModeCoverIconResId(moduleToTransitionTo,
953 mController.getAndroidContext());
955 AnimationFinishedListener listener = new AnimationFinishedListener() {
957 public void onAnimationFinished(boolean success) {
959 mHideCoverRunnable = new Runnable() {
962 mModeTransitionView.startPeepHoleAnimation();
965 mModeCoverState = COVER_SHOWN;
966 // Go to new module when the previous operation is successful.
967 mController.onModeSelected(moduleToTransitionTo);
972 } else if (swipeState == SWIPE_LEFT) {
973 // Pass the touch sequence to filmstrip layout.
974 mAppRootView.redirectTouchEventsTo(mFilmstripLayout);
975 } else if (swipeState == SWIPE_RIGHT) {
976 // Pass the touch to mode switcher
977 mAppRootView.redirectTouchEventsTo(mModeListView);
982 * Gets called when activity resumes in preview.
984 public void resume() {
985 // Show mode theme cover until preview is ready
986 showModeCoverUntilPreviewReady();
988 // Hide action bar first since we are in full screen mode first, and
989 // switch the system UI to lights-out mode.
990 mFilmstripPanel.hide();
992 // Show UI that is meant to only be used when spoken feedback is
994 mAccessibilityEnabled = isSpokenFeedbackAccessibilityEnabled();
995 mAccessibilityAffordances.setVisibility(mAccessibilityEnabled ? View.VISIBLE : View.GONE);
999 * @return Whether any spoken feedback accessibility feature is currently
1002 private boolean isSpokenFeedbackAccessibilityEnabled() {
1003 AccessibilityManager accessibilityManager = AndroidServices.instance()
1004 .provideAccessibilityManager();
1005 List<AccessibilityServiceInfo> infos = accessibilityManager
1006 .getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_SPOKEN);
1007 return infos != null && !infos.isEmpty();
1011 * Opens the mode list (e.g. because of the menu button being pressed) and
1012 * adapts the rest of the UI.
1014 public void openModeList() {
1015 mModeOptionsOverlay.closeModeOptions();
1016 mModeListView.onMenuPressed();
1020 * A cover view showing the mode theme color and mode icon will be visible on
1021 * top of preview until preview is ready (i.e. camera preview is started and
1022 * the first frame has been received).
1024 private void showModeCoverUntilPreviewReady() {
1025 int modeId = mController.getCurrentModuleIndex();
1026 int colorId = R.color.mode_cover_default_color;;
1027 int iconId = CameraUtil.getCameraModeCoverIconResId(modeId, mController.getAndroidContext());
1028 mModeTransitionView.setupModeCover(colorId, iconId);
1029 mHideCoverRunnable = new Runnable() {
1032 mModeTransitionView.hideModeCover(null);
1033 if (!mDisableAllUserInteractions) {
1034 showShimmyDelayed();
1038 mModeCoverState = COVER_SHOWN;
1041 private void showShimmyDelayed() {
1042 if (!mIsCaptureIntent) {
1043 // Show shimmy in SHIMMY_DELAY_MS
1044 mModeListView.showModeSwitcherHint();
1048 private void hideModeCover() {
1049 if (mHideCoverRunnable != null) {
1050 mAppRootView.post(mHideCoverRunnable);
1051 mHideCoverRunnable = null;
1053 mModeCoverState = COVER_HIDDEN;
1054 if (mCoverHiddenTime < 0) {
1055 mCoverHiddenTime = System.currentTimeMillis();
1060 public void onPreviewVisiblityChanged(int visibility) {
1061 if (visibility == ModuleController.VISIBILITY_HIDDEN) {
1062 setIndicatorBottomBarWrapperVisible(false);
1063 mAccessibilityAffordances.setVisibility(View.GONE);
1065 setIndicatorBottomBarWrapperVisible(true);
1066 if (mAccessibilityEnabled) {
1067 mAccessibilityAffordances.setVisibility(View.VISIBLE);
1069 mAccessibilityAffordances.setVisibility(View.GONE);
1075 * Call to stop the preview from being rendered.
1077 public void pausePreviewRendering() {
1078 mTextureView.setVisibility(View.INVISIBLE);
1082 * Call to begin rendering the preview again.
1084 public void resumePreviewRendering() {
1085 mTextureView.setVisibility(View.VISIBLE);
1089 * Returns the transform associated with the preview view.
1091 * @param m the Matrix in which to copy the current transform.
1092 * @return The specified matrix if not null or a new Matrix instance
1095 public Matrix getPreviewTransform(Matrix m) {
1096 return mTextureView.getTransform(m);
1100 public void onOpenFullScreen() {
1105 public void onModeListOpenProgress(float progress) {
1106 progress = 1 - progress;
1107 float interpolatedProgress = Gusterpolator.INSTANCE.getInterpolation(progress);
1108 mModeOptionsToggle.setAlpha(interpolatedProgress);
1109 // Change shutter button alpha linearly based on the mode list open progress:
1110 // set the alpha to disabled alpha when list is fully open, to enabled alpha
1111 // when the list is fully closed.
1112 mShutterButton.setAlpha(progress * ShutterButton.ALPHA_WHEN_ENABLED
1113 + (1 - progress) * ShutterButton.ALPHA_WHEN_DISABLED);
1117 public void onModeListClosed() {
1118 // Make sure the alpha on mode options ellipse is reset when mode drawer
1120 mModeOptionsToggle.setAlpha(1f);
1121 mShutterButton.setAlpha(ShutterButton.ALPHA_WHEN_ENABLED);
1125 * Called when the back key is pressed.
1127 * @return Whether the UI responded to the key event.
1129 public boolean onBackPressed() {
1130 if (mFilmstripLayout.getVisibility() == View.VISIBLE) {
1131 return mFilmstripLayout.onBackPressed();
1133 return mModeListView.onBackPressed();
1138 * Sets a {@link com.android.camera.ui.PreviewStatusListener} that
1139 * listens to SurfaceTexture changes. In addition, listeners are set on
1140 * dependent app ui elements.
1142 * @param previewStatusListener the listener that gets notified when SurfaceTexture
1145 public void setPreviewStatusListener(PreviewStatusListener previewStatusListener) {
1146 mPreviewStatusListener = previewStatusListener;
1147 if (mPreviewStatusListener != null) {
1148 onPreviewListenerChanged();
1153 * When the PreviewStatusListener changes, listeners need to be
1154 * set on the following app ui elements:
1155 * {@link com.android.camera.ui.PreviewOverlay},
1156 * {@link com.android.camera.ui.BottomBar},
1157 * {@link com.android.camera.ui.IndicatorIconController}.
1159 private void onPreviewListenerChanged() {
1160 // Set a listener for recognizing preview gestures.
1161 GestureDetector.OnGestureListener gestureListener
1162 = mPreviewStatusListener.getGestureListener();
1163 if (gestureListener != null) {
1164 mPreviewOverlay.setGestureListener(gestureListener);
1166 View.OnTouchListener touchListener = mPreviewStatusListener.getTouchListener();
1167 if (touchListener != null) {
1168 mPreviewOverlay.setTouchListener(touchListener);
1171 mTextureViewHelper.setAutoAdjustTransform(
1172 mPreviewStatusListener.shouldAutoAdjustTransformMatrixOnLayout());
1176 * This method should be called in onCameraOpened. It defines CameraAppUI
1177 * specific changes that depend on the camera or camera settings.
1179 public void onChangeCamera() {
1180 ModuleController moduleController = mController.getCurrentModuleController();
1181 applyModuleSpecs(moduleController.getHardwareSpec(), moduleController.getBottomBarSpec());
1183 if (mIndicatorIconController != null) {
1184 // Sync the settings state with the indicator state.
1185 mIndicatorIconController.syncIndicators();
1190 * Adds a listener to receive callbacks when preview area changes.
1192 public void addPreviewAreaChangedListener(
1193 PreviewStatusListener.PreviewAreaChangedListener listener) {
1194 mTextureViewHelper.addPreviewAreaSizeChangedListener(listener);
1198 * Removes a listener that receives callbacks when preview area changes.
1200 public void removePreviewAreaChangedListener(
1201 PreviewStatusListener.PreviewAreaChangedListener listener) {
1202 mTextureViewHelper.removePreviewAreaSizeChangedListener(listener);
1206 * This inflates generic_module layout, which contains all the shared views across
1207 * modules. Then each module inflates their own views in the given view group. For
1208 * now, this is called every time switching from a not-yet-refactored module to a
1209 * refactored module. In the future, this should only need to be done once per app
1212 public void prepareModuleUI() {
1213 mController.getSettingsManager().addListener(this);
1214 mModuleUI = (FrameLayout) mCameraRootView.findViewById(R.id.module_layout);
1215 mTextureView = (TextureView) mCameraRootView.findViewById(R.id.preview_content);
1216 mTextureViewHelper = new TextureViewHelper(mTextureView, mCaptureLayoutHelper,
1217 mController.getCameraProvider());
1218 mTextureViewHelper.setSurfaceTextureListener(this);
1219 mTextureViewHelper.setOnLayoutChangeListener(mPreviewLayoutChangeListener);
1221 mBottomBar = (BottomBar) mCameraRootView.findViewById(R.id.bottom_bar);
1222 int unpressedColor = mController.getAndroidContext().getResources()
1223 .getColor(R.color.bottombar_unpressed);
1224 setBottomBarColor(unpressedColor);
1225 updateModeSpecificUIColors();
1227 mBottomBar.setCaptureLayoutHelper(mCaptureLayoutHelper);
1230 = (ModeOptionsOverlay) mCameraRootView.findViewById(R.id.mode_options_overlay);
1232 // Sets the visibility of the bottom bar and the mode options.
1233 resetBottomControls(mController.getCurrentModuleController(),
1234 mController.getCurrentModuleIndex());
1235 mModeOptionsOverlay.setCaptureLayoutHelper(mCaptureLayoutHelper);
1237 mShutterButton = (ShutterButton) mCameraRootView.findViewById(R.id.shutter_button);
1238 addShutterListener(mController.getCurrentModuleController());
1239 addShutterListener(mModeOptionsOverlay);
1240 addShutterListener(this);
1242 mGridLines = (GridLines) mCameraRootView.findViewById(R.id.grid_lines);
1243 mTextureViewHelper.addPreviewAreaSizeChangedListener(mGridLines);
1245 mPreviewOverlay = (PreviewOverlay) mCameraRootView.findViewById(R.id.preview_overlay);
1246 mPreviewOverlay.setOnTouchListener(new MyTouchListener());
1247 mPreviewOverlay.setOnPreviewTouchedListener(mModeOptionsOverlay);
1249 mCaptureOverlay = (CaptureAnimationOverlay)
1250 mCameraRootView.findViewById(R.id.capture_overlay);
1251 mTextureViewHelper.addPreviewAreaSizeChangedListener(mPreviewOverlay);
1252 mTextureViewHelper.addPreviewAreaSizeChangedListener(mCaptureOverlay);
1254 if (mIndicatorIconController == null) {
1255 mIndicatorIconController =
1256 new IndicatorIconController(mController, mAppRootView);
1259 mController.getButtonManager().load(mCameraRootView);
1260 mController.getButtonManager().setListener(mIndicatorIconController);
1261 mController.getSettingsManager().addListener(mIndicatorIconController);
1263 mModeOptionsToggle = mCameraRootView.findViewById(R.id.mode_options_toggle);
1264 mFocusRing = (FocusRing) mCameraRootView.findViewById(R.id.focus_ring);
1265 mTutorialsPlaceHolderWrapper = (FrameLayout) mCameraRootView
1266 .findViewById(R.id.tutorials_placeholder_wrapper);
1267 mStickyBottomCaptureLayout = (StickyBottomCaptureLayout) mAppRootView
1268 .findViewById(R.id.sticky_bottom_capture_layout);
1269 mStickyBottomCaptureLayout.setCaptureLayoutHelper(mCaptureLayoutHelper);
1271 mTextureViewHelper.addPreviewAreaSizeChangedListener(mModeListView);
1272 mTextureViewHelper.addAspectRatioChangedListener(
1273 new PreviewStatusListener.PreviewAspectRatioChangedListener() {
1275 public void onPreviewAspectRatioChanged(float aspectRatio) {
1276 mModeOptionsOverlay.requestLayout();
1277 mBottomBar.requestLayout();
1284 * Called indirectly from each module in their initialization to get a view group
1285 * to inflate the module specific views in.
1287 * @return a view group for modules to attach views to
1289 public FrameLayout getModuleRootView() {
1290 // TODO: Change it to mModuleUI when refactor is done
1291 return mCameraRootView;
1295 * Remove all the module specific views.
1297 public void clearModuleUI() {
1298 if (mModuleUI != null) {
1299 mModuleUI.removeAllViews();
1301 removeShutterListener(mController.getCurrentModuleController());
1302 mTutorialsPlaceHolderWrapper.removeAllViews();
1303 mTutorialsPlaceHolderWrapper.setVisibility(View.GONE);
1305 setShutterButtonEnabled(true);
1306 mPreviewStatusListener = null;
1307 mPreviewOverlay.reset();
1309 Log.v(TAG, "mFocusRing.stopFocusAnimations()");
1310 mFocusRing.stopFocusAnimations();
1314 * Gets called when preview is ready to start. It sets up one shot preview callback
1315 * in order to receive a callback when the preview frame is available, so that
1316 * the preview cover can be hidden to reveal preview.
1318 * An alternative for getting the timing to hide preview cover is through
1319 * {@link CameraAppUI#onSurfaceTextureUpdated(android.graphics.SurfaceTexture)},
1320 * which is less accurate but therefore is the fallback for modules that manage
1321 * their own preview callbacks (as setting one preview callback will override
1322 * any other installed preview callbacks), or use camera2 API.
1324 public void onPreviewReadyToStart() {
1325 if (mModeCoverState == COVER_SHOWN) {
1326 mModeCoverState = COVER_WILL_HIDE_AT_NEXT_FRAME;
1327 mController.setupOneShotPreviewListener();
1332 * Gets called when preview is started.
1334 public void onPreviewStarted() {
1335 Log.v(TAG, "onPreviewStarted");
1336 if (mModeCoverState == COVER_SHOWN) {
1337 mModeCoverState = COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE;
1339 enableModeOptions();
1343 * Gets notified when next preview frame comes in.
1345 public void onNewPreviewFrame() {
1346 Log.v(TAG, "onNewPreviewFrame");
1347 CameraPerformanceTracker.onEvent(CameraPerformanceTracker.FIRST_PREVIEW_FRAME);
1352 public void onShutterButtonClick() {
1354 * Set the mode options toggle unclickable, generally
1355 * throughout the app, whenever the shutter button is clicked.
1357 * This could be done in the OnShutterButtonListener of the
1358 * ModeOptionsOverlay, but since it is very important that we
1359 * can clearly see when the toggle becomes clickable again,
1360 * keep all of that logic at this level.
1362 // disableModeOptions();
1366 public void onShutterCoordinate(TouchCoordinate coord) {
1371 public void onShutterButtonFocus(boolean pressed) {
1376 public void onShutterButtonLongPressed() {
1381 * Set the mode options toggle clickable.
1383 public void enableModeOptions() {
1385 * For modules using camera 1 api, this gets called in
1386 * onSurfaceTextureUpdated whenever the preview gets stopped and
1387 * started after each capture. This also takes care of the
1388 * case where the mode options might be unclickable when we
1391 * For modules using camera 2 api, they're required to call this
1392 * method when a capture is "completed". Unfortunately this differs
1393 * per module implementation.
1395 if (!mDisableAllUserInteractions) {
1396 mModeOptionsOverlay.setToggleClickable(true);
1401 * Set the mode options toggle not clickable.
1403 public void disableModeOptions() {
1404 mModeOptionsOverlay.setToggleClickable(false);
1407 public void setDisableAllUserInteractions(boolean disable) {
1409 disableModeOptions();
1410 setShutterButtonEnabled(false);
1411 setSwipeEnabled(false);
1412 mModeListView.hideAnimated();
1414 enableModeOptions();
1415 setShutterButtonEnabled(true);
1416 setSwipeEnabled(true);
1418 mDisableAllUserInteractions = disable;
1422 public void onModeButtonPressed(int modeIndex) {
1423 // TODO: Make CameraActivity listen to ModeListView's events.
1424 int pressedModuleId = mController.getModuleId(modeIndex);
1425 int currentModuleId = mController.getCurrentModuleIndex();
1426 if (pressedModuleId != currentModuleId) {
1427 hideCaptureIndicator();
1432 * Gets called when a mode is selected from {@link com.android.camera.ui.ModeListView}
1434 * @param modeIndex mode index of the selected mode
1437 public void onModeSelected(int modeIndex) {
1438 mHideCoverRunnable = new Runnable() {
1441 mModeListView.startModeSelectionAnimation();
1444 mShutterButton.setAlpha(ShutterButton.ALPHA_WHEN_ENABLED);
1445 mModeCoverState = COVER_SHOWN;
1447 int lastIndex = mController.getCurrentModuleIndex();
1448 // Actual mode teardown / new mode initialization happens here
1449 mController.onModeSelected(modeIndex);
1450 int currentIndex = mController.getCurrentModuleIndex();
1452 if (lastIndex == currentIndex) {
1456 updateModeSpecificUIColors();
1459 private void updateModeSpecificUIColors() {
1460 setBottomBarColorsForModeIndex(mController.getCurrentModuleIndex());
1464 public void onSettingsSelected() {
1465 mController.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL,
1466 Keys.KEY_SHOULD_SHOW_SETTINGS_BUTTON_CLING, false);
1467 mModeListView.setShouldShowSettingsCling(false);
1468 mController.onSettingsSelected();
1472 public int getCurrentModeIndex() {
1473 return mController.getCurrentModuleIndex();
1476 /********************** Capture animation **********************/
1477 /* TODO: This session is subject to UX changes. In addition to the generic
1478 flash animation and post capture animation, consider designating a parameter
1479 for specifying the type of animation, as well as an animation finished listener
1480 so that modules can have more knowledge of the status of the animation. */
1483 * Starts the capture indicator pop-out animation.
1485 * @param accessibilityString An accessibility String to be announced during the peek animation.
1487 public void startCaptureIndicatorRevealAnimation(String accessibilityString) {
1488 if (mFilmstripLayout.getVisibility() == View.VISIBLE) {
1491 mRoundedThumbnailView.startRevealThumbnailAnimation(accessibilityString);
1495 * Updates the thumbnail image in the capture indicator.
1497 * @param thumbnailBitmap The thumbnail image to be shown.
1499 public void updateCaptureIndicatorThumbnail(Bitmap thumbnailBitmap, int rotation) {
1500 mRoundedThumbnailView.setThumbnail(thumbnailBitmap);
1501 mRoundedThumbnailView.setRotation(rotation);
1505 * Hides the capture indicator.
1507 public void hideCaptureIndicator() {
1508 mRoundedThumbnailView.hideThumbnail();
1512 * Starts the flash animation.
1514 public void startFlashAnimation(boolean shortFlash) {
1515 mCaptureOverlay.startFlashAnimation(shortFlash);
1519 * Cancels the pre-capture animation.
1521 public void cancelPreCaptureAnimation() {
1522 mAnimationManager.cancelAnimations();
1526 * Cancels the post-capture animation.
1528 public void cancelPostCaptureAnimation() {
1529 mAnimationManager.cancelAnimations();
1532 public FilmstripContentPanel getFilmstripContentPanel() {
1533 return mFilmstripPanel;
1537 * @return The {@link com.android.camera.app.CameraAppUI.BottomPanel} on the
1538 * bottom of the filmstrip.
1540 public BottomPanel getFilmstripBottomControls() {
1541 return mFilmstripBottomControls;
1544 public void showBottomControls() {
1545 mFilmstripBottomControls.show();
1548 public void hideBottomControls() {
1549 mFilmstripBottomControls.hide();
1553 * @param listener The listener for bottom controls.
1555 public void setFilmstripBottomControlsListener(BottomPanel.Listener listener) {
1556 mFilmstripBottomControls.setListener(listener);
1559 /***************************SurfaceTexture Api and Listener*********************************/
1562 * Return the shared surface texture.
1564 public SurfaceTexture getSurfaceTexture() {
1569 * Return the shared {@link android.graphics.SurfaceTexture}'s width.
1571 public int getSurfaceWidth() {
1572 return mSurfaceWidth;
1576 * Return the shared {@link android.graphics.SurfaceTexture}'s height.
1578 public int getSurfaceHeight() {
1579 return mSurfaceHeight;
1583 public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
1585 mSurfaceWidth = width;
1586 mSurfaceHeight = height;
1587 Log.v(TAG, "SurfaceTexture is available");
1588 if (mPreviewStatusListener != null) {
1589 mPreviewStatusListener.onSurfaceTextureAvailable(surface, width, height);
1591 enableModeOptions();
1595 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
1597 mSurfaceWidth = width;
1598 mSurfaceHeight = height;
1599 if (mPreviewStatusListener != null) {
1600 mPreviewStatusListener.onSurfaceTextureSizeChanged(surface, width, height);
1605 public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
1607 Log.v(TAG, "SurfaceTexture is destroyed");
1608 if (mPreviewStatusListener != null) {
1609 return mPreviewStatusListener.onSurfaceTextureDestroyed(surface);
1615 public void onSurfaceTextureUpdated(SurfaceTexture surface) {
1617 if (mPreviewStatusListener != null) {
1618 mPreviewStatusListener.onSurfaceTextureUpdated(surface);
1620 if (mModeCoverState == COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE) {
1621 Log.v(TAG, "hiding cover via onSurfaceTextureUpdated");
1622 CameraPerformanceTracker.onEvent(CameraPerformanceTracker.FIRST_PREVIEW_FRAME);
1627 /****************************Grid lines api ******************************/
1630 * Show a set of evenly spaced lines over the preview. The number
1631 * of lines horizontally and vertically is determined by
1632 * {@link com.android.camera.ui.GridLines}.
1634 public void showGridLines() {
1635 if (mGridLines != null) {
1636 mGridLines.setVisibility(View.VISIBLE);
1641 * Hide the set of evenly spaced grid lines overlaying the preview.
1643 public void hideGridLines() {
1644 if (mGridLines != null) {
1645 mGridLines.setVisibility(View.INVISIBLE);
1650 * Return a callback which shows or hide the preview grid lines
1651 * depending on whether the grid lines setting is set on.
1653 public ButtonManager.ButtonCallback getGridLinesCallback() {
1654 return new ButtonManager.ButtonCallback() {
1656 public void onStateChanged(int state) {
1657 if (!mController.isPaused()) {
1658 if (Keys.areGridLinesOn(mController.getSettingsManager())) {
1668 /***************************Mode options api *****************************/
1671 * Set the mode options visible.
1673 public void showModeOptions() {
1674 /* Make mode options clickable. */
1675 enableModeOptions();
1676 mModeOptionsOverlay.setVisibility(View.VISIBLE);
1680 * Set the mode options invisible. This is necessary for modes
1681 * that don't show a bottom bar for the capture UI.
1683 public void hideModeOptions() {
1684 mModeOptionsOverlay.setVisibility(View.INVISIBLE);
1687 /****************************Bottom bar api ******************************/
1690 * Sets up the bottom bar and mode options with the correct
1691 * shutter button and visibility based on the current module.
1693 public void resetBottomControls(ModuleController module, int moduleIndex) {
1694 if (areBottomControlsUsed(module)) {
1695 setBottomBarShutterIcon(moduleIndex);
1696 mCaptureLayoutHelper.setShowBottomBar(true);
1698 mCaptureLayoutHelper.setShowBottomBar(false);
1703 * Show or hide the mode options and bottom bar, based on
1704 * whether the current module is using the bottom bar. Returns
1705 * whether the mode options and bottom bar are used.
1707 private boolean areBottomControlsUsed(ModuleController module) {
1708 if (module.isUsingBottomBar()) {
1720 * Set the bottom bar visible.
1722 public void showBottomBar() {
1723 mBottomBar.setVisibility(View.VISIBLE);
1727 * Set the bottom bar invisible.
1729 public void hideBottomBar() {
1730 mBottomBar.setVisibility(View.INVISIBLE);
1734 * Sets the color of the bottom bar.
1736 public void setBottomBarColor(int colorId) {
1737 mBottomBar.setBackgroundColor(colorId);
1741 * Sets the pressed color of the bottom bar for a camera mode index.
1743 public void setBottomBarColorsForModeIndex(int index) {
1744 mBottomBar.setColorsForModeIndex(index);
1748 * Sets the shutter button icon on the bottom bar, based on
1751 public void setBottomBarShutterIcon(int modeIndex) {
1752 int shutterIconId = CameraUtil.getCameraShutterIconId(modeIndex,
1753 mController.getAndroidContext());
1754 mBottomBar.setShutterButtonIcon(shutterIconId);
1757 public void animateBottomBarToVideoStop(int shutterIconId) {
1758 mBottomBar.animateToVideoStop(shutterIconId);
1761 public void animateBottomBarToFullSize(int shutterIconId) {
1762 mBottomBar.animateToFullSize(shutterIconId);
1765 public void setShutterButtonEnabled(final boolean enabled) {
1766 if (!mDisableAllUserInteractions) {
1767 mBottomBar.post(new Runnable() {
1770 mBottomBar.setShutterButtonEnabled(enabled);
1776 public void setShutterButtonImportantToA11y(boolean important) {
1777 mBottomBar.setShutterButtonImportantToA11y(important);
1780 public boolean isShutterButtonEnabled() {
1781 return mBottomBar.isShutterButtonEnabled();
1784 public void setIndicatorBottomBarWrapperVisible(boolean visible) {
1785 mStickyBottomCaptureLayout.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
1789 * Set the visibility of the bottom bar.
1791 // TODO: needed for when panorama is managed by the generic module ui.
1792 public void setBottomBarVisible(boolean visible) {
1793 mBottomBar.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
1797 * Add a {@link #ShutterButton.OnShutterButtonListener} to the shutter button.
1799 public void addShutterListener(ShutterButton.OnShutterButtonListener listener) {
1800 mShutterButton.addOnShutterButtonListener(listener);
1804 * Remove a {@link #ShutterButton.OnShutterButtonListener} from the shutter button.
1806 public void removeShutterListener(ShutterButton.OnShutterButtonListener listener) {
1807 mShutterButton.removeOnShutterButtonListener(listener);
1811 * Performs a transition to the capture layout of the bottom bar.
1813 public void transitionToCapture() {
1814 ModuleController moduleController = mController.getCurrentModuleController();
1815 applyModuleSpecs(moduleController.getHardwareSpec(),
1816 moduleController.getBottomBarSpec());
1817 mBottomBar.transitionToCapture();
1821 * Displays the Cancel button instead of the capture button.
1823 public void transitionToCancel() {
1824 ModuleController moduleController = mController.getCurrentModuleController();
1825 applyModuleSpecs(moduleController.getHardwareSpec(),
1826 moduleController.getBottomBarSpec());
1827 mBottomBar.transitionToCancel();
1831 * Performs a transition to the global intent layout.
1833 public void transitionToIntentCaptureLayout() {
1834 ModuleController moduleController = mController.getCurrentModuleController();
1835 applyModuleSpecs(moduleController.getHardwareSpec(),
1836 moduleController.getBottomBarSpec());
1837 mBottomBar.transitionToIntentCaptureLayout();
1841 * Performs a transition to the global intent review layout.
1843 public void transitionToIntentReviewLayout() {
1844 ModuleController moduleController = mController.getCurrentModuleController();
1845 applyModuleSpecs(moduleController.getHardwareSpec(),
1846 moduleController.getBottomBarSpec());
1847 mBottomBar.transitionToIntentReviewLayout();
1851 * @return whether UI is in intent review mode
1853 public boolean isInIntentReview() {
1854 return mBottomBar.isInIntentReview();
1858 public void onSettingChanged(SettingsManager settingsManager, String key) {
1859 // Update the mode options based on the hardware spec,
1860 // when hdr changes to prevent flash from getting out of sync.
1861 if (key.equals(Keys.KEY_CAMERA_HDR)) {
1862 ModuleController moduleController = mController.getCurrentModuleController();
1863 applyModuleSpecs(moduleController.getHardwareSpec(),
1864 moduleController.getBottomBarSpec());
1869 * Applies a {@link com.android.camera.CameraAppUI.BottomBarUISpec}
1870 * to the bottom bar mode options based on limitations from a
1871 * {@link com.android.camera.hardware.HardwareSpec}.
1873 * Options not supported by the hardware are either hidden
1874 * or disabled, depending on the option.
1876 * Otherwise, the option is fully enabled and clickable.
1878 public void applyModuleSpecs(final HardwareSpec hardwareSpec,
1879 final BottomBarUISpec bottomBarSpec) {
1880 if (hardwareSpec == null || bottomBarSpec == null) {
1884 ButtonManager buttonManager = mController.getButtonManager();
1885 SettingsManager settingsManager = mController.getSettingsManager();
1887 buttonManager.setToInitialState();
1889 /** Standard mode options */
1890 if (mController.getCameraProvider().getNumberOfCameras() > 1 &&
1891 hardwareSpec.isFrontCameraSupported()) {
1892 if (bottomBarSpec.enableCamera) {
1893 buttonManager.initializeButton(ButtonManager.BUTTON_CAMERA,
1894 bottomBarSpec.cameraCallback);
1896 buttonManager.disableButton(ButtonManager.BUTTON_CAMERA);
1899 // Hide camera icon if front camera not available.
1900 buttonManager.hideButton(ButtonManager.BUTTON_CAMERA);
1903 if (bottomBarSpec.hideFlash) {
1904 // Hide both flash and torch button in flash disable logic
1905 buttonManager.hideButton(ButtonManager.BUTTON_FLASH);
1906 buttonManager.hideButton(ButtonManager.BUTTON_TORCH);
1908 if (hardwareSpec.isFlashSupported()) {
1909 if (bottomBarSpec.enableFlash) {
1910 buttonManager.initializeButton(ButtonManager.BUTTON_FLASH,
1911 bottomBarSpec.flashCallback);
1912 } else if (bottomBarSpec.enableTorchFlash) {
1913 buttonManager.initializeButton(ButtonManager.BUTTON_TORCH,
1914 bottomBarSpec.flashCallback);
1915 } else if (bottomBarSpec.enableHdrPlusFlash) {
1916 buttonManager.initializeButton(ButtonManager.BUTTON_HDR_PLUS_FLASH,
1917 bottomBarSpec.flashCallback);
1919 // Hide both flash and torch button in flash disable logic
1920 buttonManager.disableButton(ButtonManager.BUTTON_FLASH);
1921 buttonManager.disableButton(ButtonManager.BUTTON_TORCH);
1924 // Disable both flash and torch icon if not supported
1925 // by the chosen camera hardware.
1926 buttonManager.disableButton(ButtonManager.BUTTON_FLASH);
1927 buttonManager.disableButton(ButtonManager.BUTTON_TORCH);
1931 if (bottomBarSpec.hideHdr || mIsCaptureIntent) {
1932 // Force hide hdr or hdr plus icon.
1933 buttonManager.hideButton(ButtonManager.BUTTON_HDR_PLUS);
1935 if (hardwareSpec.isHdrPlusSupported()) {
1936 if (bottomBarSpec.enableHdr && Keys.isCameraBackFacing(settingsManager,
1937 mController.getModuleScope())) {
1938 buttonManager.initializeButton(ButtonManager.BUTTON_HDR_PLUS,
1939 bottomBarSpec.hdrCallback);
1941 buttonManager.disableButton(ButtonManager.BUTTON_HDR_PLUS);
1943 } else if (hardwareSpec.isHdrSupported()) {
1944 if (bottomBarSpec.enableHdr && Keys.isCameraBackFacing(settingsManager,
1945 mController.getModuleScope())) {
1946 buttonManager.initializeButton(ButtonManager.BUTTON_HDR,
1947 bottomBarSpec.hdrCallback);
1949 buttonManager.disableButton(ButtonManager.BUTTON_HDR);
1952 // Hide hdr plus or hdr icon if neither are supported.
1953 buttonManager.hideButton(ButtonManager.BUTTON_HDR_PLUS);
1957 if (bottomBarSpec.hideGridLines) {
1958 // Force hide grid lines icon.
1959 buttonManager.hideButton(ButtonManager.BUTTON_GRID_LINES);
1962 if (bottomBarSpec.enableGridLines) {
1963 buttonManager.initializeButton(ButtonManager.BUTTON_GRID_LINES,
1964 bottomBarSpec.gridLinesCallback != null ?
1965 bottomBarSpec.gridLinesCallback : getGridLinesCallback()
1968 buttonManager.disableButton(ButtonManager.BUTTON_GRID_LINES);
1973 if (bottomBarSpec.enableSelfTimer) {
1974 buttonManager.initializeButton(ButtonManager.BUTTON_COUNTDOWN, null);
1976 if (bottomBarSpec.showSelfTimer) {
1977 buttonManager.disableButton(ButtonManager.BUTTON_COUNTDOWN);
1979 buttonManager.hideButton(ButtonManager.BUTTON_COUNTDOWN);
1983 if (bottomBarSpec.enablePanoOrientation
1984 && PhotoSphereHelper.getPanoramaOrientationOptionArrayId() > 0) {
1985 buttonManager.initializePanoOrientationButtons(bottomBarSpec.panoOrientationCallback);
1988 boolean enableExposureCompensation = bottomBarSpec.enableExposureCompensation &&
1989 !(bottomBarSpec.minExposureCompensation == 0 && bottomBarSpec.maxExposureCompensation == 0) &&
1990 mController.getSettingsManager().getBoolean(SettingsManager.SCOPE_GLOBAL,
1991 Keys.KEY_EXPOSURE_COMPENSATION_ENABLED);
1992 if (enableExposureCompensation) {
1993 buttonManager.initializePushButton(ButtonManager.BUTTON_EXPOSURE_COMPENSATION, null);
1994 buttonManager.setExposureCompensationParameters(
1995 bottomBarSpec.minExposureCompensation,
1996 bottomBarSpec.maxExposureCompensation,
1997 bottomBarSpec.exposureCompensationStep);
1999 buttonManager.setExposureCompensationCallback(
2000 bottomBarSpec.exposureCompensationSetCallback);
2001 buttonManager.updateExposureButtons();
2003 buttonManager.hideButton(ButtonManager.BUTTON_EXPOSURE_COMPENSATION);
2004 buttonManager.setExposureCompensationCallback(null);
2008 if (bottomBarSpec.showCancel) {
2009 buttonManager.initializePushButton(ButtonManager.BUTTON_CANCEL,
2010 bottomBarSpec.cancelCallback);
2012 if (bottomBarSpec.showDone) {
2013 buttonManager.initializePushButton(ButtonManager.BUTTON_DONE,
2014 bottomBarSpec.doneCallback);
2016 if (bottomBarSpec.showRetake) {
2017 buttonManager.initializePushButton(ButtonManager.BUTTON_RETAKE,
2018 bottomBarSpec.retakeCallback,
2020 R.string.retake_button_description);
2022 if (bottomBarSpec.showReview) {
2023 buttonManager.initializePushButton(ButtonManager.BUTTON_REVIEW,
2024 bottomBarSpec.reviewCallback,
2026 R.string.review_button_description);
2031 * Shows the given tutorial on the screen.
2033 public void showTutorial(AbstractTutorialOverlay tutorial, LayoutInflater inflater) {
2034 tutorial.show(mTutorialsPlaceHolderWrapper, inflater);
2037 /***************************Filmstrip api *****************************/
2039 public void showFilmstrip() {
2040 mModeListView.onBackPressed();
2041 mFilmstripLayout.showFilmstrip();
2044 public void hideFilmstrip() {
2045 mFilmstripLayout.hideFilmstrip();