CameraActivity.java revision 9cdfe00cf57f05f81e6d02ca050e6afd8cc4a25f
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        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);
126        int totaldrawid = (LightCycleHelper.hasLightCycleCapture(this)
127                                ? DRAW_IDS.length : DRAW_IDS.length - 1);
128        if (!ApiHelper.HAS_OLD_PANORAMA) totaldrawid--;
129
130        int[] drawids = new int[totaldrawid];
131        int[] moduleids = new int[totaldrawid];
132        int ix = 0;
133        for (int i = 0; i < mDrawables.length; i++) {
134            if (i == PANORAMA_MODULE_INDEX && !ApiHelper.HAS_OLD_PANORAMA) {
135                continue; // not enabled, so don't add to UI
136            }
137            if (i == LIGHTCYCLE_MODULE_INDEX && !LightCycleHelper.hasLightCycleCapture(this)) {
138                continue; // not enabled, so don't add to UI
139            }
140            moduleids[ix] = i;
141            drawids[ix++] = DRAW_IDS[i];
142        }
143        mSwitcher.setIds(moduleids, drawids);
144        mSwitcher.setSwitchListener(this);
145        mSwitcher.setCurrentIndex(mCurrentModuleIndex);
146    }
147
148    @Override
149    public void onDestroy() {
150        unbindMediaSaveService();
151        super.onDestroy();
152    }
153
154    // Return whether the auto-rotate screen in system settings
155    // is turned on.
156    public boolean isAutoRotateScreen() {
157        return mAutoRotateScreen;
158    }
159
160    private class MyOrientationEventListener
161            extends OrientationEventListener {
162        public MyOrientationEventListener(Context context) {
163            super(context);
164        }
165
166        @Override
167        public void onOrientationChanged(int orientation) {
168            // We keep the last known orientation. So if the user first orient
169            // the camera then point the camera to floor or sky, we still have
170            // the correct orientation.
171            if (orientation == ORIENTATION_UNKNOWN) return;
172            mLastRawOrientation = orientation;
173            mCurrentModule.onOrientationChanged(orientation);
174        }
175    }
176
177    private ObjectAnimator mCameraSwitchAnimator;
178
179    @Override
180    public void onCameraSelected(final int i) {
181        if (mPaused) return;
182        if (i != mCurrentModuleIndex) {
183            mPaused = true;
184            CameraScreenNail screenNail = getCameraScreenNail();
185            if (screenNail != null) {
186                if (mCameraSwitchAnimator != null && mCameraSwitchAnimator.isRunning()) {
187                    mCameraSwitchAnimator.cancel();
188                }
189                mCameraSwitchAnimator = ObjectAnimator.ofFloat(
190                        screenNail, "alpha", screenNail.getAlpha(), 0f);
191                mCameraSwitchAnimator.addListener(new AnimatorListenerAdapter() {
192                    @Override
193                    public void onAnimationEnd(Animator animation) {
194                        super.onAnimationEnd(animation);
195                        doChangeCamera(i);
196                    }
197                });
198                mCameraSwitchAnimator.start();
199            } else {
200                doChangeCamera(i);
201            }
202        }
203    }
204
205    private void doChangeCamera(int i) {
206        boolean canReuse = canReuseScreenNail();
207        CameraHolder.instance().keep();
208        closeModule(mCurrentModule);
209        mCurrentModuleIndex = i;
210        switch (i) {
211            case VIDEO_MODULE_INDEX:
212                mCurrentModule = new VideoModule();
213                break;
214            case PHOTO_MODULE_INDEX:
215                mCurrentModule = new PhotoModule();
216                break;
217            case PANORAMA_MODULE_INDEX:
218                mCurrentModule = new PanoramaModule();
219                break;
220            case LIGHTCYCLE_MODULE_INDEX:
221                mCurrentModule = LightCycleHelper.createPanoramaModule();
222                break;
223        }
224        showPieMenuButton(mCurrentModule.needsPieMenu());
225
226        openModule(mCurrentModule, canReuse);
227        mCurrentModule.onOrientationChanged(mLastRawOrientation);
228        if (mMediaSaveService != null) {
229            mCurrentModule.onMediaSaveServiceConnected(mMediaSaveService);
230        }
231        getCameraScreenNail().setAlpha(0f);
232        getCameraScreenNail().setOnFrameDrawnOneShot(mOnFrameDrawn);
233    }
234
235    public void showPieMenuButton(boolean show) {
236        if (show) {
237            findViewById(R.id.blocker).setVisibility(View.VISIBLE);
238            findViewById(R.id.menu).setVisibility(View.VISIBLE);
239            findViewById(R.id.on_screen_indicators).setVisibility(View.VISIBLE);
240        } else {
241            findViewById(R.id.blocker).setVisibility(View.INVISIBLE);
242            findViewById(R.id.menu).setVisibility(View.INVISIBLE);
243            findViewById(R.id.on_screen_indicators).setVisibility(View.INVISIBLE);
244        }
245    }
246
247    private Runnable mOnFrameDrawn = new Runnable() {
248
249        @Override
250        public void run() {
251            runOnUiThread(mFadeInCameraScreenNail);
252        }
253    };
254
255    private Runnable mFadeInCameraScreenNail = new Runnable() {
256
257        @Override
258        public void run() {
259            mCameraSwitchAnimator = ObjectAnimator.ofFloat(
260                    getCameraScreenNail(), "alpha", 0f, 1f);
261            mCameraSwitchAnimator.setStartDelay(50);
262            mCameraSwitchAnimator.start();
263        }
264    };
265
266    @Override
267    public void onShowSwitcherPopup() {
268        mCurrentModule.onShowSwitcherPopup();
269    }
270
271    private void openModule(CameraModule module, boolean canReuse) {
272        module.init(this, mFrame, canReuse && canReuseScreenNail());
273        mPaused = false;
274        module.onResumeBeforeSuper();
275        module.onResumeAfterSuper();
276    }
277
278    private void closeModule(CameraModule module) {
279        module.onPauseBeforeSuper();
280        module.onPauseAfterSuper();
281        mFrame.removeAllViews();
282    }
283
284    public ShutterButton getShutterButton() {
285        return mShutter;
286    }
287
288    public void hideUI() {
289        mCameraControls.setVisibility(View.INVISIBLE);
290        hideSwitcher();
291        mShutter.setVisibility(View.GONE);
292    }
293
294    public void showUI() {
295        mCameraControls.setVisibility(View.VISIBLE);
296        showSwitcher();
297        mShutter.setVisibility(View.VISIBLE);
298        // Force a layout change to show shutter button
299        mShutter.requestLayout();
300    }
301
302    public void hideSwitcher() {
303        mSwitcher.closePopup();
304        mSwitcher.setVisibility(View.INVISIBLE);
305    }
306
307    public void showSwitcher() {
308        if (mCurrentModule.needsSwitcher()) {
309            mSwitcher.setVisibility(View.VISIBLE);
310        }
311    }
312
313    public boolean isInCameraApp() {
314        return mShowCameraAppView;
315    }
316
317    @Override
318    public void onConfigurationChanged(Configuration config) {
319        super.onConfigurationChanged(config);
320        mCurrentModule.onConfigurationChanged(config);
321    }
322
323    @Override
324    public void onPause() {
325        mPaused = true;
326        mOrientationListener.disable();
327        mCurrentModule.onPauseBeforeSuper();
328        super.onPause();
329        mCurrentModule.onPauseAfterSuper();
330    }
331
332    @Override
333    public void onResume() {
334        mPaused = false;
335        if (Settings.System.getInt(getContentResolver(),
336                Settings.System.ACCELEROMETER_ROTATION, 0) == 0) {// auto-rotate off
337            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
338            mAutoRotateScreen = false;
339        } else {
340            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
341            mAutoRotateScreen = true;
342        }
343        mOrientationListener.enable();
344        mCurrentModule.onResumeBeforeSuper();
345        super.onResume();
346        mCurrentModule.onResumeAfterSuper();
347    }
348
349    private void bindMediaSaveService() {
350        Intent intent = new Intent(this, MediaSaveService.class);
351        startService(intent);  // start service before binding it so the
352                               // service won't be killed if we unbind it.
353        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
354    }
355
356    private void unbindMediaSaveService() {
357        if (mMediaSaveService != null) {
358            mMediaSaveService.setListener(null);
359        }
360        if (mConnection != null) {
361            unbindService(mConnection);
362        }
363    }
364
365    @Override
366    protected void onFullScreenChanged(boolean full) {
367        if (full) {
368            showUI();
369        } else {
370            hideUI();
371        }
372        super.onFullScreenChanged(full);
373        if (ApiHelper.HAS_ROTATION_ANIMATION) {
374            setRotationAnimation(full);
375        }
376        mCurrentModule.onFullScreenChanged(full);
377    }
378
379    private void setRotationAnimation(boolean fullscreen) {
380        int rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
381        if (fullscreen) {
382            rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE;
383        }
384        Window win = getWindow();
385        WindowManager.LayoutParams winParams = win.getAttributes();
386        winParams.rotationAnimation = rotationAnimation;
387        win.setAttributes(winParams);
388    }
389
390    @Override
391    protected void onStop() {
392        super.onStop();
393        mCurrentModule.onStop();
394        getStateManager().clearTasks();
395    }
396
397    @Override
398    protected void onNewIntent(Intent intent) {
399        super.onNewIntent(intent);
400        getStateManager().clearActivityResult();
401    }
402
403    @Override
404    protected void installIntentFilter() {
405        super.installIntentFilter();
406        mCurrentModule.installIntentFilter();
407    }
408
409    @Override
410    protected void onActivityResult(
411            int requestCode, int resultCode, Intent data) {
412        // Only PhotoPage understands ProxyLauncher.RESULT_USER_CANCELED
413        if (resultCode == ProxyLauncher.RESULT_USER_CANCELED
414                && !(getStateManager().getTopState() instanceof PhotoPage)) {
415            resultCode = RESULT_CANCELED;
416        }
417        super.onActivityResult(requestCode, resultCode, data);
418        // Unmap cancel vs. reset
419        if (resultCode == ProxyLauncher.RESULT_USER_CANCELED) {
420            resultCode = RESULT_CANCELED;
421        }
422        mCurrentModule.onActivityResult(requestCode, resultCode, data);
423    }
424
425    // Preview area is touched. Handle touch focus.
426    // Touch to focus is handled by PreviewGestures, this function call
427    // is no longer needed. TODO: Clean it up in the next refactor
428    @Override
429    protected void onSingleTapUp(View view, int x, int y) {
430    }
431
432    @Override
433    public void onBackPressed() {
434        if (!mCurrentModule.onBackPressed()) {
435            super.onBackPressed();
436        }
437    }
438
439    @Override
440    public boolean onKeyDown(int keyCode, KeyEvent event) {
441        return mCurrentModule.onKeyDown(keyCode,  event)
442                || super.onKeyDown(keyCode, event);
443    }
444
445    @Override
446    public boolean onKeyUp(int keyCode, KeyEvent event) {
447        return mCurrentModule.onKeyUp(keyCode,  event)
448                || super.onKeyUp(keyCode, event);
449    }
450
451    public void cancelActivityTouchHandling() {
452        if (mDown != null) {
453            MotionEvent cancel = MotionEvent.obtain(mDown);
454            cancel.setAction(MotionEvent.ACTION_CANCEL);
455            super.dispatchTouchEvent(cancel);
456        }
457    }
458
459    @Override
460    public boolean dispatchTouchEvent(MotionEvent m) {
461        if (m.getActionMasked() == MotionEvent.ACTION_DOWN) {
462            mDown = m;
463        }
464        if ((mSwitcher != null) && mSwitcher.showsPopup() && !mSwitcher.isInsidePopup(m)) {
465            return mSwitcher.onTouch(null, m);
466        } else if ((mSwitcher != null) && mSwitcher.isInsidePopup(m)) {
467            return superDispatchTouchEvent(m);
468        } else {
469            return mCurrentModule.dispatchTouchEvent(m);
470        }
471    }
472
473    @Override
474    public void startActivityForResult(Intent intent, int requestCode) {
475        Intent proxyIntent = new Intent(this, ProxyLauncher.class);
476        proxyIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
477        proxyIntent.putExtra(Intent.EXTRA_INTENT, intent);
478        super.startActivityForResult(proxyIntent, requestCode);
479    }
480
481    public boolean superDispatchTouchEvent(MotionEvent m) {
482        return super.dispatchTouchEvent(m);
483    }
484
485    // Preview texture has been copied. Now camera can be released and the
486    // animation can be started.
487    @Override
488    public void onPreviewTextureCopied() {
489        mCurrentModule.onPreviewTextureCopied();
490    }
491
492    @Override
493    public void onCaptureTextureCopied() {
494        mCurrentModule.onCaptureTextureCopied();
495    }
496
497    @Override
498    public void onUserInteraction() {
499        super.onUserInteraction();
500        mCurrentModule.onUserInteraction();
501    }
502
503    @Override
504    protected boolean updateStorageHintOnResume() {
505        return mCurrentModule.updateStorageHintOnResume();
506    }
507
508    @Override
509    public void updateCameraAppView() {
510        super.updateCameraAppView();
511        mCurrentModule.updateCameraAppView();
512    }
513
514    private boolean canReuseScreenNail() {
515        return mCurrentModuleIndex == PHOTO_MODULE_INDEX
516                || mCurrentModuleIndex == VIDEO_MODULE_INDEX
517                || mCurrentModuleIndex == LIGHTCYCLE_MODULE_INDEX;
518    }
519
520    @Override
521    public boolean isPanoramaActivity() {
522        return (mCurrentModuleIndex == PANORAMA_MODULE_INDEX);
523    }
524
525    // Accessor methods for getting latency times used in performance testing
526    public long getAutoFocusTime() {
527        return (mCurrentModule instanceof PhotoModule) ?
528                ((PhotoModule) mCurrentModule).mAutoFocusTime : -1;
529    }
530
531    public long getShutterLag() {
532        return (mCurrentModule instanceof PhotoModule) ?
533                ((PhotoModule) mCurrentModule).mShutterLag : -1;
534    }
535
536    public long getShutterToPictureDisplayedTime() {
537        return (mCurrentModule instanceof PhotoModule) ?
538                ((PhotoModule) mCurrentModule).mShutterToPictureDisplayedTime : -1;
539    }
540
541    public long getPictureDisplayedToJpegCallbackTime() {
542        return (mCurrentModule instanceof PhotoModule) ?
543                ((PhotoModule) mCurrentModule).mPictureDisplayedToJpegCallbackTime : -1;
544    }
545
546    public long getJpegCallbackFinishTime() {
547        return (mCurrentModule instanceof PhotoModule) ?
548                ((PhotoModule) mCurrentModule).mJpegCallbackFinishTime : -1;
549    }
550
551    public long getCaptureStartTime() {
552        return (mCurrentModule instanceof PhotoModule) ?
553                ((PhotoModule) mCurrentModule).mCaptureStartTime : -1;
554    }
555
556    public boolean isRecording() {
557        return (mCurrentModule instanceof VideoModule) ?
558                ((VideoModule) mCurrentModule).isRecording() : false;
559    }
560
561    public CameraScreenNail getCameraScreenNail() {
562        return (CameraScreenNail) mCameraScreenNail;
563    }
564
565    public MediaSaveService getMediaSaveService() {
566        return mMediaSaveService;
567    }
568}
569