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