blob: 0953b26b5928068ef78b019bf5ef8fa28e689141 [file] [log] [blame]
Doris Liuf55f3c42013-11-20 00:24:46 -08001/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.camera.app;
18
19import android.content.Context;
Doris Liu9d264302014-02-11 10:41:40 -080020import android.content.res.Configuration;
Doris Liu213a4a02014-02-04 16:57:55 -080021import android.graphics.Bitmap;
22import android.graphics.Canvas;
Doris Liuf55f3c42013-11-20 00:24:46 -080023import android.graphics.Matrix;
Doris Liu213a4a02014-02-04 16:57:55 -080024import android.graphics.RectF;
Doris Liu06db7422013-12-09 19:36:25 -080025import android.graphics.SurfaceTexture;
Doris Liu11ddd732014-01-07 10:52:11 -080026import android.hardware.display.DisplayManager;
Angus Kong50521172014-01-17 17:23:59 -080027import android.util.CameraPerformanceTracker;
Doris Liuf55f3c42013-11-20 00:24:46 -080028import android.view.GestureDetector;
29import android.view.LayoutInflater;
30import android.view.MotionEvent;
31import android.view.TextureView;
32import android.view.View;
33import android.view.ViewConfiguration;
Angus Konge0aff892013-12-11 20:51:01 -080034import android.view.ViewGroup;
Doris Liuc6c97402013-12-06 21:02:53 -080035import android.widget.FrameLayout;
Doris Liuf55f3c42013-11-20 00:24:46 -080036
37import com.android.camera.AnimationManager;
Erin Dahlgrenb1641f52014-01-14 15:58:52 -080038import com.android.camera.ButtonManager;
Erin Dahlgrenb1641f52014-01-14 15:58:52 -080039import com.android.camera.ShutterButton;
Sascha Haeberlinge2914fd2014-01-17 19:03:52 -080040import com.android.camera.TextureViewHelper;
Angus Kong2bca2102014-03-11 16:27:30 -070041import com.android.camera.debug.Log;
Angus Kongb2510252013-12-10 22:58:15 -080042import com.android.camera.filmstrip.FilmstripContentPanel;
Erin Dahlgrenb1641f52014-01-14 15:58:52 -080043import com.android.camera.hardware.HardwareSpec;
44import com.android.camera.module.ModuleController;
45import com.android.camera.settings.SettingsManager;
Sascha Haeberlingc813ce12014-03-10 15:35:07 -070046import com.android.camera.ui.AbstractTutorialOverlay;
Spike Sprague0f3c4b42013-12-10 19:50:17 -080047import com.android.camera.ui.BottomBar;
Doris Liu1dfe7822013-12-12 00:02:08 -080048import com.android.camera.ui.CaptureAnimationOverlay;
Erin Dahlgrenb3f59a82014-02-04 15:23:30 -080049import com.android.camera.ui.GridLines;
Doris Liuf55f3c42013-11-20 00:24:46 -080050import com.android.camera.ui.MainActivityLayout;
51import com.android.camera.ui.ModeListView;
52import com.android.camera.ui.ModeTransitionView;
Doris Liuf9e4f8f2013-12-04 18:04:22 -080053import com.android.camera.ui.PreviewOverlay;
Doris Liu06db7422013-12-09 19:36:25 -080054import com.android.camera.ui.PreviewStatusListener;
Doris Liu11ddd732014-01-07 10:52:11 -080055import com.android.camera.util.ApiHelper;
56import com.android.camera.util.CameraUtil;
Doris Liu9d264302014-02-11 10:41:40 -080057import com.android.camera.util.Gusterpolator;
Erin Dahlgrend706ea62014-01-21 16:16:06 -080058import com.android.camera.util.PhotoSphereHelper;
Erin Dahlgren77295162014-01-29 17:29:18 -080059import com.android.camera.util.UsageStatistics;
Sascha Haeberlingab05a742013-12-13 23:28:30 -080060import com.android.camera.widget.FilmstripLayout;
Sascha Haeberling8793eff2014-01-15 16:33:59 -080061import com.android.camera.widget.IndicatorIconController;
Erin Dahlgren165bb8d2014-01-30 14:11:20 -080062import com.android.camera.widget.ModeOptionsOverlay;
Angus Kong50521172014-01-17 17:23:59 -080063import com.android.camera.widget.PeekView;
Doris Liuf55f3c42013-11-20 00:24:46 -080064import com.android.camera2.R;
Erin Dahlgren77295162014-01-29 17:29:18 -080065import com.google.common.logging.eventprotos;
66
Doris Liuf55f3c42013-11-20 00:24:46 -080067/**
68 * CameraAppUI centralizes control of views shared across modules. Whereas module
69 * specific views will be handled in each Module UI. For example, we can now
70 * bring the flash animation and capture animation up from each module to app
71 * level, as these animations are largely the same for all modules.
72 *
73 * This class also serves to disambiguate touch events. It recognizes all the
74 * swipe gestures that happen on the preview by attaching a touch listener to
75 * a full-screen view on top of preview TextureView. Since CameraAppUI has knowledge
76 * of how swipe from each direction should be handled, it can then redirect these
77 * events to appropriate recipient views.
78 */
Doris Liu06db7422013-12-09 19:36:25 -080079public class CameraAppUI implements ModeListView.ModeSwitchListener,
Doris Liu2c559c42014-01-08 16:04:38 -080080 TextureView.SurfaceTextureListener, ModeListView.ModeListOpenListener {
Angus Konge0aff892013-12-11 20:51:01 -080081
82 /**
83 * The bottom controls on the filmstrip.
84 */
Angus Kong7ae25c22014-02-25 10:37:39 -080085 public static interface BottomPanel {
Angus Konge0aff892013-12-11 20:51:01 -080086 /** Values for the view state of the button. */
Angus Kong00b33712014-01-16 11:06:46 -080087 public final int VIEWER_NONE = 0;
88 public final int VIEWER_PHOTO_SPHERE = 1;
89 public final int VIEWER_REFOCUS = 2;
90 public final int VIEWER_OTHER = 3;
Angus Konge0aff892013-12-11 20:51:01 -080091
92 /**
93 * Sets a new or replaces an existing listener for bottom control events.
94 */
95 void setListener(Listener listener);
96
97 /**
98 * Set if the bottom controls are visible.
99 * @param visible {@code true} if visible.
100 */
101 void setVisible(boolean visible);
102
103 /**
104 * @param visible Whether the button is visible.
105 */
106 void setEditButtonVisibility(boolean visible);
107
108 /**
Angus Konge4002f32013-12-13 22:24:11 -0800109 * @param enabled Whether the button is enabled.
110 */
111 void setEditEnabled(boolean enabled);
112
113 /**
Angus Konge0aff892013-12-11 20:51:01 -0800114 * Sets the visibility of the view-photosphere button.
115 *
Angus Kong00b33712014-01-16 11:06:46 -0800116 * @param state one of {@link #VIEWER_NONE}, {@link #VIEWER_PHOTO_SPHERE},
117 * {@link #VIEWER_REFOCUS}.
Angus Konge0aff892013-12-11 20:51:01 -0800118 */
Angus Kong00b33712014-01-16 11:06:46 -0800119 void setViewerButtonVisibility(int state);
Angus Konge0aff892013-12-11 20:51:01 -0800120
121 /**
Angus Konge4002f32013-12-13 22:24:11 -0800122 * @param enabled Whether the button is enabled.
123 */
124 void setViewEnabled(boolean enabled);
125
126 /**
Angus Konge4002f32013-12-13 22:24:11 -0800127 * @param enabled Whether the button is enabled.
128 */
129 void setTinyPlanetEnabled(boolean enabled);
130
131 /**
Angus Konge0aff892013-12-11 20:51:01 -0800132 * @param visible Whether the button is visible.
133 */
134 void setDeleteButtonVisibility(boolean visible);
135
136 /**
Angus Konge4002f32013-12-13 22:24:11 -0800137 * @param enabled Whether the button is enabled.
138 */
139 void setDeleteEnabled(boolean enabled);
140
141 /**
Angus Konge0aff892013-12-11 20:51:01 -0800142 * @param visible Whether the button is visible.
143 */
144 void setShareButtonVisibility(boolean visible);
145
146 /**
Angus Konge4002f32013-12-13 22:24:11 -0800147 * @param enabled Whether the button is enabled.
148 */
149 void setShareEnabled(boolean enabled);
150
151 /**
Angus Kong7ae25c22014-02-25 10:37:39 -0800152 * Sets the texts for progress UI.
153 *
154 * @param text The text to show.
155 */
156 void setProgressText(CharSequence text);
157
158 /**
159 * Sets the progress.
160 *
161 * @param progress The progress value. Should be between 0 and 100.
162 */
163 void setProgress(int progress);
164
165 /**
Sascha Haeberling597c1a02014-03-06 11:46:06 -0800166 * Replaces the progress UI with an error message.
167 */
168 void showProgressError(CharSequence message);
169
170 /**
171 * Hide the progress error message.
172 */
173 void hideProgressError();
174
175 /**
Angus Kong001dc312014-02-27 16:58:11 -0800176 * Shows the progress.
Angus Kong7ae25c22014-02-25 10:37:39 -0800177 */
178 void showProgress();
179
180 /**
Angus Kong001dc312014-02-27 16:58:11 -0800181 * Hides the progress.
182 */
183 void hideProgress();
184
185 /**
186 * Shows the controls.
Angus Kong7ae25c22014-02-25 10:37:39 -0800187 */
188 void showControls();
189
190 /**
Angus Kong001dc312014-02-27 16:58:11 -0800191 * Hides the controls.
192 */
193 void hideControls();
194
195 /**
Angus Konge0aff892013-12-11 20:51:01 -0800196 * Classes implementing this interface can listen for events on the bottom
197 * controls.
198 */
199 public static interface Listener {
200 /**
201 * Called when the user pressed the "view" button to e.g. view a photo
202 * sphere or RGBZ image.
203 */
Angus Kong8a2350a2013-12-16 15:02:34 -0800204 public void onExternalViewer();
Angus Konge0aff892013-12-11 20:51:01 -0800205
206 /**
207 * Called when the "edit" button is pressed.
208 */
209 public void onEdit();
210
211 /**
212 * Called when the "tiny planet" button is pressed.
213 */
214 public void onTinyPlanet();
215
216 /**
217 * Called when the "delete" button is pressed.
218 */
219 public void onDelete();
220
221 /**
222 * Called when the "share" button is pressed.
223 */
224 public void onShare();
Sascha Haeberling597c1a02014-03-06 11:46:06 -0800225
226 /**
227 * Called when the progress error message is clicked.
228 */
229 public void onProgressErrorClicked();
Angus Konge0aff892013-12-11 20:51:01 -0800230 }
231 }
232
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800233 /**
234 * BottomBarUISpec provides a structure for modules
235 * to specify their ideal bottom bar mode options layout.
236 *
237 * Once constructed by a module, this class should be
238 * treated as read only.
239 *
240 * The application then edits this spec according to
241 * hardware limitations and displays the final bottom
242 * bar ui.
243 */
244 public static class BottomBarUISpec {
245 /** Mode options UI */
246
247 /**
248 * Set true if the camera option should be enabled.
249 * If not set or false, and multiple cameras are supported,
250 * the camera option will be disabled.
251 *
252 * If multiple cameras are not supported, this preference
253 * is ignored and the camera option will not be visible.
254 */
255 public boolean enableCamera;
256
257 /**
Erin Dahlgrena9ceaf72014-02-12 10:45:24 -0800258 * Set true if the camera option should not be visible, regardless
259 * of hardware limitations.
260 */
261 public boolean hideCamera;
262
263 /**
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800264 * Set true if the photo flash option should be enabled.
265 * If not set or false, the photo flash option will be
266 * disabled.
267 *
268 * If the hardware does not support multiple flash values,
269 * this preference is ignored and the flash option will
270 * be disabled. It will not be made invisible in order to
271 * preserve a consistent experience across devices and between
272 * front and back cameras.
273 */
274 public boolean enableFlash;
275
276 /**
277 * Set true if the video flash option should be enabled.
278 * Same disable rules apply as the photo flash option.
279 */
280 public boolean enableTorchFlash;
281
282 /**
Erin Dahlgrend5e51462014-02-07 12:38:57 -0800283 * Set true if flash should not be visible, regardless of
284 * hardware limitations.
285 */
286 public boolean hideFlash;
287
288 /**
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800289 * Set true if the hdr/hdr+ option should be enabled.
290 * If not set or false, the hdr/hdr+ option will be disabled.
291 *
292 * Hdr or hdr+ will be chosen based on hardware limitations,
293 * with hdr+ prefered.
294 *
295 * If hardware supports neither hdr nor hdr+, then the hdr/hdr+
296 * will not be visible.
297 */
298 public boolean enableHdr;
299
300 /**
301 * Set true if hdr/hdr+ should not be visible, regardless of
302 * hardware limitations.
303 */
304 public boolean hideHdr;
305
306 /**
Erin Dahlgrend5e51462014-02-07 12:38:57 -0800307 * Set true if grid lines should be visible. Not setting this
308 * causes grid lines to be disabled. This option is agnostic to
309 * the hardware.
310 */
311 public boolean enableGridLines;
312
313 /**
314 * Set true if grid lines should not be visible.
315 */
316 public boolean hideGridLines;
317
318 /**
Erin Dahlgrenf80ac9e2014-02-18 11:20:34 -0800319 * Set true if the panorama orientation option should be visible.
Erin Dahlgrena1fab412014-01-21 09:31:11 -0800320 *
321 * This option is not constrained by hardware limitations.
322 */
Erin Dahlgrenf80ac9e2014-02-18 11:20:34 -0800323 public boolean enablePanoOrientation;
Erin Dahlgrena1fab412014-01-21 09:31:11 -0800324
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800325 /** Intent UI */
326
327 /**
328 * Set true if the intent ui cancel option should be visible.
329 */
330 public boolean showCancel;
331 /**
332 * Set true if the intent ui done option should be visible.
333 */
334 public boolean showDone;
335 /**
336 * Set true if the intent ui retake option should be visible.
337 */
338 public boolean showRetake;
339 /**
340 * Set true if the intent ui review option should be visible.
341 */
342 public boolean showReview;
343
344 /** Mode options callbacks */
345
346 /**
Erin Dahlgrenf80ac9e2014-02-18 11:20:34 -0800347 * A {@link com.android.camera.ButtonManager.ButtonCallback}
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800348 * that will be executed when the camera option is pressed. This
349 * callback can be null.
350 */
351 public ButtonManager.ButtonCallback cameraCallback;
352
353 /**
Erin Dahlgrenf80ac9e2014-02-18 11:20:34 -0800354 * A {@link com.android.camera.ButtonManager.ButtonCallback}
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800355 * that will be executed when the flash option is pressed. This
356 * callback can be null.
357 */
358 public ButtonManager.ButtonCallback flashCallback;
359
360 /**
Erin Dahlgrenf80ac9e2014-02-18 11:20:34 -0800361 * A {@link com.android.camera.ButtonManager.ButtonCallback}
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800362 * that will be executed when the hdr/hdr+ option is pressed. This
363 * callback can be null.
364 */
365 public ButtonManager.ButtonCallback hdrCallback;
366
367 /**
Erin Dahlgrenf80ac9e2014-02-18 11:20:34 -0800368 * A {@link com.android.camera.ButtonManager.ButtonCallback}
Erin Dahlgrend5e51462014-02-07 12:38:57 -0800369 * that will be executed when the grid lines option is pressed. This
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800370 * callback can be null.
371 */
Erin Dahlgrend5e51462014-02-07 12:38:57 -0800372 public ButtonManager.ButtonCallback gridLinesCallback;
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800373
Erin Dahlgrena1fab412014-01-21 09:31:11 -0800374 /**
Erin Dahlgrenf80ac9e2014-02-18 11:20:34 -0800375 * A {@link com.android.camera.ButtonManager.ButtonCallback}
376 * that will execute when the panorama orientation option is pressed.
Erin Dahlgrena1fab412014-01-21 09:31:11 -0800377 * This callback can be null.
378 */
Erin Dahlgrenf80ac9e2014-02-18 11:20:34 -0800379 public ButtonManager.ButtonCallback panoOrientationCallback;
Erin Dahlgrena1fab412014-01-21 09:31:11 -0800380
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800381 /** Intent UI callbacks */
382
383 /**
384 * A {@link android.view.View.OnClickListener} that will execute
385 * when the cancel option is pressed. This callback can be null.
386 */
387 public View.OnClickListener cancelCallback;
388
389 /**
390 * A {@link android.view.View.OnClickListener} that will execute
391 * when the done option is pressed. This callback can be null.
392 */
393 public View.OnClickListener doneCallback;
394
395 /**
396 * A {@link android.view.View.OnClickListener} that will execute
397 * when the retake option is pressed. This callback can be null.
398 */
399 public View.OnClickListener retakeCallback;
400
401 /**
402 * A {@link android.view.View.OnClickListener} that will execute
403 * when the review option is pressed. This callback can be null.
404 */
405 public View.OnClickListener reviewCallback;
406 }
407
408
Angus Kong2bca2102014-03-11 16:27:30 -0700409 private final static Log.Tag TAG = new Log.Tag("CameraAppUI");
Doris Liuf55f3c42013-11-20 00:24:46 -0800410
411 private final AppController mController;
412 private final boolean mIsCaptureIntent;
Doris Liuf55f3c42013-11-20 00:24:46 -0800413 private final AnimationManager mAnimationManager;
414
415 // Swipe states:
Doris Liu2b906b82013-12-10 16:34:08 -0800416 private final static int IDLE = 0;
417 private final static int SWIPE_UP = 1;
418 private final static int SWIPE_DOWN = 2;
419 private final static int SWIPE_LEFT = 3;
420 private final static int SWIPE_RIGHT = 4;
Sascha Haeberling8793eff2014-01-15 16:33:59 -0800421 private boolean mSwipeEnabled = true;
Doris Liuf55f3c42013-11-20 00:24:46 -0800422
Erin Dahlgrend8de0772014-02-03 10:12:27 -0800423 // Shared Surface Texture properities.
424 private SurfaceTexture mSurface;
425 private int mSurfaceWidth;
426 private int mSurfaceHeight;
427
Doris Liuf55f3c42013-11-20 00:24:46 -0800428 // Touch related measures:
429 private final int mSlop;
Doris Liu2b906b82013-12-10 16:34:08 -0800430 private final static int SWIPE_TIME_OUT_MS = 500;
431
Doris Liu2b906b82013-12-10 16:34:08 -0800432 // Mode cover states:
433 private final static int COVER_HIDDEN = 0;
434 private final static int COVER_SHOWN = 1;
435 private final static int COVER_WILL_HIDE_AT_NEXT_FRAME = 2;
Doris Liu4d4a4bc2013-12-19 18:55:54 -0800436 private static final int COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE = 3;
Doris Liuf55f3c42013-11-20 00:24:46 -0800437
438 // App level views:
Doris Liub41034b2013-12-06 21:58:41 -0800439 private final FrameLayout mCameraRootView;
Doris Liuf55f3c42013-11-20 00:24:46 -0800440 private final ModeTransitionView mModeTransitionView;
441 private final MainActivityLayout mAppRootView;
442 private final ModeListView mModeListView;
Angus Kong166e36f2013-12-03 08:54:42 -0800443 private final FilmstripLayout mFilmstripLayout;
Doris Liuf55f3c42013-11-20 00:24:46 -0800444 private TextureView mTextureView;
Doris Liuc6c97402013-12-06 21:02:53 -0800445 private FrameLayout mModuleUI;
Erin Dahlgren5d187692014-02-25 19:16:12 -0800446 private ShutterButton mShutterButton;
Doris Liu70da9182013-12-17 18:41:15 -0800447 private BottomBar mBottomBar;
Erin Dahlgren165bb8d2014-01-30 14:11:20 -0800448 private ModeOptionsOverlay mModeOptionsOverlay;
Erin Dahlgrene2a1f542014-01-08 14:34:35 -0800449 private IndicatorIconController mIndicatorIconController;
Doris Liuaf656832014-02-19 14:12:06 -0800450 private View mFocusOverlay;
Sascha Haeberlinga9a9d152014-03-05 08:46:55 -0800451 private FrameLayout mTutorialsPlaceholder;
Erin Dahlgren4efa8b52013-12-17 18:31:35 -0800452
Doris Liu70da9182013-12-17 18:41:15 -0800453 private TextureViewHelper mTextureViewHelper;
Sascha Haeberlingab05a742013-12-13 23:28:30 -0800454 private final GestureDetector mGestureDetector;
Doris Liu11ddd732014-01-07 10:52:11 -0800455 private DisplayManager.DisplayListener mDisplayListener;
456 private int mLastRotation;
Doris Liuf55f3c42013-11-20 00:24:46 -0800457 private int mSwipeState = IDLE;
Doris Liuf9e4f8f2013-12-04 18:04:22 -0800458 private PreviewOverlay mPreviewOverlay;
Erin Dahlgrenb3f59a82014-02-04 15:23:30 -0800459 private GridLines mGridLines;
Doris Liu1dfe7822013-12-12 00:02:08 -0800460 private CaptureAnimationOverlay mCaptureOverlay;
Doris Liu06db7422013-12-09 19:36:25 -0800461 private PreviewStatusListener mPreviewStatusListener;
Doris Liu2b906b82013-12-10 16:34:08 -0800462 private int mModeCoverState = COVER_HIDDEN;
Angus Kong7ae25c22014-02-25 10:37:39 -0800463 private final FilmstripBottomPanel mFilmstripBottomControls;
Sascha Haeberlingab05a742013-12-13 23:28:30 -0800464 private final FilmstripContentPanel mFilmstripPanel;
Doris Liub6eaa8c2013-12-11 18:39:14 -0800465 private Runnable mHideCoverRunnable;
Doris Liu9d264302014-02-11 10:41:40 -0800466 private final UncoveredPreviewAreaSizeChangedListener mUncoverPreviewAreaChangedListener;
Doris Liu4a010db2013-12-16 18:44:49 -0800467 private final View.OnLayoutChangeListener mPreviewLayoutChangeListener
468 = new View.OnLayoutChangeListener() {
469 @Override
470 public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
471 int oldTop, int oldRight, int oldBottom) {
472 if (mPreviewStatusListener != null) {
473 mPreviewStatusListener.onPreviewLayoutChanged(v, left, top, right, bottom, oldLeft,
474 oldTop, oldRight, oldBottom);
475 }
476 }
477 };
Doris Liu9d264302014-02-11 10:41:40 -0800478 private View mModeOptionsToggle;
Sascha Haeberlinga9a9d152014-03-05 08:46:55 -0800479 private final RectF mBottomBarRect = new RectF();
Doris Liu9d264302014-02-11 10:41:40 -0800480 private final View.OnLayoutChangeListener mBottomBarLayoutChangeListener
481 = new View.OnLayoutChangeListener() {
482 @Override
483 public void onLayoutChange(View v, int left, int top, int right, int bottom,
484 int oldLeft, int oldTop, int oldRight, int oldBottom) {
485 if (mBottomBar.getVisibility() == View.VISIBLE) {
486 mBottomBarRect.set(left, top, right, bottom);
487 } else {
488 // If bottom bar is not visible, treat it as a 0x0 rect at the
489 // bottom right corner of the screen.
490 mBottomBarRect.set(right, bottom, right, bottom);
491 }
492
493 RectF previewArea = mTextureViewHelper.getPreviewArea();
494 // Use preview area and bottom bar rect to calculate the preview that is
495 // not covered by bottom bar.
496 if (mBottomBar.getResources().getConfiguration().orientation
497 == Configuration.ORIENTATION_PORTRAIT) {
498 previewArea.bottom = Math.min(mBottomBarRect.top, previewArea.bottom);
499 } else {
500 previewArea.right = Math.min(mBottomBarRect.left, previewArea.right);
501 }
502
503 if (mUncoverPreviewAreaChangedListener != null) {
504 mUncoverPreviewAreaChangedListener.uncoveredPreviewAreaChanged(previewArea);
505 }
506 }
507 };
Sascha Haeberlinga9a9d152014-03-05 08:46:55 -0800508 private final PeekView mPeekView;
Doris Liuf55f3c42013-11-20 00:24:46 -0800509
Doris Liu213a4a02014-02-04 16:57:55 -0800510 /**
511 * Provides current preview frame and the controls/overlay from the module that
512 * are shown on top of the preview.
513 */
514 public interface CameraModuleScreenShotProvider {
515 /**
516 * Returns the current preview frame down-sampled using the given down-sample
517 * factor.
518 *
519 * @param downSampleFactor the down sample factor for down sampling the
520 * preview frame. (e.g. a down sample factor of
521 * 2 means to scale down the preview frame to 1/2
522 * the width and height.)
523 * @return down-sampled preview frame
524 */
525 public Bitmap getPreviewFrame(int downSampleFactor);
526
527 /**
528 * @return the controls and overlays that are currently showing on top of
529 * the preview drawn into a bitmap with no scaling applied.
530 */
531 public Bitmap getPreviewOverlayAndControls();
532 }
533
Doris Liu9d264302014-02-11 10:41:40 -0800534 /**
535 * Gets notified when the preview area that is not covered by bottom bar is
536 * changed.
537 */
538 public interface UncoveredPreviewAreaSizeChangedListener {
539 /**
540 * Gets called when the preview area that is not covered by bottom bar is
541 * changed.
542 *
543 * @param uncoveredPreviewArea the rect of the preview area that is not
544 * under bottom bar
545 */
546 public void uncoveredPreviewAreaChanged(RectF uncoveredPreviewArea);
547 }
548
Doris Liu213a4a02014-02-04 16:57:55 -0800549 private final CameraModuleScreenShotProvider mCameraModuleScreenShotProvider =
550 new CameraModuleScreenShotProvider() {
551 @Override
552 public Bitmap getPreviewFrame(int downSampleFactor) {
553 if (mCameraRootView == null || mTextureView == null) {
554 return null;
555 }
Doris Liu213a4a02014-02-04 16:57:55 -0800556 RectF previewArea = mTextureViewHelper.getPreviewArea();
557 // Gets the bitmap from the preview TextureView.
558 Bitmap preview = mTextureView.getBitmap(
559 (int) previewArea.width() / downSampleFactor,
560 (int) previewArea.height() / downSampleFactor);
561 return preview;
562 }
563
564 @Override
565 public Bitmap getPreviewOverlayAndControls() {
566 Bitmap overlays = Bitmap.createBitmap(mCameraRootView.getWidth(),
567 mCameraRootView.getHeight(), Bitmap.Config.ARGB_8888);
568 Canvas canvas = new Canvas(overlays);
569 mCameraRootView.draw(canvas);
570 return overlays;
571 }
572 };
573
Kevin Gabayana0e83472014-01-15 15:21:13 -0800574 private long mCoverHiddenTime = -1; // System time when preview cover was hidden.
575
576 public long getCoverHiddenTime() {
577 return mCoverHiddenTime;
578 }
579
Erin Dahlgrend8de0772014-02-03 10:12:27 -0800580 /**
581 * This resets the preview to have no applied transform matrix.
582 */
583 public void clearPreviewTransform() {
584 mTextureViewHelper.clearTransform();
585 }
586
Doris Liu70da9182013-12-17 18:41:15 -0800587 public void updatePreviewAspectRatio(float aspectRatio) {
588 mTextureViewHelper.updateAspectRatio(aspectRatio);
Spike Sprague0f3c4b42013-12-10 19:50:17 -0800589 }
590
Doris Liub897a1e2013-12-16 15:00:12 -0800591 /**
592 * This is to support modules that calculate their own transform matrix because
593 * they need to use a transform matrix to rotate the preview.
594 *
Doris Liub897a1e2013-12-16 15:00:12 -0800595 * @param matrix transform matrix to be set on the TextureView
596 */
Doris Liu70da9182013-12-17 18:41:15 -0800597 public void updatePreviewTransform(Matrix matrix) {
598 mTextureViewHelper.updateTransform(matrix);
Doris Liub897a1e2013-12-16 15:00:12 -0800599 }
600
Doris Liuf55f3c42013-11-20 00:24:46 -0800601 public interface AnimationFinishedListener {
602 public void onAnimationFinished(boolean success);
603 }
604
605 private class MyTouchListener implements View.OnTouchListener {
Doris Liuf9e4f8f2013-12-04 18:04:22 -0800606 private boolean mScaleStarted = false;
Doris Liuf55f3c42013-11-20 00:24:46 -0800607 @Override
608 public boolean onTouch(View v, MotionEvent event) {
Doris Liuf9e4f8f2013-12-04 18:04:22 -0800609 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
610 mScaleStarted = false;
611 } else if (event.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) {
612 mScaleStarted = true;
613 }
614 return (!mScaleStarted) && mGestureDetector.onTouchEvent(event);
Doris Liuf55f3c42013-11-20 00:24:46 -0800615 }
616 }
617
618 /**
619 * This gesture listener finds out the direction of the scroll gestures and
620 * sends them to CameraAppUI to do further handling.
621 */
622 private class MyGestureListener extends GestureDetector.SimpleOnGestureListener {
623 private MotionEvent mDown;
624
625 @Override
626 public boolean onScroll(MotionEvent e1, MotionEvent ev, float distanceX, float distanceY) {
Doris Liu2b906b82013-12-10 16:34:08 -0800627 if (ev.getEventTime() - ev.getDownTime() > SWIPE_TIME_OUT_MS
Spike Spraguec050a942013-12-19 17:05:45 -0800628 || mSwipeState != IDLE
Sascha Haeberling8793eff2014-01-15 16:33:59 -0800629 || mIsCaptureIntent
630 || !mSwipeEnabled) {
Doris Liub6eaa8c2013-12-11 18:39:14 -0800631 return false;
Doris Liuf55f3c42013-11-20 00:24:46 -0800632 }
633
634 int deltaX = (int) (ev.getX() - mDown.getX());
635 int deltaY = (int) (ev.getY() - mDown.getY());
636 if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) {
637 if (Math.abs(deltaX) > mSlop || Math.abs(deltaY) > mSlop) {
638 // Calculate the direction of the swipe.
639 if (deltaX >= Math.abs(deltaY)) {
640 // Swipe right.
641 setSwipeState(SWIPE_RIGHT);
642 } else if (deltaX <= -Math.abs(deltaY)) {
643 // Swipe left.
644 setSwipeState(SWIPE_LEFT);
Doris Liuf55f3c42013-11-20 00:24:46 -0800645 }
646 }
647 }
648 return true;
649 }
650
651 private void setSwipeState(int swipeState) {
652 mSwipeState = swipeState;
653 // Notify new swipe detected.
654 onSwipeDetected(swipeState);
655 }
656
657 @Override
658 public boolean onDown(MotionEvent ev) {
659 mDown = MotionEvent.obtain(ev);
660 mSwipeState = IDLE;
Doris Liuf9e4f8f2013-12-04 18:04:22 -0800661 return false;
Doris Liuf55f3c42013-11-20 00:24:46 -0800662 }
663 }
664
665 public CameraAppUI(AppController controller, MainActivityLayout appRootView,
Sascha Haeberling8793eff2014-01-15 16:33:59 -0800666 boolean isCaptureIntent) {
Doris Liuf55f3c42013-11-20 00:24:46 -0800667 mSlop = ViewConfiguration.get(controller.getAndroidContext()).getScaledTouchSlop();
668 mController = controller;
Doris Liuf55f3c42013-11-20 00:24:46 -0800669 mIsCaptureIntent = isCaptureIntent;
670
671 mAppRootView = appRootView;
Angus Kong166e36f2013-12-03 08:54:42 -0800672 mFilmstripLayout = (FilmstripLayout) appRootView.findViewById(R.id.filmstrip_layout);
Doris Liub41034b2013-12-06 21:58:41 -0800673 mCameraRootView = (FrameLayout) appRootView.findViewById(R.id.camera_app_root);
Doris Liuf55f3c42013-11-20 00:24:46 -0800674 mModeTransitionView = (ModeTransitionView)
675 mAppRootView.findViewById(R.id.mode_transition_view);
Angus Kong7ae25c22014-02-25 10:37:39 -0800676 mFilmstripBottomControls = new FilmstripBottomPanel(controller,
Angus Kong001dc312014-02-27 16:58:11 -0800677 (ViewGroup) mAppRootView.findViewById(R.id.filmstrip_bottom_panel));
Angus Kongb2510252013-12-10 22:58:15 -0800678 mFilmstripPanel = (FilmstripContentPanel) mAppRootView.findViewById(R.id.filmstrip_layout);
Doris Liuf55f3c42013-11-20 00:24:46 -0800679 mGestureDetector = new GestureDetector(controller.getAndroidContext(),
680 new MyGestureListener());
681 mModeListView = (ModeListView) appRootView.findViewById(R.id.mode_list_layout);
682 if (mModeListView != null) {
683 mModeListView.setModeSwitchListener(this);
Doris Liu2c559c42014-01-08 16:04:38 -0800684 mModeListView.setModeListOpenListener(this);
Doris Liu213a4a02014-02-04 16:57:55 -0800685 mModeListView.setCameraModuleScreenShotProvider(mCameraModuleScreenShotProvider);
Doris Liuf55f3c42013-11-20 00:24:46 -0800686 } else {
687 Log.e(TAG, "Cannot find mode list in the view hierarchy");
688 }
Doris Liue7e0c022014-02-12 11:14:08 -0800689 mUncoverPreviewAreaChangedListener =
690 mModeListView.getUncoveredPreviewAreaSizeChangedListener();
Doris Liuf55f3c42013-11-20 00:24:46 -0800691 mAnimationManager = new AnimationManager();
Angus Kong50521172014-01-17 17:23:59 -0800692 mPeekView = (PeekView) appRootView.findViewById(R.id.peek_view);
Doris Liu11ddd732014-01-07 10:52:11 -0800693 initDisplayListener();
694 }
695
696 /**
Sascha Haeberling8793eff2014-01-15 16:33:59 -0800697 * Enable or disable swipe gestures. We want to disable them e.g. while we
698 * record a video.
699 */
700 public void setSwipeEnabled(boolean enabled) {
Sascha Haeberling8793eff2014-01-15 16:33:59 -0800701 mSwipeEnabled = enabled;
702 }
703
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100704 public void onDestroy() {
705 ((DisplayManager) mController.getAndroidContext()
706 .getSystemService(Context.DISPLAY_SERVICE))
707 .unregisterDisplayListener(mDisplayListener);
708 }
709
Sascha Haeberling8793eff2014-01-15 16:33:59 -0800710 /**
Doris Liu11ddd732014-01-07 10:52:11 -0800711 * Initializes the display listener to listen to display changes such as
712 * 180-degree rotation change, which will not have an onConfigurationChanged
713 * callback.
714 */
715 private void initDisplayListener() {
716 if (ApiHelper.HAS_DISPLAY_LISTENER) {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100717 mLastRotation = CameraUtil.getDisplayRotation(mController.getAndroidContext());
Doris Liu11ddd732014-01-07 10:52:11 -0800718
719 mDisplayListener = new DisplayManager.DisplayListener() {
720 @Override
721 public void onDisplayAdded(int arg0) {
722 // Do nothing.
723 }
724
725 @Override
726 public void onDisplayChanged(int displayId) {
727 int rotation = CameraUtil.getDisplayRotation(
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100728 mController.getAndroidContext());
Doris Liu6365bad2014-01-30 13:11:29 -0800729 if ((rotation - mLastRotation + 360) % 360 == 180
730 && mPreviewStatusListener != null) {
Doris Liu11ddd732014-01-07 10:52:11 -0800731 mPreviewStatusListener.onPreviewFlipped();
732 }
733 mLastRotation = rotation;
734 }
735
736 @Override
737 public void onDisplayRemoved(int arg0) {
738 // Do nothing.
739 }
740 };
741
742 ((DisplayManager) mController.getAndroidContext()
743 .getSystemService(Context.DISPLAY_SERVICE))
744 .registerDisplayListener(mDisplayListener, null);
745 }
Doris Liuf55f3c42013-11-20 00:24:46 -0800746 }
747
748 /**
749 * Redirects touch events to appropriate recipient views based on swipe direction.
750 * More specifically, swipe up and swipe down will be handled by the view that handles
751 * mode transition; swipe left will be send to filmstrip; swipe right will be redirected
752 * to mode list in order to bring up mode list.
753 */
754 private void onSwipeDetected(int swipeState) {
755 if (swipeState == SWIPE_UP || swipeState == SWIPE_DOWN) {
Doris Liu9d52fc92014-02-25 14:06:55 -0800756 // TODO: Polish quick switch after this release.
Doris Liubd1b8f92014-01-03 17:59:51 -0800757 // Quick switch between modes.
758 int currentModuleIndex = mController.getCurrentModuleIndex();
759 final int moduleToTransitionTo =
760 mController.getQuickSwitchToModuleId(currentModuleIndex);
761 if (currentModuleIndex != moduleToTransitionTo) {
Doris Liuf55f3c42013-11-20 00:24:46 -0800762 mAppRootView.redirectTouchEventsTo(mModeTransitionView);
763
Doris Liu040e3c02014-02-10 14:42:08 -0800764 int shadeColorId = R.color.mode_cover_default_color;
Doris Liub520b972014-02-14 14:14:46 -0800765 int iconRes = CameraUtil.getCameraModeCoverIconResId(moduleToTransitionTo,
Doris Liubd1b8f92014-01-03 17:59:51 -0800766 mController.getAndroidContext());
Doris Liuf55f3c42013-11-20 00:24:46 -0800767
768 AnimationFinishedListener listener = new AnimationFinishedListener() {
Sascha Haeberlingab05a742013-12-13 23:28:30 -0800769 @Override
Doris Liuf55f3c42013-11-20 00:24:46 -0800770 public void onAnimationFinished(boolean success) {
771 if (success) {
Doris Liue038c162013-12-13 23:06:11 -0800772 mHideCoverRunnable = new Runnable() {
773 @Override
774 public void run() {
775 mModeTransitionView.startPeepHoleAnimation();
776 }
777 };
778 mModeCoverState = COVER_SHOWN;
Doris Liuf55f3c42013-11-20 00:24:46 -0800779 // Go to new module when the previous operation is successful.
780 mController.onModeSelected(moduleToTransitionTo);
Doris Liuf55f3c42013-11-20 00:24:46 -0800781 }
782 }
783 };
784 if (mSwipeState == SWIPE_UP) {
785 mModeTransitionView.prepareToPullUpShade(shadeColorId, iconRes, listener);
786 } else {
787 mModeTransitionView.prepareToPullDownShade(shadeColorId, iconRes, listener);
788 }
789 }
790 } else if (swipeState == SWIPE_LEFT) {
Angus Kong166e36f2013-12-03 08:54:42 -0800791 // Pass the touch sequence to filmstrip layout.
Erin Dahlgren77295162014-01-29 17:29:18 -0800792 UsageStatistics.changeScreen(eventprotos.NavigationChange.Mode.FILMSTRIP,
793 eventprotos.CameraEvent.InteractionCause.SWIPE_LEFT);
Angus Kong166e36f2013-12-03 08:54:42 -0800794 mAppRootView.redirectTouchEventsTo(mFilmstripLayout);
Doris Liuf55f3c42013-11-20 00:24:46 -0800795 } else if (swipeState == SWIPE_RIGHT) {
796 // Pass the touch to mode switcher
797 mAppRootView.redirectTouchEventsTo(mModeListView);
798 }
799 }
800
801 /**
Doris Liu2b906b82013-12-10 16:34:08 -0800802 * Gets called when activity resumes in preview.
803 */
804 public void resume() {
Doris Liub00d6432014-02-26 10:18:47 -0800805 // Show mode theme cover until preview is ready
806 showModeCoverUntilPreviewReady();
807
Angus Kongb2510252013-12-10 22:58:15 -0800808 // Hide action bar first since we are in full screen mode first, and
809 // switch the system UI to lights-out mode.
810 mFilmstripPanel.hide();
Doris Liu2b906b82013-12-10 16:34:08 -0800811 }
812
813 /**
814 * A cover view showing the mode theme color and mode icon will be visible on
815 * top of preview until preview is ready (i.e. camera preview is started and
816 * the first frame has been received).
817 */
818 private void showModeCoverUntilPreviewReady() {
819 int modeId = mController.getCurrentModuleIndex();
Doris Liu040e3c02014-02-10 14:42:08 -0800820 int colorId = R.color.mode_cover_default_color;;
Doris Liub520b972014-02-14 14:14:46 -0800821 int iconId = CameraUtil.getCameraModeCoverIconResId(modeId, mController.getAndroidContext());
Doris Liu2b906b82013-12-10 16:34:08 -0800822 mModeTransitionView.setupModeCover(colorId, iconId);
Doris Liub6eaa8c2013-12-11 18:39:14 -0800823 mHideCoverRunnable = new Runnable() {
824 @Override
825 public void run() {
Doris Liub00d6432014-02-26 10:18:47 -0800826 mModeTransitionView.hideModeCover(null);
827 showShimmyDelayed();
Doris Liub6eaa8c2013-12-11 18:39:14 -0800828 }
829 };
Doris Liu2b906b82013-12-10 16:34:08 -0800830 mModeCoverState = COVER_SHOWN;
831 }
832
Doris Liu2c559c42014-01-08 16:04:38 -0800833 private void showShimmyDelayed() {
834 if (!mIsCaptureIntent) {
835 // Show shimmy in SHIMMY_DELAY_MS
Angus Kong2ed67842014-03-24 16:40:12 -0700836 mModeListView.showModeSwitcherHint();
Doris Liu2c559c42014-01-08 16:04:38 -0800837 }
838 }
839
Doris Liu2b906b82013-12-10 16:34:08 -0800840 private void hideModeCover() {
Doris Liub6eaa8c2013-12-11 18:39:14 -0800841 if (mHideCoverRunnable != null) {
842 mAppRootView.post(mHideCoverRunnable);
843 mHideCoverRunnable = null;
844 }
Doris Liue038c162013-12-13 23:06:11 -0800845 mModeCoverState = COVER_HIDDEN;
Kevin Gabayana0e83472014-01-15 15:21:13 -0800846 if (mCoverHiddenTime < 0) {
847 mCoverHiddenTime = System.currentTimeMillis();
848 }
Doris Liu2b906b82013-12-10 16:34:08 -0800849 }
850
Sam Judd1b125322014-03-21 16:53:27 -0700851 /**
852 * Call to stop the preview from being rendered.
853 */
854 public void pausePreviewRendering() {
855 mTextureView.setVisibility(View.INVISIBLE);
856 }
857
858 /**
859 * Call to begin rendering the preview again.
860 */
861 public void resumePreviewRendering() {
862 mTextureView.setVisibility(View.VISIBLE);
863 }
864
Doris Liu2c559c42014-01-08 16:04:38 -0800865 @Override
866 public void onOpenFullScreen() {
Angus Kong2ed67842014-03-24 16:40:12 -0700867 // Do nothing.
Doris Liu2c559c42014-01-08 16:04:38 -0800868 }
869
Doris Liu9d264302014-02-11 10:41:40 -0800870 @Override
871 public void onModeListOpenProgress(float progress) {
872 progress = 1 - progress;
873 float interpolatedProgress = Gusterpolator.INSTANCE.getInterpolation(progress);
874 mModeOptionsToggle.setAlpha(interpolatedProgress);
Doris Liu948de992014-02-19 18:06:00 -0800875 // Change shutter button alpha linearly based on the mode list open progress:
876 // set the alpha to disabled alpha when list is fully open, to enabled alpha
877 // when the list is fully closed.
878 mShutterButton.setAlpha(progress * ShutterButton.ALPHA_WHEN_ENABLED
879 + (1 - progress) * ShutterButton.ALPHA_WHEN_DISABLED);
Doris Liu9d264302014-02-11 10:41:40 -0800880 }
881
882 @Override
883 public void onModeListClosed() {
884 // Make sure the alpha on mode options ellipse is reset when mode drawer
885 // is closed.
886 mModeOptionsToggle.setAlpha(1f);
Doris Liu948de992014-02-19 18:06:00 -0800887 mShutterButton.setAlpha(ShutterButton.ALPHA_WHEN_ENABLED);
Doris Liu9d264302014-02-11 10:41:40 -0800888 }
889
Doris Liu2b906b82013-12-10 16:34:08 -0800890 /**
Angus Kong166e36f2013-12-03 08:54:42 -0800891 * Called when the back key is pressed.
892 *
893 * @return Whether the UI responded to the key event.
894 */
895 public boolean onBackPressed() {
Doris Liub9b72402013-12-19 19:07:58 -0800896 if (mFilmstripLayout.getVisibility() == View.VISIBLE) {
897 return mFilmstripLayout.onBackPressed();
898 } else {
899 return mModeListView.onBackPressed();
900 }
Angus Kong166e36f2013-12-03 08:54:42 -0800901 }
902
903 /**
Doris Liu06db7422013-12-09 19:36:25 -0800904 * Sets a {@link com.android.camera.ui.PreviewStatusListener} that
Erin Dahlgren1648c362014-01-06 15:06:04 -0800905 * listens to SurfaceTexture changes. In addition, listeners are set on
906 * dependent app ui elements.
Doris Liu06db7422013-12-09 19:36:25 -0800907 *
908 * @param previewStatusListener the listener that gets notified when SurfaceTexture
909 * changes
910 */
911 public void setPreviewStatusListener(PreviewStatusListener previewStatusListener) {
912 mPreviewStatusListener = previewStatusListener;
913 if (mPreviewStatusListener != null) {
Erin Dahlgren1648c362014-01-06 15:06:04 -0800914 onPreviewListenerChanged();
915 }
916 }
917
918 /**
919 * When the PreviewStatusListener changes, listeners need to be
920 * set on the following app ui elements:
921 * {@link com.android.camera.ui.PreviewOverlay},
922 * {@link com.android.camera.ui.BottomBar},
Erin Dahlgrene2a1f542014-01-08 14:34:35 -0800923 * {@link com.android.camera.ui.IndicatorIconController}.
Erin Dahlgren1648c362014-01-06 15:06:04 -0800924 */
925 private void onPreviewListenerChanged() {
926 // Set a listener for recognizing preview gestures.
927 GestureDetector.OnGestureListener gestureListener
928 = mPreviewStatusListener.getGestureListener();
929 if (gestureListener != null) {
930 mPreviewOverlay.setGestureListener(gestureListener);
931 }
Erin Dahlgrend8de0772014-02-03 10:12:27 -0800932 View.OnTouchListener touchListener = mPreviewStatusListener.getTouchListener();
933 if (touchListener != null) {
934 mPreviewOverlay.setTouchListener(touchListener);
935 }
Erin Dahlgren1648c362014-01-06 15:06:04 -0800936
Erin Dahlgren1648c362014-01-06 15:06:04 -0800937 mTextureViewHelper.setAutoAdjustTransform(
Angus Kongb2597012014-03-10 12:12:09 -0700938 mPreviewStatusListener.shouldAutoAdjustTransformMatrixOnLayout());
Erin Dahlgren1648c362014-01-06 15:06:04 -0800939 }
940
941 /**
942 * This method should be called in onCameraOpened. It defines CameraAppUI
943 * specific changes that depend on the camera or camera settings.
944 */
945 public void onChangeCamera() {
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800946 ModuleController moduleController = mController.getCurrentModuleController();
Angus Kong50521172014-01-17 17:23:59 -0800947 applyModuleSpecs(moduleController.getHardwareSpec(), moduleController.getBottomBarSpec());
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800948
Erin Dahlgrene2a1f542014-01-08 14:34:35 -0800949 if (mIndicatorIconController != null) {
Erin Dahlgren18484942014-01-07 11:46:57 -0800950 // Sync the settings state with the indicator state.
Erin Dahlgrena9068092014-01-09 15:09:05 -0800951 mIndicatorIconController.syncIndicators();
Doris Liu06db7422013-12-09 19:36:25 -0800952 }
953 }
954
955 /**
Angus Kong2bacca72014-03-06 12:57:29 -0800956 * Adds a listener to receive callbacks when preview area changes.
Doris Liu482de022013-12-18 19:18:16 -0800957 */
Angus Kong2bacca72014-03-06 12:57:29 -0800958 public void addPreviewAreaChangedListener(
959 PreviewStatusListener.PreviewAreaChangedListener listener) {
Doris Liu482de022013-12-18 19:18:16 -0800960 mTextureViewHelper.addPreviewAreaSizeChangedListener(listener);
961 }
962
963 /**
Angus Kong2bacca72014-03-06 12:57:29 -0800964 * Removes a listener that receives callbacks when preview area changes.
Doris Liu482de022013-12-18 19:18:16 -0800965 */
Angus Kong2bacca72014-03-06 12:57:29 -0800966 public void removePreviewAreaChangedListener(
967 PreviewStatusListener.PreviewAreaChangedListener listener) {
Doris Liu482de022013-12-18 19:18:16 -0800968 mTextureViewHelper.removePreviewAreaSizeChangedListener(listener);
969 }
970
971 /**
Doris Liuf55f3c42013-11-20 00:24:46 -0800972 * This inflates generic_module layout, which contains all the shared views across
973 * modules. Then each module inflates their own views in the given view group. For
974 * now, this is called every time switching from a not-yet-refactored module to a
975 * refactored module. In the future, this should only need to be done once per app
976 * start.
977 */
978 public void prepareModuleUI() {
979 mCameraRootView.removeAllViews();
980 LayoutInflater inflater = (LayoutInflater) mController.getAndroidContext()
981 .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
982 inflater.inflate(R.layout.generic_module, mCameraRootView, true);
983
Doris Liuc6c97402013-12-06 21:02:53 -0800984 mModuleUI = (FrameLayout) mCameraRootView.findViewById(R.id.module_layout);
Doris Liuf55f3c42013-11-20 00:24:46 -0800985 mTextureView = (TextureView) mCameraRootView.findViewById(R.id.preview_content);
Doris Liu70da9182013-12-17 18:41:15 -0800986 mTextureViewHelper = new TextureViewHelper(mTextureView);
987 mTextureViewHelper.setSurfaceTextureListener(this);
988 mTextureViewHelper.setOnLayoutChangeListener(mPreviewLayoutChangeListener);
989
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800990 mBottomBar = (BottomBar) mCameraRootView.findViewById(R.id.bottom_bar);
Erin Dahlgrend8de0772014-02-03 10:12:27 -0800991 int unpressedColor = mController.getAndroidContext().getResources()
992 .getColor(R.color.bottombar_unpressed);
993 setBottomBarColor(unpressedColor);
994 int pressedColor = mController.getAndroidContext().getResources()
995 .getColor(R.color.bottombar_pressed);
996 setBottomBarPressedColor(pressedColor);
997
Erin Dahlgren165bb8d2014-01-30 14:11:20 -0800998 mModeOptionsOverlay
999 = (ModeOptionsOverlay) mCameraRootView.findViewById(R.id.mode_options_overlay);
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001000 mTextureViewHelper.addPreviewAreaSizeChangedListener(mModeOptionsOverlay);
1001
1002 // Sets the visibility of the bottom bar and the mode options.
1003 resetBottomControls(mController.getCurrentModuleController(),
1004 mController.getCurrentModuleIndex());
Erin Dahlgren56688f72014-01-09 14:30:34 -08001005
Erin Dahlgren5d187692014-02-25 19:16:12 -08001006 mShutterButton = (ShutterButton) mCameraRootView.findViewById(R.id.shutter_button);
1007 addShutterListener(mController.getCurrentModuleController());
1008 addShutterListener(mModeOptionsOverlay);
1009
Erin Dahlgrenb3f59a82014-02-04 15:23:30 -08001010 mGridLines = (GridLines) mCameraRootView.findViewById(R.id.grid_lines);
1011 mTextureViewHelper.addPreviewAreaSizeChangedListener(mGridLines);
1012
Doris Liuf9e4f8f2013-12-04 18:04:22 -08001013 mPreviewOverlay = (PreviewOverlay) mCameraRootView.findViewById(R.id.preview_overlay);
1014 mPreviewOverlay.setOnTouchListener(new MyTouchListener());
Erin Dahlgren165bb8d2014-01-30 14:11:20 -08001015 mPreviewOverlay.setOnPreviewTouchedListener(mModeOptionsOverlay);
Erin Dahlgren56688f72014-01-09 14:30:34 -08001016
Doris Liu1dfe7822013-12-12 00:02:08 -08001017 mCaptureOverlay = (CaptureAnimationOverlay)
1018 mCameraRootView.findViewById(R.id.capture_overlay);
Doris Liuca4a5662014-01-02 17:40:12 -08001019 mTextureViewHelper.addPreviewAreaSizeChangedListener(mPreviewOverlay);
1020 mTextureViewHelper.addPreviewAreaSizeChangedListener(mCaptureOverlay);
Doris Liu213a4a02014-02-04 16:57:55 -08001021 mTextureViewHelper.addPreviewAreaSizeChangedListener(mModeListView);
Erin Dahlgren4efa8b52013-12-17 18:31:35 -08001022
Erin Dahlgrene2a1f542014-01-08 14:34:35 -08001023 if (mIndicatorIconController == null) {
1024 mIndicatorIconController =
1025 new IndicatorIconController(mController, mAppRootView);
1026 }
Erin Dahlgren6e07fe22014-02-03 15:55:31 -08001027 mIndicatorIconController.setListener(mModeOptionsOverlay);
Erin Dahlgrenf1b2e402014-01-14 11:08:59 -08001028
1029 mController.getButtonManager().load(mCameraRootView);
Erin Dahlgrene2a1f542014-01-08 14:34:35 -08001030 mController.getButtonManager().setListener(mIndicatorIconController);
Erin Dahlgren6e07fe22014-02-03 15:55:31 -08001031 mController.getSettingsManager().addListener(mIndicatorIconController);
Doris Liu9d264302014-02-11 10:41:40 -08001032
1033 mModeOptionsToggle = mCameraRootView.findViewById(R.id.mode_options_toggle);
1034 mBottomBar.addOnLayoutChangeListener(mBottomBarLayoutChangeListener);
Doris Liuaf656832014-02-19 14:12:06 -08001035 mFocusOverlay = mCameraRootView.findViewById(R.id.focus_overlay);
Sascha Haeberlinga9a9d152014-03-05 08:46:55 -08001036 mTutorialsPlaceholder = (FrameLayout) mCameraRootView
1037 .findViewById(R.id.tutorials_placeholder);
Angus Kongb2597012014-03-10 12:12:09 -07001038
1039 mTextureViewHelper.addPreviewAreaSizeChangedListener(
1040 new PreviewStatusListener.PreviewAreaChangedListener() {
1041 @Override
1042 public void onPreviewAreaChanged(RectF previewArea) {
1043 if (mPreviewStatusListener != null &&
1044 mPreviewStatusListener.shouldAutoAdjustBottomBar()) {
1045 mBottomBar.onPreviewAreaChanged(previewArea);
1046 } else {
1047 mPeekView.setTranslationX(previewArea.right - mAppRootView.getRight());
1048 }
1049 }
1050 });
1051
1052 mBottomBar.setAdjustPreviewAreaListener(new BottomBar.AdjustPreviewAreaListener() {
1053 @Override
1054 public void fitAndCenterPreviewAreaInRect(RectF rect) {
1055 mPeekView.setTranslationX(0f);
1056 if (mPreviewStatusListener != null &&
1057 mPreviewStatusListener.shouldAutoAdjustTransformMatrixOnLayout()) {
1058 mTextureViewHelper.centerPreviewInRect(rect);
1059 }
1060 }
1061
1062 @Override
1063 public void fitAndAlignBottomInRect(RectF rect) {
1064 mPeekView.setTranslationX(0f);
1065 if (mPreviewStatusListener != null &&
1066 mPreviewStatusListener.shouldAutoAdjustTransformMatrixOnLayout()) {
1067 mTextureViewHelper.alignBottomInRect(rect);
1068 }
1069 }
1070
1071 @Override
1072 public void fitAndAlignRightInRect(RectF rect) {
1073 mPeekView.setTranslationX(rect.right - mAppRootView.getRight());
1074 if (mPreviewStatusListener != null &&
1075 mPreviewStatusListener.shouldAutoAdjustTransformMatrixOnLayout()) {
1076 mTextureViewHelper.alignRightInRect(rect);
1077 }
1078 }
1079 });
Doris Liuf55f3c42013-11-20 00:24:46 -08001080 }
1081
Doris Liuf55f3c42013-11-20 00:24:46 -08001082 /**
1083 * Called indirectly from each module in their initialization to get a view group
1084 * to inflate the module specific views in.
1085 *
1086 * @return a view group for modules to attach views to
1087 */
Doris Liuc6c97402013-12-06 21:02:53 -08001088 public FrameLayout getModuleRootView() {
Doris Liub41034b2013-12-06 21:58:41 -08001089 // TODO: Change it to mModuleUI when refactor is done
1090 return mCameraRootView;
Doris Liuf55f3c42013-11-20 00:24:46 -08001091 }
1092
1093 /**
1094 * Remove all the module specific views.
1095 */
1096 public void clearModuleUI() {
1097 if (mModuleUI != null) {
1098 mModuleUI.removeAllViews();
1099 }
Erin Dahlgren5d187692014-02-25 19:16:12 -08001100 removeShutterListener(mController.getCurrentModuleController());
Sascha Haeberlinga9a9d152014-03-05 08:46:55 -08001101 mTutorialsPlaceholder.removeAllViews();
Marco Nelissen0744e4a2013-11-22 01:47:37 +00001102
Doris Liu06db7422013-12-09 19:36:25 -08001103 mPreviewStatusListener = null;
Doris Liuf9e4f8f2013-12-04 18:04:22 -08001104 mPreviewOverlay.reset();
Doris Liuaf656832014-02-19 14:12:06 -08001105 mFocusOverlay.setVisibility(View.INVISIBLE);
Doris Liuf55f3c42013-11-20 00:24:46 -08001106 }
1107
Doris Liu2b906b82013-12-10 16:34:08 -08001108 /**
Doris Liu5a367542014-01-17 17:21:42 -08001109 * Gets called when preview is ready to start. It sets up one shot preview callback
1110 * in order to receive a callback when the preview frame is available, so that
1111 * the preview cover can be hidden to reveal preview.
1112 *
1113 * An alternative for getting the timing to hide preview cover is through
1114 * {@link CameraAppUI#onSurfaceTextureUpdated(android.graphics.SurfaceTexture)},
1115 * which is less accurate but therefore is the fallback for modules that manage
1116 * their own preview callbacks (as setting one preview callback will override
1117 * any other installed preview callbacks), or use camera2 API.
1118 */
1119 public void onPreviewReadyToStart() {
1120 if (mModeCoverState == COVER_SHOWN) {
1121 mModeCoverState = COVER_WILL_HIDE_AT_NEXT_FRAME;
1122 mController.setupOneShotPreviewListener();
1123 }
1124 }
1125
1126 /**
Doris Liu2b906b82013-12-10 16:34:08 -08001127 * Gets called when preview is started.
1128 */
1129 public void onPreviewStarted() {
1130 if (mModeCoverState == COVER_SHOWN) {
Doris Liuc42677f2013-12-20 19:53:01 -08001131 mModeCoverState = COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE;
Doris Liu2b906b82013-12-10 16:34:08 -08001132 }
1133 }
1134
Doris Liub6eaa8c2013-12-11 18:39:14 -08001135 /**
Doris Liu4d4a4bc2013-12-19 18:55:54 -08001136 * Gets notified when next preview frame comes in.
1137 */
1138 public void onNewPreviewFrame() {
Doris Liu7cbecee2014-01-30 12:29:27 -08001139 CameraPerformanceTracker.onEvent(CameraPerformanceTracker.FIRST_PREVIEW_FRAME);
Doris Liu4d4a4bc2013-12-19 18:55:54 -08001140 hideModeCover();
1141 mModeCoverState = COVER_HIDDEN;
1142 }
1143
1144 /**
Doris Liub6eaa8c2013-12-11 18:39:14 -08001145 * Gets called when a mode is selected from {@link com.android.camera.ui.ModeListView}
1146 *
1147 * @param modeIndex mode index of the selected mode
1148 */
Doris Liuf55f3c42013-11-20 00:24:46 -08001149 @Override
1150 public void onModeSelected(int modeIndex) {
Doris Liub6eaa8c2013-12-11 18:39:14 -08001151 mHideCoverRunnable = new Runnable() {
1152 @Override
1153 public void run() {
1154 mModeListView.startModeSelectionAnimation();
1155 }
1156 };
Doris Liue038c162013-12-13 23:06:11 -08001157 mModeCoverState = COVER_SHOWN;
1158
1159 int lastIndex = mController.getCurrentModuleIndex();
1160 mController.onModeSelected(modeIndex);
1161 int currentIndex = mController.getCurrentModuleIndex();
Doris Liub6eaa8c2013-12-11 18:39:14 -08001162
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001163 if (lastIndex == currentIndex) {
Doris Liub6eaa8c2013-12-11 18:39:14 -08001164 hideModeCover();
Doris Liub6eaa8c2013-12-11 18:39:14 -08001165 }
Doris Liuf55f3c42013-11-20 00:24:46 -08001166 }
1167
Doris Liu213a4a02014-02-04 16:57:55 -08001168 @Override
Doris Liu9d264302014-02-11 10:41:40 -08001169 public void onSettingsSelected() {
1170 mController.onSettingsSelected();
1171 }
1172
1173 @Override
Doris Liu213a4a02014-02-04 16:57:55 -08001174 public int getCurrentModeIndex() {
1175 return mController.getCurrentModuleIndex();
1176 }
1177
Doris Liuf55f3c42013-11-20 00:24:46 -08001178 /********************** Capture animation **********************/
1179 /* TODO: This session is subject to UX changes. In addition to the generic
1180 flash animation and post capture animation, consider designating a parameter
1181 for specifying the type of animation, as well as an animation finished listener
1182 so that modules can have more knowledge of the status of the animation. */
1183
1184 /**
Angus Kong50521172014-01-17 17:23:59 -08001185 * Starts the filmstrip peek animation.
1186 *
1187 * @param bitmap The bitmap to show.
1188 * @param strong Whether the animation shows more portion of the bitmap or
1189 * not.
1190 */
1191 public void startPeekAnimation(Bitmap bitmap, boolean strong) {
1192 if (mFilmstripLayout.getVisibility() == View.VISIBLE) {
1193 return;
1194 }
1195 mPeekView.startPeekAnimation(bitmap, strong);
1196 }
1197
1198 /**
Doris Liuf55f3c42013-11-20 00:24:46 -08001199 * Starts the pre-capture animation.
1200 */
1201 public void startPreCaptureAnimation() {
Doris Liu1dfe7822013-12-12 00:02:08 -08001202 mCaptureOverlay.startFlashAnimation();
Doris Liuf55f3c42013-11-20 00:24:46 -08001203 }
1204
1205 /**
1206 * Cancels the pre-capture animation.
1207 */
1208 public void cancelPreCaptureAnimation() {
1209 mAnimationManager.cancelAnimations();
1210 }
1211
1212 /**
Doris Liuf55f3c42013-11-20 00:24:46 -08001213 * Cancels the post-capture animation.
1214 */
1215 public void cancelPostCaptureAnimation() {
1216 mAnimationManager.cancelAnimations();
1217 }
Doris Liu06db7422013-12-09 19:36:25 -08001218
Angus Kongb2510252013-12-10 22:58:15 -08001219 public FilmstripContentPanel getFilmstripContentPanel() {
1220 return mFilmstripPanel;
1221 }
1222
1223 /**
Angus Kong7ae25c22014-02-25 10:37:39 -08001224 * @return The {@link com.android.camera.app.CameraAppUI.BottomPanel} on the
Angus Kongb2510252013-12-10 22:58:15 -08001225 * bottom of the filmstrip.
1226 */
Angus Kong7ae25c22014-02-25 10:37:39 -08001227 public BottomPanel getFilmstripBottomControls() {
Angus Konge0aff892013-12-11 20:51:01 -08001228 return mFilmstripBottomControls;
Angus Kongb2510252013-12-10 22:58:15 -08001229 }
1230
1231 /**
1232 * @param listener The listener for bottom controls.
1233 */
Angus Kong7ae25c22014-02-25 10:37:39 -08001234 public void setFilmstripBottomControlsListener(BottomPanel.Listener listener) {
Angus Konge0aff892013-12-11 20:51:01 -08001235 mFilmstripBottomControls.setListener(listener);
Angus Kongb2510252013-12-10 22:58:15 -08001236 }
1237
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001238 /***************************SurfaceTexture Api and Listener*********************************/
1239
1240 /**
1241 * Return the shared surface texture.
1242 */
1243 public SurfaceTexture getSurfaceTexture() {
1244 return mSurface;
1245 }
1246
1247 /**
1248 * Return the shared {@link android.graphics.SurfaceTexture}'s width.
1249 */
1250 public int getSurfaceWidth() {
1251 return mSurfaceWidth;
1252 }
1253
1254 /**
1255 * Return the shared {@link android.graphics.SurfaceTexture}'s height.
1256 */
1257 public int getSurfaceHeight() {
1258 return mSurfaceHeight;
1259 }
Doris Liu06db7422013-12-09 19:36:25 -08001260
1261 @Override
1262 public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001263 mSurface = surface;
1264 mSurfaceWidth = width;
1265 mSurfaceHeight = height;
Doris Liu2b906b82013-12-10 16:34:08 -08001266 Log.v(TAG, "SurfaceTexture is available");
Doris Liu06db7422013-12-09 19:36:25 -08001267 if (mPreviewStatusListener != null) {
1268 mPreviewStatusListener.onSurfaceTextureAvailable(surface, width, height);
1269 }
1270 }
1271
1272 @Override
1273 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001274 mSurface = surface;
1275 mSurfaceWidth = width;
1276 mSurfaceHeight = height;
Doris Liu06db7422013-12-09 19:36:25 -08001277 if (mPreviewStatusListener != null) {
1278 mPreviewStatusListener.onSurfaceTextureSizeChanged(surface, width, height);
1279 }
1280 }
1281
1282 @Override
1283 public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001284 mSurface = null;
Doris Liu2b906b82013-12-10 16:34:08 -08001285 Log.v(TAG, "SurfaceTexture is destroyed");
Doris Liu06db7422013-12-09 19:36:25 -08001286 if (mPreviewStatusListener != null) {
1287 return mPreviewStatusListener.onSurfaceTextureDestroyed(surface);
1288 }
1289 return false;
1290 }
1291
1292 @Override
1293 public void onSurfaceTextureUpdated(SurfaceTexture surface) {
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001294 mSurface = surface;
Doris Liu4d4a4bc2013-12-19 18:55:54 -08001295 if (mModeCoverState == COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE) {
Doris Liu7cbecee2014-01-30 12:29:27 -08001296 CameraPerformanceTracker.onEvent(CameraPerformanceTracker.FIRST_PREVIEW_FRAME);
Doris Liu2b906b82013-12-10 16:34:08 -08001297 hideModeCover();
1298 mModeCoverState = COVER_HIDDEN;
1299 }
Doris Liu06db7422013-12-09 19:36:25 -08001300 if (mPreviewStatusListener != null) {
1301 mPreviewStatusListener.onSurfaceTextureUpdated(surface);
1302 }
1303 }
Erin Dahlgren4efa8b52013-12-17 18:31:35 -08001304
Erin Dahlgrenb3f59a82014-02-04 15:23:30 -08001305 /****************************Grid lines api ******************************/
1306
1307 /**
1308 * Show a set of evenly spaced lines over the preview. The number
1309 * of lines horizontally and vertically is determined by
1310 * {@link com.android.camera.ui.GridLines}.
1311 */
1312 public void showGridLines() {
1313 if (mGridLines != null) {
1314 mGridLines.setVisibility(View.VISIBLE);
1315 }
1316 }
1317
1318 /**
1319 * Hide the set of evenly spaced grid lines overlaying the preview.
1320 */
1321 public void hideGridLines() {
1322 if (mGridLines != null) {
1323 mGridLines.setVisibility(View.INVISIBLE);
1324 }
1325 }
1326
Erin Dahlgrend5e51462014-02-07 12:38:57 -08001327 /**
1328 * Return a callback which shows or hide the preview grid lines
1329 * depending on whether the grid lines setting is set on.
1330 */
1331 public ButtonManager.ButtonCallback getGridLinesCallback() {
1332 return new ButtonManager.ButtonCallback() {
1333 @Override
1334 public void onStateChanged(int state) {
1335 if (mController.getSettingsManager().areGridLinesOn()) {
1336 showGridLines();
1337 } else {
1338 hideGridLines();
1339 }
1340 }
1341 };
1342 }
1343
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001344 /***************************Mode options api *****************************/
1345
1346 /**
1347 * Set the mode options visible.
1348 */
1349 public void showModeOptions() {
1350 mModeOptionsOverlay.setVisibility(View.VISIBLE);
1351 }
1352
1353 /**
1354 * Set the mode options invisible. This is necessary for modes
1355 * that don't show a bottom bar for the capture UI.
1356 */
1357 public void hideModeOptions() {
1358 mModeOptionsOverlay.setVisibility(View.INVISIBLE);
1359 }
Erin Dahlgrenb3f59a82014-02-04 15:23:30 -08001360
1361 /****************************Bottom bar api ******************************/
1362
Erin Dahlgren4efa8b52013-12-17 18:31:35 -08001363 /**
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001364 * Sets up the bottom bar and mode options with the correct
1365 * shutter button and visibility based on the current module.
1366 */
1367 public void resetBottomControls(ModuleController module, int moduleIndex) {
1368 if (areBottomControlsUsed(module)) {
1369 setBottomBarShutterIcon(moduleIndex);
1370 }
1371 }
1372
1373 /**
1374 * Show or hide the mode options and bottom bar, based on
1375 * whether the current module is using the bottom bar. Returns
1376 * whether the mode options and bottom bar are used.
1377 */
1378 private boolean areBottomControlsUsed(ModuleController module) {
1379 if (module.isUsingBottomBar()) {
1380 showBottomBar();
1381 showModeOptions();
1382 return true;
1383 } else {
1384 hideBottomBar();
1385 hideModeOptions();
1386 return false;
1387 }
1388 }
1389
1390 /**
1391 * Set the bottom bar visible.
1392 */
1393 public void showBottomBar() {
1394 mBottomBar.setVisibility(View.VISIBLE);
1395 }
1396
1397 /**
1398 * Set the bottom bar invisible.
1399 */
1400 public void hideBottomBar() {
1401 mBottomBar.setVisibility(View.INVISIBLE);
1402 }
1403
1404 /**
Erin Dahlgren4efa8b52013-12-17 18:31:35 -08001405 * Sets the color of the bottom bar.
1406 */
1407 public void setBottomBarColor(int colorId) {
1408 mBottomBar.setBackgroundColor(colorId);
1409 }
1410
1411 /**
Spike Spragueb92910e2014-01-13 18:10:54 -08001412 * Sets the pressed color of the bottom bar.
1413 */
1414 public void setBottomBarPressedColor(int colorId) {
1415 mBottomBar.setBackgroundPressedColor(colorId);
1416 }
1417
1418 /**
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001419 * Sets the shutter button icon on the bottom bar, based on
1420 * the mode index.
Spike Sprague39f8a762014-01-13 14:22:18 -08001421 */
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001422 public void setBottomBarShutterIcon(int modeIndex) {
1423 int shutterIconId = CameraUtil.getCameraShutterIconId(modeIndex,
1424 mController.getAndroidContext());
Erin Dahlgrenfecd7232014-01-16 13:29:42 -08001425 mBottomBar.setShutterButtonIcon(shutterIconId);
Spike Sprague39f8a762014-01-13 14:22:18 -08001426 }
1427
Spike Sprague45a821d2014-03-14 14:32:54 -07001428 public void animateBottomBarToVideoStop(int shutterIconId) {
1429 mBottomBar.animateToVideoStop(shutterIconId);
Spike Sprague39f8a762014-01-13 14:22:18 -08001430 }
1431
Erin Dahlgrenfecd7232014-01-16 13:29:42 -08001432 public void animateBottomBarToFullSize(int shutterIconId) {
1433 mBottomBar.animateToFullSize(shutterIconId);
Spike Sprague39f8a762014-01-13 14:22:18 -08001434 }
1435
Spike Spraguea93909f2014-03-07 17:06:13 -08001436 public void setCaptureButtonEnabled(boolean enabled) {
1437 mBottomBar.setCaptureButtonEnabled(enabled);
1438 }
1439
Alan Newbergerf495ea82014-03-24 18:47:16 -07001440 public void setModeOptionsVisible(boolean visible) {
1441 mModeOptionsOverlay.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
1442 }
1443
Spike Sprague39f8a762014-01-13 14:22:18 -08001444 /**
Erin Dahlgren4efa8b52013-12-17 18:31:35 -08001445 * Set the visibility of the bottom bar.
1446 */
1447 // TODO: needed for when panorama is managed by the generic module ui.
1448 public void setBottomBarVisible(boolean visible) {
1449 mBottomBar.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
1450 }
1451
1452 /**
Erin Dahlgren5d187692014-02-25 19:16:12 -08001453 * Add a {@link #ShutterButton.OnShutterButtonListener} to the shutter button.
Erin Dahlgren4efa8b52013-12-17 18:31:35 -08001454 */
Erin Dahlgren5d187692014-02-25 19:16:12 -08001455 public void addShutterListener(ShutterButton.OnShutterButtonListener listener) {
1456 mShutterButton.addOnShutterButtonListener(listener);
1457 }
1458
1459 /**
1460 * Remove a {@link #ShutterButton.OnShutterButtonListener} from the shutter button.
1461 */
1462 public void removeShutterListener(ShutterButton.OnShutterButtonListener listener) {
1463 mShutterButton.removeOnShutterButtonListener(listener);
Erin Dahlgren4efa8b52013-12-17 18:31:35 -08001464 }
1465
1466 /**
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001467 * Performs a transition to the capture layout of the bottom bar.
1468 */
1469 public void transitionToCapture() {
1470 ModuleController moduleController = mController.getCurrentModuleController();
1471 applyModuleSpecs(moduleController.getHardwareSpec(),
1472 moduleController.getBottomBarSpec());
1473 mBottomBar.transitionToCapture();
1474 }
1475
1476 /**
Seth Raphael2db51d72014-03-11 10:52:05 -07001477 * Displays the Cancel button instead of the capture button.
1478 */
1479 public void transitionToCancel() {
1480 ModuleController moduleController = mController.getCurrentModuleController();
1481 applyModuleSpecs(moduleController.getHardwareSpec(),
1482 moduleController.getBottomBarSpec());
1483 mBottomBar.transitionToCancel();
1484 }
1485
1486 /**
Erin Dahlgren4efa8b52013-12-17 18:31:35 -08001487 * Performs a transition to the global intent layout.
1488 */
Spike Sprague15690d02014-02-10 12:11:20 -08001489 public void transitionToIntentCaptureLayout() {
Erin Dahlgrenb1641f52014-01-14 15:58:52 -08001490 ModuleController moduleController = mController.getCurrentModuleController();
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001491 applyModuleSpecs(moduleController.getHardwareSpec(),
1492 moduleController.getBottomBarSpec());
Spike Sprague15690d02014-02-10 12:11:20 -08001493 mBottomBar.transitionToIntentCaptureLayout();
Erin Dahlgren4efa8b52013-12-17 18:31:35 -08001494 }
1495
1496 /**
1497 * Performs a transition to the global intent review layout.
1498 */
1499 public void transitionToIntentReviewLayout() {
Erin Dahlgrenb1641f52014-01-14 15:58:52 -08001500 ModuleController moduleController = mController.getCurrentModuleController();
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001501 applyModuleSpecs(moduleController.getHardwareSpec(),
1502 moduleController.getBottomBarSpec());
1503 mBottomBar.transitionToIntentReviewLayout();
Erin Dahlgrenb1641f52014-01-14 15:58:52 -08001504 }
1505
1506 /**
1507 * Applies a {@link com.android.camera.CameraAppUI.BottomBarUISpec}
1508 * to the bottom bar mode options based on limitations from a
1509 * {@link com.android.camera.hardware.HardwareSpec}.
1510 *
1511 * Options not supported by the hardware are either hidden
1512 * or disabled, depending on the option.
1513 *
1514 * Otherwise, the option is fully enabled and clickable.
1515 */
1516 public void applyModuleSpecs(final HardwareSpec hardwareSpec,
1517 final BottomBarUISpec bottomBarSpec) {
Erin Dahlgren49ab9222014-01-28 17:40:28 -08001518 if (hardwareSpec == null || bottomBarSpec == null) {
1519 return;
Erin Dahlgrenb1641f52014-01-14 15:58:52 -08001520 }
1521
1522 ButtonManager buttonManager = mController.getButtonManager();
1523 SettingsManager settingsManager = mController.getSettingsManager();
1524
1525 /** Standard mode options */
1526 if (hardwareSpec.isFrontCameraSupported()) {
1527 if (bottomBarSpec.enableCamera) {
1528 buttonManager.enableButton(ButtonManager.BUTTON_CAMERA,
1529 bottomBarSpec.cameraCallback);
1530 } else {
1531 buttonManager.disableButton(ButtonManager.BUTTON_CAMERA);
1532 }
1533 } else {
1534 // Hide camera icon if front camera not available.
1535 buttonManager.hideButton(ButtonManager.BUTTON_CAMERA);
1536 }
1537
Erin Dahlgrene346fb22014-02-19 17:23:01 -08001538 boolean flashBackCamera = mController.getSettingsManager().getBoolean(
1539 SettingsManager.SETTING_FLASH_SUPPORTED_BACK_CAMERA);
1540 if (bottomBarSpec.hideFlash || !flashBackCamera) {
Erin Dahlgrena6587a12014-02-03 13:24:55 -08001541 buttonManager.hideButton(ButtonManager.BUTTON_FLASH);
1542 } else {
1543 if (hardwareSpec.isFlashSupported()) {
Erin Dahlgrene346fb22014-02-19 17:23:01 -08001544 if (bottomBarSpec.enableFlash) {
Erin Dahlgrena6587a12014-02-03 13:24:55 -08001545 buttonManager.enableButton(ButtonManager.BUTTON_FLASH, bottomBarSpec.flashCallback);
Erin Dahlgrene346fb22014-02-19 17:23:01 -08001546 } else if (bottomBarSpec.enableTorchFlash) {
Erin Dahlgrena6587a12014-02-03 13:24:55 -08001547 buttonManager.enableButton(ButtonManager.BUTTON_TORCH, bottomBarSpec.flashCallback);
1548 } else {
1549 buttonManager.disableButton(ButtonManager.BUTTON_FLASH);
1550 }
Erin Dahlgrenb1641f52014-01-14 15:58:52 -08001551 } else {
Erin Dahlgrena6587a12014-02-03 13:24:55 -08001552 // Disable flash icon if not supported by the hardware.
Erin Dahlgrenb1641f52014-01-14 15:58:52 -08001553 buttonManager.disableButton(ButtonManager.BUTTON_FLASH);
1554 }
Erin Dahlgrenb1641f52014-01-14 15:58:52 -08001555 }
1556
Erin Dahlgren8c1f9702014-03-04 17:08:37 -08001557 if (bottomBarSpec.hideHdr || mIsCaptureIntent) {
Erin Dahlgrenb1641f52014-01-14 15:58:52 -08001558 // Force hide hdr or hdr plus icon.
1559 buttonManager.hideButton(ButtonManager.BUTTON_HDRPLUS);
1560 } else {
1561 if (hardwareSpec.isHdrPlusSupported()) {
1562 if (bottomBarSpec.enableHdr && settingsManager.isCameraBackFacing()) {
1563 buttonManager.enableButton(ButtonManager.BUTTON_HDRPLUS,
1564 bottomBarSpec.hdrCallback);
1565 } else {
1566 buttonManager.disableButton(ButtonManager.BUTTON_HDRPLUS);
1567 }
1568 } else if (hardwareSpec.isHdrSupported()) {
1569 if (bottomBarSpec.enableHdr && settingsManager.isCameraBackFacing()) {
1570 buttonManager.enableButton(ButtonManager.BUTTON_HDR,
1571 bottomBarSpec.hdrCallback);
1572 } else {
1573 buttonManager.disableButton(ButtonManager.BUTTON_HDR);
1574 }
1575 } else {
1576 // Hide hdr plus or hdr icon if neither are supported.
1577 buttonManager.hideButton(ButtonManager.BUTTON_HDRPLUS);
1578 }
1579 }
1580
Erin Dahlgrend5e51462014-02-07 12:38:57 -08001581 if (bottomBarSpec.hideGridLines) {
1582 // Force hide grid lines icon.
1583 buttonManager.hideButton(ButtonManager.BUTTON_GRID_LINES);
1584 hideGridLines();
1585 } else {
1586 if (bottomBarSpec.enableGridLines) {
1587 buttonManager.enableButton(ButtonManager.BUTTON_GRID_LINES,
1588 bottomBarSpec.gridLinesCallback != null ?
1589 bottomBarSpec.gridLinesCallback : getGridLinesCallback());
1590 } else {
1591 buttonManager.disableButton(ButtonManager.BUTTON_GRID_LINES);
1592 hideGridLines();
1593 }
1594 }
1595
Erin Dahlgrenf80ac9e2014-02-18 11:20:34 -08001596 if (bottomBarSpec.enablePanoOrientation
1597 && PhotoSphereHelper.getPanoramaOrientationOptionArrayId() > 0) {
1598 buttonManager.enableButton(ButtonManager.BUTTON_PANO_ORIENTATION,
Doris Liuaf656832014-02-19 14:12:06 -08001599 bottomBarSpec.panoOrientationCallback);
Erin Dahlgrena9ceaf72014-02-12 10:45:24 -08001600 } else {
Erin Dahlgrenf80ac9e2014-02-18 11:20:34 -08001601 buttonManager.hideButton(ButtonManager.BUTTON_PANO_ORIENTATION);
Erin Dahlgrena1fab412014-01-21 09:31:11 -08001602 }
1603
Erin Dahlgrenb1641f52014-01-14 15:58:52 -08001604 /** Intent UI */
1605 if (bottomBarSpec.showCancel) {
1606 buttonManager.enablePushButton(ButtonManager.BUTTON_CANCEL,
1607 bottomBarSpec.cancelCallback);
1608 }
1609 if (bottomBarSpec.showDone) {
1610 buttonManager.enablePushButton(ButtonManager.BUTTON_DONE,
1611 bottomBarSpec.doneCallback);
1612 }
1613 if (bottomBarSpec.showRetake) {
1614 buttonManager.enablePushButton(ButtonManager.BUTTON_RETAKE,
1615 bottomBarSpec.retakeCallback);
1616 }
Spike Sprague51c877c2014-02-18 11:14:12 -08001617 if (bottomBarSpec.showReview) {
1618 buttonManager.enablePushButton(ButtonManager.BUTTON_REVIEW,
1619 bottomBarSpec.reviewCallback,
1620 R.drawable.ic_play);
1621 }
Erin Dahlgren4efa8b52013-12-17 18:31:35 -08001622 }
Sascha Haeberlingc813ce12014-03-10 15:35:07 -07001623
1624 /**
1625 * Shows the given tutorial on the screen.
1626 */
1627 public void showTutorial(AbstractTutorialOverlay tutorial, LayoutInflater inflater) {
1628 tutorial.show(mTutorialsPlaceholder, inflater);
1629 }
Alan Newberger623dd0c2014-03-24 15:55:23 -07001630
1631 /***************************Filmstrip api *****************************/
1632
1633 public void showFilmstrip() {
1634 mFilmstripLayout.showFilmstrip();
1635 }
Alan Newberger8099a372014-03-24 17:17:38 -07001636
1637 public void hideFilmstrip() {
1638 mFilmstripLayout.hideFilmstrip();
1639 }
Doris Liuf55f3c42013-11-20 00:24:46 -08001640}