1 /*
2  * Copyright (C) 2015 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 
17 package com.android.camera.captureintent.state;
18 
19 import android.view.View;
20 
21 import com.android.camera.ButtonManager;
22 import com.android.camera.app.CameraAppUI;
23 import com.android.camera.async.RefCountBase;
24 import com.android.camera.captureintent.event.EventOnOpenCameraFailed;
25 import com.android.camera.captureintent.event.EventOnOpenCameraSucceeded;
26 import com.android.camera.captureintent.event.EventPause;
27 import com.android.camera.captureintent.event.EventTapOnCancelIntentButton;
28 import com.android.camera.captureintent.event.EventTapOnConfirmPhotoButton;
29 import com.android.camera.captureintent.event.EventTapOnRetakePhotoButton;
30 import com.android.camera.captureintent.event.EventTapOnSwitchCameraButton;
31 import com.android.camera.captureintent.resource.ResourceConstructed;
32 import com.android.camera.captureintent.resource.ResourceSurfaceTexture;
33 import com.android.camera.captureintent.stateful.EventHandler;
34 import com.android.camera.captureintent.stateful.State;
35 import com.android.camera.captureintent.stateful.StateImpl;
36 import com.android.camera.debug.Log;
37 import com.android.camera.device.CameraId;
38 import com.android.camera.hardware.HardwareSpec;
39 import com.android.camera.one.OneCamera;
40 import com.android.camera.one.OneCameraAccessException;
41 import com.android.camera.one.OneCameraCaptureSetting;
42 import com.android.camera.one.OneCameraCharacteristics;
43 import com.android.camera.one.v2.photo.ImageRotationCalculator;
44 import com.android.camera.one.v2.photo.ImageRotationCalculatorImpl;
45 import com.android.camera.settings.Keys;
46 import com.android.camera.settings.SettingsManager;
47 import com.android.camera.util.Size;
48 import com.google.common.annotations.VisibleForTesting;
49 import com.google.common.base.Optional;
50 
51 import javax.annotation.Nonnull;
52 
53 /**
54  * Represents a state that the module is waiting for a camera to be opened.
55  */
56 public final class StateOpeningCamera extends StateImpl {
57     private static final Log.Tag TAG = new Log.Tag("StateOpeningCamera");
58 
59     private final RefCountBase<ResourceConstructed> mResourceConstructed;
60     private final RefCountBase<ResourceSurfaceTexture> mResourceSurfaceTexture;
61     private final OneCamera.Facing mCameraFacing;
62     private final CameraId mCameraId;
63     private final OneCameraCharacteristics mCameraCharacteristics;
64     private final String mCameraSettingsScope;
65 
66     /** The desired picture size. */
67     private Size mPictureSize;
68 
69     /** Whether is paused in the middle of opening camera. */
70     private boolean mIsPaused;
71 
72     private OneCameraCaptureSetting mOneCameraCaptureSetting;
73 
74     private OneCamera.OpenCallback mCameraOpenCallback = new OneCamera.OpenCallback() {
75         @Override
76         public void onFailure() {
77             getStateMachine().processEvent(new EventOnOpenCameraFailed());
78         }
79 
80         @Override
81         public void onCameraClosed() {
82             // Not used anymore.
83         }
84 
85         @Override
86         public void onCameraOpened(@Nonnull final OneCamera camera) {
87             getStateMachine().processEvent(new EventOnOpenCameraSucceeded(camera));
88         }
89     };
90 
from( State previousState, RefCountBase<ResourceConstructed> resourceConstructed, RefCountBase<ResourceSurfaceTexture> resourceSurfaceTexture, OneCamera.Facing cameraFacing, CameraId cameraId, OneCameraCharacteristics cameraCharacteristics)91     public static StateOpeningCamera from(
92             State previousState,
93             RefCountBase<ResourceConstructed> resourceConstructed,
94             RefCountBase<ResourceSurfaceTexture> resourceSurfaceTexture,
95             OneCamera.Facing cameraFacing,
96             CameraId cameraId,
97             OneCameraCharacteristics cameraCharacteristics) {
98         return new StateOpeningCamera(previousState, resourceConstructed,
99                 resourceSurfaceTexture, cameraFacing, cameraId, cameraCharacteristics);
100     }
101 
StateOpeningCamera(State previousState, RefCountBase<ResourceConstructed> resourceConstructed, RefCountBase<ResourceSurfaceTexture> resourceSurfaceTexture, OneCamera.Facing cameraFacing, CameraId cameraId, OneCameraCharacteristics cameraCharacteristics)102     private StateOpeningCamera(State previousState,
103             RefCountBase<ResourceConstructed> resourceConstructed,
104             RefCountBase<ResourceSurfaceTexture> resourceSurfaceTexture,
105             OneCamera.Facing cameraFacing,
106             CameraId cameraId,
107             OneCameraCharacteristics cameraCharacteristics) {
108         super(previousState);
109         mResourceConstructed = resourceConstructed;
110         mResourceConstructed.addRef();     // Will be balanced in onLeave().
111         mResourceSurfaceTexture = resourceSurfaceTexture;
112         mResourceSurfaceTexture.addRef();  // Will be balanced in onLeave().
113         mCameraFacing = cameraFacing;
114         mCameraId = cameraId;
115         mCameraCharacteristics = cameraCharacteristics;
116         mIsPaused = false;
117         mCameraSettingsScope = SettingsManager.getCameraSettingScope(mCameraId.getValue());
118         registerEventHandlers();
119     }
120 
registerEventHandlers()121     private void registerEventHandlers() {
122         /** Handles EventPause. */
123         EventHandler<EventPause> pauseHandler = new EventHandler<EventPause>() {
124             @Override
125             public Optional<State> processEvent(EventPause event) {
126                 mIsPaused = true;
127                 return NO_CHANGE;
128             }
129         };
130         setEventHandler(EventPause.class, pauseHandler);
131 
132         /** Handles EventOnOpenCameraSucceeded. */
133         EventHandler<EventOnOpenCameraSucceeded> onOpenCameraSucceededHandler =
134                 new EventHandler<EventOnOpenCameraSucceeded>() {
135                     @Override
136                     public Optional<State> processEvent(EventOnOpenCameraSucceeded event) {
137                         final OneCamera camera = event.getCamera();
138                         if (mIsPaused) {
139                             // Just close the camera and finish.
140                             camera.close();
141                             return Optional.of((State) StateBackgroundWithSurfaceTexture.from(
142                                     StateOpeningCamera.this,
143                                     mResourceConstructed,
144                                     mResourceSurfaceTexture));
145                         }
146 
147                         mResourceConstructed.get().getMainThread().execute(new Runnable() {
148                             @Override
149                             public void run() {
150                                 mResourceConstructed.get().getModuleUI().applyModuleSpecs(
151                                         getHardwareSpec(), getBottomBarSpec());
152                             }
153                         });
154 
155                         return Optional.of((State) StateStartingPreview.from(
156                                 StateOpeningCamera.this,
157                                 mResourceConstructed,
158                                 mResourceSurfaceTexture,
159                                 camera,
160                                 mCameraId,
161                                 mCameraFacing,
162                                 mCameraCharacteristics,
163                                 mPictureSize,
164                                 mOneCameraCaptureSetting));
165                     }
166                 };
167         setEventHandler(EventOnOpenCameraSucceeded.class, onOpenCameraSucceededHandler);
168 
169         /** Handles EventOnOpenCameraFailed. */
170         EventHandler<EventOnOpenCameraFailed> onOpenCameraFailedHandler =
171                 new EventHandler<EventOnOpenCameraFailed>() {
172                     @Override
173                     public Optional<State> processEvent(EventOnOpenCameraFailed event) {
174                         Log.e(TAG, "processOnCameraOpenFailure");
175                         return Optional.of((State) StateFatal.from(
176                                 StateOpeningCamera.this, mResourceConstructed));
177                     }
178                 };
179         setEventHandler(EventOnOpenCameraFailed.class, onOpenCameraFailedHandler);
180     }
181 
182     @Override
onEnter()183     public Optional<State> onEnter() {
184         if (mCameraCharacteristics == null) {
185             Log.e(TAG, "mCameraCharacteristics is null");
186             return Optional.of((State) StateFatal.from(this, mResourceConstructed));
187         }
188         try {
189             mPictureSize = mResourceConstructed.get().getResolutionSetting().getPictureSize(
190                     mCameraId, mCameraFacing);
191             mOneCameraCaptureSetting = OneCameraCaptureSetting.create(
192                     mPictureSize,
193                     mResourceConstructed.get().getAppController().getSettingsManager(),
194                     getHardwareSpec(),
195                     mCameraSettingsScope,
196                     false);
197         } catch (OneCameraAccessException ex) {
198             Log.e(TAG, "Failed while open camera", ex);
199             return Optional.of((State) StateFatal.from(this, mResourceConstructed));
200         }
201 
202         final ImageRotationCalculator imageRotationCalculator = ImageRotationCalculatorImpl.from(
203                 mResourceConstructed.get().getOrientationManager(), mCameraCharacteristics);
204 
205         mResourceConstructed.get().getOneCameraOpener().open(
206                 mCameraId,
207                 mOneCameraCaptureSetting,
208                 mResourceConstructed.get().getCameraHandler(),
209                 mResourceConstructed.get().getMainThread(),
210                 imageRotationCalculator,
211                 mResourceConstructed.get().getBurstFacade(),
212                 mResourceConstructed.get().getSoundPlayer(),
213                 mCameraOpenCallback,
214                 mResourceConstructed.get().getFatalErrorHandler());
215         return Optional.absent();
216     }
217 
218     @Override
onLeave()219     public void onLeave() {
220         mResourceConstructed.close();
221         mResourceSurfaceTexture.close();
222     }
223 
224     @VisibleForTesting
isPaused()225     public boolean isPaused() {
226         return mIsPaused;
227     }
228 
getHardwareSpec()229     private HardwareSpec getHardwareSpec() {
230         return new HardwareSpec() {
231             @Override
232             public boolean isFrontCameraSupported() {
233                 return mResourceConstructed.get()
234                       .getOneCameraManager().hasCameraFacing(OneCamera.Facing.FRONT);
235             }
236 
237             @Override
238             public boolean isHdrSupported() {
239                 return false;
240             }
241 
242             @Override
243             public boolean isHdrPlusSupported() {
244                 return false;
245             }
246 
247             @Override
248             public boolean isFlashSupported() {
249                 return mCameraCharacteristics.isFlashSupported();
250             }
251         };
252     }
253 
254     private CameraAppUI.BottomBarUISpec getBottomBarSpec() {
255         CameraAppUI.BottomBarUISpec bottomBarSpec = new CameraAppUI.BottomBarUISpec();
256         /** Camera switch button UI spec. */
257         bottomBarSpec.enableCamera = true;
258         bottomBarSpec.cameraCallback = new ButtonManager.ButtonCallback() {
259             @Override
260             public void onStateChanged(int cameraId) {
261                 getStateMachine().processEvent(new EventTapOnSwitchCameraButton());
262             }
263         };
264         /** Grid lines button UI spec. */
265         bottomBarSpec.enableGridLines = true;
266         /** HDR button UI spec. */
267         bottomBarSpec.enableHdr = false;
268         bottomBarSpec.hideHdr = true;
269         bottomBarSpec.hdrCallback = null;
270         /** Timer button UI spec. */
271         bottomBarSpec.enableSelfTimer = true;
272         bottomBarSpec.showSelfTimer = true;
273         /** Flash button UI spec. */
274         bottomBarSpec.enableFlash = mCameraCharacteristics.isFlashSupported();
275 
276         /** Setup exposure compensation */
277         bottomBarSpec.isExposureCompensationSupported = mCameraCharacteristics
278                 .isExposureCompensationSupported();
279         bottomBarSpec.enableExposureCompensation = bottomBarSpec.isExposureCompensationSupported;
280         bottomBarSpec.minExposureCompensation =
281                 mCameraCharacteristics.getMinExposureCompensation();
282         bottomBarSpec.maxExposureCompensation =
283                 mCameraCharacteristics.getMaxExposureCompensation();
284         bottomBarSpec.exposureCompensationStep =
285                 mCameraCharacteristics.getExposureCompensationStep();
286         bottomBarSpec.exposureCompensationSetCallback =
287                 new CameraAppUI.BottomBarUISpec.ExposureCompensationSetCallback() {
288                     @Override
289                     public void setExposure(int value) {
290                         mResourceConstructed.get().getSettingsManager().set(
291                                 mCameraSettingsScope, Keys.KEY_EXPOSURE, value);
292                     }
293                 };
294 
295         /** Intent image review UI spec. */
296         bottomBarSpec.showCancel = true;
297         bottomBarSpec.cancelCallback = new View.OnClickListener() {
298             @Override
299             public void onClick(View v) {
300                 getStateMachine().processEvent(new EventTapOnCancelIntentButton());
301             }
302         };
303         bottomBarSpec.showDone = true;
304         bottomBarSpec.doneCallback = new View.OnClickListener() {
305             @Override
306             public void onClick(View v) {
307                 getStateMachine().processEvent(new EventTapOnConfirmPhotoButton());
308             }
309         };
310         bottomBarSpec.showRetake = true;
311         bottomBarSpec.retakeCallback = new View.OnClickListener() {
312             @Override
313             public void onClick(View v) {
314                 getStateMachine().processEvent(new EventTapOnRetakePhotoButton());
315             }
316         };
317         return bottomBarSpec;
318     }
319 }
320