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