CameraActivity.java revision 8d2d0f5f0f7fffce9dbcc70da6924af545eba26d
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        mControlsBackground = findViewById(R.id.blocker);
121        mCameraControls = findViewById(R.id.camera_controls);
122        mShutter = (ShutterButton) findViewById(R.id.shutter_button);
123        mSwitcher = (CameraSwitcher) findViewById(R.id.camera_switcher);
124        mPieMenuButton = findViewById(R.id.menu);
125        int totaldrawid = (LightCycleHelper.hasLightCycleCapture(this)
126                                ? DRAW_IDS.length : DRAW_IDS.length - 1);
127        if (!ApiHelper.HAS_OLD_PANORAMA) totaldrawid--;
128
129        int[] drawids = new int[totaldrawid];
130        int[] moduleids = new int[totaldrawid];
131        int ix = 0;
132        for (int i = 0; i < mDrawables.length; i++) {
133            if (i == PANORAMA_MODULE_INDEX && !ApiHelper.HAS_OLD_PANORAMA) {
134                continue; // not enabled, so don't add to UI
135            }
136            if (i == LIGHTCYCLE_MODULE_INDEX && !LightCycleHelper.hasLightCycleCapture(this)) {
137                continue; // not enabled, so don't add to UI
138            }
139            moduleids[ix] = i;
140            drawids[ix++] = DRAW_IDS[i];
141        }
142        mSwitcher.setIds(moduleids, drawids);
143        mSwitcher.setSwitchListener(this);
144        mSwitcher.setCurrentIndex(mCurrentModuleIndex);
145    }
146
147    @Override
148    public void onDestroy() {
149        unbindMediaSaveService();
150        super.onDestroy();
151    }
152
153    // Return whether the auto-rotate screen in system settings
154    // is turned on.
155    public boolean isAutoRotateScreen() {
156        return mAutoRotateScreen;
157    }
158
159    private class MyOrientationEventListener
160            extends OrientationEventListener {
161        public MyOrientationEventListener(Context context) {
162            super(context);
163        }
164
165        @Override
166        public void onOrientationChanged(int orientation) {
167            // We keep the last known orientation. So if the user first orient
168            // the camera then point the camera to floor or sky, we still have
169            // the correct orientation.
170            if (orientation == ORIENTATION_UNKNOWN) return;
171            mLastRawOrientation = orientation;
172            mCurrentModule.onOrientationChanged(orientation);
173        }
174    }
175
176    private ObjectAnimator mCameraSwitchAnimator;
177
178    @Override
179    public void onCameraSelected(final int i) {
180        if (mPaused) return;
181        if (i != mCurrentModuleIndex) {
182            mPaused = true;
183            CameraScreenNail screenNail = getCameraScreenNail();
184            if (screenNail != null) {
185                if (mCameraSwitchAnimator != null && mCameraSwitchAnimator.isRunning()) {
186                    mCameraSwitchAnimator.cancel();
187                }
188                mCameraSwitchAnimator = ObjectAnimator.ofFloat(
189                        screenNail, "alpha", screenNail.getAlpha(), 0f);
190                mCameraSwitchAnimator.addListener(new AnimatorListenerAdapter() {
191                    @Override
192                    public void onAnimationEnd(Animator animation) {
193                        super.onAnimationEnd(animation);
194                        doChangeCamera(i);
195                    }
196                });
197                mCameraSwitchAnimator.start();
198            } else {
199                doChangeCamera(i);
200            }
201        }
202    }
203
204    private void doChangeCamera(int i) {
205        boolean canReuse = canReuseScreenNail();
206        CameraHolder.instance().keep();
207        closeModule(mCurrentModule);
208        mCurrentModuleIndex = i;
209        switch (i) {
210            case VIDEO_MODULE_INDEX:
211                mCurrentModule = new VideoModule();
212                break;
213            case PHOTO_MODULE_INDEX:
214                mCurrentModule = new PhotoModule();
215                break;
216            case PANORAMA_MODULE_INDEX:
217                mCurrentModule = new PanoramaModule();
218                break;
219            case LIGHTCYCLE_MODULE_INDEX:
220                mCurrentModule = LightCycleHelper.createPanoramaModule();
221                break;
222        }
223        showPieMenuButton(mCurrentModule.needsPieMenu());
224
225        openModule(mCurrentModule, canReuse);
226        mCurrentModule.onOrientationChanged(mLastRawOrientation);
227        if (mMediaSaveService != null) {
228            mCurrentModule.onMediaSaveServiceConnected(mMediaSaveService);
229        }
230        getCameraScreenNail().setAlpha(0f);
231        getCameraScreenNail().setOnFrameDrawnOneShot(mOnFrameDrawn);
232    }
233
234    public void showPieMenuButton(boolean show) {
235        if (show) {
236            findViewById(R.id.blocker).setVisibility(View.VISIBLE);
237            findViewById(R.id.menu).setVisibility(View.VISIBLE);
238            findViewById(R.id.on_screen_indicators).setVisibility(View.VISIBLE);
239        } else {
240            findViewById(R.id.blocker).setVisibility(View.INVISIBLE);
241            findViewById(R.id.menu).setVisibility(View.INVISIBLE);
242            findViewById(R.id.on_screen_indicators).setVisibility(View.INVISIBLE);
243        }
244    }
245
246    private Runnable mOnFrameDrawn = new Runnable() {
247
248        @Override
249        public void run() {
250            runOnUiThread(mFadeInCameraScreenNail);
251        }
252    };
253
254    private Runnable mFadeInCameraScreenNail = new Runnable() {
255
256        @Override
257        public void run() {
258            mCameraSwitchAnimator = ObjectAnimator.ofFloat(
259                    getCameraScreenNail(), "alpha", 0f, 1f);
260            mCameraSwitchAnimator.setStartDelay(50);
261            mCameraSwitchAnimator.start();
262        }
263    };
264
265    @Override
266    public void onShowSwitcherPopup() {
267        mCurrentModule.onShowSwitcherPopup();
268    }
269
270    private void openModule(CameraModule module, boolean canReuse) {
271        module.init(this, mFrame, canReuse && canReuseScreenNail());
272        mPaused = false;
273        module.onResumeBeforeSuper();
274        module.onResumeAfterSuper();
275    }
276
277    private void closeModule(CameraModule module) {
278        module.onPauseBeforeSuper();
279        module.onPauseAfterSuper();
280        mFrame.removeAllViews();
281    }
282
283    public ShutterButton getShutterButton() {
284        return mShutter;
285    }
286
287    public void hideUI() {
288        mCameraControls.setVisibility(View.INVISIBLE);
289        hideSwitcher();
290        mShutter.setVisibility(View.GONE);
291    }
292
293    public void showUI() {
294        mCameraControls.setVisibility(View.VISIBLE);
295        showSwitcher();
296        mShutter.setVisibility(View.VISIBLE);
297        // Force a layout change to show shutter button
298        mShutter.requestLayout();
299    }
300
301    public void hideSwitcher() {
302        mSwitcher.closePopup();
303        mSwitcher.setVisibility(View.INVISIBLE);
304    }
305
306    public void showSwitcher() {
307        if (mCurrentModule.needsSwitcher()) {
308            mSwitcher.setVisibility(View.VISIBLE);
309        }
310    }
311
312    public boolean isInCameraApp() {
313        return mShowCameraAppView;
314    }
315
316    @Override
317    public void onConfigurationChanged(Configuration config) {
318        super.onConfigurationChanged(config);
319
320        ViewGroup appRoot = (ViewGroup) findViewById(R.id.content);
321        boolean landscape = (config.orientation == Configuration.ORIENTATION_LANDSCAPE);
322        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) appRoot.getLayoutParams();
323        int offset = getResources().getDimensionPixelSize(R.dimen.margin_systemui_offset);
324        int navBarHeight = getResources().getDimensionPixelSize(R.dimen.navigation_bar_height);
325        if (landscape) {
326            lp.rightMargin = offset;
327        } else {
328            lp.rightMargin = 0;
329        }
330        appRoot.setLayoutParams(lp);
331
332        // Set padding to move camera controls away from the edge of the screen
333        // so that they are in the same place as if there was a navigation bar between
334        // the screen edge and the controls
335        if (landscape) {
336            mCameraControls.setPadding(navBarHeight, 0, 0, 0);
337        } else {
338            mCameraControls.setPadding(0, navBarHeight, 0, 0);
339        }
340        mCurrentModule.onConfigurationChanged(config);
341    }
342
343    @Override
344    public void onPause() {
345        mPaused = true;
346        mOrientationListener.disable();
347        mCurrentModule.onPauseBeforeSuper();
348        super.onPause();
349        mCurrentModule.onPauseAfterSuper();
350    }
351
352    @Override
353    public void onResume() {
354        mPaused = false;
355        if (Settings.System.getInt(getContentResolver(),
356                Settings.System.ACCELEROMETER_ROTATION, 0) == 0) {// auto-rotate off
357            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
358            mAutoRotateScreen = false;
359        } else {
360            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
361            mAutoRotateScreen = true;
362        }
363        mOrientationListener.enable();
364        mCurrentModule.onResumeBeforeSuper();
365        super.onResume();
366        mCurrentModule.onResumeAfterSuper();
367    }
368
369    private void bindMediaSaveService() {
370        Intent intent = new Intent(this, MediaSaveService.class);
371        startService(intent);  // start service before binding it so the
372                               // service won't be killed if we unbind it.
373        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
374    }
375
376    private void unbindMediaSaveService() {
377        mMediaSaveService.setListener(null);
378        unbindService(mConnection);
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 {
482            return mCameraControls.dispatchTouchEvent(m)
483                    || mCurrentModule.dispatchTouchEvent(m);
484        }
485    }
486
487    @Override
488    public void startActivityForResult(Intent intent, int requestCode) {
489        Intent proxyIntent = new Intent(this, ProxyLauncher.class);
490        proxyIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
491        proxyIntent.putExtra(Intent.EXTRA_INTENT, intent);
492        super.startActivityForResult(proxyIntent, requestCode);
493    }
494
495    public boolean superDispatchTouchEvent(MotionEvent m) {
496        return super.dispatchTouchEvent(m);
497    }
498
499    // Preview texture has been copied. Now camera can be released and the
500    // animation can be started.
501    @Override
502    public void onPreviewTextureCopied() {
503        mCurrentModule.onPreviewTextureCopied();
504    }
505
506    @Override
507    public void onCaptureTextureCopied() {
508        mCurrentModule.onCaptureTextureCopied();
509    }
510
511    @Override
512    public void onUserInteraction() {
513        super.onUserInteraction();
514        mCurrentModule.onUserInteraction();
515    }
516
517    @Override
518    protected boolean updateStorageHintOnResume() {
519        return mCurrentModule.updateStorageHintOnResume();
520    }
521
522    @Override
523    public void updateCameraAppView() {
524        super.updateCameraAppView();
525        mCurrentModule.updateCameraAppView();
526    }
527
528    private boolean canReuseScreenNail() {
529        return mCurrentModuleIndex == PHOTO_MODULE_INDEX
530                || mCurrentModuleIndex == VIDEO_MODULE_INDEX
531                || mCurrentModuleIndex == LIGHTCYCLE_MODULE_INDEX;
532    }
533
534    @Override
535    public boolean isPanoramaActivity() {
536        return (mCurrentModuleIndex == PANORAMA_MODULE_INDEX);
537    }
538
539    // Accessor methods for getting latency times used in performance testing
540    public long getAutoFocusTime() {
541        return (mCurrentModule instanceof PhotoModule) ?
542                ((PhotoModule) mCurrentModule).mAutoFocusTime : -1;
543    }
544
545    public long getShutterLag() {
546        return (mCurrentModule instanceof PhotoModule) ?
547                ((PhotoModule) mCurrentModule).mShutterLag : -1;
548    }
549
550    public long getShutterToPictureDisplayedTime() {
551        return (mCurrentModule instanceof PhotoModule) ?
552                ((PhotoModule) mCurrentModule).mShutterToPictureDisplayedTime : -1;
553    }
554
555    public long getPictureDisplayedToJpegCallbackTime() {
556        return (mCurrentModule instanceof PhotoModule) ?
557                ((PhotoModule) mCurrentModule).mPictureDisplayedToJpegCallbackTime : -1;
558    }
559
560    public long getJpegCallbackFinishTime() {
561        return (mCurrentModule instanceof PhotoModule) ?
562                ((PhotoModule) mCurrentModule).mJpegCallbackFinishTime : -1;
563    }
564
565    public long getCaptureStartTime() {
566        return (mCurrentModule instanceof PhotoModule) ?
567                ((PhotoModule) mCurrentModule).mCaptureStartTime : -1;
568    }
569
570    public boolean isRecording() {
571        return (mCurrentModule instanceof VideoModule) ?
572                ((VideoModule) mCurrentModule).isRecording() : false;
573    }
574
575    public CameraScreenNail getCameraScreenNail() {
576        return (CameraScreenNail) mCameraScreenNail;
577    }
578
579    public MediaSaveService getMediaSaveService() {
580        return mMediaSaveService;
581    }
582}
583