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