1/*
2 * Copyright (C) 2012 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;
18
19import android.content.Context;
20import android.content.Intent;
21import android.content.res.Configuration;
22import android.graphics.drawable.Drawable;
23import android.os.Bundle;
24import android.provider.MediaStore;
25import android.view.KeyEvent;
26import android.view.LayoutInflater;
27import android.view.MotionEvent;
28import android.view.OrientationEventListener;
29import android.view.View;
30import android.view.ViewGroup;
31import android.widget.FrameLayout;
32
33import com.android.camera.ui.CameraSwitcher;
34import com.android.gallery3d.app.PhotoPage;
35import com.android.gallery3d.common.ApiHelper;
36import com.android.gallery3d.util.LightCycleHelper;
37
38public class CameraActivity extends ActivityBase
39        implements CameraSwitcher.CameraSwitchListener {
40    public static final int PHOTO_MODULE_INDEX = 0;
41    public static final int VIDEO_MODULE_INDEX = 1;
42    public static final int PANORAMA_MODULE_INDEX = 2;
43    public static final int LIGHTCYCLE_MODULE_INDEX = 3;
44
45    CameraModule mCurrentModule;
46    private FrameLayout mFrame;
47    private ShutterButton mShutter;
48    private CameraSwitcher mSwitcher;
49    private View mShutterSwitcher;
50    private View mControlsBackground;
51    private Drawable[] mDrawables;
52    private int mCurrentModuleIndex;
53    private MotionEvent mDown;
54
55    private MyOrientationEventListener mOrientationListener;
56    // The degrees of the device rotated clockwise from its natural orientation.
57    private int mLastRawOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
58
59    private static final String TAG = "CAM_activity";
60
61    private static final int[] DRAW_IDS = {
62            R.drawable.ic_switch_camera,
63            R.drawable.ic_switch_video,
64            R.drawable.ic_switch_pan,
65            R.drawable.ic_switch_photosphere
66    };
67
68    @Override
69    public void onCreate(Bundle state) {
70        super.onCreate(state);
71        setContentView(R.layout.camera_main);
72        mFrame = (FrameLayout) findViewById(R.id.main_content);
73        mDrawables = new Drawable[DRAW_IDS.length];
74        for (int i = 0; i < DRAW_IDS.length; i++) {
75            mDrawables[i] = getResources().getDrawable(DRAW_IDS[i]);
76        }
77        init();
78        if (MediaStore.INTENT_ACTION_VIDEO_CAMERA.equals(getIntent().getAction())
79                || MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction())) {
80            mCurrentModule = new VideoModule();
81            mCurrentModuleIndex = VIDEO_MODULE_INDEX;
82        } else {
83            mCurrentModule = new PhotoModule();
84            mCurrentModuleIndex = PHOTO_MODULE_INDEX;
85        }
86        mCurrentModule.init(this, mFrame, true);
87        mSwitcher.setCurrentIndex(mCurrentModuleIndex);
88        mOrientationListener = new MyOrientationEventListener(this);
89    }
90
91    public void init() {
92        mControlsBackground = findViewById(R.id.controls);
93        mShutterSwitcher = findViewById(R.id.camera_shutter_switcher);
94        mShutter = (ShutterButton) findViewById(R.id.shutter_button);
95        mSwitcher = (CameraSwitcher) findViewById(R.id.camera_switcher);
96        int totaldrawid = (LightCycleHelper.hasLightCycleCapture(this)
97                                ? DRAW_IDS.length : DRAW_IDS.length - 1);
98        if (!ApiHelper.HAS_OLD_PANORAMA) totaldrawid--;
99
100        int[] drawids = new int[totaldrawid];
101        int[] moduleids = new int[totaldrawid];
102        int ix = 0;
103        for (int i = 0; i < mDrawables.length; i++) {
104            if (i == PANORAMA_MODULE_INDEX && !ApiHelper.HAS_OLD_PANORAMA) {
105                continue; // not enabled, so don't add to UI
106            }
107            if (i == LIGHTCYCLE_MODULE_INDEX && !LightCycleHelper.hasLightCycleCapture(this)) {
108                continue; // not enabled, so don't add to UI
109            }
110            moduleids[ix] = i;
111            drawids[ix++] = DRAW_IDS[i];
112        }
113        mSwitcher.setIds(moduleids, drawids);
114        mSwitcher.setSwitchListener(this);
115        mSwitcher.setCurrentIndex(mCurrentModuleIndex);
116    }
117
118    private class MyOrientationEventListener
119            extends OrientationEventListener {
120        public MyOrientationEventListener(Context context) {
121            super(context);
122        }
123
124        @Override
125        public void onOrientationChanged(int orientation) {
126            // We keep the last known orientation. So if the user first orient
127            // the camera then point the camera to floor or sky, we still have
128            // the correct orientation.
129            if (orientation == ORIENTATION_UNKNOWN) return;
130            mLastRawOrientation = orientation;
131            mCurrentModule.onOrientationChanged(orientation);
132        }
133    }
134
135    @Override
136    public void onCameraSelected(int i) {
137        if (mPaused) return;
138        if (i != mCurrentModuleIndex) {
139            mPaused = true;
140            boolean canReuse = canReuseScreenNail();
141            CameraHolder.instance().keep();
142            closeModule(mCurrentModule);
143            mCurrentModuleIndex = i;
144            switch (i) {
145                case VIDEO_MODULE_INDEX:
146                    mCurrentModule = new VideoModule();
147                    break;
148                case PHOTO_MODULE_INDEX:
149                    mCurrentModule = new PhotoModule();
150                    break;
151                case PANORAMA_MODULE_INDEX:
152                    mCurrentModule = new PanoramaModule();
153                    break;
154                case LIGHTCYCLE_MODULE_INDEX:
155                    mCurrentModule = LightCycleHelper.createPanoramaModule();
156                    break;
157            }
158            openModule(mCurrentModule, canReuse);
159            mCurrentModule.onOrientationChanged(mLastRawOrientation);
160        }
161    }
162
163    @Override
164    public void onShowSwitcherPopup() {
165        mCurrentModule.onShowSwitcherPopup();
166    }
167
168    private void openModule(CameraModule module, boolean canReuse) {
169        module.init(this, mFrame, canReuse && canReuseScreenNail());
170        mPaused = false;
171        module.onResumeBeforeSuper();
172        module.onResumeAfterSuper();
173    }
174
175    private void closeModule(CameraModule module) {
176        module.onPauseBeforeSuper();
177        module.onPauseAfterSuper();
178        mFrame.removeAllViews();
179    }
180
181    public ShutterButton getShutterButton() {
182        return mShutter;
183    }
184
185    public void hideUI() {
186        mControlsBackground.setVisibility(View.INVISIBLE);
187        hideSwitcher();
188        mShutter.setVisibility(View.GONE);
189    }
190
191    public void showUI() {
192        mControlsBackground.setVisibility(View.VISIBLE);
193        showSwitcher();
194        mShutter.setVisibility(View.VISIBLE);
195        // Force a layout change to show shutter button
196        mShutter.requestLayout();
197    }
198
199    public void hideSwitcher() {
200        mSwitcher.closePopup();
201        mSwitcher.setVisibility(View.INVISIBLE);
202    }
203
204    public void showSwitcher() {
205        if (mCurrentModule.needsSwitcher()) {
206            mSwitcher.setVisibility(View.VISIBLE);
207        }
208    }
209
210    public boolean isInCameraApp() {
211        return mShowCameraAppView;
212    }
213
214    @Override
215    public void onConfigurationChanged(Configuration config) {
216        super.onConfigurationChanged(config);
217
218        ViewGroup appRoot = (ViewGroup) findViewById(R.id.content);
219        // remove old switcher, shutter and shutter icon
220        View cameraControlsView = findViewById(R.id.camera_shutter_switcher);
221        appRoot.removeView(cameraControlsView);
222
223        // create new layout with the current orientation
224        LayoutInflater inflater = getLayoutInflater();
225        inflater.inflate(R.layout.camera_shutter_switcher, appRoot);
226        init();
227
228        if (mShowCameraAppView) {
229            showUI();
230        } else {
231            hideUI();
232        }
233        mCurrentModule.onConfigurationChanged(config);
234    }
235
236    @Override
237    public void onPause() {
238        mPaused = true;
239        mOrientationListener.disable();
240        mCurrentModule.onPauseBeforeSuper();
241        super.onPause();
242        mCurrentModule.onPauseAfterSuper();
243    }
244
245    @Override
246    public void onResume() {
247        mPaused = false;
248        mOrientationListener.enable();
249        mCurrentModule.onResumeBeforeSuper();
250        super.onResume();
251        mCurrentModule.onResumeAfterSuper();
252    }
253
254    @Override
255    protected void onFullScreenChanged(boolean full) {
256        if (full) {
257            showUI();
258        } else {
259            hideUI();
260        }
261        super.onFullScreenChanged(full);
262        mCurrentModule.onFullScreenChanged(full);
263    }
264
265    @Override
266    protected void onStop() {
267        super.onStop();
268        mCurrentModule.onStop();
269        getStateManager().clearTasks();
270    }
271
272    @Override
273    protected void onNewIntent(Intent intent) {
274        super.onNewIntent(intent);
275        getStateManager().clearActivityResult();
276    }
277
278    @Override
279    protected void installIntentFilter() {
280        super.installIntentFilter();
281        mCurrentModule.installIntentFilter();
282    }
283
284    @Override
285    protected void onActivityResult(
286            int requestCode, int resultCode, Intent data) {
287        // Only PhotoPage understands ProxyLauncher.RESULT_USER_CANCELED
288        if (resultCode == ProxyLauncher.RESULT_USER_CANCELED
289                && !(getStateManager().getTopState() instanceof PhotoPage)) {
290            resultCode = RESULT_CANCELED;
291        }
292        super.onActivityResult(requestCode, resultCode, data);
293        // Unmap cancel vs. reset
294        if (resultCode == ProxyLauncher.RESULT_USER_CANCELED) {
295            resultCode = RESULT_CANCELED;
296        }
297        mCurrentModule.onActivityResult(requestCode, resultCode, data);
298    }
299
300    // Preview area is touched. Handle touch focus.
301    @Override
302    protected void onSingleTapUp(View view, int x, int y) {
303        mCurrentModule.onSingleTapUp(view, x, y);
304    }
305
306    @Override
307    public void onBackPressed() {
308        if (!mCurrentModule.onBackPressed()) {
309            super.onBackPressed();
310        }
311    }
312
313    @Override
314    public boolean onKeyDown(int keyCode, KeyEvent event) {
315        return mCurrentModule.onKeyDown(keyCode,  event)
316                || super.onKeyDown(keyCode, event);
317    }
318
319    @Override
320    public boolean onKeyUp(int keyCode, KeyEvent event) {
321        return mCurrentModule.onKeyUp(keyCode,  event)
322                || super.onKeyUp(keyCode, event);
323    }
324
325    public void cancelActivityTouchHandling() {
326        if (mDown != null) {
327            MotionEvent cancel = MotionEvent.obtain(mDown);
328            cancel.setAction(MotionEvent.ACTION_CANCEL);
329            super.dispatchTouchEvent(cancel);
330        }
331    }
332
333    @Override
334    public boolean dispatchTouchEvent(MotionEvent m) {
335        if (m.getActionMasked() == MotionEvent.ACTION_DOWN) {
336            mDown = m;
337        }
338        if ((mSwitcher != null) && mSwitcher.showsPopup() && !mSwitcher.isInsidePopup(m)) {
339            return mSwitcher.onTouch(null, m);
340        } else {
341            return mShutterSwitcher.dispatchTouchEvent(m)
342                    || mCurrentModule.dispatchTouchEvent(m);
343        }
344    }
345
346    @Override
347    public void startActivityForResult(Intent intent, int requestCode) {
348        Intent proxyIntent = new Intent(this, ProxyLauncher.class);
349        proxyIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
350        proxyIntent.putExtra(Intent.EXTRA_INTENT, intent);
351        super.startActivityForResult(proxyIntent, requestCode);
352    }
353
354    public boolean superDispatchTouchEvent(MotionEvent m) {
355        return super.dispatchTouchEvent(m);
356    }
357
358    // Preview texture has been copied. Now camera can be released and the
359    // animation can be started.
360    @Override
361    public void onPreviewTextureCopied() {
362        mCurrentModule.onPreviewTextureCopied();
363    }
364
365    @Override
366    public void onCaptureTextureCopied() {
367        mCurrentModule.onCaptureTextureCopied();
368    }
369
370    @Override
371    public void onUserInteraction() {
372        super.onUserInteraction();
373        mCurrentModule.onUserInteraction();
374    }
375
376    @Override
377    protected boolean updateStorageHintOnResume() {
378        return mCurrentModule.updateStorageHintOnResume();
379    }
380
381    @Override
382    public void updateCameraAppView() {
383        super.updateCameraAppView();
384        mCurrentModule.updateCameraAppView();
385    }
386
387    private boolean canReuseScreenNail() {
388        return mCurrentModuleIndex == PHOTO_MODULE_INDEX
389                || mCurrentModuleIndex == VIDEO_MODULE_INDEX
390                || mCurrentModuleIndex == LIGHTCYCLE_MODULE_INDEX;
391    }
392
393    @Override
394    public boolean isPanoramaActivity() {
395        return (mCurrentModuleIndex == PANORAMA_MODULE_INDEX);
396    }
397
398    // Accessor methods for getting latency times used in performance testing
399    public long getAutoFocusTime() {
400        return (mCurrentModule instanceof PhotoModule) ?
401                ((PhotoModule) mCurrentModule).mAutoFocusTime : -1;
402    }
403
404    public long getShutterLag() {
405        return (mCurrentModule instanceof PhotoModule) ?
406                ((PhotoModule) mCurrentModule).mShutterLag : -1;
407    }
408
409    public long getShutterToPictureDisplayedTime() {
410        return (mCurrentModule instanceof PhotoModule) ?
411                ((PhotoModule) mCurrentModule).mShutterToPictureDisplayedTime : -1;
412    }
413
414    public long getPictureDisplayedToJpegCallbackTime() {
415        return (mCurrentModule instanceof PhotoModule) ?
416                ((PhotoModule) mCurrentModule).mPictureDisplayedToJpegCallbackTime : -1;
417    }
418
419    public long getJpegCallbackFinishTime() {
420        return (mCurrentModule instanceof PhotoModule) ?
421                ((PhotoModule) mCurrentModule).mJpegCallbackFinishTime : -1;
422    }
423
424    public long getCaptureStartTime() {
425        return (mCurrentModule instanceof PhotoModule) ?
426                ((PhotoModule) mCurrentModule).mCaptureStartTime : -1;
427    }
428
429    public boolean isRecording() {
430        return (mCurrentModule instanceof VideoModule) ?
431                ((VideoModule) mCurrentModule).isRecording() : false;
432    }
433
434    public CameraScreenNail getCameraScreenNail() {
435        return (CameraScreenNail) mCameraScreenNail;
436    }
437}
438