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