CameraActivity.java revision d475c670423404931e1b7281ac2dcb430490c27b
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.app.Activity;
20import android.content.BroadcastReceiver;
21import android.content.ComponentName;
22import android.content.ContentResolver;
23import android.content.Context;
24import android.content.Intent;
25import android.content.IntentFilter;
26import android.content.ServiceConnection;
27import android.content.pm.ActivityInfo;
28import android.content.res.Configuration;
29import android.graphics.drawable.ColorDrawable;
30import android.net.Uri;
31import android.os.Bundle;
32import android.os.Handler;
33import android.os.IBinder;
34import android.provider.Settings;
35import android.view.KeyEvent;
36import android.view.LayoutInflater;
37import android.view.OrientationEventListener;
38import android.view.View;
39import android.view.ViewGroup;
40import android.view.Window;
41import android.view.WindowManager;
42import android.widget.ImageView;
43
44import com.android.camera.data.CameraDataAdapter;
45import com.android.camera.data.CameraPreviewData;
46import com.android.camera.data.FixedFirstDataAdapter;
47import com.android.camera.data.FixedLastDataAdapter;
48import com.android.camera.data.LocalData;
49import com.android.camera.data.LocalDataAdapter;
50import com.android.camera.ui.CameraSwitcher;
51import com.android.camera.ui.CameraSwitcher.CameraSwitchListener;
52import com.android.camera.ui.FilmStripView;
53import com.android.gallery3d.R;
54import com.android.gallery3d.common.ApiHelper;
55import com.android.gallery3d.util.LightCycleHelper;
56import com.android.gallery3d.util.RefocusHelper;
57import com.android.gallery3d.util.LightCycleHelper.PanoramaViewHelper;
58
59public class CameraActivity extends Activity
60    implements CameraSwitchListener {
61
62    private static final String TAG = "CAM_Activity";
63
64    private static final String INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE =
65            "android.media.action.STILL_IMAGE_CAMERA_SECURE";
66    public static final String ACTION_IMAGE_CAPTURE_SECURE =
67            "android.media.action.IMAGE_CAPTURE_SECURE";
68
69    // The intent extra for camera from secure lock screen. True if the gallery
70    // should only show newly captured pictures. sSecureAlbumId does not
71    // increment. This is used when switching between camera, camcorder, and
72    // panorama. If the extra is not set, it is in the normal camera mode.
73    public static final String SECURE_CAMERA_EXTRA = "secure_camera";
74
75    /** This data adapter is used by FilmStirpView. */
76    private LocalDataAdapter mDataAdapter;
77    /** This data adapter represents the real local camera data. */
78    private LocalDataAdapter mWrappedDataAdapter;
79
80    private PanoramaStitchingManager mPanoramaManager;
81    private int mCurrentModuleIndex;
82    private CameraModule mCurrentModule;
83    private View mRootView;
84    private FilmStripView mFilmStripView;
85    private int mResultCodeForTesting;
86    private Intent mResultDataForTesting;
87    private OnScreenHint mStorageHint;
88    private long mStorageSpace = Storage.LOW_STORAGE_THRESHOLD;
89    private boolean mAutoRotateScreen;
90    private boolean mSecureCamera;
91    // This is a hack to speed up the start of SecureCamera.
92    private static boolean sFirstStartAfterScreenOn = true;
93    private boolean mShowCameraPreview;
94    private int mLastRawOrientation;
95    private MyOrientationEventListener mOrientationListener;
96    private Handler mMainHandler;
97    private PanoramaViewHelper mPanoramaViewHelper;
98    private CameraPreviewData mCameraPreviewData;
99
100    private class MyOrientationEventListener
101        extends OrientationEventListener {
102        public MyOrientationEventListener(Context context) {
103            super(context);
104        }
105
106        @Override
107        public void onOrientationChanged(int orientation) {
108            // We keep the last known orientation. So if the user first orient
109            // the camera then point the camera to floor or sky, we still have
110            // the correct orientation.
111            if (orientation == ORIENTATION_UNKNOWN) return;
112            mLastRawOrientation = orientation;
113            mCurrentModule.onOrientationChanged(orientation);
114        }
115    }
116
117    private MediaSaveService mMediaSaveService;
118    private ServiceConnection mConnection = new ServiceConnection() {
119            @Override
120            public void onServiceConnected(ComponentName className, IBinder b) {
121                mMediaSaveService = ((MediaSaveService.LocalBinder) b).getService();
122                mCurrentModule.onMediaSaveServiceConnected(mMediaSaveService);
123            }
124            @Override
125            public void onServiceDisconnected(ComponentName className) {
126                mMediaSaveService = null;
127            }};
128
129    // close activity when screen turns off
130    private BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() {
131        @Override
132        public void onReceive(Context context, Intent intent) {
133            finish();
134        }
135    };
136
137    private static BroadcastReceiver sScreenOffReceiver;
138    private static class ScreenOffReceiver extends BroadcastReceiver {
139        @Override
140        public void onReceive(Context context, Intent intent) {
141            sFirstStartAfterScreenOn = true;
142        }
143    }
144
145    public static boolean isFirstStartAfterScreenOn() {
146        return sFirstStartAfterScreenOn;
147    }
148
149    public static void resetFirstStartAfterScreenOn() {
150        sFirstStartAfterScreenOn = false;
151    }
152
153    private FilmStripView.Listener mFilmStripListener = new FilmStripView.Listener() {
154            @Override
155            public void onDataPromoted(int dataID) {
156                removeData(dataID);
157            }
158
159            @Override
160            public void onDataDemoted(int dataID) {
161                removeData(dataID);
162            }
163
164            @Override
165            public void onDataFullScreenChange(int dataID, boolean full) {
166            }
167
168            @Override
169            public void onSwitchMode(boolean toCamera) {
170                mCurrentModule.onSwitchMode(toCamera);
171            }
172        };
173
174    private Runnable mDeletionRunnable = new Runnable() {
175            @Override
176            public void run() {
177                mDataAdapter.executeDeletion(CameraActivity.this);
178            }
179        };
180
181    private ImageTaskManager.TaskListener mStitchingListener =
182            new ImageTaskManager.TaskListener() {
183                @Override
184                public void onTaskQueued(String filePath, Uri imageUri) {
185                }
186
187                @Override
188                public void onTaskDone(String filePath, Uri imageUri) {
189                }
190
191                @Override
192                public void onTaskProgress(
193                        String filePath, Uri imageUri, int progress) {
194                }
195            };
196
197    public MediaSaveService getMediaSaveService() {
198        return mMediaSaveService;
199    }
200
201    public void notifyNewMedia(Uri uri) {
202        ContentResolver cr = getContentResolver();
203        String mimeType = cr.getType(uri);
204        if (mimeType.startsWith("video/")) {
205            sendBroadcast(new Intent(Util.ACTION_NEW_VIDEO, uri));
206            mDataAdapter.addNewVideo(cr, uri);
207        } else if (mimeType.startsWith("image/")) {
208            Util.broadcastNewPicture(this, uri);
209            mDataAdapter.addNewPhoto(cr, uri);
210        } else {
211            android.util.Log.w(TAG, "Unknown new media with MIME type:"
212                    + mimeType + ", uri:" + uri);
213        }
214    }
215
216    private void removeData(int dataID) {
217        mDataAdapter.removeData(CameraActivity.this, dataID);
218        mMainHandler.removeCallbacks(mDeletionRunnable);
219        mMainHandler.postDelayed(mDeletionRunnable, 3000);
220    }
221
222    private void bindMediaSaveService() {
223        Intent intent = new Intent(this, MediaSaveService.class);
224        startService(intent);  // start service before binding it so the
225                               // service won't be killed if we unbind it.
226        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
227    }
228
229    private void unbindMediaSaveService() {
230        if (mMediaSaveService != null) {
231            mMediaSaveService.setListener(null);
232        }
233        if (mConnection != null) {
234            unbindService(mConnection);
235        }
236    }
237
238    @Override
239    public void onCreate(Bundle state) {
240        super.onCreate(state);
241        setContentView(R.layout.camera_filmstrip);
242        if (ApiHelper.HAS_ROTATION_ANIMATION) {
243            setRotationAnimation();
244        }
245        // Check if this is in the secure camera mode.
246        Intent intent = getIntent();
247        String action = intent.getAction();
248        if (INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action)
249                || ACTION_IMAGE_CAPTURE_SECURE.equals(action)) {
250            mSecureCamera = true;
251        } else {
252            mSecureCamera = intent.getBooleanExtra(SECURE_CAMERA_EXTRA, false);
253        }
254
255        if (mSecureCamera) {
256            // Change the window flags so that secure camera can show when locked
257            Window win = getWindow();
258            WindowManager.LayoutParams params = win.getAttributes();
259            params.flags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
260            win.setAttributes(params);
261
262            // Filter for screen off so that we can finish secure camera activity
263            // when screen is off.
264            IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
265            registerReceiver(mScreenOffReceiver, filter);
266            // TODO: This static screen off event receiver is a workaround to the
267            // double onResume() invocation (onResume->onPause->onResume). We should
268            // find a better solution to this.
269            if (sScreenOffReceiver == null) {
270                sScreenOffReceiver = new ScreenOffReceiver();
271                registerReceiver(sScreenOffReceiver, filter);
272            }
273        }
274        mPanoramaManager = new PanoramaStitchingManager(CameraActivity.this);
275        mPanoramaManager.addTaskListener(mStitchingListener);
276        LayoutInflater inflater = getLayoutInflater();
277        View rootLayout = inflater.inflate(R.layout.camera, null, false);
278        mRootView = rootLayout.findViewById(R.id.camera_app_root);
279        mCameraPreviewData = new CameraPreviewData(rootLayout,
280                FilmStripView.ImageData.SIZE_FULL,
281                FilmStripView.ImageData.SIZE_FULL);
282        // Put a CameraPreviewData at the first position.
283        mWrappedDataAdapter = new FixedFirstDataAdapter(
284                new CameraDataAdapter(new ColorDrawable(
285                        getResources().getColor(R.color.photo_placeholder))),
286                mCameraPreviewData);
287        mFilmStripView = (FilmStripView) findViewById(R.id.filmstrip_view);
288        mFilmStripView.setViewGap(
289                getResources().getDimensionPixelSize(R.dimen.camera_film_strip_gap));
290        mPanoramaViewHelper = new PanoramaViewHelper(this);
291        mPanoramaViewHelper.onCreate();
292        mFilmStripView.setPanoramaViewHelper(mPanoramaViewHelper);
293        // Set up the camera preview first so the preview shows up ASAP.
294        mFilmStripView.setListener(mFilmStripListener);
295        mCurrentModule = new PhotoModule();
296        mCurrentModule.init(this, mRootView);
297        mOrientationListener = new MyOrientationEventListener(this);
298        mMainHandler = new Handler(getMainLooper());
299        bindMediaSaveService();
300
301        if (!mSecureCamera) {
302            mDataAdapter = mWrappedDataAdapter;
303            mDataAdapter.requestLoad(getContentResolver());
304        } else {
305            // Put a lock placeholder as the last image by setting its date to 0.
306            ImageView v = (ImageView) getLayoutInflater().inflate(
307                    R.layout.secure_album_placeholder, null);
308            mDataAdapter = new FixedLastDataAdapter(
309                    mWrappedDataAdapter,
310                    new LocalData.LocalViewData(
311                            v,
312                            v.getDrawable().getIntrinsicWidth(),
313                            v.getDrawable().getIntrinsicHeight(),
314                            0, 0));
315            // Flush out all the original data.
316            mDataAdapter.flush();
317        }
318        mFilmStripView.setDataAdapter(mDataAdapter);
319    }
320
321    private void setRotationAnimation() {
322        int rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
323        rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE;
324        Window win = getWindow();
325        WindowManager.LayoutParams winParams = win.getAttributes();
326        winParams.rotationAnimation = rotationAnimation;
327        win.setAttributes(winParams);
328    }
329
330    @Override
331    public void onUserInteraction() {
332        super.onUserInteraction();
333        mCurrentModule.onUserInteraction();
334    }
335
336    @Override
337    public void onPause() {
338        mOrientationListener.disable();
339        mCurrentModule.onPauseBeforeSuper();
340        super.onPause();
341        mCurrentModule.onPauseAfterSuper();
342    }
343
344    @Override
345    public void onResume() {
346        if (Settings.System.getInt(getContentResolver(),
347                Settings.System.ACCELEROMETER_ROTATION, 0) == 0) {// auto-rotate off
348            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
349            mAutoRotateScreen = false;
350        } else {
351            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
352            mAutoRotateScreen = true;
353        }
354        mOrientationListener.enable();
355        mCurrentModule.onResumeBeforeSuper();
356        super.onResume();
357        mCurrentModule.onResumeAfterSuper();
358
359        setSwipingEnabled(true);
360    }
361
362    @Override
363    public void onStart() {
364        super.onStart();
365
366        mPanoramaViewHelper.onStart();
367    }
368
369    @Override
370    protected void onStop() {
371        super.onStop();
372        mPanoramaViewHelper.onStop();
373    }
374
375    @Override
376    public void onDestroy() {
377        unbindMediaSaveService();
378        if (mSecureCamera) unregisterReceiver(mScreenOffReceiver);
379        super.onDestroy();
380    }
381
382    @Override
383    public void onConfigurationChanged(Configuration config) {
384        super.onConfigurationChanged(config);
385        mCurrentModule.onConfigurationChanged(config);
386    }
387
388    @Override
389    public boolean onKeyDown(int keyCode, KeyEvent event) {
390        if (mCurrentModule.onKeyDown(keyCode, event)) return true;
391        // Prevent software keyboard or voice search from showing up.
392        if (keyCode == KeyEvent.KEYCODE_SEARCH
393                || keyCode == KeyEvent.KEYCODE_MENU) {
394            if (event.isLongPress()) return true;
395        }
396        if (keyCode == KeyEvent.KEYCODE_MENU && mShowCameraPreview) {
397            return true;
398        }
399
400        return super.onKeyDown(keyCode, event);
401    }
402
403    @Override
404    public boolean onKeyUp(int keyCode, KeyEvent event) {
405        if (mCurrentModule.onKeyUp(keyCode, event)) return true;
406        if (keyCode == KeyEvent.KEYCODE_MENU && mShowCameraPreview) {
407            return true;
408        }
409        return super.onKeyUp(keyCode, event);
410    }
411
412    public boolean isAutoRotateScreen() {
413        return mAutoRotateScreen;
414    }
415
416    protected void updateStorageSpace() {
417        mStorageSpace = Storage.getAvailableSpace();
418    }
419
420    protected long getStorageSpace() {
421        return mStorageSpace;
422    }
423
424    protected void updateStorageSpaceAndHint() {
425        updateStorageSpace();
426        updateStorageHint(mStorageSpace);
427    }
428
429    protected void updateStorageHint() {
430        updateStorageHint(mStorageSpace);
431    }
432
433    protected boolean updateStorageHintOnResume() {
434        return true;
435    }
436
437    protected void updateStorageHint(long storageSpace) {
438        String message = null;
439        if (storageSpace == Storage.UNAVAILABLE) {
440            message = getString(R.string.no_storage);
441        } else if (storageSpace == Storage.PREPARING) {
442            message = getString(R.string.preparing_sd);
443        } else if (storageSpace == Storage.UNKNOWN_SIZE) {
444            message = getString(R.string.access_sd_fail);
445        } else if (storageSpace <= Storage.LOW_STORAGE_THRESHOLD) {
446            message = getString(R.string.spaceIsLow_content);
447        }
448
449        if (message != null) {
450            if (mStorageHint == null) {
451                mStorageHint = OnScreenHint.makeText(this, message);
452            } else {
453                mStorageHint.setText(message);
454            }
455            mStorageHint.show();
456        } else if (mStorageHint != null) {
457            mStorageHint.cancel();
458            mStorageHint = null;
459        }
460    }
461
462    protected void setResultEx(int resultCode) {
463        mResultCodeForTesting = resultCode;
464        setResult(resultCode);
465    }
466
467    protected void setResultEx(int resultCode, Intent data) {
468        mResultCodeForTesting = resultCode;
469        mResultDataForTesting = data;
470        setResult(resultCode, data);
471    }
472
473    public int getResultCode() {
474        return mResultCodeForTesting;
475    }
476
477    public Intent getResultData() {
478        return mResultDataForTesting;
479    }
480
481    public boolean isSecureCamera() {
482        return mSecureCamera;
483    }
484
485    @Override
486    public void onCameraSelected(int i) {
487        if (mCurrentModuleIndex == i) return;
488
489        CameraHolder.instance().keep();
490        closeModule(mCurrentModule);
491        mCurrentModuleIndex = i;
492        switch (i) {
493            case CameraSwitcher.VIDEO_MODULE_INDEX:
494                mCurrentModule = new VideoModule();
495                break;
496            case CameraSwitcher.PHOTO_MODULE_INDEX:
497                mCurrentModule = new PhotoModule();
498                break;
499            case CameraSwitcher.LIGHTCYCLE_MODULE_INDEX:
500                mCurrentModule = LightCycleHelper.createPanoramaModule();
501                break;
502            case CameraSwitcher.REFOCUS_MODULE_INDEX:
503                mCurrentModule = RefocusHelper.createRefocusModule();
504                break;
505           default:
506               break;
507        }
508
509        openModule(mCurrentModule);
510        mCurrentModule.onOrientationChanged(mLastRawOrientation);
511        if (mMediaSaveService != null) {
512            mCurrentModule.onMediaSaveServiceConnected(mMediaSaveService);
513        }
514    }
515
516    private void openModule(CameraModule module) {
517        module.init(this, mRootView);
518        module.onResumeBeforeSuper();
519        module.onResumeAfterSuper();
520    }
521
522    private void closeModule(CameraModule module) {
523        module.onPauseBeforeSuper();
524        module.onPauseAfterSuper();
525        ((ViewGroup) mRootView).removeAllViews();
526    }
527
528    @Override
529    public void onShowSwitcherPopup() {
530    }
531
532    public void setSwipingEnabled(boolean enable) {
533        mCameraPreviewData.lockPreview(!enable);
534    }
535
536    // Accessor methods for getting latency times used in performance testing
537    public long getAutoFocusTime() {
538        return (mCurrentModule instanceof PhotoModule) ?
539                ((PhotoModule) mCurrentModule).mAutoFocusTime : -1;
540    }
541
542    public long getShutterLag() {
543        return (mCurrentModule instanceof PhotoModule) ?
544                ((PhotoModule) mCurrentModule).mShutterLag : -1;
545    }
546
547    public long getShutterToPictureDisplayedTime() {
548        return (mCurrentModule instanceof PhotoModule) ?
549                ((PhotoModule) mCurrentModule).mShutterToPictureDisplayedTime : -1;
550    }
551
552    public long getPictureDisplayedToJpegCallbackTime() {
553        return (mCurrentModule instanceof PhotoModule) ?
554                ((PhotoModule) mCurrentModule).mPictureDisplayedToJpegCallbackTime : -1;
555    }
556
557    public long getJpegCallbackFinishTime() {
558        return (mCurrentModule instanceof PhotoModule) ?
559                ((PhotoModule) mCurrentModule).mJpegCallbackFinishTime : -1;
560    }
561
562    public long getCaptureStartTime() {
563        return (mCurrentModule instanceof PhotoModule) ?
564                ((PhotoModule) mCurrentModule).mCaptureStartTime : -1;
565    }
566
567    public boolean isRecording() {
568        return (mCurrentModule instanceof VideoModule) ?
569                ((VideoModule) mCurrentModule).isRecording() : false;
570    }
571}
572