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