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