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