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