blob: c385d685953e2a92b0d95158efe4c1cab064478a [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
Sascha Haeberlingd4c33332014-05-19 15:41:00 -070019import android.accessibilityservice.AccessibilityServiceInfo;
Doris Liuf55f3c42013-11-20 00:24:46 -080020import android.content.Context;
Doris Liu28a29502014-04-10 16:01:51 -070021import android.content.res.Resources;
Doris Liu213a4a02014-02-04 16:57:55 -080022import android.graphics.Bitmap;
23import android.graphics.Canvas;
Doris Liuf55f3c42013-11-20 00:24:46 -080024import android.graphics.Matrix;
Doris Liu213a4a02014-02-04 16:57:55 -080025import android.graphics.RectF;
Doris Liu06db7422013-12-09 19:36:25 -080026import android.graphics.SurfaceTexture;
Doris Liu11ddd732014-01-07 10:52:11 -080027import android.hardware.display.DisplayManager;
Angus Kong50521172014-01-17 17:23:59 -080028import android.util.CameraPerformanceTracker;
Doris Liuf55f3c42013-11-20 00:24:46 -080029import android.view.GestureDetector;
30import android.view.LayoutInflater;
31import android.view.MotionEvent;
32import android.view.TextureView;
33import android.view.View;
34import android.view.ViewConfiguration;
Angus Konge0aff892013-12-11 20:51:01 -080035import android.view.ViewGroup;
Doris Liudfb5a6f2014-04-28 22:36:08 -070036import android.view.accessibility.AccessibilityManager;
Doris Liuc6c97402013-12-06 21:02:53 -080037import android.widget.FrameLayout;
Doris Liuf55f3c42013-11-20 00:24:46 -080038
39import com.android.camera.AnimationManager;
Erin Dahlgrenb1641f52014-01-14 15:58:52 -080040import com.android.camera.ButtonManager;
Doris Liu28a29502014-04-10 16:01:51 -070041import com.android.camera.CaptureLayoutHelper;
Erin Dahlgrenb1641f52014-01-14 15:58:52 -080042import com.android.camera.ShutterButton;
Sascha Haeberlinge2914fd2014-01-17 19:03:52 -080043import com.android.camera.TextureViewHelper;
Angus Kong5596b4c2014-03-11 16:27:30 -070044import com.android.camera.debug.Log;
Angus Kongb2510252013-12-10 22:58:15 -080045import com.android.camera.filmstrip.FilmstripContentPanel;
Erin Dahlgrenb1641f52014-01-14 15:58:52 -080046import com.android.camera.hardware.HardwareSpec;
47import com.android.camera.module.ModuleController;
Erin Dahlgren6190c362014-06-13 14:12:08 -070048import com.android.camera.settings.Keys;
Erin Dahlgrenb1641f52014-01-14 15:58:52 -080049import com.android.camera.settings.SettingsManager;
Sascha Haeberlingc813ce12014-03-10 15:35:07 -070050import com.android.camera.ui.AbstractTutorialOverlay;
Spike Sprague0f3c4b42013-12-10 19:50:17 -080051import com.android.camera.ui.BottomBar;
Doris Liu28a29502014-04-10 16:01:51 -070052import com.android.camera.ui.BottomBarModeOptionsWrapper;
Doris Liu1dfe7822013-12-12 00:02:08 -080053import com.android.camera.ui.CaptureAnimationOverlay;
Erin Dahlgrenb3f59a82014-02-04 15:23:30 -080054import com.android.camera.ui.GridLines;
Doris Liuf55f3c42013-11-20 00:24:46 -080055import com.android.camera.ui.MainActivityLayout;
56import com.android.camera.ui.ModeListView;
57import com.android.camera.ui.ModeTransitionView;
Doris Liuf9e4f8f2013-12-04 18:04:22 -080058import com.android.camera.ui.PreviewOverlay;
Doris Liu06db7422013-12-09 19:36:25 -080059import com.android.camera.ui.PreviewStatusListener;
Andy Huibersb7c7d9a2014-06-18 22:26:14 -070060import com.android.camera.ui.TouchCoordinate;
Doris Liu11ddd732014-01-07 10:52:11 -080061import com.android.camera.util.ApiHelper;
62import com.android.camera.util.CameraUtil;
Doris Liu9d264302014-02-11 10:41:40 -080063import com.android.camera.util.Gusterpolator;
Erin Dahlgrend706ea62014-01-21 16:16:06 -080064import com.android.camera.util.PhotoSphereHelper;
Doris Liue7d7b9e2014-03-31 15:27:40 -070065import com.android.camera.widget.Cling;
Sascha Haeberlingab05a742013-12-13 23:28:30 -080066import com.android.camera.widget.FilmstripLayout;
Sascha Haeberling8793eff2014-01-15 16:33:59 -080067import com.android.camera.widget.IndicatorIconController;
Erin Dahlgren165bb8d2014-01-30 14:11:20 -080068import com.android.camera.widget.ModeOptionsOverlay;
Angus Kong50521172014-01-17 17:23:59 -080069import com.android.camera.widget.PeekView;
Doris Liuf55f3c42013-11-20 00:24:46 -080070import com.android.camera2.R;
Erin Dahlgren77295162014-01-29 17:29:18 -080071
Sascha Haeberlingd4c33332014-05-19 15:41:00 -070072import java.util.List;
73
Doris Liuf55f3c42013-11-20 00:24:46 -080074/**
75 * CameraAppUI centralizes control of views shared across modules. Whereas module
76 * specific views will be handled in each Module UI. For example, we can now
77 * bring the flash animation and capture animation up from each module to app
78 * level, as these animations are largely the same for all modules.
79 *
80 * This class also serves to disambiguate touch events. It recognizes all the
81 * swipe gestures that happen on the preview by attaching a touch listener to
82 * a full-screen view on top of preview TextureView. Since CameraAppUI has knowledge
83 * of how swipe from each direction should be handled, it can then redirect these
84 * events to appropriate recipient views.
85 */
Doris Liu06db7422013-12-09 19:36:25 -080086public class CameraAppUI implements ModeListView.ModeSwitchListener,
Erin Dahlgren3c622212014-04-11 14:44:04 -070087 TextureView.SurfaceTextureListener,
88 ModeListView.ModeListOpenListener,
Erin Dahlgrena7919c72014-05-16 14:53:21 -070089 SettingsManager.OnSettingChangedListener,
90 ShutterButton.OnShutterButtonListener {
Angus Konge0aff892013-12-11 20:51:01 -080091
92 /**
93 * The bottom controls on the filmstrip.
94 */
Angus Kong7ae25c22014-02-25 10:37:39 -080095 public static interface BottomPanel {
Angus Konge0aff892013-12-11 20:51:01 -080096 /** Values for the view state of the button. */
Angus Kong00b33712014-01-16 11:06:46 -080097 public final int VIEWER_NONE = 0;
98 public final int VIEWER_PHOTO_SPHERE = 1;
99 public final int VIEWER_REFOCUS = 2;
100 public final int VIEWER_OTHER = 3;
Angus Konge0aff892013-12-11 20:51:01 -0800101
102 /**
103 * Sets a new or replaces an existing listener for bottom control events.
104 */
105 void setListener(Listener listener);
106
107 /**
Doris Liue7d7b9e2014-03-31 15:27:40 -0700108 * Sets cling for external viewer button.
109 */
110 void setClingForViewer(int viewerType, Cling cling);
111
112 /**
113 * Clears cling for external viewer button.
114 */
115 void clearClingForViewer(int viewerType);
116
117 /**
118 * Returns a cling for the specified viewer type.
119 */
120 Cling getClingForViewer(int viewerType);
121
122 /**
Angus Konge0aff892013-12-11 20:51:01 -0800123 * Set if the bottom controls are visible.
124 * @param visible {@code true} if visible.
125 */
126 void setVisible(boolean visible);
127
128 /**
129 * @param visible Whether the button is visible.
130 */
131 void setEditButtonVisibility(boolean visible);
132
133 /**
Angus Konge4002f32013-12-13 22:24:11 -0800134 * @param enabled Whether the button is enabled.
135 */
136 void setEditEnabled(boolean enabled);
137
138 /**
Angus Konge0aff892013-12-11 20:51:01 -0800139 * Sets the visibility of the view-photosphere button.
140 *
Angus Kong00b33712014-01-16 11:06:46 -0800141 * @param state one of {@link #VIEWER_NONE}, {@link #VIEWER_PHOTO_SPHERE},
142 * {@link #VIEWER_REFOCUS}.
Angus Konge0aff892013-12-11 20:51:01 -0800143 */
Angus Kong00b33712014-01-16 11:06:46 -0800144 void setViewerButtonVisibility(int state);
Angus Konge0aff892013-12-11 20:51:01 -0800145
146 /**
Angus Konge4002f32013-12-13 22:24:11 -0800147 * @param enabled Whether the button is enabled.
148 */
149 void setViewEnabled(boolean enabled);
150
151 /**
Angus Konge4002f32013-12-13 22:24:11 -0800152 * @param enabled Whether the button is enabled.
153 */
154 void setTinyPlanetEnabled(boolean enabled);
155
156 /**
Angus Konge0aff892013-12-11 20:51:01 -0800157 * @param visible Whether the button is visible.
158 */
159 void setDeleteButtonVisibility(boolean visible);
160
161 /**
Angus Konge4002f32013-12-13 22:24:11 -0800162 * @param enabled Whether the button is enabled.
163 */
164 void setDeleteEnabled(boolean enabled);
165
166 /**
Angus Konge0aff892013-12-11 20:51:01 -0800167 * @param visible Whether the button is visible.
168 */
169 void setShareButtonVisibility(boolean visible);
170
171 /**
Angus Konge4002f32013-12-13 22:24:11 -0800172 * @param enabled Whether the button is enabled.
173 */
174 void setShareEnabled(boolean enabled);
175
176 /**
Angus Kong7ae25c22014-02-25 10:37:39 -0800177 * Sets the texts for progress UI.
178 *
179 * @param text The text to show.
180 */
181 void setProgressText(CharSequence text);
182
183 /**
184 * Sets the progress.
185 *
186 * @param progress The progress value. Should be between 0 and 100.
187 */
188 void setProgress(int progress);
189
190 /**
Sascha Haeberling597c1a02014-03-06 11:46:06 -0800191 * Replaces the progress UI with an error message.
192 */
193 void showProgressError(CharSequence message);
194
195 /**
196 * Hide the progress error message.
197 */
198 void hideProgressError();
199
200 /**
Angus Kong001dc312014-02-27 16:58:11 -0800201 * Shows the progress.
Angus Kong7ae25c22014-02-25 10:37:39 -0800202 */
203 void showProgress();
204
205 /**
Angus Kong001dc312014-02-27 16:58:11 -0800206 * Hides the progress.
207 */
208 void hideProgress();
209
210 /**
211 * Shows the controls.
Angus Kong7ae25c22014-02-25 10:37:39 -0800212 */
213 void showControls();
214
215 /**
Angus Kong001dc312014-02-27 16:58:11 -0800216 * Hides the controls.
217 */
218 void hideControls();
219
220 /**
Angus Konge0aff892013-12-11 20:51:01 -0800221 * Classes implementing this interface can listen for events on the bottom
222 * controls.
223 */
224 public static interface Listener {
225 /**
226 * Called when the user pressed the "view" button to e.g. view a photo
227 * sphere or RGBZ image.
228 */
Angus Kong8a2350a2013-12-16 15:02:34 -0800229 public void onExternalViewer();
Angus Konge0aff892013-12-11 20:51:01 -0800230
231 /**
232 * Called when the "edit" button is pressed.
233 */
234 public void onEdit();
235
236 /**
237 * Called when the "tiny planet" button is pressed.
238 */
239 public void onTinyPlanet();
240
241 /**
242 * Called when the "delete" button is pressed.
243 */
244 public void onDelete();
245
246 /**
247 * Called when the "share" button is pressed.
248 */
249 public void onShare();
Sascha Haeberling597c1a02014-03-06 11:46:06 -0800250
251 /**
252 * Called when the progress error message is clicked.
253 */
254 public void onProgressErrorClicked();
Angus Konge0aff892013-12-11 20:51:01 -0800255 }
256 }
257
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800258 /**
259 * BottomBarUISpec provides a structure for modules
260 * to specify their ideal bottom bar mode options layout.
261 *
262 * Once constructed by a module, this class should be
263 * treated as read only.
264 *
265 * The application then edits this spec according to
266 * hardware limitations and displays the final bottom
267 * bar ui.
268 */
269 public static class BottomBarUISpec {
270 /** Mode options UI */
271
272 /**
273 * Set true if the camera option should be enabled.
274 * If not set or false, and multiple cameras are supported,
275 * the camera option will be disabled.
276 *
277 * If multiple cameras are not supported, this preference
278 * is ignored and the camera option will not be visible.
279 */
280 public boolean enableCamera;
281
282 /**
Erin Dahlgrena9ceaf72014-02-12 10:45:24 -0800283 * Set true if the camera option should not be visible, regardless
284 * of hardware limitations.
285 */
286 public boolean hideCamera;
287
288 /**
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800289 * Set true if the photo flash option should be enabled.
290 * If not set or false, the photo flash option will be
291 * disabled.
292 *
293 * If the hardware does not support multiple flash values,
294 * this preference is ignored and the flash option will
295 * be disabled. It will not be made invisible in order to
296 * preserve a consistent experience across devices and between
297 * front and back cameras.
298 */
299 public boolean enableFlash;
300
301 /**
302 * Set true if the video flash option should be enabled.
303 * Same disable rules apply as the photo flash option.
304 */
305 public boolean enableTorchFlash;
306
307 /**
Erin Dahlgren15473d42014-05-14 14:01:31 -0700308 * Set true if the HDR+ flash option should be enabled.
309 * Same disable rules apply as the photo flash option.
310 */
311 public boolean enableHdrPlusFlash;
312
313 /**
Erin Dahlgrend5e51462014-02-07 12:38:57 -0800314 * Set true if flash should not be visible, regardless of
315 * hardware limitations.
316 */
317 public boolean hideFlash;
318
319 /**
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800320 * Set true if the hdr/hdr+ option should be enabled.
321 * If not set or false, the hdr/hdr+ option will be disabled.
322 *
323 * Hdr or hdr+ will be chosen based on hardware limitations,
324 * with hdr+ prefered.
325 *
326 * If hardware supports neither hdr nor hdr+, then the hdr/hdr+
327 * will not be visible.
328 */
329 public boolean enableHdr;
330
331 /**
332 * Set true if hdr/hdr+ should not be visible, regardless of
333 * hardware limitations.
334 */
335 public boolean hideHdr;
336
337 /**
Erin Dahlgrend5e51462014-02-07 12:38:57 -0800338 * Set true if grid lines should be visible. Not setting this
339 * causes grid lines to be disabled. This option is agnostic to
340 * the hardware.
341 */
342 public boolean enableGridLines;
343
344 /**
345 * Set true if grid lines should not be visible.
346 */
347 public boolean hideGridLines;
348
349 /**
Erin Dahlgrenf80ac9e2014-02-18 11:20:34 -0800350 * Set true if the panorama orientation option should be visible.
Erin Dahlgrena1fab412014-01-21 09:31:11 -0800351 *
352 * This option is not constrained by hardware limitations.
353 */
Erin Dahlgrenf80ac9e2014-02-18 11:20:34 -0800354 public boolean enablePanoOrientation;
Erin Dahlgrena1fab412014-01-21 09:31:11 -0800355
Spike Spragueabf54e22014-03-27 15:41:28 -0700356 public boolean enableExposureCompensation;
357
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800358 /** Intent UI */
359
360 /**
361 * Set true if the intent ui cancel option should be visible.
362 */
363 public boolean showCancel;
364 /**
365 * Set true if the intent ui done option should be visible.
366 */
367 public boolean showDone;
368 /**
369 * Set true if the intent ui retake option should be visible.
370 */
371 public boolean showRetake;
372 /**
373 * Set true if the intent ui review option should be visible.
374 */
375 public boolean showReview;
376
377 /** Mode options callbacks */
378
379 /**
Erin Dahlgrenf80ac9e2014-02-18 11:20:34 -0800380 * A {@link com.android.camera.ButtonManager.ButtonCallback}
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800381 * that will be executed when the camera option is pressed. This
382 * callback can be null.
383 */
384 public ButtonManager.ButtonCallback cameraCallback;
385
386 /**
Erin Dahlgrenf80ac9e2014-02-18 11:20:34 -0800387 * A {@link com.android.camera.ButtonManager.ButtonCallback}
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800388 * that will be executed when the flash option is pressed. This
389 * callback can be null.
390 */
391 public ButtonManager.ButtonCallback flashCallback;
392
393 /**
Erin Dahlgrenf80ac9e2014-02-18 11:20:34 -0800394 * A {@link com.android.camera.ButtonManager.ButtonCallback}
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800395 * that will be executed when the hdr/hdr+ option is pressed. This
396 * callback can be null.
397 */
398 public ButtonManager.ButtonCallback hdrCallback;
399
400 /**
Erin Dahlgrenf80ac9e2014-02-18 11:20:34 -0800401 * A {@link com.android.camera.ButtonManager.ButtonCallback}
Erin Dahlgrend5e51462014-02-07 12:38:57 -0800402 * that will be executed when the grid lines option is pressed. This
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800403 * callback can be null.
404 */
Erin Dahlgrend5e51462014-02-07 12:38:57 -0800405 public ButtonManager.ButtonCallback gridLinesCallback;
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800406
Erin Dahlgrena1fab412014-01-21 09:31:11 -0800407 /**
Erin Dahlgrenf80ac9e2014-02-18 11:20:34 -0800408 * A {@link com.android.camera.ButtonManager.ButtonCallback}
409 * that will execute when the panorama orientation option is pressed.
Erin Dahlgrena1fab412014-01-21 09:31:11 -0800410 * This callback can be null.
411 */
Erin Dahlgrenf80ac9e2014-02-18 11:20:34 -0800412 public ButtonManager.ButtonCallback panoOrientationCallback;
Erin Dahlgrena1fab412014-01-21 09:31:11 -0800413
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800414 /** Intent UI callbacks */
415
416 /**
417 * A {@link android.view.View.OnClickListener} that will execute
418 * when the cancel option is pressed. This callback can be null.
419 */
420 public View.OnClickListener cancelCallback;
421
422 /**
423 * A {@link android.view.View.OnClickListener} that will execute
424 * when the done option is pressed. This callback can be null.
425 */
426 public View.OnClickListener doneCallback;
427
428 /**
429 * A {@link android.view.View.OnClickListener} that will execute
430 * when the retake option is pressed. This callback can be null.
431 */
432 public View.OnClickListener retakeCallback;
433
434 /**
435 * A {@link android.view.View.OnClickListener} that will execute
436 * when the review option is pressed. This callback can be null.
437 */
438 public View.OnClickListener reviewCallback;
Spike Spragueabf54e22014-03-27 15:41:28 -0700439
440 /**
441 * A ExposureCompensationSetCallback that will execute
442 * when an expsosure button is pressed. This callback can be null.
443 */
444 public interface ExposureCompensationSetCallback {
Spike Spraguea479fca2014-04-11 17:51:24 -0700445 public void setExposure(int value);
Spike Spragueabf54e22014-03-27 15:41:28 -0700446 }
447 public ExposureCompensationSetCallback exposureCompensationSetCallback;
Spike Spraguea479fca2014-04-11 17:51:24 -0700448
449 /**
450 * Exposure compensation parameters.
451 */
452 public int minExposureCompensation;
453 public int maxExposureCompensation;
454 public float exposureCompensationStep;
Doris Liu6c751642014-05-05 18:43:26 -0700455
456 /**
Sascha Haeberling4333fac2014-05-20 16:28:11 -0700457 * Whether self-timer is enabled.
Doris Liu6c751642014-05-05 18:43:26 -0700458 */
459 public boolean enableSelfTimer = false;
Sascha Haeberling4333fac2014-05-20 16:28:11 -0700460
461 /**
462 * Whether the option for self-timer should show. If true and
463 * {@link #enableSelfTimer} is false, then the option should be shown
464 * disabled.
465 */
466 public boolean showSelfTimer = false;
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800467 }
468
469
Angus Kong5596b4c2014-03-11 16:27:30 -0700470 private final static Log.Tag TAG = new Log.Tag("CameraAppUI");
Doris Liuf55f3c42013-11-20 00:24:46 -0800471
472 private final AppController mController;
473 private final boolean mIsCaptureIntent;
Doris Liuf55f3c42013-11-20 00:24:46 -0800474 private final AnimationManager mAnimationManager;
475
476 // Swipe states:
Doris Liu2b906b82013-12-10 16:34:08 -0800477 private final static int IDLE = 0;
478 private final static int SWIPE_UP = 1;
479 private final static int SWIPE_DOWN = 2;
480 private final static int SWIPE_LEFT = 3;
481 private final static int SWIPE_RIGHT = 4;
Sascha Haeberling8793eff2014-01-15 16:33:59 -0800482 private boolean mSwipeEnabled = true;
Doris Liuf55f3c42013-11-20 00:24:46 -0800483
Erin Dahlgrend8de0772014-02-03 10:12:27 -0800484 // Shared Surface Texture properities.
485 private SurfaceTexture mSurface;
486 private int mSurfaceWidth;
487 private int mSurfaceHeight;
488
Doris Liuf55f3c42013-11-20 00:24:46 -0800489 // Touch related measures:
490 private final int mSlop;
Doris Liu2b906b82013-12-10 16:34:08 -0800491 private final static int SWIPE_TIME_OUT_MS = 500;
492
Doris Liu2b906b82013-12-10 16:34:08 -0800493 // Mode cover states:
494 private final static int COVER_HIDDEN = 0;
495 private final static int COVER_SHOWN = 1;
496 private final static int COVER_WILL_HIDE_AT_NEXT_FRAME = 2;
Doris Liu4d4a4bc2013-12-19 18:55:54 -0800497 private static final int COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE = 3;
Doris Liuf55f3c42013-11-20 00:24:46 -0800498
Doris Liu6809eb82014-05-21 13:16:59 -0700499 /**
500 * Preview down-sample rate when taking a screenshot.
501 */
502 private final static int DOWN_SAMPLE_RATE_FOR_SCREENSHOT = 2;
503
Doris Liuf55f3c42013-11-20 00:24:46 -0800504 // App level views:
Doris Liub41034b2013-12-06 21:58:41 -0800505 private final FrameLayout mCameraRootView;
Doris Liuf55f3c42013-11-20 00:24:46 -0800506 private final ModeTransitionView mModeTransitionView;
507 private final MainActivityLayout mAppRootView;
508 private final ModeListView mModeListView;
Angus Kong166e36f2013-12-03 08:54:42 -0800509 private final FilmstripLayout mFilmstripLayout;
Doris Liuf55f3c42013-11-20 00:24:46 -0800510 private TextureView mTextureView;
Doris Liuc6c97402013-12-06 21:02:53 -0800511 private FrameLayout mModuleUI;
Erin Dahlgren5d187692014-02-25 19:16:12 -0800512 private ShutterButton mShutterButton;
Doris Liu70da9182013-12-17 18:41:15 -0800513 private BottomBar mBottomBar;
Erin Dahlgren165bb8d2014-01-30 14:11:20 -0800514 private ModeOptionsOverlay mModeOptionsOverlay;
Erin Dahlgrene2a1f542014-01-08 14:34:35 -0800515 private IndicatorIconController mIndicatorIconController;
Doris Liuaf656832014-02-19 14:12:06 -0800516 private View mFocusOverlay;
Sascha Haeberling1e0810c2014-04-07 17:29:44 -0700517 private FrameLayout mTutorialsPlaceHolderWrapper;
Doris Liu28a29502014-04-10 16:01:51 -0700518 private BottomBarModeOptionsWrapper mIndicatorBottomBarWrapper;
Doris Liu70da9182013-12-17 18:41:15 -0800519 private TextureViewHelper mTextureViewHelper;
Sascha Haeberlingab05a742013-12-13 23:28:30 -0800520 private final GestureDetector mGestureDetector;
Doris Liu11ddd732014-01-07 10:52:11 -0800521 private DisplayManager.DisplayListener mDisplayListener;
522 private int mLastRotation;
Doris Liuf55f3c42013-11-20 00:24:46 -0800523 private int mSwipeState = IDLE;
Doris Liuf9e4f8f2013-12-04 18:04:22 -0800524 private PreviewOverlay mPreviewOverlay;
Erin Dahlgrenb3f59a82014-02-04 15:23:30 -0800525 private GridLines mGridLines;
Doris Liu1dfe7822013-12-12 00:02:08 -0800526 private CaptureAnimationOverlay mCaptureOverlay;
Doris Liu06db7422013-12-09 19:36:25 -0800527 private PreviewStatusListener mPreviewStatusListener;
Doris Liu2b906b82013-12-10 16:34:08 -0800528 private int mModeCoverState = COVER_HIDDEN;
Angus Kong7ae25c22014-02-25 10:37:39 -0800529 private final FilmstripBottomPanel mFilmstripBottomControls;
Sascha Haeberlingab05a742013-12-13 23:28:30 -0800530 private final FilmstripContentPanel mFilmstripPanel;
Doris Liub6eaa8c2013-12-11 18:39:14 -0800531 private Runnable mHideCoverRunnable;
Doris Liu4a010db2013-12-16 18:44:49 -0800532 private final View.OnLayoutChangeListener mPreviewLayoutChangeListener
533 = new View.OnLayoutChangeListener() {
534 @Override
535 public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
536 int oldTop, int oldRight, int oldBottom) {
537 if (mPreviewStatusListener != null) {
538 mPreviewStatusListener.onPreviewLayoutChanged(v, left, top, right, bottom, oldLeft,
539 oldTop, oldRight, oldBottom);
540 }
541 }
542 };
Doris Liu9d264302014-02-11 10:41:40 -0800543 private View mModeOptionsToggle;
Sascha Haeberlinga9a9d152014-03-05 08:46:55 -0800544 private final PeekView mPeekView;
Doris Liu28a29502014-04-10 16:01:51 -0700545 private final CaptureLayoutHelper mCaptureLayoutHelper;
Doris Liudfb5a6f2014-04-28 22:36:08 -0700546 private boolean mAccessibilityEnabled;
Sascha Haeberling256427b2014-05-15 14:45:02 -0700547 private final View mAccessibilityAffordances;
Doris Liuf55f3c42013-11-20 00:24:46 -0800548
Spike Spragued2d8b712014-10-02 10:14:55 -0700549 private boolean mDisableAllUserInteractions;
Doris Liu213a4a02014-02-04 16:57:55 -0800550 /**
551 * Provides current preview frame and the controls/overlay from the module that
552 * are shown on top of the preview.
553 */
554 public interface CameraModuleScreenShotProvider {
555 /**
556 * Returns the current preview frame down-sampled using the given down-sample
557 * factor.
558 *
559 * @param downSampleFactor the down sample factor for down sampling the
560 * preview frame. (e.g. a down sample factor of
561 * 2 means to scale down the preview frame to 1/2
562 * the width and height.)
563 * @return down-sampled preview frame
564 */
565 public Bitmap getPreviewFrame(int downSampleFactor);
566
567 /**
568 * @return the controls and overlays that are currently showing on top of
569 * the preview drawn into a bitmap with no scaling applied.
570 */
571 public Bitmap getPreviewOverlayAndControls();
Doris Liu6809eb82014-05-21 13:16:59 -0700572
573 /**
574 * Returns a bitmap containing the current screenshot.
575 *
576 * @param previewDownSampleFactor the downsample factor applied on the
577 * preview frame when taking the screenshot
578 */
579 public Bitmap getScreenShot(int previewDownSampleFactor);
Doris Liu213a4a02014-02-04 16:57:55 -0800580 }
581
Doris Liu9d264302014-02-11 10:41:40 -0800582 /**
Doris Liu28a29502014-04-10 16:01:51 -0700583 * This listener gets called when the size of the window (excluding the system
584 * decor such as status bar and nav bar) has changed.
Doris Liu9d264302014-02-11 10:41:40 -0800585 */
Doris Liu28a29502014-04-10 16:01:51 -0700586 public interface NonDecorWindowSizeChangedListener {
Doris Liu344320c2014-04-22 14:47:49 -0700587 public void onNonDecorWindowSizeChanged(int width, int height, int rotation);
Doris Liu9d264302014-02-11 10:41:40 -0800588 }
589
Doris Liu213a4a02014-02-04 16:57:55 -0800590 private final CameraModuleScreenShotProvider mCameraModuleScreenShotProvider =
591 new CameraModuleScreenShotProvider() {
592 @Override
593 public Bitmap getPreviewFrame(int downSampleFactor) {
594 if (mCameraRootView == null || mTextureView == null) {
595 return null;
596 }
Doris Liu213a4a02014-02-04 16:57:55 -0800597 // Gets the bitmap from the preview TextureView.
Seth Raphael274f6e92014-05-21 17:11:53 -0700598 Bitmap preview = mTextureViewHelper.getPreviewBitmap(downSampleFactor);
Doris Liu213a4a02014-02-04 16:57:55 -0800599 return preview;
600 }
601
602 @Override
603 public Bitmap getPreviewOverlayAndControls() {
604 Bitmap overlays = Bitmap.createBitmap(mCameraRootView.getWidth(),
605 mCameraRootView.getHeight(), Bitmap.Config.ARGB_8888);
606 Canvas canvas = new Canvas(overlays);
607 mCameraRootView.draw(canvas);
608 return overlays;
609 }
Doris Liu6809eb82014-05-21 13:16:59 -0700610
611 @Override
612 public Bitmap getScreenShot(int previewDownSampleFactor) {
613 Bitmap screenshot = Bitmap.createBitmap(mCameraRootView.getWidth(),
614 mCameraRootView.getHeight(), Bitmap.Config.ARGB_8888);
615 Canvas canvas = new Canvas(screenshot);
616 canvas.drawARGB(255, 0, 0, 0);
Seth Raphael274f6e92014-05-21 17:11:53 -0700617 Bitmap preview = mTextureViewHelper.getPreviewBitmap(previewDownSampleFactor);
Doris Liu6809eb82014-05-21 13:16:59 -0700618 if (preview != null) {
619 canvas.drawBitmap(preview, null, mTextureViewHelper.getPreviewArea(), null);
620 }
621 Bitmap overlay = getPreviewOverlayAndControls();
622 if (overlay != null) {
623 canvas.drawBitmap(overlay, 0f, 0f, null);
624 }
625 return screenshot;
626 }
Doris Liu213a4a02014-02-04 16:57:55 -0800627 };
628
Kevin Gabayana0e83472014-01-15 15:21:13 -0800629 private long mCoverHiddenTime = -1; // System time when preview cover was hidden.
630
631 public long getCoverHiddenTime() {
632 return mCoverHiddenTime;
633 }
634
Erin Dahlgrend8de0772014-02-03 10:12:27 -0800635 /**
636 * This resets the preview to have no applied transform matrix.
637 */
638 public void clearPreviewTransform() {
639 mTextureViewHelper.clearTransform();
640 }
641
Doris Liu70da9182013-12-17 18:41:15 -0800642 public void updatePreviewAspectRatio(float aspectRatio) {
643 mTextureViewHelper.updateAspectRatio(aspectRatio);
Spike Sprague0f3c4b42013-12-10 19:50:17 -0800644 }
645
Ruben Brunk7bae31a2014-08-26 03:16:27 -0700646 /**
647 * WAR: Reset the SurfaceTexture's default buffer size to the current view dimensions of
648 * its TextureView. This is necessary to get the expected behavior for the TextureView's
649 * HardwareLayer transform matrix (set by TextureView#setTransform) after configuring the
650 * SurfaceTexture as an output for the Camera2 API (which involves changing the default buffer
651 * size).
652 *
653 * b/17286155 - Tracking a fix for this in HardwareLayer.
654 */
655 public void setDefaultBufferSizeToViewDimens() {
656 if (mSurface == null || mTextureView == null) {
657 Log.w(TAG, "Could not set SurfaceTexture default buffer dimensions, not yet setup");
658 return;
659 }
660 mSurface.setDefaultBufferSize(mTextureView.getWidth(), mTextureView.getHeight());
661 }
Seth Raphael02c129a2014-05-19 21:46:04 -0700662
663 /**
Seth Raphael6382c702014-05-20 13:19:16 -0700664 * Updates the preview matrix without altering it.
Spike Sprague159e6e92014-05-27 18:26:30 -0700665 *
Seth Raphael02c129a2014-05-19 21:46:04 -0700666 * @param matrix
Seth Raphael6382c702014-05-20 13:19:16 -0700667 * @param aspectRatio the desired aspect ratio for the preview.
Seth Raphael02c129a2014-05-19 21:46:04 -0700668 */
Seth Raphael6382c702014-05-20 13:19:16 -0700669 public void updatePreviewTransformFullscreen(Matrix matrix, float aspectRatio) {
670 mTextureViewHelper.updateTransformFullScreen(matrix, aspectRatio);
Seth Raphael02c129a2014-05-19 21:46:04 -0700671 }
672
673 /**
674 * @return the rect that will display the preview.
675 */
676 public RectF getFullscreenRect() {
677 return mTextureViewHelper.getFullscreenRect();
678 }
679
Doris Liub897a1e2013-12-16 15:00:12 -0800680 /**
681 * This is to support modules that calculate their own transform matrix because
682 * they need to use a transform matrix to rotate the preview.
683 *
Doris Liub897a1e2013-12-16 15:00:12 -0800684 * @param matrix transform matrix to be set on the TextureView
685 */
Doris Liu70da9182013-12-17 18:41:15 -0800686 public void updatePreviewTransform(Matrix matrix) {
687 mTextureViewHelper.updateTransform(matrix);
Doris Liub897a1e2013-12-16 15:00:12 -0800688 }
689
Doris Liuf55f3c42013-11-20 00:24:46 -0800690 public interface AnimationFinishedListener {
691 public void onAnimationFinished(boolean success);
692 }
693
694 private class MyTouchListener implements View.OnTouchListener {
Doris Liuf9e4f8f2013-12-04 18:04:22 -0800695 private boolean mScaleStarted = false;
Doris Liuf55f3c42013-11-20 00:24:46 -0800696 @Override
697 public boolean onTouch(View v, MotionEvent event) {
Doris Liuf9e4f8f2013-12-04 18:04:22 -0800698 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
699 mScaleStarted = false;
700 } else if (event.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) {
701 mScaleStarted = true;
702 }
703 return (!mScaleStarted) && mGestureDetector.onTouchEvent(event);
Doris Liuf55f3c42013-11-20 00:24:46 -0800704 }
705 }
706
707 /**
708 * This gesture listener finds out the direction of the scroll gestures and
709 * sends them to CameraAppUI to do further handling.
710 */
711 private class MyGestureListener extends GestureDetector.SimpleOnGestureListener {
712 private MotionEvent mDown;
713
714 @Override
715 public boolean onScroll(MotionEvent e1, MotionEvent ev, float distanceX, float distanceY) {
Doris Liu2b906b82013-12-10 16:34:08 -0800716 if (ev.getEventTime() - ev.getDownTime() > SWIPE_TIME_OUT_MS
Spike Spraguec050a942013-12-19 17:05:45 -0800717 || mSwipeState != IDLE
Sascha Haeberling8793eff2014-01-15 16:33:59 -0800718 || mIsCaptureIntent
719 || !mSwipeEnabled) {
Doris Liub6eaa8c2013-12-11 18:39:14 -0800720 return false;
Doris Liuf55f3c42013-11-20 00:24:46 -0800721 }
722
723 int deltaX = (int) (ev.getX() - mDown.getX());
724 int deltaY = (int) (ev.getY() - mDown.getY());
725 if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) {
726 if (Math.abs(deltaX) > mSlop || Math.abs(deltaY) > mSlop) {
727 // Calculate the direction of the swipe.
728 if (deltaX >= Math.abs(deltaY)) {
729 // Swipe right.
730 setSwipeState(SWIPE_RIGHT);
731 } else if (deltaX <= -Math.abs(deltaY)) {
732 // Swipe left.
733 setSwipeState(SWIPE_LEFT);
Doris Liuf55f3c42013-11-20 00:24:46 -0800734 }
735 }
736 }
737 return true;
738 }
739
740 private void setSwipeState(int swipeState) {
741 mSwipeState = swipeState;
742 // Notify new swipe detected.
743 onSwipeDetected(swipeState);
744 }
745
746 @Override
747 public boolean onDown(MotionEvent ev) {
748 mDown = MotionEvent.obtain(ev);
749 mSwipeState = IDLE;
Doris Liuf9e4f8f2013-12-04 18:04:22 -0800750 return false;
Doris Liuf55f3c42013-11-20 00:24:46 -0800751 }
752 }
753
754 public CameraAppUI(AppController controller, MainActivityLayout appRootView,
Sascha Haeberling8793eff2014-01-15 16:33:59 -0800755 boolean isCaptureIntent) {
Doris Liuf55f3c42013-11-20 00:24:46 -0800756 mSlop = ViewConfiguration.get(controller.getAndroidContext()).getScaledTouchSlop();
757 mController = controller;
Doris Liuf55f3c42013-11-20 00:24:46 -0800758 mIsCaptureIntent = isCaptureIntent;
759
760 mAppRootView = appRootView;
Angus Kong166e36f2013-12-03 08:54:42 -0800761 mFilmstripLayout = (FilmstripLayout) appRootView.findViewById(R.id.filmstrip_layout);
Doris Liub41034b2013-12-06 21:58:41 -0800762 mCameraRootView = (FrameLayout) appRootView.findViewById(R.id.camera_app_root);
Doris Liuf55f3c42013-11-20 00:24:46 -0800763 mModeTransitionView = (ModeTransitionView)
764 mAppRootView.findViewById(R.id.mode_transition_view);
Angus Kong7ae25c22014-02-25 10:37:39 -0800765 mFilmstripBottomControls = new FilmstripBottomPanel(controller,
Angus Kong001dc312014-02-27 16:58:11 -0800766 (ViewGroup) mAppRootView.findViewById(R.id.filmstrip_bottom_panel));
Angus Kongb2510252013-12-10 22:58:15 -0800767 mFilmstripPanel = (FilmstripContentPanel) mAppRootView.findViewById(R.id.filmstrip_layout);
Doris Liuf55f3c42013-11-20 00:24:46 -0800768 mGestureDetector = new GestureDetector(controller.getAndroidContext(),
769 new MyGestureListener());
Doris Liu28a29502014-04-10 16:01:51 -0700770 Resources res = controller.getAndroidContext().getResources();
771 mCaptureLayoutHelper = new CaptureLayoutHelper(
772 res.getDimensionPixelSize(R.dimen.bottom_bar_height_min),
773 res.getDimensionPixelSize(R.dimen.bottom_bar_height_max),
774 res.getDimensionPixelSize(R.dimen.bottom_bar_height_optimal));
Doris Liuf55f3c42013-11-20 00:24:46 -0800775 mModeListView = (ModeListView) appRootView.findViewById(R.id.mode_list_layout);
776 if (mModeListView != null) {
777 mModeListView.setModeSwitchListener(this);
Doris Liu2c559c42014-01-08 16:04:38 -0800778 mModeListView.setModeListOpenListener(this);
Doris Liu213a4a02014-02-04 16:57:55 -0800779 mModeListView.setCameraModuleScreenShotProvider(mCameraModuleScreenShotProvider);
Doris Liu28a29502014-04-10 16:01:51 -0700780 mModeListView.setCaptureLayoutHelper(mCaptureLayoutHelper);
Doris Liu26aa92b2014-05-22 18:39:46 -0700781 boolean shouldShowSettingsCling = mController.getSettingsManager().getBoolean(
Erin Dahlgren6190c362014-06-13 14:12:08 -0700782 SettingsManager.SCOPE_GLOBAL,
783 Keys.KEY_SHOULD_SHOW_SETTINGS_BUTTON_CLING);
Doris Liu26aa92b2014-05-22 18:39:46 -0700784 mModeListView.setShouldShowSettingsCling(shouldShowSettingsCling);
Doris Liuf55f3c42013-11-20 00:24:46 -0800785 } else {
786 Log.e(TAG, "Cannot find mode list in the view hierarchy");
787 }
788 mAnimationManager = new AnimationManager();
Angus Kong50521172014-01-17 17:23:59 -0800789 mPeekView = (PeekView) appRootView.findViewById(R.id.peek_view);
Doris Liu28a29502014-04-10 16:01:51 -0700790 mAppRootView.setNonDecorWindowSizeChangedListener(mCaptureLayoutHelper);
Doris Liu11ddd732014-01-07 10:52:11 -0800791 initDisplayListener();
Sascha Haeberling256427b2014-05-15 14:45:02 -0700792 mAccessibilityAffordances = mAppRootView.findViewById(R.id.accessibility_affordances);
Doris Liudfb5a6f2014-04-28 22:36:08 -0700793 View modeListToggle = mAppRootView.findViewById(R.id.accessibility_mode_toggle_button);
794 modeListToggle.setOnClickListener(new View.OnClickListener() {
795 @Override
796 public void onClick(View view) {
Sascha Haeberling256427b2014-05-15 14:45:02 -0700797 openModeList();
Doris Liudfb5a6f2014-04-28 22:36:08 -0700798 }
799 });
800 View filmstripToggle = mAppRootView.findViewById(
801 R.id.accessibility_filmstrip_toggle_button);
802 filmstripToggle.setOnClickListener(new View.OnClickListener() {
803 @Override
804 public void onClick(View view) {
Sascha Haeberling256427b2014-05-15 14:45:02 -0700805 showFilmstrip();
Doris Liudfb5a6f2014-04-28 22:36:08 -0700806 }
807 });
Doris Liu11ddd732014-01-07 10:52:11 -0800808 }
809
Doris Liu6809eb82014-05-21 13:16:59 -0700810
811 /**
812 * Freeze what is currently shown on screen until the next preview frame comes
813 * in.
814 */
815 public void freezeScreenUntilPreviewReady() {
Alan Newbergerc91c8d22014-09-09 18:47:37 -0700816 Log.v(TAG, "freezeScreenUntilPreviewReady");
Doris Liu6809eb82014-05-21 13:16:59 -0700817 mModeTransitionView.setupModeCover(mCameraModuleScreenShotProvider
818 .getScreenShot(DOWN_SAMPLE_RATE_FOR_SCREENSHOT));
819 mHideCoverRunnable = new Runnable() {
820 @Override
821 public void run() {
822 mModeTransitionView.hideImageCover();
823 }
824 };
825 mModeCoverState = COVER_SHOWN;
826 }
827
Doris Liu11ddd732014-01-07 10:52:11 -0800828 /**
Doris Liue7d7b9e2014-03-31 15:27:40 -0700829 * Creates a cling for the specific viewer and links the cling to the corresponding
830 * button for layout position.
831 *
832 * @param viewerType defines which viewer the cling is for.
833 */
834 public void setupClingForViewer(int viewerType) {
835 if (viewerType == BottomPanel.VIEWER_REFOCUS) {
836 FrameLayout filmstripContent = (FrameLayout) mAppRootView
837 .findViewById(R.id.camera_filmstrip_content_layout);
838 if (filmstripContent != null) {
839 // Creates refocus cling.
840 LayoutInflater inflater = (LayoutInflater) mController.getAndroidContext()
841 .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
842 Cling refocusCling = (Cling) inflater.inflate(R.layout.cling_widget, null, false);
843 // Sets instruction text in the cling.
844 refocusCling.setText(mController.getAndroidContext().getResources()
845 .getString(R.string.cling_text_for_refocus_editor_button));
846
847 // Adds cling into view hierarchy.
848 int clingWidth = mController.getAndroidContext()
849 .getResources().getDimensionPixelSize(R.dimen.default_cling_width);
850 filmstripContent.addView(refocusCling, clingWidth,
851 ViewGroup.LayoutParams.WRAP_CONTENT);
852 mFilmstripBottomControls.setClingForViewer(viewerType, refocusCling);
853 }
854 }
855 }
856
857 /**
858 * Clears the listeners for the cling and remove it from the view hierarchy.
859 *
860 * @param viewerType defines which viewer the cling is for.
861 */
862 public void clearClingForViewer(int viewerType) {
863 Cling clingToBeRemoved = mFilmstripBottomControls.getClingForViewer(viewerType);
864 if (clingToBeRemoved == null) {
865 // No cling is created for the specific viewer type.
866 return;
867 }
868 mFilmstripBottomControls.clearClingForViewer(viewerType);
869 clingToBeRemoved.setVisibility(View.GONE);
870 mAppRootView.removeView(clingToBeRemoved);
871 }
872
873 /**
Sascha Haeberling8793eff2014-01-15 16:33:59 -0800874 * Enable or disable swipe gestures. We want to disable them e.g. while we
875 * record a video.
876 */
877 public void setSwipeEnabled(boolean enabled) {
Sascha Haeberling8793eff2014-01-15 16:33:59 -0800878 mSwipeEnabled = enabled;
Doris Liu9eb517f2014-04-01 15:53:56 -0700879 // TODO: This can be removed once we come up with a new design for handling swipe
880 // on shutter button and mode options. (More details: b/13751653)
881 mAppRootView.setSwipeEnabled(enabled);
Sascha Haeberling8793eff2014-01-15 16:33:59 -0800882 }
883
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100884 public void onDestroy() {
885 ((DisplayManager) mController.getAndroidContext()
886 .getSystemService(Context.DISPLAY_SERVICE))
887 .unregisterDisplayListener(mDisplayListener);
888 }
889
Sascha Haeberling8793eff2014-01-15 16:33:59 -0800890 /**
Doris Liu11ddd732014-01-07 10:52:11 -0800891 * Initializes the display listener to listen to display changes such as
892 * 180-degree rotation change, which will not have an onConfigurationChanged
893 * callback.
894 */
895 private void initDisplayListener() {
896 if (ApiHelper.HAS_DISPLAY_LISTENER) {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100897 mLastRotation = CameraUtil.getDisplayRotation(mController.getAndroidContext());
Doris Liu11ddd732014-01-07 10:52:11 -0800898
899 mDisplayListener = new DisplayManager.DisplayListener() {
900 @Override
901 public void onDisplayAdded(int arg0) {
902 // Do nothing.
903 }
904
905 @Override
906 public void onDisplayChanged(int displayId) {
907 int rotation = CameraUtil.getDisplayRotation(
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100908 mController.getAndroidContext());
Doris Liu6365bad2014-01-30 13:11:29 -0800909 if ((rotation - mLastRotation + 360) % 360 == 180
910 && mPreviewStatusListener != null) {
Doris Liu11ddd732014-01-07 10:52:11 -0800911 mPreviewStatusListener.onPreviewFlipped();
Doris Liu344320c2014-04-22 14:47:49 -0700912 mIndicatorBottomBarWrapper.requestLayout();
913 mModeListView.requestLayout();
914 mTextureView.requestLayout();
Doris Liu11ddd732014-01-07 10:52:11 -0800915 }
916 mLastRotation = rotation;
917 }
918
919 @Override
920 public void onDisplayRemoved(int arg0) {
921 // Do nothing.
922 }
923 };
924
925 ((DisplayManager) mController.getAndroidContext()
926 .getSystemService(Context.DISPLAY_SERVICE))
927 .registerDisplayListener(mDisplayListener, null);
928 }
Doris Liuf55f3c42013-11-20 00:24:46 -0800929 }
930
931 /**
932 * Redirects touch events to appropriate recipient views based on swipe direction.
933 * More specifically, swipe up and swipe down will be handled by the view that handles
934 * mode transition; swipe left will be send to filmstrip; swipe right will be redirected
935 * to mode list in order to bring up mode list.
936 */
937 private void onSwipeDetected(int swipeState) {
938 if (swipeState == SWIPE_UP || swipeState == SWIPE_DOWN) {
Doris Liu9d52fc92014-02-25 14:06:55 -0800939 // TODO: Polish quick switch after this release.
Doris Liubd1b8f92014-01-03 17:59:51 -0800940 // Quick switch between modes.
941 int currentModuleIndex = mController.getCurrentModuleIndex();
942 final int moduleToTransitionTo =
943 mController.getQuickSwitchToModuleId(currentModuleIndex);
944 if (currentModuleIndex != moduleToTransitionTo) {
Doris Liuf55f3c42013-11-20 00:24:46 -0800945 mAppRootView.redirectTouchEventsTo(mModeTransitionView);
Doris Liu040e3c02014-02-10 14:42:08 -0800946 int shadeColorId = R.color.mode_cover_default_color;
Doris Liub520b972014-02-14 14:14:46 -0800947 int iconRes = CameraUtil.getCameraModeCoverIconResId(moduleToTransitionTo,
Doris Liubd1b8f92014-01-03 17:59:51 -0800948 mController.getAndroidContext());
Doris Liuf55f3c42013-11-20 00:24:46 -0800949
950 AnimationFinishedListener listener = new AnimationFinishedListener() {
Sascha Haeberlingab05a742013-12-13 23:28:30 -0800951 @Override
Doris Liuf55f3c42013-11-20 00:24:46 -0800952 public void onAnimationFinished(boolean success) {
953 if (success) {
Doris Liue038c162013-12-13 23:06:11 -0800954 mHideCoverRunnable = new Runnable() {
955 @Override
956 public void run() {
957 mModeTransitionView.startPeepHoleAnimation();
958 }
959 };
960 mModeCoverState = COVER_SHOWN;
Doris Liuf55f3c42013-11-20 00:24:46 -0800961 // Go to new module when the previous operation is successful.
962 mController.onModeSelected(moduleToTransitionTo);
Doris Liuf55f3c42013-11-20 00:24:46 -0800963 }
964 }
965 };
Doris Liuf55f3c42013-11-20 00:24:46 -0800966 }
967 } else if (swipeState == SWIPE_LEFT) {
Angus Kong166e36f2013-12-03 08:54:42 -0800968 // Pass the touch sequence to filmstrip layout.
969 mAppRootView.redirectTouchEventsTo(mFilmstripLayout);
Doris Liuf55f3c42013-11-20 00:24:46 -0800970 } else if (swipeState == SWIPE_RIGHT) {
971 // Pass the touch to mode switcher
972 mAppRootView.redirectTouchEventsTo(mModeListView);
973 }
974 }
975
976 /**
Doris Liu2b906b82013-12-10 16:34:08 -0800977 * Gets called when activity resumes in preview.
978 */
979 public void resume() {
Doris Liub00d6432014-02-26 10:18:47 -0800980 // Show mode theme cover until preview is ready
981 showModeCoverUntilPreviewReady();
982
Angus Kongb2510252013-12-10 22:58:15 -0800983 // Hide action bar first since we are in full screen mode first, and
984 // switch the system UI to lights-out mode.
985 mFilmstripPanel.hide();
Doris Liudfb5a6f2014-04-28 22:36:08 -0700986
Sascha Haeberlingd4c33332014-05-19 15:41:00 -0700987 // Show UI that is meant to only be used when spoken feedback is
988 // enabled.
989 mAccessibilityEnabled = isSpokenFeedbackAccessibilityEnabled();
990 mAccessibilityAffordances.setVisibility(mAccessibilityEnabled ? View.VISIBLE : View.GONE);
991 }
992
993 /**
994 * @return Whether any spoken feedback accessibility feature is currently
995 * enabled.
996 */
997 private boolean isSpokenFeedbackAccessibilityEnabled() {
Doris Liudfb5a6f2014-04-28 22:36:08 -0700998 AccessibilityManager accessibilityManager = (AccessibilityManager) mController
999 .getAndroidContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
Sascha Haeberlingd4c33332014-05-19 15:41:00 -07001000 List<AccessibilityServiceInfo> infos = accessibilityManager
1001 .getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_SPOKEN);
1002 return infos != null && !infos.isEmpty();
Sascha Haeberling256427b2014-05-15 14:45:02 -07001003 }
Doris Liudfb5a6f2014-04-28 22:36:08 -07001004
Sascha Haeberling256427b2014-05-15 14:45:02 -07001005 /**
1006 * Opens the mode list (e.g. because of the menu button being pressed) and
1007 * adapts the rest of the UI.
1008 */
1009 public void openModeList() {
1010 mModeOptionsOverlay.closeModeOptions();
1011 mModeListView.onMenuPressed();
Doris Liu2b906b82013-12-10 16:34:08 -08001012 }
1013
1014 /**
1015 * A cover view showing the mode theme color and mode icon will be visible on
1016 * top of preview until preview is ready (i.e. camera preview is started and
1017 * the first frame has been received).
1018 */
1019 private void showModeCoverUntilPreviewReady() {
1020 int modeId = mController.getCurrentModuleIndex();
Doris Liu040e3c02014-02-10 14:42:08 -08001021 int colorId = R.color.mode_cover_default_color;;
Doris Liub520b972014-02-14 14:14:46 -08001022 int iconId = CameraUtil.getCameraModeCoverIconResId(modeId, mController.getAndroidContext());
Doris Liu2b906b82013-12-10 16:34:08 -08001023 mModeTransitionView.setupModeCover(colorId, iconId);
Doris Liub6eaa8c2013-12-11 18:39:14 -08001024 mHideCoverRunnable = new Runnable() {
1025 @Override
1026 public void run() {
Doris Liub00d6432014-02-26 10:18:47 -08001027 mModeTransitionView.hideModeCover(null);
Spike Sprague4c96d842014-10-15 15:11:06 -07001028 if (!mDisableAllUserInteractions) {
1029 showShimmyDelayed();
1030 }
Doris Liub6eaa8c2013-12-11 18:39:14 -08001031 }
1032 };
Doris Liu2b906b82013-12-10 16:34:08 -08001033 mModeCoverState = COVER_SHOWN;
1034 }
1035
Doris Liu2c559c42014-01-08 16:04:38 -08001036 private void showShimmyDelayed() {
1037 if (!mIsCaptureIntent) {
1038 // Show shimmy in SHIMMY_DELAY_MS
Angus Kong2ed67842014-03-24 16:40:12 -07001039 mModeListView.showModeSwitcherHint();
Doris Liu2c559c42014-01-08 16:04:38 -08001040 }
1041 }
1042
Doris Liu2b906b82013-12-10 16:34:08 -08001043 private void hideModeCover() {
Doris Liub6eaa8c2013-12-11 18:39:14 -08001044 if (mHideCoverRunnable != null) {
1045 mAppRootView.post(mHideCoverRunnable);
1046 mHideCoverRunnable = null;
1047 }
Doris Liue038c162013-12-13 23:06:11 -08001048 mModeCoverState = COVER_HIDDEN;
Kevin Gabayana0e83472014-01-15 15:21:13 -08001049 if (mCoverHiddenTime < 0) {
1050 mCoverHiddenTime = System.currentTimeMillis();
1051 }
Doris Liu2b906b82013-12-10 16:34:08 -08001052 }
1053
Doris Liudfb5a6f2014-04-28 22:36:08 -07001054
1055 public void onPreviewVisiblityChanged(int visibility) {
1056 if (visibility == ModuleController.VISIBILITY_HIDDEN) {
1057 setIndicatorBottomBarWrapperVisible(false);
Sascha Haeberling256427b2014-05-15 14:45:02 -07001058 mAccessibilityAffordances.setVisibility(View.GONE);
Doris Liudfb5a6f2014-04-28 22:36:08 -07001059 } else {
1060 setIndicatorBottomBarWrapperVisible(true);
1061 if (mAccessibilityEnabled) {
Sascha Haeberling256427b2014-05-15 14:45:02 -07001062 mAccessibilityAffordances.setVisibility(View.VISIBLE);
Doris Liudfb5a6f2014-04-28 22:36:08 -07001063 } else {
Sascha Haeberling256427b2014-05-15 14:45:02 -07001064 mAccessibilityAffordances.setVisibility(View.GONE);
Doris Liudfb5a6f2014-04-28 22:36:08 -07001065 }
1066 }
1067 }
1068
Sam Judd1b125322014-03-21 16:53:27 -07001069 /**
1070 * Call to stop the preview from being rendered.
1071 */
1072 public void pausePreviewRendering() {
1073 mTextureView.setVisibility(View.INVISIBLE);
1074 }
1075
1076 /**
1077 * Call to begin rendering the preview again.
1078 */
1079 public void resumePreviewRendering() {
1080 mTextureView.setVisibility(View.VISIBLE);
1081 }
1082
Sascha Haeberling08b3c942014-07-30 17:48:52 -07001083 /**
1084 * Returns the transform associated with the preview view.
1085 *
1086 * @param m the Matrix in which to copy the current transform.
1087 * @return The specified matrix if not null or a new Matrix instance
1088 * otherwise.
1089 */
1090 public Matrix getPreviewTransform(Matrix m) {
1091 return mTextureView.getTransform(m);
1092 }
1093
Doris Liu2c559c42014-01-08 16:04:38 -08001094 @Override
1095 public void onOpenFullScreen() {
Angus Kong2ed67842014-03-24 16:40:12 -07001096 // Do nothing.
Doris Liu2c559c42014-01-08 16:04:38 -08001097 }
1098
Doris Liu9d264302014-02-11 10:41:40 -08001099 @Override
1100 public void onModeListOpenProgress(float progress) {
1101 progress = 1 - progress;
1102 float interpolatedProgress = Gusterpolator.INSTANCE.getInterpolation(progress);
1103 mModeOptionsToggle.setAlpha(interpolatedProgress);
Doris Liu948de992014-02-19 18:06:00 -08001104 // Change shutter button alpha linearly based on the mode list open progress:
1105 // set the alpha to disabled alpha when list is fully open, to enabled alpha
1106 // when the list is fully closed.
1107 mShutterButton.setAlpha(progress * ShutterButton.ALPHA_WHEN_ENABLED
1108 + (1 - progress) * ShutterButton.ALPHA_WHEN_DISABLED);
Doris Liu9d264302014-02-11 10:41:40 -08001109 }
1110
1111 @Override
1112 public void onModeListClosed() {
1113 // Make sure the alpha on mode options ellipse is reset when mode drawer
1114 // is closed.
1115 mModeOptionsToggle.setAlpha(1f);
Doris Liu948de992014-02-19 18:06:00 -08001116 mShutterButton.setAlpha(ShutterButton.ALPHA_WHEN_ENABLED);
Doris Liu9d264302014-02-11 10:41:40 -08001117 }
1118
Doris Liu2b906b82013-12-10 16:34:08 -08001119 /**
Angus Kong166e36f2013-12-03 08:54:42 -08001120 * Called when the back key is pressed.
1121 *
1122 * @return Whether the UI responded to the key event.
1123 */
1124 public boolean onBackPressed() {
Doris Liub9b72402013-12-19 19:07:58 -08001125 if (mFilmstripLayout.getVisibility() == View.VISIBLE) {
1126 return mFilmstripLayout.onBackPressed();
1127 } else {
1128 return mModeListView.onBackPressed();
1129 }
Angus Kong166e36f2013-12-03 08:54:42 -08001130 }
1131
1132 /**
Doris Liu06db7422013-12-09 19:36:25 -08001133 * Sets a {@link com.android.camera.ui.PreviewStatusListener} that
Erin Dahlgren1648c362014-01-06 15:06:04 -08001134 * listens to SurfaceTexture changes. In addition, listeners are set on
1135 * dependent app ui elements.
Doris Liu06db7422013-12-09 19:36:25 -08001136 *
1137 * @param previewStatusListener the listener that gets notified when SurfaceTexture
1138 * changes
1139 */
1140 public void setPreviewStatusListener(PreviewStatusListener previewStatusListener) {
1141 mPreviewStatusListener = previewStatusListener;
1142 if (mPreviewStatusListener != null) {
Erin Dahlgren1648c362014-01-06 15:06:04 -08001143 onPreviewListenerChanged();
1144 }
1145 }
1146
1147 /**
1148 * When the PreviewStatusListener changes, listeners need to be
1149 * set on the following app ui elements:
1150 * {@link com.android.camera.ui.PreviewOverlay},
1151 * {@link com.android.camera.ui.BottomBar},
Erin Dahlgrene2a1f542014-01-08 14:34:35 -08001152 * {@link com.android.camera.ui.IndicatorIconController}.
Erin Dahlgren1648c362014-01-06 15:06:04 -08001153 */
1154 private void onPreviewListenerChanged() {
1155 // Set a listener for recognizing preview gestures.
1156 GestureDetector.OnGestureListener gestureListener
1157 = mPreviewStatusListener.getGestureListener();
1158 if (gestureListener != null) {
1159 mPreviewOverlay.setGestureListener(gestureListener);
1160 }
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001161 View.OnTouchListener touchListener = mPreviewStatusListener.getTouchListener();
1162 if (touchListener != null) {
1163 mPreviewOverlay.setTouchListener(touchListener);
1164 }
Erin Dahlgren1648c362014-01-06 15:06:04 -08001165
Erin Dahlgren1648c362014-01-06 15:06:04 -08001166 mTextureViewHelper.setAutoAdjustTransform(
Angus Kongb2597012014-03-10 12:12:09 -07001167 mPreviewStatusListener.shouldAutoAdjustTransformMatrixOnLayout());
Erin Dahlgren1648c362014-01-06 15:06:04 -08001168 }
1169
1170 /**
1171 * This method should be called in onCameraOpened. It defines CameraAppUI
1172 * specific changes that depend on the camera or camera settings.
1173 */
1174 public void onChangeCamera() {
Erin Dahlgrenb1641f52014-01-14 15:58:52 -08001175 ModuleController moduleController = mController.getCurrentModuleController();
Angus Kong50521172014-01-17 17:23:59 -08001176 applyModuleSpecs(moduleController.getHardwareSpec(), moduleController.getBottomBarSpec());
Erin Dahlgrenb1641f52014-01-14 15:58:52 -08001177
Erin Dahlgrene2a1f542014-01-08 14:34:35 -08001178 if (mIndicatorIconController != null) {
Erin Dahlgren18484942014-01-07 11:46:57 -08001179 // Sync the settings state with the indicator state.
Erin Dahlgrena9068092014-01-09 15:09:05 -08001180 mIndicatorIconController.syncIndicators();
Doris Liu06db7422013-12-09 19:36:25 -08001181 }
1182 }
1183
1184 /**
Angus Kong2bacca72014-03-06 12:57:29 -08001185 * Adds a listener to receive callbacks when preview area changes.
Doris Liu482de022013-12-18 19:18:16 -08001186 */
Angus Kong2bacca72014-03-06 12:57:29 -08001187 public void addPreviewAreaChangedListener(
1188 PreviewStatusListener.PreviewAreaChangedListener listener) {
Doris Liu482de022013-12-18 19:18:16 -08001189 mTextureViewHelper.addPreviewAreaSizeChangedListener(listener);
1190 }
1191
1192 /**
Angus Kong2bacca72014-03-06 12:57:29 -08001193 * Removes a listener that receives callbacks when preview area changes.
Doris Liu482de022013-12-18 19:18:16 -08001194 */
Angus Kong2bacca72014-03-06 12:57:29 -08001195 public void removePreviewAreaChangedListener(
1196 PreviewStatusListener.PreviewAreaChangedListener listener) {
Doris Liu482de022013-12-18 19:18:16 -08001197 mTextureViewHelper.removePreviewAreaSizeChangedListener(listener);
1198 }
1199
1200 /**
Doris Liuf55f3c42013-11-20 00:24:46 -08001201 * This inflates generic_module layout, which contains all the shared views across
1202 * modules. Then each module inflates their own views in the given view group. For
1203 * now, this is called every time switching from a not-yet-refactored module to a
1204 * refactored module. In the future, this should only need to be done once per app
1205 * start.
1206 */
1207 public void prepareModuleUI() {
Erin Dahlgren3c622212014-04-11 14:44:04 -07001208 mController.getSettingsManager().addListener(this);
Doris Liuc6c97402013-12-06 21:02:53 -08001209 mModuleUI = (FrameLayout) mCameraRootView.findViewById(R.id.module_layout);
Doris Liuf55f3c42013-11-20 00:24:46 -08001210 mTextureView = (TextureView) mCameraRootView.findViewById(R.id.preview_content);
Sol Boucher8c8fb112014-08-11 23:10:12 -07001211 mTextureViewHelper = new TextureViewHelper(mTextureView, mCaptureLayoutHelper,
1212 mController.getCameraProvider());
Doris Liu70da9182013-12-17 18:41:15 -08001213 mTextureViewHelper.setSurfaceTextureListener(this);
1214 mTextureViewHelper.setOnLayoutChangeListener(mPreviewLayoutChangeListener);
1215
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -08001216 mBottomBar = (BottomBar) mCameraRootView.findViewById(R.id.bottom_bar);
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001217 int unpressedColor = mController.getAndroidContext().getResources()
1218 .getColor(R.color.bottombar_unpressed);
1219 setBottomBarColor(unpressedColor);
Spike Spragued9516f22014-07-18 17:34:03 -07001220 updateModeSpecificUIColors();
1221
Doris Liu28a29502014-04-10 16:01:51 -07001222 mBottomBar.setCaptureLayoutHelper(mCaptureLayoutHelper);
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001223
Erin Dahlgren165bb8d2014-01-30 14:11:20 -08001224 mModeOptionsOverlay
1225 = (ModeOptionsOverlay) mCameraRootView.findViewById(R.id.mode_options_overlay);
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001226
1227 // Sets the visibility of the bottom bar and the mode options.
1228 resetBottomControls(mController.getCurrentModuleController(),
1229 mController.getCurrentModuleIndex());
Doris Liu28a29502014-04-10 16:01:51 -07001230 mModeOptionsOverlay.setCaptureLayoutHelper(mCaptureLayoutHelper);
Erin Dahlgren56688f72014-01-09 14:30:34 -08001231
Erin Dahlgren5d187692014-02-25 19:16:12 -08001232 mShutterButton = (ShutterButton) mCameraRootView.findViewById(R.id.shutter_button);
1233 addShutterListener(mController.getCurrentModuleController());
1234 addShutterListener(mModeOptionsOverlay);
Erin Dahlgrena7919c72014-05-16 14:53:21 -07001235 addShutterListener(this);
Erin Dahlgren5d187692014-02-25 19:16:12 -08001236
Erin Dahlgrenb3f59a82014-02-04 15:23:30 -08001237 mGridLines = (GridLines) mCameraRootView.findViewById(R.id.grid_lines);
1238 mTextureViewHelper.addPreviewAreaSizeChangedListener(mGridLines);
1239
Doris Liuf9e4f8f2013-12-04 18:04:22 -08001240 mPreviewOverlay = (PreviewOverlay) mCameraRootView.findViewById(R.id.preview_overlay);
1241 mPreviewOverlay.setOnTouchListener(new MyTouchListener());
Erin Dahlgren165bb8d2014-01-30 14:11:20 -08001242 mPreviewOverlay.setOnPreviewTouchedListener(mModeOptionsOverlay);
Erin Dahlgren56688f72014-01-09 14:30:34 -08001243
Doris Liu1dfe7822013-12-12 00:02:08 -08001244 mCaptureOverlay = (CaptureAnimationOverlay)
1245 mCameraRootView.findViewById(R.id.capture_overlay);
Doris Liuca4a5662014-01-02 17:40:12 -08001246 mTextureViewHelper.addPreviewAreaSizeChangedListener(mPreviewOverlay);
1247 mTextureViewHelper.addPreviewAreaSizeChangedListener(mCaptureOverlay);
Erin Dahlgren4efa8b52013-12-17 18:31:35 -08001248
Erin Dahlgrene2a1f542014-01-08 14:34:35 -08001249 if (mIndicatorIconController == null) {
1250 mIndicatorIconController =
1251 new IndicatorIconController(mController, mAppRootView);
1252 }
Erin Dahlgrenf1b2e402014-01-14 11:08:59 -08001253
1254 mController.getButtonManager().load(mCameraRootView);
Erin Dahlgrene2a1f542014-01-08 14:34:35 -08001255 mController.getButtonManager().setListener(mIndicatorIconController);
Erin Dahlgren6e07fe22014-02-03 15:55:31 -08001256 mController.getSettingsManager().addListener(mIndicatorIconController);
Doris Liu9d264302014-02-11 10:41:40 -08001257
1258 mModeOptionsToggle = mCameraRootView.findViewById(R.id.mode_options_toggle);
Doris Liuaf656832014-02-19 14:12:06 -08001259 mFocusOverlay = mCameraRootView.findViewById(R.id.focus_overlay);
Sascha Haeberling1e0810c2014-04-07 17:29:44 -07001260 mTutorialsPlaceHolderWrapper = (FrameLayout) mCameraRootView
1261 .findViewById(R.id.tutorials_placeholder_wrapper);
Doris Liu28a29502014-04-10 16:01:51 -07001262 mIndicatorBottomBarWrapper = (BottomBarModeOptionsWrapper) mAppRootView
Alan Newbergerb6db6912014-03-26 14:51:12 -07001263 .findViewById(R.id.indicator_bottombar_wrapper);
Doris Liu28a29502014-04-10 16:01:51 -07001264 mIndicatorBottomBarWrapper.setCaptureLayoutHelper(mCaptureLayoutHelper);
Angus Kongb2597012014-03-10 12:12:09 -07001265 mTextureViewHelper.addPreviewAreaSizeChangedListener(
1266 new PreviewStatusListener.PreviewAreaChangedListener() {
1267 @Override
1268 public void onPreviewAreaChanged(RectF previewArea) {
Doris Liu28a29502014-04-10 16:01:51 -07001269 mPeekView.setTranslationX(previewArea.right - mAppRootView.getRight());
Angus Kongb2597012014-03-10 12:12:09 -07001270 }
1271 });
1272
Doris Liub908d932014-05-20 15:03:24 -07001273 mTextureViewHelper.addPreviewAreaSizeChangedListener(mModeListView);
Doris Liu28a29502014-04-10 16:01:51 -07001274 mTextureViewHelper.addAspectRatioChangedListener(
1275 new PreviewStatusListener.PreviewAspectRatioChangedListener() {
1276 @Override
1277 public void onPreviewAspectRatioChanged(float aspectRatio) {
1278 mModeOptionsOverlay.requestLayout();
1279 mBottomBar.requestLayout();
1280 }
Doris Liu344320c2014-04-22 14:47:49 -07001281 }
1282 );
Doris Liuf55f3c42013-11-20 00:24:46 -08001283 }
1284
Doris Liuf55f3c42013-11-20 00:24:46 -08001285 /**
1286 * Called indirectly from each module in their initialization to get a view group
1287 * to inflate the module specific views in.
1288 *
1289 * @return a view group for modules to attach views to
1290 */
Doris Liuc6c97402013-12-06 21:02:53 -08001291 public FrameLayout getModuleRootView() {
Doris Liub41034b2013-12-06 21:58:41 -08001292 // TODO: Change it to mModuleUI when refactor is done
1293 return mCameraRootView;
Doris Liuf55f3c42013-11-20 00:24:46 -08001294 }
1295
1296 /**
1297 * Remove all the module specific views.
1298 */
1299 public void clearModuleUI() {
1300 if (mModuleUI != null) {
1301 mModuleUI.removeAllViews();
1302 }
Erin Dahlgren5d187692014-02-25 19:16:12 -08001303 removeShutterListener(mController.getCurrentModuleController());
Sascha Haeberling1e0810c2014-04-07 17:29:44 -07001304 mTutorialsPlaceHolderWrapper.removeAllViews();
Sascha Haeberlingde7633b2014-04-07 18:06:59 -07001305 mTutorialsPlaceHolderWrapper.setVisibility(View.GONE);
Marco Nelissen0744e4a2013-11-22 01:47:37 +00001306
Seth Raphael59efb9a2014-04-02 14:46:03 -07001307 setShutterButtonEnabled(true);
Doris Liu06db7422013-12-09 19:36:25 -08001308 mPreviewStatusListener = null;
Doris Liuf9e4f8f2013-12-04 18:04:22 -08001309 mPreviewOverlay.reset();
Doris Liuaf656832014-02-19 14:12:06 -08001310 mFocusOverlay.setVisibility(View.INVISIBLE);
Doris Liuf55f3c42013-11-20 00:24:46 -08001311 }
1312
Doris Liu2b906b82013-12-10 16:34:08 -08001313 /**
Doris Liu5a367542014-01-17 17:21:42 -08001314 * 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.
1317 *
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.
1323 */
1324 public void onPreviewReadyToStart() {
1325 if (mModeCoverState == COVER_SHOWN) {
1326 mModeCoverState = COVER_WILL_HIDE_AT_NEXT_FRAME;
1327 mController.setupOneShotPreviewListener();
1328 }
1329 }
1330
1331 /**
Doris Liu2b906b82013-12-10 16:34:08 -08001332 * Gets called when preview is started.
1333 */
1334 public void onPreviewStarted() {
Alan Newbergerc91c8d22014-09-09 18:47:37 -07001335 Log.v(TAG, "onPreviewStarted");
Doris Liu2b906b82013-12-10 16:34:08 -08001336 if (mModeCoverState == COVER_SHOWN) {
Doris Liuc42677f2013-12-20 19:53:01 -08001337 mModeCoverState = COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE;
Doris Liu2b906b82013-12-10 16:34:08 -08001338 }
Erin Dahlgrena7919c72014-05-16 14:53:21 -07001339 enableModeOptions();
Doris Liu2b906b82013-12-10 16:34:08 -08001340 }
1341
Doris Liub6eaa8c2013-12-11 18:39:14 -08001342 /**
Doris Liu4d4a4bc2013-12-19 18:55:54 -08001343 * Gets notified when next preview frame comes in.
1344 */
1345 public void onNewPreviewFrame() {
Alan Newbergerc91c8d22014-09-09 18:47:37 -07001346 Log.v(TAG, "onNewPreviewFrame");
Doris Liu7cbecee2014-01-30 12:29:27 -08001347 CameraPerformanceTracker.onEvent(CameraPerformanceTracker.FIRST_PREVIEW_FRAME);
Doris Liu4d4a4bc2013-12-19 18:55:54 -08001348 hideModeCover();
Doris Liu4d4a4bc2013-12-19 18:55:54 -08001349 }
1350
Spike Spraguec19d97b2014-09-22 14:53:04 -07001351 @Override
1352 public void onShutterButtonClick() {
1353 /*
1354 * Set the mode options toggle unclickable, generally
1355 * throughout the app, whenever the shutter button is clicked.
1356 *
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.
1361 */
zhuqk0617ab4f92b2015-05-26 15:51:44 +08001362 //disableModeOptions();
Spike Spraguec19d97b2014-09-22 14:53:04 -07001363 }
1364
1365 @Override
1366 public void onShutterCoordinate(TouchCoordinate coord) {
1367 // Do nothing.
1368 }
1369
1370 @Override
1371 public void onShutterButtonFocus(boolean pressed) {
1372 // noop
1373 }
1374
Doris Liu4d4a4bc2013-12-19 18:55:54 -08001375 /**
Erin Dahlgrena7919c72014-05-16 14:53:21 -07001376 * Set the mode options toggle clickable.
1377 */
1378 public void enableModeOptions() {
1379 /*
1380 * For modules using camera 1 api, this gets called in
1381 * onSurfaceTextureUpdated whenever the preview gets stopped and
1382 * started after each capture. This also takes care of the
1383 * case where the mode options might be unclickable when we
1384 * switch modes
1385 *
1386 * For modules using camera 2 api, they're required to call this
1387 * method when a capture is "completed". Unfortunately this differs
1388 * per module implementation.
1389 */
Spike Spragued2d8b712014-10-02 10:14:55 -07001390 if (!mDisableAllUserInteractions) {
1391 mModeOptionsOverlay.setToggleClickable(true);
1392 }
Erin Dahlgrena7919c72014-05-16 14:53:21 -07001393 }
1394
Spike Spraguec19d97b2014-09-22 14:53:04 -07001395 /**
1396 * Set the mode options toggle not clickable.
1397 */
1398 public void disableModeOptions() {
Erin Dahlgrena7919c72014-05-16 14:53:21 -07001399 mModeOptionsOverlay.setToggleClickable(false);
1400 }
1401
Spike Spragued2d8b712014-10-02 10:14:55 -07001402 public void setDisableAllUserInteractions(boolean disable) {
1403 if (disable) {
1404 disableModeOptions();
1405 setShutterButtonEnabled(false);
1406 setSwipeEnabled(false);
Spike Sprague4c96d842014-10-15 15:11:06 -07001407 mModeListView.hideAnimated();
Spike Spragued2d8b712014-10-02 10:14:55 -07001408 } else {
1409 enableModeOptions();
1410 setShutterButtonEnabled(true);
1411 setSwipeEnabled(true);
1412 }
1413 mDisableAllUserInteractions = disable;
1414 }
1415
Erin Dahlgrena7919c72014-05-16 14:53:21 -07001416 /**
Doris Liub6eaa8c2013-12-11 18:39:14 -08001417 * Gets called when a mode is selected from {@link com.android.camera.ui.ModeListView}
1418 *
1419 * @param modeIndex mode index of the selected mode
1420 */
Doris Liuf55f3c42013-11-20 00:24:46 -08001421 @Override
1422 public void onModeSelected(int modeIndex) {
Doris Liub6eaa8c2013-12-11 18:39:14 -08001423 mHideCoverRunnable = new Runnable() {
1424 @Override
1425 public void run() {
1426 mModeListView.startModeSelectionAnimation();
1427 }
1428 };
Spike Spraguea16219d2014-09-12 15:01:04 -07001429 mShutterButton.setAlpha(ShutterButton.ALPHA_WHEN_ENABLED);
Doris Liue038c162013-12-13 23:06:11 -08001430 mModeCoverState = COVER_SHOWN;
1431
1432 int lastIndex = mController.getCurrentModuleIndex();
Spike Sprague0496fca2014-07-25 11:40:53 -07001433 // Actual mode teardown / new mode initialization happens here
Doris Liue038c162013-12-13 23:06:11 -08001434 mController.onModeSelected(modeIndex);
1435 int currentIndex = mController.getCurrentModuleIndex();
Doris Liub6eaa8c2013-12-11 18:39:14 -08001436
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001437 if (lastIndex == currentIndex) {
Doris Liub6eaa8c2013-12-11 18:39:14 -08001438 hideModeCover();
Doris Liub6eaa8c2013-12-11 18:39:14 -08001439 }
Spike Spragued9516f22014-07-18 17:34:03 -07001440
1441 updateModeSpecificUIColors();
1442 }
1443
1444 private void updateModeSpecificUIColors() {
Spike Spragued9516f22014-07-18 17:34:03 -07001445 setBottomBarColorsForModeIndex(mController.getCurrentModuleIndex());
Doris Liuf55f3c42013-11-20 00:24:46 -08001446 }
1447
Doris Liu213a4a02014-02-04 16:57:55 -08001448 @Override
Doris Liu9d264302014-02-11 10:41:40 -08001449 public void onSettingsSelected() {
Erin Dahlgren6190c362014-06-13 14:12:08 -07001450 mController.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL,
1451 Keys.KEY_SHOULD_SHOW_SETTINGS_BUTTON_CLING, false);
Doris Liu26aa92b2014-05-22 18:39:46 -07001452 mModeListView.setShouldShowSettingsCling(false);
Doris Liu9d264302014-02-11 10:41:40 -08001453 mController.onSettingsSelected();
1454 }
1455
1456 @Override
Doris Liu213a4a02014-02-04 16:57:55 -08001457 public int getCurrentModeIndex() {
1458 return mController.getCurrentModuleIndex();
1459 }
1460
Doris Liuf55f3c42013-11-20 00:24:46 -08001461 /********************** Capture animation **********************/
1462 /* TODO: This session is subject to UX changes. In addition to the generic
1463 flash animation and post capture animation, consider designating a parameter
1464 for specifying the type of animation, as well as an animation finished listener
1465 so that modules can have more knowledge of the status of the animation. */
1466
1467 /**
Angus Kong50521172014-01-17 17:23:59 -08001468 * Starts the filmstrip peek animation.
1469 *
1470 * @param bitmap The bitmap to show.
1471 * @param strong Whether the animation shows more portion of the bitmap or
1472 * not.
Spike Sprague159e6e92014-05-27 18:26:30 -07001473 * @param accessibilityString An accessibility String to be announced
1474 during the peek animation.
Angus Kong50521172014-01-17 17:23:59 -08001475 */
Spike Sprague159e6e92014-05-27 18:26:30 -07001476 public void startPeekAnimation(Bitmap bitmap, boolean strong, String accessibilityString) {
Angus Kong50521172014-01-17 17:23:59 -08001477 if (mFilmstripLayout.getVisibility() == View.VISIBLE) {
1478 return;
1479 }
Spike Sprague159e6e92014-05-27 18:26:30 -07001480 mPeekView.startPeekAnimation(bitmap, strong, accessibilityString);
Angus Kong50521172014-01-17 17:23:59 -08001481 }
1482
1483 /**
Doris Liuf55f3c42013-11-20 00:24:46 -08001484 * Starts the pre-capture animation.
Andy Huibers17d90842014-08-28 15:05:57 -07001485 *
1486 * @param shortFlash show shortest possible flash instead of regular long version.
Doris Liuf55f3c42013-11-20 00:24:46 -08001487 */
Andy Huibers17d90842014-08-28 15:05:57 -07001488 public void startPreCaptureAnimation(boolean shortFlash) {
1489 mCaptureOverlay.startFlashAnimation(shortFlash);
Doris Liuf55f3c42013-11-20 00:24:46 -08001490 }
1491
1492 /**
1493 * Cancels the pre-capture animation.
1494 */
1495 public void cancelPreCaptureAnimation() {
1496 mAnimationManager.cancelAnimations();
1497 }
1498
1499 /**
Doris Liuf55f3c42013-11-20 00:24:46 -08001500 * Cancels the post-capture animation.
1501 */
1502 public void cancelPostCaptureAnimation() {
1503 mAnimationManager.cancelAnimations();
1504 }
Doris Liu06db7422013-12-09 19:36:25 -08001505
Angus Kongb2510252013-12-10 22:58:15 -08001506 public FilmstripContentPanel getFilmstripContentPanel() {
1507 return mFilmstripPanel;
1508 }
1509
1510 /**
Angus Kong7ae25c22014-02-25 10:37:39 -08001511 * @return The {@link com.android.camera.app.CameraAppUI.BottomPanel} on the
Angus Kongb2510252013-12-10 22:58:15 -08001512 * bottom of the filmstrip.
1513 */
Angus Kong7ae25c22014-02-25 10:37:39 -08001514 public BottomPanel getFilmstripBottomControls() {
Angus Konge0aff892013-12-11 20:51:01 -08001515 return mFilmstripBottomControls;
Angus Kongb2510252013-12-10 22:58:15 -08001516 }
1517
Spike Spragueba97d192014-08-19 15:33:45 -07001518 public void showBottomControls() {
1519 mFilmstripBottomControls.show();
1520 }
1521
1522 public void hideBottomControls() {
1523 mFilmstripBottomControls.hide();
1524 }
1525
Angus Kongb2510252013-12-10 22:58:15 -08001526 /**
1527 * @param listener The listener for bottom controls.
1528 */
Angus Kong7ae25c22014-02-25 10:37:39 -08001529 public void setFilmstripBottomControlsListener(BottomPanel.Listener listener) {
Angus Konge0aff892013-12-11 20:51:01 -08001530 mFilmstripBottomControls.setListener(listener);
Angus Kongb2510252013-12-10 22:58:15 -08001531 }
1532
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001533 /***************************SurfaceTexture Api and Listener*********************************/
1534
1535 /**
1536 * Return the shared surface texture.
1537 */
1538 public SurfaceTexture getSurfaceTexture() {
1539 return mSurface;
1540 }
1541
1542 /**
1543 * Return the shared {@link android.graphics.SurfaceTexture}'s width.
1544 */
1545 public int getSurfaceWidth() {
1546 return mSurfaceWidth;
1547 }
1548
1549 /**
1550 * Return the shared {@link android.graphics.SurfaceTexture}'s height.
1551 */
1552 public int getSurfaceHeight() {
1553 return mSurfaceHeight;
1554 }
Doris Liu06db7422013-12-09 19:36:25 -08001555
1556 @Override
1557 public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001558 mSurface = surface;
1559 mSurfaceWidth = width;
1560 mSurfaceHeight = height;
Doris Liu2b906b82013-12-10 16:34:08 -08001561 Log.v(TAG, "SurfaceTexture is available");
Doris Liu06db7422013-12-09 19:36:25 -08001562 if (mPreviewStatusListener != null) {
1563 mPreviewStatusListener.onSurfaceTextureAvailable(surface, width, height);
1564 }
Erin Dahlgrena7919c72014-05-16 14:53:21 -07001565 enableModeOptions();
Doris Liu06db7422013-12-09 19:36:25 -08001566 }
1567
1568 @Override
1569 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001570 mSurface = surface;
1571 mSurfaceWidth = width;
1572 mSurfaceHeight = height;
Doris Liu06db7422013-12-09 19:36:25 -08001573 if (mPreviewStatusListener != null) {
1574 mPreviewStatusListener.onSurfaceTextureSizeChanged(surface, width, height);
1575 }
1576 }
1577
1578 @Override
1579 public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001580 mSurface = null;
Doris Liu2b906b82013-12-10 16:34:08 -08001581 Log.v(TAG, "SurfaceTexture is destroyed");
Doris Liu06db7422013-12-09 19:36:25 -08001582 if (mPreviewStatusListener != null) {
1583 return mPreviewStatusListener.onSurfaceTextureDestroyed(surface);
1584 }
1585 return false;
1586 }
1587
1588 @Override
1589 public void onSurfaceTextureUpdated(SurfaceTexture surface) {
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001590 mSurface = surface;
Spike Sprague3c3b31d2014-09-08 10:43:04 -07001591 if (mPreviewStatusListener != null) {
1592 mPreviewStatusListener.onSurfaceTextureUpdated(surface);
1593 }
Doris Liu4d4a4bc2013-12-19 18:55:54 -08001594 if (mModeCoverState == COVER_WILL_HIDE_AT_NEXT_TEXTURE_UPDATE) {
Alan Newbergerc91c8d22014-09-09 18:47:37 -07001595 Log.v(TAG, "hiding cover via onSurfaceTextureUpdated");
Doris Liu7cbecee2014-01-30 12:29:27 -08001596 CameraPerformanceTracker.onEvent(CameraPerformanceTracker.FIRST_PREVIEW_FRAME);
Doris Liu2b906b82013-12-10 16:34:08 -08001597 hideModeCover();
Doris Liu06db7422013-12-09 19:36:25 -08001598 }
1599 }
Erin Dahlgren4efa8b52013-12-17 18:31:35 -08001600
Erin Dahlgrenb3f59a82014-02-04 15:23:30 -08001601 /****************************Grid lines api ******************************/
1602
1603 /**
1604 * Show a set of evenly spaced lines over the preview. The number
1605 * of lines horizontally and vertically is determined by
1606 * {@link com.android.camera.ui.GridLines}.
1607 */
1608 public void showGridLines() {
1609 if (mGridLines != null) {
1610 mGridLines.setVisibility(View.VISIBLE);
1611 }
1612 }
1613
1614 /**
1615 * Hide the set of evenly spaced grid lines overlaying the preview.
1616 */
1617 public void hideGridLines() {
1618 if (mGridLines != null) {
1619 mGridLines.setVisibility(View.INVISIBLE);
1620 }
1621 }
1622
Erin Dahlgrend5e51462014-02-07 12:38:57 -08001623 /**
1624 * Return a callback which shows or hide the preview grid lines
1625 * depending on whether the grid lines setting is set on.
1626 */
1627 public ButtonManager.ButtonCallback getGridLinesCallback() {
1628 return new ButtonManager.ButtonCallback() {
1629 @Override
1630 public void onStateChanged(int state) {
Erin Dahlgren6190c362014-06-13 14:12:08 -07001631 if (Keys.areGridLinesOn(mController.getSettingsManager())) {
Erin Dahlgrend5e51462014-02-07 12:38:57 -08001632 showGridLines();
1633 } else {
1634 hideGridLines();
1635 }
1636 }
1637 };
1638 }
1639
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001640 /***************************Mode options api *****************************/
1641
1642 /**
1643 * Set the mode options visible.
1644 */
1645 public void showModeOptions() {
Erin Dahlgrena7919c72014-05-16 14:53:21 -07001646 /* Make mode options clickable. */
1647 enableModeOptions();
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001648 mModeOptionsOverlay.setVisibility(View.VISIBLE);
1649 }
1650
1651 /**
1652 * Set the mode options invisible. This is necessary for modes
1653 * that don't show a bottom bar for the capture UI.
1654 */
1655 public void hideModeOptions() {
1656 mModeOptionsOverlay.setVisibility(View.INVISIBLE);
1657 }
Erin Dahlgrenb3f59a82014-02-04 15:23:30 -08001658
1659 /****************************Bottom bar api ******************************/
1660
Erin Dahlgren4efa8b52013-12-17 18:31:35 -08001661 /**
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001662 * Sets up the bottom bar and mode options with the correct
1663 * shutter button and visibility based on the current module.
1664 */
1665 public void resetBottomControls(ModuleController module, int moduleIndex) {
1666 if (areBottomControlsUsed(module)) {
1667 setBottomBarShutterIcon(moduleIndex);
Doris Liu344320c2014-04-22 14:47:49 -07001668 mCaptureLayoutHelper.setShowBottomBar(true);
1669 } else {
1670 mCaptureLayoutHelper.setShowBottomBar(false);
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001671 }
1672 }
1673
1674 /**
1675 * Show or hide the mode options and bottom bar, based on
1676 * whether the current module is using the bottom bar. Returns
1677 * whether the mode options and bottom bar are used.
1678 */
1679 private boolean areBottomControlsUsed(ModuleController module) {
1680 if (module.isUsingBottomBar()) {
1681 showBottomBar();
1682 showModeOptions();
1683 return true;
1684 } else {
1685 hideBottomBar();
1686 hideModeOptions();
1687 return false;
1688 }
1689 }
1690
1691 /**
1692 * Set the bottom bar visible.
1693 */
1694 public void showBottomBar() {
1695 mBottomBar.setVisibility(View.VISIBLE);
1696 }
1697
1698 /**
1699 * Set the bottom bar invisible.
1700 */
1701 public void hideBottomBar() {
1702 mBottomBar.setVisibility(View.INVISIBLE);
1703 }
1704
1705 /**
Erin Dahlgren4efa8b52013-12-17 18:31:35 -08001706 * Sets the color of the bottom bar.
1707 */
1708 public void setBottomBarColor(int colorId) {
1709 mBottomBar.setBackgroundColor(colorId);
1710 }
1711
1712 /**
Spike Spragued9516f22014-07-18 17:34:03 -07001713 * Sets the pressed color of the bottom bar for a camera mode index.
Spike Spragueb92910e2014-01-13 18:10:54 -08001714 */
Spike Spragued9516f22014-07-18 17:34:03 -07001715 public void setBottomBarColorsForModeIndex(int index) {
1716 mBottomBar.setColorsForModeIndex(index);
Spike Spragueb92910e2014-01-13 18:10:54 -08001717 }
1718
1719 /**
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001720 * Sets the shutter button icon on the bottom bar, based on
1721 * the mode index.
Spike Sprague39f8a762014-01-13 14:22:18 -08001722 */
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001723 public void setBottomBarShutterIcon(int modeIndex) {
1724 int shutterIconId = CameraUtil.getCameraShutterIconId(modeIndex,
1725 mController.getAndroidContext());
Erin Dahlgrenfecd7232014-01-16 13:29:42 -08001726 mBottomBar.setShutterButtonIcon(shutterIconId);
Spike Sprague39f8a762014-01-13 14:22:18 -08001727 }
1728
Spike Sprague45a821d2014-03-14 14:32:54 -07001729 public void animateBottomBarToVideoStop(int shutterIconId) {
1730 mBottomBar.animateToVideoStop(shutterIconId);
Spike Sprague39f8a762014-01-13 14:22:18 -08001731 }
1732
Erin Dahlgrenfecd7232014-01-16 13:29:42 -08001733 public void animateBottomBarToFullSize(int shutterIconId) {
1734 mBottomBar.animateToFullSize(shutterIconId);
Spike Sprague39f8a762014-01-13 14:22:18 -08001735 }
1736
Sascha Haeberling0cf4a022014-07-31 11:36:58 -07001737 public void setShutterButtonEnabled(final boolean enabled) {
Spike Spragued2d8b712014-10-02 10:14:55 -07001738 if (!mDisableAllUserInteractions) {
1739 mBottomBar.post(new Runnable() {
1740 @Override
1741 public void run() {
1742 mBottomBar.setShutterButtonEnabled(enabled);
1743 }
1744 });
1745 }
Erin Dahlgren667630d2014-04-01 14:03:25 -07001746 }
1747
Spike Spragueb7e41a72014-07-07 15:13:27 -07001748 public void setShutterButtonImportantToA11y(boolean important) {
1749 mBottomBar.setShutterButtonImportantToA11y(important);
1750 }
1751
Erin Dahlgren667630d2014-04-01 14:03:25 -07001752 public boolean isShutterButtonEnabled() {
1753 return mBottomBar.isShutterButtonEnabled();
Spike Spraguea93909f2014-03-07 17:06:13 -08001754 }
1755
Alan Newbergerb6db6912014-03-26 14:51:12 -07001756 public void setIndicatorBottomBarWrapperVisible(boolean visible) {
1757 mIndicatorBottomBarWrapper.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
Alan Newbergerf495ea82014-03-24 18:47:16 -07001758 }
1759
Spike Sprague39f8a762014-01-13 14:22:18 -08001760 /**
Erin Dahlgren4efa8b52013-12-17 18:31:35 -08001761 * Set the visibility of the bottom bar.
1762 */
1763 // TODO: needed for when panorama is managed by the generic module ui.
1764 public void setBottomBarVisible(boolean visible) {
1765 mBottomBar.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
1766 }
1767
1768 /**
Erin Dahlgren5d187692014-02-25 19:16:12 -08001769 * Add a {@link #ShutterButton.OnShutterButtonListener} to the shutter button.
Erin Dahlgren4efa8b52013-12-17 18:31:35 -08001770 */
Erin Dahlgren5d187692014-02-25 19:16:12 -08001771 public void addShutterListener(ShutterButton.OnShutterButtonListener listener) {
1772 mShutterButton.addOnShutterButtonListener(listener);
1773 }
1774
1775 /**
1776 * Remove a {@link #ShutterButton.OnShutterButtonListener} from the shutter button.
1777 */
1778 public void removeShutterListener(ShutterButton.OnShutterButtonListener listener) {
1779 mShutterButton.removeOnShutterButtonListener(listener);
Erin Dahlgren4efa8b52013-12-17 18:31:35 -08001780 }
1781
1782 /**
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001783 * Performs a transition to the capture layout of the bottom bar.
1784 */
1785 public void transitionToCapture() {
1786 ModuleController moduleController = mController.getCurrentModuleController();
1787 applyModuleSpecs(moduleController.getHardwareSpec(),
1788 moduleController.getBottomBarSpec());
1789 mBottomBar.transitionToCapture();
1790 }
1791
1792 /**
Seth Raphael2db51d72014-03-11 10:52:05 -07001793 * Displays the Cancel button instead of the capture button.
1794 */
1795 public void transitionToCancel() {
1796 ModuleController moduleController = mController.getCurrentModuleController();
1797 applyModuleSpecs(moduleController.getHardwareSpec(),
1798 moduleController.getBottomBarSpec());
1799 mBottomBar.transitionToCancel();
1800 }
1801
1802 /**
Erin Dahlgren4efa8b52013-12-17 18:31:35 -08001803 * Performs a transition to the global intent layout.
1804 */
Spike Sprague15690d02014-02-10 12:11:20 -08001805 public void transitionToIntentCaptureLayout() {
Erin Dahlgrenb1641f52014-01-14 15:58:52 -08001806 ModuleController moduleController = mController.getCurrentModuleController();
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001807 applyModuleSpecs(moduleController.getHardwareSpec(),
1808 moduleController.getBottomBarSpec());
Spike Sprague15690d02014-02-10 12:11:20 -08001809 mBottomBar.transitionToIntentCaptureLayout();
Erin Dahlgren4efa8b52013-12-17 18:31:35 -08001810 }
1811
1812 /**
1813 * Performs a transition to the global intent review layout.
1814 */
1815 public void transitionToIntentReviewLayout() {
Erin Dahlgrenb1641f52014-01-14 15:58:52 -08001816 ModuleController moduleController = mController.getCurrentModuleController();
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001817 applyModuleSpecs(moduleController.getHardwareSpec(),
1818 moduleController.getBottomBarSpec());
1819 mBottomBar.transitionToIntentReviewLayout();
Erin Dahlgrenb1641f52014-01-14 15:58:52 -08001820 }
1821
Spike Sprague9f3d01d2014-06-30 14:52:57 -07001822 /**
1823 * @return whether UI is in intent review mode
1824 */
1825 public boolean isInIntentReview() {
1826 return mBottomBar.isInIntentReview();
1827 }
1828
Erin Dahlgren3c622212014-04-11 14:44:04 -07001829 @Override
Erin Dahlgren6190c362014-06-13 14:12:08 -07001830 public void onSettingChanged(SettingsManager settingsManager, String key) {
Erin Dahlgren3c622212014-04-11 14:44:04 -07001831 // Update the mode options based on the hardware spec,
1832 // when hdr changes to prevent flash from getting out of sync.
Erin Dahlgren6190c362014-06-13 14:12:08 -07001833 if (key.equals(Keys.KEY_CAMERA_HDR)) {
Erin Dahlgren3c622212014-04-11 14:44:04 -07001834 ModuleController moduleController = mController.getCurrentModuleController();
1835 applyModuleSpecs(moduleController.getHardwareSpec(),
1836 moduleController.getBottomBarSpec());
1837 }
1838 }
1839
Erin Dahlgrenb1641f52014-01-14 15:58:52 -08001840 /**
1841 * Applies a {@link com.android.camera.CameraAppUI.BottomBarUISpec}
1842 * to the bottom bar mode options based on limitations from a
1843 * {@link com.android.camera.hardware.HardwareSpec}.
1844 *
1845 * Options not supported by the hardware are either hidden
1846 * or disabled, depending on the option.
1847 *
1848 * Otherwise, the option is fully enabled and clickable.
1849 */
1850 public void applyModuleSpecs(final HardwareSpec hardwareSpec,
1851 final BottomBarUISpec bottomBarSpec) {
Erin Dahlgren49ab9222014-01-28 17:40:28 -08001852 if (hardwareSpec == null || bottomBarSpec == null) {
1853 return;
Erin Dahlgrenb1641f52014-01-14 15:58:52 -08001854 }
1855
1856 ButtonManager buttonManager = mController.getButtonManager();
1857 SettingsManager settingsManager = mController.getSettingsManager();
1858
Spike Sprague59345142014-04-22 18:04:05 -07001859 buttonManager.setToInitialState();
1860
Erin Dahlgrenb1641f52014-01-14 15:58:52 -08001861 /** Standard mode options */
Alan Newberger347dccb2014-09-24 17:19:25 -07001862 if (mController.getCameraProvider().getNumberOfCameras() > 1 &&
1863 hardwareSpec.isFrontCameraSupported()) {
Erin Dahlgrenb1641f52014-01-14 15:58:52 -08001864 if (bottomBarSpec.enableCamera) {
Doris Liu8ad8ad42014-03-27 14:43:31 -07001865 buttonManager.initializeButton(ButtonManager.BUTTON_CAMERA,
1866 bottomBarSpec.cameraCallback);
Erin Dahlgrenb1641f52014-01-14 15:58:52 -08001867 } else {
1868 buttonManager.disableButton(ButtonManager.BUTTON_CAMERA);
1869 }
1870 } else {
1871 // Hide camera icon if front camera not available.
1872 buttonManager.hideButton(ButtonManager.BUTTON_CAMERA);
1873 }
1874
Erin Dahlgrene346fb22014-02-19 17:23:01 -08001875 boolean flashBackCamera = mController.getSettingsManager().getBoolean(
Erin Dahlgren6190c362014-06-13 14:12:08 -07001876 SettingsManager.SCOPE_GLOBAL, Keys.KEY_FLASH_SUPPORTED_BACK_CAMERA);
Erin Dahlgrene346fb22014-02-19 17:23:01 -08001877 if (bottomBarSpec.hideFlash || !flashBackCamera) {
I-Jong Lin0784a832014-10-17 14:53:30 -07001878 // Hide both flash and torch button in flash disable logic
Erin Dahlgrena6587a12014-02-03 13:24:55 -08001879 buttonManager.hideButton(ButtonManager.BUTTON_FLASH);
I-Jong Lin0784a832014-10-17 14:53:30 -07001880 buttonManager.hideButton(ButtonManager.BUTTON_TORCH);
Erin Dahlgrena6587a12014-02-03 13:24:55 -08001881 } else {
1882 if (hardwareSpec.isFlashSupported()) {
Erin Dahlgrene346fb22014-02-19 17:23:01 -08001883 if (bottomBarSpec.enableFlash) {
Erin Dahlgren15473d42014-05-14 14:01:31 -07001884 buttonManager.initializeButton(ButtonManager.BUTTON_FLASH,
1885 bottomBarSpec.flashCallback);
Erin Dahlgrene346fb22014-02-19 17:23:01 -08001886 } else if (bottomBarSpec.enableTorchFlash) {
Erin Dahlgren15473d42014-05-14 14:01:31 -07001887 buttonManager.initializeButton(ButtonManager.BUTTON_TORCH,
1888 bottomBarSpec.flashCallback);
1889 } else if (bottomBarSpec.enableHdrPlusFlash) {
Sam Hasinoffb4dd5652014-05-21 13:27:45 -07001890 buttonManager.initializeButton(ButtonManager.BUTTON_HDR_PLUS_FLASH,
Erin Dahlgren15473d42014-05-14 14:01:31 -07001891 bottomBarSpec.flashCallback);
Erin Dahlgrena6587a12014-02-03 13:24:55 -08001892 } else {
I-Jong Lin0784a832014-10-17 14:53:30 -07001893 // Hide both flash and torch button in flash disable logic
Erin Dahlgrena6587a12014-02-03 13:24:55 -08001894 buttonManager.disableButton(ButtonManager.BUTTON_FLASH);
I-Jong Lin0784a832014-10-17 14:53:30 -07001895 buttonManager.disableButton(ButtonManager.BUTTON_TORCH);
Erin Dahlgrena6587a12014-02-03 13:24:55 -08001896 }
Erin Dahlgrenb1641f52014-01-14 15:58:52 -08001897 } else {
I-Jong Lin0784a832014-10-17 14:53:30 -07001898 // Disable both flash and torch icon if not supported
1899 // by the chosen camera hardware.
Erin Dahlgrenb1641f52014-01-14 15:58:52 -08001900 buttonManager.disableButton(ButtonManager.BUTTON_FLASH);
I-Jong Lin0784a832014-10-17 14:53:30 -07001901 buttonManager.disableButton(ButtonManager.BUTTON_TORCH);
Erin Dahlgrenb1641f52014-01-14 15:58:52 -08001902 }
Erin Dahlgrenb1641f52014-01-14 15:58:52 -08001903 }
1904
Erin Dahlgren8c1f9702014-03-04 17:08:37 -08001905 if (bottomBarSpec.hideHdr || mIsCaptureIntent) {
Erin Dahlgrenb1641f52014-01-14 15:58:52 -08001906 // Force hide hdr or hdr plus icon.
Erin Dahlgren15473d42014-05-14 14:01:31 -07001907 buttonManager.hideButton(ButtonManager.BUTTON_HDR_PLUS);
Erin Dahlgrenb1641f52014-01-14 15:58:52 -08001908 } else {
1909 if (hardwareSpec.isHdrPlusSupported()) {
Erin Dahlgren6190c362014-06-13 14:12:08 -07001910 if (bottomBarSpec.enableHdr && Keys.isCameraBackFacing(settingsManager,
1911 mController.getModuleScope())) {
Erin Dahlgren15473d42014-05-14 14:01:31 -07001912 buttonManager.initializeButton(ButtonManager.BUTTON_HDR_PLUS,
Doris Liu8ad8ad42014-03-27 14:43:31 -07001913 bottomBarSpec.hdrCallback);
Erin Dahlgrenb1641f52014-01-14 15:58:52 -08001914 } else {
Erin Dahlgren15473d42014-05-14 14:01:31 -07001915 buttonManager.disableButton(ButtonManager.BUTTON_HDR_PLUS);
Erin Dahlgrenb1641f52014-01-14 15:58:52 -08001916 }
1917 } else if (hardwareSpec.isHdrSupported()) {
Erin Dahlgren6190c362014-06-13 14:12:08 -07001918 if (bottomBarSpec.enableHdr && Keys.isCameraBackFacing(settingsManager,
1919 mController.getModuleScope())) {
Doris Liu8ad8ad42014-03-27 14:43:31 -07001920 buttonManager.initializeButton(ButtonManager.BUTTON_HDR,
1921 bottomBarSpec.hdrCallback);
Erin Dahlgrenb1641f52014-01-14 15:58:52 -08001922 } else {
1923 buttonManager.disableButton(ButtonManager.BUTTON_HDR);
1924 }
1925 } else {
1926 // Hide hdr plus or hdr icon if neither are supported.
Erin Dahlgren15473d42014-05-14 14:01:31 -07001927 buttonManager.hideButton(ButtonManager.BUTTON_HDR_PLUS);
Erin Dahlgrenb1641f52014-01-14 15:58:52 -08001928 }
1929 }
1930
Erin Dahlgrend5e51462014-02-07 12:38:57 -08001931 if (bottomBarSpec.hideGridLines) {
1932 // Force hide grid lines icon.
1933 buttonManager.hideButton(ButtonManager.BUTTON_GRID_LINES);
1934 hideGridLines();
1935 } else {
1936 if (bottomBarSpec.enableGridLines) {
Doris Liu8ad8ad42014-03-27 14:43:31 -07001937 buttonManager.initializeButton(ButtonManager.BUTTON_GRID_LINES,
1938 bottomBarSpec.gridLinesCallback != null ?
1939 bottomBarSpec.gridLinesCallback : getGridLinesCallback()
1940 );
Erin Dahlgrend5e51462014-02-07 12:38:57 -08001941 } else {
1942 buttonManager.disableButton(ButtonManager.BUTTON_GRID_LINES);
1943 hideGridLines();
1944 }
1945 }
1946
Doris Liu6c751642014-05-05 18:43:26 -07001947 if (bottomBarSpec.enableSelfTimer) {
1948 buttonManager.initializeButton(ButtonManager.BUTTON_COUNTDOWN, null);
1949 } else {
Sascha Haeberling4333fac2014-05-20 16:28:11 -07001950 if (bottomBarSpec.showSelfTimer) {
1951 buttonManager.disableButton(ButtonManager.BUTTON_COUNTDOWN);
1952 } else {
1953 buttonManager.hideButton(ButtonManager.BUTTON_COUNTDOWN);
1954 }
Doris Liu6c751642014-05-05 18:43:26 -07001955 }
1956
Erin Dahlgrenf80ac9e2014-02-18 11:20:34 -08001957 if (bottomBarSpec.enablePanoOrientation
1958 && PhotoSphereHelper.getPanoramaOrientationOptionArrayId() > 0) {
Spike Sprague59345142014-04-22 18:04:05 -07001959 buttonManager.initializePanoOrientationButtons(bottomBarSpec.panoOrientationCallback);
Erin Dahlgrena1fab412014-01-21 09:31:11 -08001960 }
1961
Spike Spragueabf54e22014-03-27 15:41:28 -07001962 boolean enableExposureCompensation = bottomBarSpec.enableExposureCompensation &&
Spike Sprague156f3e52014-05-15 15:32:49 -07001963 !(bottomBarSpec.minExposureCompensation == 0 && bottomBarSpec.maxExposureCompensation == 0) &&
Erin Dahlgren6190c362014-06-13 14:12:08 -07001964 mController.getSettingsManager().getBoolean(SettingsManager.SCOPE_GLOBAL,
1965 Keys.KEY_EXPOSURE_COMPENSATION_ENABLED);
Spike Spragueabf54e22014-03-27 15:41:28 -07001966 if (enableExposureCompensation) {
1967 buttonManager.initializePushButton(ButtonManager.BUTTON_EXPOSURE_COMPENSATION, null);
Spike Sprague46acdc52014-05-06 13:55:38 -07001968 buttonManager.setExposureCompensationParameters(
1969 bottomBarSpec.minExposureCompensation,
1970 bottomBarSpec.maxExposureCompensation,
1971 bottomBarSpec.exposureCompensationStep);
1972
Spike Spragueabf54e22014-03-27 15:41:28 -07001973 buttonManager.setExposureCompensationCallback(
1974 bottomBarSpec.exposureCompensationSetCallback);
1975 buttonManager.updateExposureButtons();
1976 } else {
1977 buttonManager.hideButton(ButtonManager.BUTTON_EXPOSURE_COMPENSATION);
1978 buttonManager.setExposureCompensationCallback(null);
1979 }
1980
Erin Dahlgrenb1641f52014-01-14 15:58:52 -08001981 /** Intent UI */
1982 if (bottomBarSpec.showCancel) {
Doris Liu8ad8ad42014-03-27 14:43:31 -07001983 buttonManager.initializePushButton(ButtonManager.BUTTON_CANCEL,
1984 bottomBarSpec.cancelCallback);
Erin Dahlgrenb1641f52014-01-14 15:58:52 -08001985 }
1986 if (bottomBarSpec.showDone) {
Doris Liu8ad8ad42014-03-27 14:43:31 -07001987 buttonManager.initializePushButton(ButtonManager.BUTTON_DONE,
1988 bottomBarSpec.doneCallback);
Erin Dahlgrenb1641f52014-01-14 15:58:52 -08001989 }
1990 if (bottomBarSpec.showRetake) {
Doris Liu8ad8ad42014-03-27 14:43:31 -07001991 buttonManager.initializePushButton(ButtonManager.BUTTON_RETAKE,
1992 bottomBarSpec.retakeCallback);
Erin Dahlgrenb1641f52014-01-14 15:58:52 -08001993 }
Spike Sprague51c877c2014-02-18 11:14:12 -08001994 if (bottomBarSpec.showReview) {
Doris Liu8ad8ad42014-03-27 14:43:31 -07001995 buttonManager.initializePushButton(ButtonManager.BUTTON_REVIEW,
1996 bottomBarSpec.reviewCallback,
1997 R.drawable.ic_play);
Spike Sprague51c877c2014-02-18 11:14:12 -08001998 }
Erin Dahlgren4efa8b52013-12-17 18:31:35 -08001999 }
Sascha Haeberlingc813ce12014-03-10 15:35:07 -07002000
2001 /**
2002 * Shows the given tutorial on the screen.
2003 */
2004 public void showTutorial(AbstractTutorialOverlay tutorial, LayoutInflater inflater) {
Sascha Haeberling1e0810c2014-04-07 17:29:44 -07002005 tutorial.show(mTutorialsPlaceHolderWrapper, inflater);
Sascha Haeberlingc813ce12014-03-10 15:35:07 -07002006 }
Alan Newberger623dd0c2014-03-24 15:55:23 -07002007
2008 /***************************Filmstrip api *****************************/
2009
2010 public void showFilmstrip() {
Sascha Haeberling256427b2014-05-15 14:45:02 -07002011 mModeListView.onBackPressed();
Alan Newberger623dd0c2014-03-24 15:55:23 -07002012 mFilmstripLayout.showFilmstrip();
2013 }
Alan Newberger8099a372014-03-24 17:17:38 -07002014
2015 public void hideFilmstrip() {
2016 mFilmstripLayout.hideFilmstrip();
2017 }
Doris Liuf55f3c42013-11-20 00:24:46 -08002018}