CameraActivity.java revision c7fe2c5b1792edfc4054022e72ab3f45e272f361
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 View mSwitcherControl;
65    private Drawable[] mDrawables;
66    private int mCurrentModuleIndex;
67    private MotionEvent mDown;
68    private boolean mAutoRotateScreen;
69    private int mHeightOrWidth = -1;
70
71    private MyOrientationEventListener mOrientationListener;
72    // The degrees of the device rotated clockwise from its natural orientation.
73    private int mLastRawOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
74
75    private MediaSaveService mMediaSaveService;
76    private ServiceConnection mConnection = new ServiceConnection() {
77            @Override
78            public void onServiceConnected(ComponentName className, IBinder b) {
79                mMediaSaveService = ((MediaSaveService.LocalBinder) b).getService();
80                mCurrentModule.onMediaSaveServiceConnected(mMediaSaveService);
81            }
82            @Override
83            public void onServiceDisconnected(ComponentName className) {
84                mMediaSaveService = null;
85            }};
86
87    private static final String TAG = "CAM_activity";
88
89    private static final int[] DRAW_IDS = {
90            R.drawable.ic_switch_camera,
91            R.drawable.ic_switch_video,
92            R.drawable.ic_switch_pan,
93            R.drawable.ic_switch_photosphere
94    };
95
96    @Override
97    public void onCreate(Bundle state) {
98        super.onCreate(state);
99        setContentView(R.layout.camera_main);
100        mFrame = (FrameLayout) findViewById(R.id.camera_app_root);
101        mDrawables = new Drawable[DRAW_IDS.length];
102        for (int i = 0; i < DRAW_IDS.length; i++) {
103            mDrawables[i] = getResources().getDrawable(DRAW_IDS[i]);
104        }
105        init();
106        if (MediaStore.INTENT_ACTION_VIDEO_CAMERA.equals(getIntent().getAction())
107                || MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction())) {
108            mCurrentModule = new VideoModule();
109            mCurrentModuleIndex = VIDEO_MODULE_INDEX;
110        } else {
111            mCurrentModule = new PhotoModule();
112            mCurrentModuleIndex = PHOTO_MODULE_INDEX;
113        }
114        mCurrentModule.init(this, mFrame, true);
115        mSwitcher.setCurrentIndex(mCurrentModuleIndex);
116        mOrientationListener = new MyOrientationEventListener(this);
117        bindMediaSaveService();
118    }
119
120    public void init() {
121        mControlsBackground = findViewById(R.id.blocker);
122        mCameraControls = findViewById(R.id.camera_controls);
123        mShutter = (ShutterButton) findViewById(R.id.shutter_button);
124        mSwitcher = (CameraSwitcher) findViewById(R.id.camera_switcher);
125        mPieMenuButton = findViewById(R.id.menu_button);
126        mSwitcherControl = findViewById(R.id.switcher_control);
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        if (mCurrentModule.needsPieMenu()) {
226            mPieMenuButton.setVisibility(View.VISIBLE);
227        } else {
228            mPieMenuButton.setVisibility(View.INVISIBLE);
229        }
230        openModule(mCurrentModule, canReuse);
231        mCurrentModule.onOrientationChanged(mLastRawOrientation);
232        if (mMediaSaveService != null) {
233            mCurrentModule.onMediaSaveServiceConnected(mMediaSaveService);
234        }
235        getCameraScreenNail().setAlpha(0f);
236        getCameraScreenNail().setOnFrameDrawnOneShot(mOnFrameDrawn);
237    }
238
239    private Runnable mOnFrameDrawn = new Runnable() {
240
241        @Override
242        public void run() {
243            runOnUiThread(mFadeInCameraScreenNail);
244        }
245    };
246
247    private Runnable mFadeInCameraScreenNail = new Runnable() {
248
249        @Override
250        public void run() {
251            mCameraSwitchAnimator = ObjectAnimator.ofFloat(
252                    getCameraScreenNail(), "alpha", 0f, 1f);
253            mCameraSwitchAnimator.setStartDelay(50);
254            mCameraSwitchAnimator.start();
255        }
256    };
257
258    @Override
259    public void onShowSwitcherPopup() {
260        mCurrentModule.onShowSwitcherPopup();
261    }
262
263    private void openModule(CameraModule module, boolean canReuse) {
264        module.init(this, mFrame, canReuse && canReuseScreenNail());
265        mPaused = false;
266        module.onResumeBeforeSuper();
267        module.onResumeAfterSuper();
268    }
269
270    private void closeModule(CameraModule module) {
271        module.onPauseBeforeSuper();
272        module.onPauseAfterSuper();
273        mFrame.removeAllViews();
274    }
275
276    public ShutterButton getShutterButton() {
277        return mShutter;
278    }
279
280    public void hideUI() {
281        mCameraControls.setVisibility(View.INVISIBLE);
282        hideSwitcher();
283        mShutter.setVisibility(View.GONE);
284    }
285
286    public void showUI() {
287        mCameraControls.setVisibility(View.VISIBLE);
288        showSwitcher();
289        mShutter.setVisibility(View.VISIBLE);
290        // Force a layout change to show shutter button
291        mShutter.requestLayout();
292    }
293
294    public void hideSwitcher() {
295        mSwitcher.closePopup();
296        mSwitcher.setVisibility(View.INVISIBLE);
297    }
298
299    public void showSwitcher() {
300        if (mCurrentModule.needsSwitcher()) {
301            mSwitcher.setVisibility(View.VISIBLE);
302        }
303    }
304
305    public boolean isInCameraApp() {
306        return mShowCameraAppView;
307    }
308
309    @Override
310    public void onConfigurationChanged(Configuration config) {
311        super.onConfigurationChanged(config);
312
313        ViewGroup appRoot = (ViewGroup) findViewById(R.id.content);
314        boolean landscape = (config.orientation == Configuration.ORIENTATION_LANDSCAPE);
315        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) appRoot.getLayoutParams();
316        if (landscape) {
317            lp.rightMargin = getResources().getDimensionPixelSize(R.dimen.margin_systemui_offset);
318        } else {
319            lp.rightMargin = 0;
320        }
321        appRoot.setLayoutParams(lp);
322
323        // Reset the background after rotation
324        mControlsBackground.setBackgroundResource(0);  // remove the current background
325        mControlsBackground.setBackgroundResource(R.drawable.switcher_bg);
326
327        mCurrentModule.onConfigurationChanged(config);
328    }
329
330    @Override
331    public void onPause() {
332        mPaused = true;
333        mOrientationListener.disable();
334        mCurrentModule.onPauseBeforeSuper();
335        super.onPause();
336        mCurrentModule.onPauseAfterSuper();
337    }
338
339    @Override
340    public void onResume() {
341        mPaused = false;
342        if (Settings.System.getInt(getContentResolver(),
343                Settings.System.ACCELEROMETER_ROTATION, 0) == 0) {// auto-rotate off
344            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
345            mAutoRotateScreen = false;
346        } else {
347            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
348            mAutoRotateScreen = true;
349        }
350        mOrientationListener.enable();
351        mCurrentModule.onResumeBeforeSuper();
352        super.onResume();
353        mCurrentModule.onResumeAfterSuper();
354    }
355
356    private void bindMediaSaveService() {
357        Intent intent = new Intent(this, MediaSaveService.class);
358        startService(intent);  // start service before binding it so the
359                               // service won't be killed if we unbind it.
360        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
361    }
362
363    private void unbindMediaSaveService() {
364        mMediaSaveService.setListener(null);
365        unbindService(mConnection);
366    }
367
368    @Override
369    protected void onFullScreenChanged(boolean full) {
370        if (full) {
371            showUI();
372        } else {
373            hideUI();
374        }
375        super.onFullScreenChanged(full);
376        if (ApiHelper.HAS_ROTATION_ANIMATION) {
377            setRotationAnimation(full);
378        }
379        mCurrentModule.onFullScreenChanged(full);
380    }
381
382    private void setRotationAnimation(boolean fullscreen) {
383        int rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
384        if (fullscreen) {
385            rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE;
386        }
387        Window win = getWindow();
388        WindowManager.LayoutParams winParams = win.getAttributes();
389        winParams.rotationAnimation = rotationAnimation;
390        win.setAttributes(winParams);
391    }
392
393    @Override
394    protected void onStop() {
395        super.onStop();
396        mCurrentModule.onStop();
397        getStateManager().clearTasks();
398    }
399
400    @Override
401    protected void onNewIntent(Intent intent) {
402        super.onNewIntent(intent);
403        getStateManager().clearActivityResult();
404    }
405
406    @Override
407    protected void installIntentFilter() {
408        super.installIntentFilter();
409        mCurrentModule.installIntentFilter();
410    }
411
412    @Override
413    protected void onActivityResult(
414            int requestCode, int resultCode, Intent data) {
415        // Only PhotoPage understands ProxyLauncher.RESULT_USER_CANCELED
416        if (resultCode == ProxyLauncher.RESULT_USER_CANCELED
417                && !(getStateManager().getTopState() instanceof PhotoPage)) {
418            resultCode = RESULT_CANCELED;
419        }
420        super.onActivityResult(requestCode, resultCode, data);
421        // Unmap cancel vs. reset
422        if (resultCode == ProxyLauncher.RESULT_USER_CANCELED) {
423            resultCode = RESULT_CANCELED;
424        }
425        mCurrentModule.onActivityResult(requestCode, resultCode, data);
426    }
427
428    // Preview area is touched. Handle touch focus.
429    @Override
430    protected void onSingleTapUp(View view, int x, int y) {
431        mCurrentModule.onSingleTapUp(view, x, y);
432    }
433
434    @Override
435    public void onBackPressed() {
436        if (!mCurrentModule.onBackPressed()) {
437            super.onBackPressed();
438        }
439    }
440
441    @Override
442    public boolean onKeyDown(int keyCode, KeyEvent event) {
443        return mCurrentModule.onKeyDown(keyCode,  event)
444                || super.onKeyDown(keyCode, event);
445    }
446
447    @Override
448    public boolean onKeyUp(int keyCode, KeyEvent event) {
449        return mCurrentModule.onKeyUp(keyCode,  event)
450                || super.onKeyUp(keyCode, event);
451    }
452
453    public void cancelActivityTouchHandling() {
454        if (mDown != null) {
455            MotionEvent cancel = MotionEvent.obtain(mDown);
456            cancel.setAction(MotionEvent.ACTION_CANCEL);
457            super.dispatchTouchEvent(cancel);
458        }
459    }
460
461    @Override
462    public boolean dispatchTouchEvent(MotionEvent m) {
463        if (m.getActionMasked() == MotionEvent.ACTION_DOWN) {
464            mDown = m;
465        }
466        if ((mSwitcher != null) && mSwitcher.showsPopup() && !mSwitcher.isInsidePopup(m)) {
467            return mSwitcher.onTouch(null, m);
468        } else {
469            return mSwitcherControl.dispatchTouchEvent(m)
470                    || mCurrentModule.dispatchTouchEvent(m);
471        }
472    }
473
474    @Override
475    public void startActivityForResult(Intent intent, int requestCode) {
476        Intent proxyIntent = new Intent(this, ProxyLauncher.class);
477        proxyIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
478        proxyIntent.putExtra(Intent.EXTRA_INTENT, intent);
479        super.startActivityForResult(proxyIntent, requestCode);
480    }
481
482    public boolean superDispatchTouchEvent(MotionEvent m) {
483        return super.dispatchTouchEvent(m);
484    }
485
486    // Preview texture has been copied. Now camera can be released and the
487    // animation can be started.
488    @Override
489    public void onPreviewTextureCopied() {
490        mCurrentModule.onPreviewTextureCopied();
491    }
492
493    @Override
494    public void onCaptureTextureCopied() {
495        mCurrentModule.onCaptureTextureCopied();
496    }
497
498    @Override
499    public void onUserInteraction() {
500        super.onUserInteraction();
501        mCurrentModule.onUserInteraction();
502    }
503
504    @Override
505    protected boolean updateStorageHintOnResume() {
506        return mCurrentModule.updateStorageHintOnResume();
507    }
508
509    @Override
510    public void updateCameraAppView() {
511        super.updateCameraAppView();
512        mCurrentModule.updateCameraAppView();
513    }
514
515    private boolean canReuseScreenNail() {
516        return mCurrentModuleIndex == PHOTO_MODULE_INDEX
517                || mCurrentModuleIndex == VIDEO_MODULE_INDEX
518                || mCurrentModuleIndex == LIGHTCYCLE_MODULE_INDEX;
519    }
520
521    @Override
522    public boolean isPanoramaActivity() {
523        return (mCurrentModuleIndex == PANORAMA_MODULE_INDEX);
524    }
525
526    // Accessor methods for getting latency times used in performance testing
527    public long getAutoFocusTime() {
528        return (mCurrentModule instanceof PhotoModule) ?
529                ((PhotoModule) mCurrentModule).mAutoFocusTime : -1;
530    }
531
532    public long getShutterLag() {
533        return (mCurrentModule instanceof PhotoModule) ?
534                ((PhotoModule) mCurrentModule).mShutterLag : -1;
535    }
536
537    public long getShutterToPictureDisplayedTime() {
538        return (mCurrentModule instanceof PhotoModule) ?
539                ((PhotoModule) mCurrentModule).mShutterToPictureDisplayedTime : -1;
540    }
541
542    public long getPictureDisplayedToJpegCallbackTime() {
543        return (mCurrentModule instanceof PhotoModule) ?
544                ((PhotoModule) mCurrentModule).mPictureDisplayedToJpegCallbackTime : -1;
545    }
546
547    public long getJpegCallbackFinishTime() {
548        return (mCurrentModule instanceof PhotoModule) ?
549                ((PhotoModule) mCurrentModule).mJpegCallbackFinishTime : -1;
550    }
551
552    public long getCaptureStartTime() {
553        return (mCurrentModule instanceof PhotoModule) ?
554                ((PhotoModule) mCurrentModule).mCaptureStartTime : -1;
555    }
556
557    public boolean isRecording() {
558        return (mCurrentModule instanceof VideoModule) ?
559                ((VideoModule) mCurrentModule).isRecording() : false;
560    }
561
562    public CameraScreenNail getCameraScreenNail() {
563        return (CameraScreenNail) mCameraScreenNail;
564    }
565
566    public MediaSaveService getMediaSaveService() {
567        return mMediaSaveService;
568    }
569}
570