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;
18
19import android.content.Intent;
20import android.graphics.Point;
21import android.graphics.SurfaceTexture;
22import android.view.GestureDetector;
23import android.view.KeyEvent;
24import android.view.MotionEvent;
25import android.view.View;
26
27import com.android.camera.CameraActivity;
28import com.android.camera.CameraModule;
29import com.android.camera.app.AppController;
30import com.android.camera.app.CameraAppUI;
31import com.android.camera.async.MainThread;
32import com.android.camera.async.RefCountBase;
33import com.android.camera.burst.BurstFacadeFactory;
34import com.android.camera.captureintent.event.EventClickOnCameraKey;
35import com.android.camera.captureintent.event.EventOnSurfaceTextureAvailable;
36import com.android.camera.captureintent.event.EventOnSurfaceTextureDestroyed;
37import com.android.camera.captureintent.event.EventOnSurfaceTextureUpdated;
38import com.android.camera.captureintent.event.EventOnTextureViewLayoutChanged;
39import com.android.camera.captureintent.event.EventPause;
40import com.android.camera.captureintent.event.EventResume;
41import com.android.camera.captureintent.event.EventTapOnCancelShutterButton;
42import com.android.camera.captureintent.event.EventTapOnPreview;
43import com.android.camera.captureintent.event.EventTapOnShutterButton;
44import com.android.camera.captureintent.event.EventZoomRatioChanged;
45import com.android.camera.captureintent.resource.ResourceConstructed;
46import com.android.camera.captureintent.resource.ResourceConstructedImpl;
47import com.android.camera.captureintent.state.StateBackground;
48import com.android.camera.captureintent.stateful.State;
49import com.android.camera.captureintent.stateful.StateMachine;
50import com.android.camera.captureintent.stateful.StateMachineImpl;
51import com.android.camera.debug.Log;
52import com.android.camera.hardware.HardwareSpec;
53import com.android.camera.one.OneCameraException;
54import com.android.camera.one.OneCameraModule;
55import com.android.camera.settings.SettingsManager;
56import com.android.camera.ui.PreviewStatusListener;
57import com.android.camera.ui.TouchCoordinate;
58import com.android.camera.util.Size;
59import com.android.camera2.R;
60import com.android.ex.camera2.portability.CameraAgent;
61
62/**
63 * The camera module that handles image capture intent.
64 */
65public class CaptureIntentModule extends CameraModule {
66    private static final Log.Tag TAG = new Log.Tag("CapIntModule");
67
68    /** The module UI. */
69    private final CaptureIntentModuleUI mModuleUI;
70
71    /** The available resources after construction. */
72    private final RefCountBase<ResourceConstructed> mResourceConstructed;
73
74    /** The module state machine. */
75    private final StateMachine mStateMachine;
76
77    private TouchCoordinate mTouchPointInsideShutterButton;
78
79    public CaptureIntentModule(AppController appController, Intent intent,
80            String settingScopeNamespace) throws OneCameraException {
81        super(appController);
82        mModuleUI = new CaptureIntentModuleUI(
83                appController.getCameraAppUI(),
84                appController.getModuleLayoutRoot(),
85                mUIListener);
86        mStateMachine = new StateMachineImpl();
87        mResourceConstructed = ResourceConstructedImpl.create(
88                intent,
89                mModuleUI,
90                settingScopeNamespace,
91                MainThread.create(),
92                appController.getAndroidContext(),
93                appController.getCameraOpener(),
94                OneCameraModule.provideOneCameraManager(),
95                appController.getLocationManager(),
96                appController.getOrientationManager(),
97                appController.getSettingsManager(),
98                new BurstFacadeFactory.BurstFacadeStub(),
99                appController,
100                appController.getFatalErrorHandler());
101        final State initialState = StateBackground.create(mStateMachine, mResourceConstructed);
102        // Set the initial state.
103        mStateMachine.setInitialState(initialState);
104    }
105
106    @Override
107    public void onCameraAvailable(CameraAgent.CameraProxy cameraProxy) {
108        // Do nothing for capture intent.
109    }
110
111    @Override
112    public void onShutterButtonFocus(boolean pressed) {
113        // Do nothing for capture intent.
114    }
115
116    @Override
117    public void onShutterCoordinate(TouchCoordinate touchCoordinate) {
118        mTouchPointInsideShutterButton = touchCoordinate;
119    }
120
121    @Override
122    public void onShutterButtonClick() {
123        mStateMachine.processEvent(new EventTapOnShutterButton(mTouchPointInsideShutterButton));
124    }
125
126    @Override
127    public void onShutterButtonLongPressed() {
128        // Do nothing for capture intent.
129    }
130
131    @Override
132    public void init(
133            final CameraActivity activity, boolean isSecureCamera, boolean isCaptureIntent) {
134        mResourceConstructed.get().getAppController()
135                .setPreviewStatusListener(mPreviewStatusListener);
136
137        // Issue cancel countdown event when the button is pressed.
138        // TODO: Make this part of the official API the way shutter button events are.
139        mResourceConstructed.get().getAppController().getCameraAppUI()
140                .setCancelShutterButtonListener(new View.OnClickListener() {
141                    @Override
142                    public void onClick(View v) {
143                        mStateMachine.processEvent(new EventTapOnCancelShutterButton());
144                    }
145                });
146
147    }
148
149    @Override
150    public void resume() {
151        mModuleUI.onModuleResumed();
152        mStateMachine.processEvent(new EventResume());
153    }
154
155    @Override
156    public void pause() {
157        mModuleUI.setCountdownFinishedListener(null);
158        mModuleUI.onModulePaused();
159        mStateMachine.processEvent(new EventPause());
160    }
161
162    @Override
163    public void destroy() {
164        // Never called. Do nothing here.
165    }
166
167    @Override
168    public void onPreviewVisibilityChanged(int visibility) {
169        // Do nothing.
170    }
171
172    @Override
173    public void onLayoutOrientationChanged(boolean isLandscape) {
174        // Do nothing.
175    }
176
177    @Override
178    public void hardResetSettings(SettingsManager settingsManager) {
179        // Do nothing.
180    }
181
182    @Override
183    public HardwareSpec getHardwareSpec() {
184        /**
185         * Instead of passively providing CameraAppUI the hardware spec here,
186         * {@link com.android.camera.captureintent.state.StateOpeningCamera}
187         * will actively specify hardware spec.
188         */
189        return null;
190    }
191
192    @Override
193    public CameraAppUI.BottomBarUISpec getBottomBarSpec() {
194        /**
195         * Instead of passively providing CameraAppUI the bottom bar spec here,
196         * {@link com.android.camera.captureintent.state.StateOpeningCamera}
197         * will actively specify bottom bar spec.
198         */
199        return null;
200    }
201
202    @Override
203    public boolean isUsingBottomBar() {
204        return true;
205    }
206
207    @Override
208    public String getPeekAccessibilityString() {
209        return mResourceConstructed.get().getContext().getResources()
210                .getString(R.string.photo_accessibility_peek);
211    }
212
213    @Override
214    public boolean onKeyDown(int keyCode, KeyEvent event) {
215        switch (keyCode) {
216            case KeyEvent.KEYCODE_CAMERA:
217            case KeyEvent.KEYCODE_DPAD_CENTER:
218                mStateMachine.processEvent(new EventClickOnCameraKey());
219                return true;
220            case KeyEvent.KEYCODE_VOLUME_UP:
221            case KeyEvent.KEYCODE_VOLUME_DOWN:
222                // Prevent default.
223                return true;
224        }
225        return false;
226    }
227
228    @Override
229    public boolean onKeyUp(int keyCode, KeyEvent event) {
230        switch (keyCode) {
231            case KeyEvent.KEYCODE_VOLUME_UP:
232            case KeyEvent.KEYCODE_VOLUME_DOWN:
233                mStateMachine.processEvent(new EventClickOnCameraKey());
234                return true;
235        }
236        return false;
237    }
238
239    /** The listener to listen events from the UI. */
240    private final CaptureIntentModuleUI.Listener mUIListener =
241            new CaptureIntentModuleUI.Listener() {
242                @Override
243                public void onZoomRatioChanged(final float zoomRatio) {
244                    mStateMachine.processEvent(new EventZoomRatioChanged(zoomRatio));
245                }
246            };
247
248    /** The listener to listen events from the preview. */
249    private final PreviewStatusListener mPreviewStatusListener = new PreviewStatusListener() {
250        @Override
251        public void onPreviewLayoutChanged(View v, int left, int top, int right,
252                int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
253            final Size previewLayoutSize = new Size(right - left, bottom - top);
254            mStateMachine.processEvent(new EventOnTextureViewLayoutChanged(previewLayoutSize));
255        }
256
257        @Override
258        public boolean shouldAutoAdjustTransformMatrixOnLayout() {
259            return CaptureIntentConfig.WORKAROUND_PREVIEW_STRETCH_BUG_NEXUS4;
260        }
261
262        @Override
263        public void onPreviewFlipped() {
264            // Do nothing because when preview is flipped, TextureView will lay
265            // itself out again, which will then trigger a transform matrix
266            // update.
267        }
268
269        @Override
270        public GestureDetector.OnGestureListener getGestureListener() {
271            return new GestureDetector.SimpleOnGestureListener() {
272                @Override
273                public boolean onSingleTapUp(MotionEvent ev) {
274                    final Point tapPoint = new Point((int) ev.getX(), (int) ev.getY());
275                    mStateMachine.processEvent(new EventTapOnPreview(tapPoint));
276                    return true;
277                }
278            };
279        }
280
281        @Override
282        public View.OnTouchListener getTouchListener() {
283            return null;
284        }
285
286        @Override
287        public void onSurfaceTextureAvailable(
288                final SurfaceTexture surfaceTexture, int width, int height) {
289            mStateMachine.processEvent(new EventOnSurfaceTextureAvailable(surfaceTexture));
290        }
291
292        @Override
293        public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
294            mStateMachine.processEvent(new EventOnSurfaceTextureDestroyed());
295            return true;
296        }
297
298        @Override
299        public void onSurfaceTextureSizeChanged(
300                SurfaceTexture surfaceTexture, int width, int height) {
301        }
302
303        @Override
304        public void onSurfaceTextureUpdated(SurfaceTexture surface) {
305            mStateMachine.processEvent(new EventOnSurfaceTextureUpdated());
306        }
307    };
308}
309