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