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