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
17package com.android.camera.captureintent.state;
18
19import com.android.camera.CaptureModuleUtil;
20import com.android.camera.async.RefCountBase;
21import com.android.camera.captureintent.event.EventOnStartPreviewFailed;
22import com.android.camera.captureintent.event.EventOnStartPreviewSucceeded;
23import com.android.camera.captureintent.event.EventOnTextureViewLayoutChanged;
24import com.android.camera.captureintent.event.EventPause;
25import com.android.camera.captureintent.resource.ResourceConstructed;
26import com.android.camera.captureintent.resource.ResourceOpenedCamera;
27import com.android.camera.captureintent.resource.ResourceOpenedCameraImpl;
28import com.android.camera.captureintent.resource.ResourceSurfaceTexture;
29import com.android.camera.captureintent.stateful.EventHandler;
30import com.android.camera.captureintent.stateful.State;
31import com.android.camera.captureintent.stateful.StateImpl;
32import com.android.camera.debug.Log;
33import com.android.camera.device.CameraId;
34import com.android.camera.exif.Rational;
35import com.android.camera.one.OneCamera;
36import com.android.camera.one.OneCameraAccessException;
37import com.android.camera.one.OneCameraCaptureSetting;
38import com.android.camera.one.OneCameraCharacteristics;
39import com.android.camera.util.Size;
40import com.google.common.base.Optional;
41
42import java.util.List;
43
44/**
45 * Represents a state that the module is waiting for the preview video stream
46 * to be started.
47 */
48public final class StateStartingPreview extends StateImpl {
49    private static final Log.Tag TAG = new Log.Tag("StStartingPreview");
50
51    private final RefCountBase<ResourceConstructed> mResourceConstructed;
52    private final RefCountBase<ResourceSurfaceTexture> mResourceSurfaceTexture;
53    private final RefCountBase<ResourceOpenedCamera> mResourceOpenedCamera;
54
55    public static StateStartingPreview from(
56            State previousState,
57            RefCountBase<ResourceConstructed> resourceConstructed,
58            RefCountBase<ResourceSurfaceTexture> resourceSurfaceTexture,
59            OneCamera camera,
60            CameraId cameraId,
61            OneCamera.Facing cameraFacing,
62            OneCameraCharacteristics cameraCharacteristics,
63            Size pictureSize,
64            OneCameraCaptureSetting captureSetting) {
65        return new StateStartingPreview(
66                previousState,
67                resourceConstructed,
68                resourceSurfaceTexture,
69                camera,
70                cameraId,
71                cameraFacing,
72                cameraCharacteristics,
73                pictureSize,
74                captureSetting);
75    }
76
77    private StateStartingPreview(
78            State previousState,
79            RefCountBase<ResourceConstructed> resourceConstructed,
80            RefCountBase<ResourceSurfaceTexture> resourceSurfaceTexture,
81            OneCamera camera,
82            CameraId cameraId,
83            OneCamera.Facing cameraFacing,
84            OneCameraCharacteristics cameraCharacteristics,
85            Size pictureSize,
86            OneCameraCaptureSetting captureSetting) {
87        super(previousState);
88        mResourceConstructed = resourceConstructed;
89        mResourceConstructed.addRef();     // Will be balanced in onLeave().
90        mResourceSurfaceTexture = resourceSurfaceTexture;
91        mResourceSurfaceTexture.addRef();  // Will be balanced in onLeave().
92        mResourceOpenedCamera = ResourceOpenedCameraImpl.create(
93                camera, cameraId, cameraFacing, cameraCharacteristics, pictureSize, captureSetting);
94        registerEventHandlers();
95    }
96
97    public void registerEventHandlers() {
98        /** Handles EventPause. */
99        EventHandler<EventPause> pauseHandler = new EventHandler<EventPause>() {
100            @Override
101            public Optional<State> processEvent(EventPause event) {
102                return Optional.of((State) StateBackgroundWithSurfaceTexture.from(
103                        StateStartingPreview.this,
104                        mResourceConstructed,
105                        mResourceSurfaceTexture));
106            }
107        };
108        setEventHandler(EventPause.class, pauseHandler);
109
110        /** Handles EventOnTextureViewLayoutChanged. */
111        EventHandler<EventOnTextureViewLayoutChanged> onTextureViewLayoutChangedHandler =
112                new EventHandler<EventOnTextureViewLayoutChanged>() {
113                    @Override
114                    public Optional<State> processEvent(EventOnTextureViewLayoutChanged event) {
115                        mResourceSurfaceTexture.get().setPreviewLayoutSize(event.getLayoutSize());
116                        return NO_CHANGE;
117                    }
118                };
119        setEventHandler(EventOnTextureViewLayoutChanged.class, onTextureViewLayoutChangedHandler);
120
121        /** Handles EventOnStartPreviewSucceeded. */
122        EventHandler<EventOnStartPreviewSucceeded> onStartPreviewSucceededHandler =
123                new EventHandler<EventOnStartPreviewSucceeded>() {
124                    @Override
125                    public Optional<State> processEvent(EventOnStartPreviewSucceeded event) {
126                        mResourceConstructed.get().getMainThread().execute(new Runnable() {
127                            @Override
128                            public void run() {
129                                mResourceConstructed.get().getModuleUI().onPreviewStarted();
130                            }
131                        });
132                        return Optional.of((State) StateReadyForCapture.from(
133                                StateStartingPreview.this,
134                                mResourceConstructed,
135                                mResourceSurfaceTexture,
136                                mResourceOpenedCamera));
137                    }
138                };
139        setEventHandler(EventOnStartPreviewSucceeded.class, onStartPreviewSucceededHandler);
140
141        /** Handles EventOnStartPreviewFailed. */
142        EventHandler<EventOnStartPreviewFailed> onStartPreviewFailedHandler =
143                new EventHandler<EventOnStartPreviewFailed>() {
144                    @Override
145                    public Optional<State> processEvent(EventOnStartPreviewFailed event) {
146                        Log.e(TAG, "processOnPreviewSetupFailed");
147                        return Optional.of((State) StateFatal.from(
148                                StateStartingPreview.this, mResourceConstructed));
149                    }
150                };
151        setEventHandler(EventOnStartPreviewFailed.class, onStartPreviewFailedHandler);
152    }
153
154    @Override
155    public Optional<State> onEnter() {
156        final Size previewSize;
157        try {
158            // Pick a preview size with the right aspect ratio.
159            final List<Size> supportedPreviewSizes = mResourceOpenedCamera.get()
160                    .getCameraCharacteristics().getSupportedPreviewSizes();
161            if (supportedPreviewSizes.isEmpty()) {
162                return Optional.of((State) StateFatal.from(this, mResourceConstructed));
163            }
164
165            final Rational pictureAspectRatio =
166                    mResourceConstructed.get().getResolutionSetting().getPictureAspectRatio(
167                          mResourceOpenedCamera.get().getCameraId(),
168                          mResourceOpenedCamera.get().getCameraFacing());
169            previewSize = CaptureModuleUtil.getOptimalPreviewSize(
170                    supportedPreviewSizes.toArray(new Size[(supportedPreviewSizes.size())]),
171                    pictureAspectRatio.toDouble(),
172                    null);
173            if (previewSize == null) {
174                // TODO: Try to avoid entering StateFatal by seeing if there is
175                // another way to get the correct preview size.
176                return Optional.of((State) StateFatal.from(this, mResourceConstructed));
177            }
178        } catch (OneCameraAccessException ex) {
179            return Optional.of((State) StateFatal.from(this, mResourceConstructed));
180        }
181
182        // Must do this before calling ResourceOpenedCamera.startPreview()
183        // since SurfaceTexture.setDefaultBufferSize() needs to be called
184        // before starting preview. Otherwise the size of preview video stream
185        // will be wrong.
186        mResourceSurfaceTexture.get().setPreviewSize(previewSize);
187
188        OneCamera.CaptureReadyCallback captureReadyCallback =
189                new OneCamera.CaptureReadyCallback() {
190                    @Override
191                    public void onSetupFailed() {
192                        getStateMachine().processEvent(new EventOnStartPreviewFailed());
193                    }
194
195                    @Override
196                    public void onReadyForCapture() {
197                        getStateMachine().processEvent(new EventOnStartPreviewSucceeded());
198                    }
199                };
200
201        // Start preview right away. Don't dispatch it on other threads or it
202        // will cause race condition. b/19522251.
203        mResourceOpenedCamera.get().startPreview(
204                mResourceSurfaceTexture.get().createPreviewSurface(), captureReadyCallback);
205        return Optional.absent();
206    }
207
208    @Override
209    public void onLeave() {
210        mResourceConstructed.close();
211        mResourceSurfaceTexture.close();
212        mResourceOpenedCamera.close();
213    }
214}
215