2 * Copyright (C) 2012 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.camera;
19 import android.animation.Animator;
20 import android.annotation.TargetApi;
21 import android.app.ActionBar;
22 import android.app.Activity;
23 import android.app.AlertDialog;
24 import android.content.ActivityNotFoundException;
25 import android.content.BroadcastReceiver;
26 import android.content.ContentResolver;
27 import android.content.Context;
28 import android.content.Intent;
29 import android.content.IntentFilter;
30 import android.content.SharedPreferences;
31 import android.content.pm.ActivityInfo;
32 import android.content.res.Configuration;
33 import android.graphics.Bitmap;
34 import android.graphics.Color;
35 import android.graphics.Matrix;
36 import android.graphics.SurfaceTexture;
37 import android.graphics.drawable.ColorDrawable;
38 import android.net.Uri;
39 import android.nfc.NfcAdapter;
40 import android.nfc.NfcAdapter.CreateBeamUrisCallback;
41 import android.nfc.NfcEvent;
42 import android.os.Build;
43 import android.os.Bundle;
44 import android.os.Handler;
45 import android.os.Looper;
46 import android.os.Message;
47 import android.preference.PreferenceManager;
48 import android.provider.MediaStore;
49 import android.provider.Settings;
50 import android.util.Log;
51 import android.view.Gravity;
52 import android.view.KeyEvent;
53 import android.view.LayoutInflater;
54 import android.view.MenuItem;
55 import android.view.MotionEvent;
56 import android.view.View;
57 import android.view.ViewGroup;
58 import android.view.Window;
59 import android.view.WindowManager;
60 import android.widget.FrameLayout;
61 import android.widget.FrameLayout.LayoutParams;
62 import android.widget.ImageView;
63 import android.widget.PopupWindow;
64 import android.widget.ProgressBar;
65 import android.widget.ShareActionProvider;
66 import android.widget.TextView;
68 import com.android.camera.app.AppController;
69 import com.android.camera.app.CameraAppUI;
70 import com.android.camera.app.CameraController;
71 import com.android.camera.app.CameraManager;
72 import com.android.camera.app.CameraManagerFactory;
73 import com.android.camera.app.CameraProvider;
74 import com.android.camera.app.CameraServices;
75 import com.android.camera.app.LocationManager;
76 import com.android.camera.app.ModuleManagerImpl;
77 import com.android.camera.app.OrientationManager;
78 import com.android.camera.app.OrientationManagerImpl;
79 import com.android.camera.data.CameraDataAdapter;
80 import com.android.camera.data.FixedLastDataAdapter;
81 import com.android.camera.data.InProgressDataWrapper;
82 import com.android.camera.data.LocalData;
83 import com.android.camera.data.LocalDataAdapter;
84 import com.android.camera.data.LocalMediaObserver;
85 import com.android.camera.data.PanoramaMetadataLoader;
86 import com.android.camera.data.RgbzMetadataLoader;
87 import com.android.camera.data.SimpleViewData;
88 import com.android.camera.filmstrip.FilmstripContentPanel;
89 import com.android.camera.filmstrip.FilmstripController;
90 import com.android.camera.module.ModulesInfo;
91 import com.android.camera.session.CaptureSessionManager;
92 import com.android.camera.session.CaptureSessionManager.SessionListener;
93 import com.android.camera.session.PlaceholderManager;
94 import com.android.camera.settings.SettingsManager;
95 import com.android.camera.settings.SettingsManager.SettingsCapabilities;
96 import com.android.camera.tinyplanet.TinyPlanetFragment;
97 import com.android.camera.ui.MainActivityLayout;
98 import com.android.camera.ui.ModeListView;
99 import com.android.camera.ui.PreviewStatusListener;
100 import com.android.camera.ui.SettingsView;
101 import com.android.camera.util.ApiHelper;
102 import com.android.camera.util.CameraUtil;
103 import com.android.camera.util.FeedbackHelper;
104 import com.android.camera.util.GcamHelper;
105 import com.android.camera.util.IntentHelper;
106 import com.android.camera.util.PhotoSphereHelper.PanoramaViewHelper;
107 import com.android.camera.util.UsageStatistics;
108 import com.android.camera.widget.FilmstripView;
109 import com.android.camera2.R;
112 import java.util.List;
114 public class CameraActivity extends Activity
115 implements AppController, CameraManager.CameraOpenCallback,
116 ActionBar.OnMenuVisibilityListener, ShareActionProvider.OnShareTargetSelectedListener,
117 OrientationManager.OnOrientationChangeListener {
119 private static final String TAG = "CAM_Activity";
121 private static final String INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE =
122 "android.media.action.STILL_IMAGE_CAMERA_SECURE";
123 public static final String ACTION_IMAGE_CAPTURE_SECURE =
124 "android.media.action.IMAGE_CAPTURE_SECURE";
126 // The intent extra for camera from secure lock screen. True if the gallery
127 // should only show newly captured pictures. sSecureAlbumId does not
128 // increment. This is used when switching between camera, camcorder, and
129 // panorama. If the extra is not set, it is in the normal camera mode.
130 public static final String SECURE_CAMERA_EXTRA = "secure_camera";
133 * Request code from an activity we started that indicated that we do not want
134 * to reset the view to the preview in onResume.
136 public static final int REQ_CODE_DONT_SWITCH_TO_PREVIEW = 142;
138 public static final int REQ_CODE_GCAM_DEBUG_POSTCAPTURE = 999;
140 private static final int MSG_HIDE_ACTION_BAR = 1;
141 private static final int MSG_CLEAR_SCREEN_ON_FLAG = 2;
142 private static final long SCREEN_DELAY_MS = 2 * 60 * 1000; // 2 mins.
145 * Whether onResume should reset the view to the preview.
147 private boolean mResetToPreviewOnResume = true;
150 * This data adapter is used by FilmStripView.
152 private LocalDataAdapter mDataAdapter;
155 * TODO: This should be moved to the app level.
157 private SettingsManager mSettingsManager;
160 * TODO: This should be moved to the app level.
162 private SettingsController mSettingsController;
163 private ModeListView mModeListView;
164 private int mCurrentModeIndex;
165 private CameraModule mCurrentModule;
166 private ModuleManagerImpl mModuleManager;
167 private FrameLayout mAboveFilmstripControlLayout;
168 private FilmstripController mFilmstripController;
169 private boolean mFilmstripVisible;
170 private TextView mBottomProgressText;
171 private ProgressBar mBottomProgressBar;
172 private View mSessionProgressPanel;
173 private int mResultCodeForTesting;
174 private Intent mResultDataForTesting;
175 private OnScreenHint mStorageHint;
176 private long mStorageSpaceBytes = Storage.LOW_STORAGE_THRESHOLD_BYTES;
177 private boolean mAutoRotateScreen;
178 private boolean mSecureCamera;
179 private int mLastRawOrientation;
180 private OrientationManagerImpl mOrientationManager;
181 private LocationManager mLocationManager;
182 private ButtonManager mButtonManager;
183 private Handler mMainHandler;
184 private PanoramaViewHelper mPanoramaViewHelper;
185 private ActionBar mActionBar;
186 private ViewGroup mUndoDeletionBar;
187 private boolean mIsUndoingDeletion = false;
189 private final Uri[] mNfcPushUris = new Uri[1];
191 private LocalMediaObserver mLocalImagesObserver;
192 private LocalMediaObserver mLocalVideosObserver;
194 private boolean mPendingDeletion = false;
196 private CameraController mCameraController;
197 private boolean mPaused;
198 private CameraAppUI mCameraAppUI;
200 private FeedbackHelper mFeedbackHelper;
203 public CameraAppUI getCameraAppUI() {
207 // close activity when screen turns off
208 private final BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() {
210 public void onReceive(Context context, Intent intent) {
216 * Whether the screen is kept turned on.
218 private boolean mKeepScreenOn;
219 private int mLastLayoutOrientation;
220 private final CameraAppUI.BottomControls.Listener mMyFilmstripBottomControlListener =
221 new CameraAppUI.BottomControls.Listener() {
224 * If the current photo is a photo sphere, this will launch the Photo Sphere
228 public void onExternalViewer() {
229 if (mPanoramaViewHelper == null) {
232 final LocalData data = getCurrentLocalData();
236 final Uri contentUri = data.getContentUri();
237 if (contentUri == Uri.EMPTY) {
241 if (PanoramaMetadataLoader.isPanorama(data)) {
242 mPanoramaViewHelper.showPanorama(contentUri);
243 } else if (RgbzMetadataLoader.hasRGBZData(data)) {
244 mPanoramaViewHelper.showRgbz(contentUri);
249 public void onEdit() {
250 LocalData data = getCurrentLocalData();
258 public void onTinyPlanet() {
259 LocalData data = getCurrentLocalData();
263 final int currentDataId = getCurrentDataId();
264 UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
265 UsageStatistics.ACTION_EDIT, null, 0,
266 UsageStatistics.hashFileName(fileNameFromDataID(currentDataId)));
267 launchTinyPlanetEditor(data);
271 public void onDelete() {
272 final int currentDataId = getCurrentDataId();
273 UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
274 UsageStatistics.ACTION_DELETE, null, 0,
275 UsageStatistics.hashFileName(fileNameFromDataID(currentDataId)));
276 removeData(currentDataId);
280 public void onShare() {
281 final LocalData data = getCurrentLocalData();
282 Intent shareIntent = getShareIntentByData(data);
283 if (shareIntent != null) {
285 launchActivityByIntent(shareIntent);
286 mCameraAppUI.getFilmstripBottomControls().setShareEnabled(false);
287 } catch (ActivityNotFoundException ex) {
294 public void onGallery() {
298 private int getCurrentDataId() {
299 return mFilmstripController.getCurrentId();
302 private LocalData getCurrentLocalData() {
303 return mDataAdapter.getLocalData(getCurrentDataId());
307 * Sets up the share intent and NFC properly according to the data.
309 * @param data The data to be shared.
311 private Intent getShareIntentByData(final LocalData data) {
312 Intent intent = null;
313 final Uri contentUri = data.getContentUri();
314 if (PanoramaMetadataLoader.isPanorama360(data) &&
315 data.getContentUri() != Uri.EMPTY) {
316 intent = new Intent(Intent.ACTION_SEND);
317 intent.setType("application/vnd.google.panorama360+jpg");
318 intent.putExtra(Intent.EXTRA_STREAM, contentUri);
319 } else if (data.isDataActionSupported(LocalData.DATA_ACTION_SHARE)) {
320 final String mimeType = data.getMimeType();
321 intent = getShareIntentFromType(mimeType);
322 if (intent != null) {
323 intent.putExtra(Intent.EXTRA_STREAM, contentUri);
324 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
331 * Get the share intent according to the mimeType
333 * @param mimeType The mimeType of current data.
334 * @return the video/image's ShareIntent or null if mimeType is invalid.
336 private Intent getShareIntentFromType(String mimeType) {
337 // Lazily create the intent object.
338 Intent intent = new Intent(Intent.ACTION_SEND);
339 if (mimeType.startsWith("video/")) {
340 intent.setType("video/*");
342 if (mimeType.startsWith("image/")) {
343 intent.setType("image/*");
345 Log.w(TAG, "unsupported mimeType " + mimeType);
352 private ComboPreferences mPreferences;
353 private ContentResolver mContentResolver;
356 public void onCameraOpened(CameraManager.CameraProxy camera) {
357 if (!mModuleManager.getModuleAgent(mCurrentModeIndex).requestAppForCamera()) {
358 // We shouldn't be here. Just close the camera and leave.
359 camera.release(false);
360 throw new IllegalStateException("Camera opened but the module shouldn't be " +
363 if (mCurrentModule != null) {
364 SettingsCapabilities capabilities =
365 SettingsController.getSettingsCapabilities(camera);
366 mSettingsManager.changeCamera(camera.getCameraId(), capabilities);
367 mCurrentModule.onCameraAvailable(camera);
372 public void onCameraDisabled(int cameraId) {
373 UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA, UsageStatistics.ACTION_OPEN_FAIL,
376 CameraUtil.showErrorAndFinish(this, R.string.camera_disabled);
380 public void onDeviceOpenFailure(int cameraId) {
381 UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
382 UsageStatistics.ACTION_OPEN_FAIL, "open");
384 CameraUtil.showErrorAndFinish(this, R.string.cannot_connect_camera);
388 public void onReconnectionFailure(CameraManager mgr) {
389 UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
390 UsageStatistics.ACTION_OPEN_FAIL, "reconnect");
392 CameraUtil.showErrorAndFinish(this, R.string.cannot_connect_camera);
395 private class MainHandler extends Handler {
396 public MainHandler(Looper looper) {
401 public void handleMessage(Message msg) {
403 case MSG_HIDE_ACTION_BAR: {
404 removeMessages(MSG_HIDE_ACTION_BAR);
405 CameraActivity.this.setFilmstripUiVisibility(false);
409 case MSG_CLEAR_SCREEN_ON_FLAG: {
411 getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
421 private String fileNameFromDataID(int dataID) {
422 final LocalData localData = mDataAdapter.getLocalData(dataID);
424 File localFile = new File(localData.getPath());
425 return localFile.getName();
428 private final FilmstripContentPanel.Listener mFilmstripListener =
429 new FilmstripContentPanel.Listener() {
432 public void onFilmstripHidden() {
433 mFilmstripVisible = false;
434 CameraActivity.this.setFilmstripUiVisibility(false);
435 // When the user hide the filmstrip (either swipe out or
436 // tap on back key) we move to the first item so next time
437 // when the user swipe in the filmstrip, the most recent
439 mFilmstripController.goToFirstItem();
440 if (mCurrentModule != null) {
441 mCurrentModule.onPreviewVisibilityChanged(true);
446 public void onFilmstripShown() {
447 mFilmstripVisible = true;
448 updateUiByData(mFilmstripController.getCurrentId());
449 if (mCurrentModule != null) {
450 mCurrentModule.onPreviewVisibilityChanged(false);
455 public void onDataPromoted(int dataID) {
456 UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
457 UsageStatistics.ACTION_DELETE, "promoted", 0,
458 UsageStatistics.hashFileName(fileNameFromDataID(dataID)));
464 public void onDataDemoted(int dataID) {
465 UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
466 UsageStatistics.ACTION_DELETE, "demoted", 0,
467 UsageStatistics.hashFileName(fileNameFromDataID(dataID)));
473 public void onEnterFullScreen(int dataId) {
474 if (mFilmstripVisible) {
475 CameraActivity.this.setFilmstripUiVisibility(false);
480 public void onLeaveFullScreen(int dataId) {
485 public void onEnterFilmstrip(int dataId) {
486 if (mFilmstripVisible) {
487 CameraActivity.this.setFilmstripUiVisibility(true);
492 public void onLeaveFilmstrip(int dataId) {
497 public void onDataReloaded() {
498 if (!mFilmstripVisible) {
501 updateUiByData(mFilmstripController.getCurrentId());
505 public void onDataUpdated(int dataId) {
506 if (!mFilmstripVisible) {
509 updateUiByData(mFilmstripController.getCurrentId());
513 public void onEnterZoomView(int dataID) {
514 if (mFilmstripVisible) {
515 CameraActivity.this.setFilmstripUiVisibility(false);
520 public void onDataFocusChanged(final int prevDataId, final int newDataId) {
521 if (!mFilmstripVisible) {
524 // TODO: This callback is UI event callback, should always
525 // happen on UI thread. Find the reason for this
526 // runOnUiThread() and fix it.
527 runOnUiThread(new Runnable() {
530 updateUiByData(newDataId);
536 private LocalDataAdapter.LocalDataListener mLocalDataListener =
537 new LocalDataAdapter.LocalDataListener() {
539 public void onMetadataUpdated(List<Integer> updatedData) {
540 int currentDataId = mFilmstripController.getCurrentId();
541 for (Integer dataId : updatedData) {
542 if (dataId == currentDataId) {
543 updateBottomControlsByData(mDataAdapter.getLocalData(dataId));
549 public void gotoGallery() {
550 UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA, UsageStatistics.ACTION_FILMSTRIP,
553 mFilmstripController.goToNextItem();
557 * If {@param visible} is false, this hides the action bar and switches the
558 * filmstrip UI to lights-out mode.
560 // TODO: This should not be called outside of the activity.
561 public void setFilmstripUiVisibility(boolean visible) {
562 mMainHandler.removeMessages(MSG_HIDE_ACTION_BAR);
564 int currentSystemUIVisibility = mAboveFilmstripControlLayout.getSystemUiVisibility();
565 int newSystemUIVisibility = (visible ? View.SYSTEM_UI_FLAG_VISIBLE :
566 View.SYSTEM_UI_FLAG_LOW_PROFILE | View.SYSTEM_UI_FLAG_FULLSCREEN);
567 if (newSystemUIVisibility != currentSystemUIVisibility) {
568 mAboveFilmstripControlLayout.setSystemUiVisibility(newSystemUIVisibility);
571 boolean currentActionBarVisibility = mActionBar.isShowing();
572 mCameraAppUI.getFilmstripBottomControls().setVisible(visible);
573 if (visible != currentActionBarVisibility) {
582 private void hideSessionProgress() {
583 mSessionProgressPanel.setVisibility(View.GONE);
586 private void showSessionProgress(CharSequence message) {
587 mBottomProgressText.setText(message);
588 mSessionProgressPanel.setVisibility(View.VISIBLE);
591 private void updateSessionProgress(int progress) {
592 mBottomProgressBar.setProgress(progress);
595 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
596 private void setupNfcBeamPush() {
597 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(CameraActivity.this);
598 if (adapter == null) {
602 if (!ApiHelper.HAS_SET_BEAM_PUSH_URIS) {
604 adapter.setNdefPushMessage(null, CameraActivity.this);
608 adapter.setBeamPushUris(null, CameraActivity.this);
609 adapter.setBeamPushUrisCallback(new CreateBeamUrisCallback() {
611 public Uri[] createBeamUris(NfcEvent event) {
614 }, CameraActivity.this);
618 public void onMenuVisibilityChanged(boolean isVisible) {
619 // TODO: Remove this or bring back the original implementation: cancel
620 // auto-hide actionbar.
624 public boolean onShareTargetSelected(ShareActionProvider shareActionProvider, Intent intent) {
625 int currentDataId = mFilmstripController.getCurrentId();
626 if (currentDataId < 0) {
629 UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA, UsageStatistics.ACTION_SHARE,
630 intent.getComponent().getPackageName(), 0,
631 UsageStatistics.hashFileName(fileNameFromDataID(currentDataId)));
635 // Note: All callbacks come back on the main thread.
636 private final SessionListener mSessionListener =
637 new SessionListener() {
639 public void onSessionQueued(final Uri uri) {
641 int dataID = mDataAdapter.findDataByContentUri(uri);
643 // Don't allow special UI actions (swipe to
644 // delete, for example) on in-progress data.
645 LocalData d = mDataAdapter.getLocalData(dataID);
646 InProgressDataWrapper newData = new InProgressDataWrapper(d);
647 mDataAdapter.updateData(dataID, newData);
652 public void onSessionDone(final Uri uri) {
653 Log.v(TAG, "onSessionDone:" + uri);
654 int doneID = mDataAdapter.findDataByContentUri(uri);
655 int currentDataId = mFilmstripController.getCurrentId();
657 if (currentDataId == doneID) {
658 hideSessionProgress();
659 updateSessionProgress(0);
661 mDataAdapter.refresh(CameraActivity.this, uri, /* isInProgress */ false);
665 public void onSessionProgress(final Uri uri, final int progress) {
667 // Do nothing, there is no task for this URI.
670 int currentDataId = mFilmstripController.getCurrentId();
671 if (currentDataId == -1) {
675 mDataAdapter.getLocalData(currentDataId).getContentUri())) {
676 updateSessionProgress(progress);
681 public void onSessionUpdated(Uri uri) {
682 mDataAdapter.refresh(CameraActivity.this, uri, /* isInProgress */ true);
687 public Context getAndroidContext() {
692 public void launchActivityByIntent(Intent intent) {
693 startActivityForResult(intent, REQ_CODE_DONT_SWITCH_TO_PREVIEW);
697 public int getCurrentModuleIndex() {
698 return mCurrentModeIndex;
702 public SurfaceTexture getPreviewBuffer() {
703 // TODO: implement this
708 public void onPreviewStarted() {
709 mCameraAppUI.onPreviewStarted();
713 public void updatePreviewAspectRatio(float aspectRatio) {
714 mCameraAppUI.updatePreviewAspectRatio(aspectRatio);
718 public void updatePreviewTransform(Matrix matrix) {
719 mCameraAppUI.updatePreviewTransform(matrix);
723 public void setPreviewStatusListener(PreviewStatusListener previewStatusListener) {
724 mCameraAppUI.setPreviewStatusListener(previewStatusListener);
728 public FrameLayout getModuleLayoutRoot() {
729 return mCameraAppUI.getModuleRootView();
733 public void setShutterEventsListener(ShutterEventsListener listener) {
734 // TODO: implement this
738 public void setShutterEnabled(boolean enabled) {
739 // TODO: implement this
743 public boolean isShutterEnabled() {
744 // TODO: implement this
749 public void startPreCaptureAnimation() {
750 mCameraAppUI.startPreCaptureAnimation();
754 public void cancelPreCaptureAnimation() {
755 // TODO: implement this
759 public void startPostCaptureAnimation() {
760 // TODO: implement this
764 public void startPostCaptureAnimation(Bitmap thumbnail) {
765 // TODO: implement this
769 public void cancelPostCaptureAnimation() {
770 // TODO: implement this
774 public OrientationManager getOrientationManager() {
775 return mOrientationManager;
779 public LocationManager getLocationManager() {
780 return mLocationManager;
784 public void lockOrientation() {
785 mOrientationManager.lockOrientation();
789 public void unlockOrientation() {
790 mOrientationManager.unlockOrientation();
794 public void notifyNewMedia(Uri uri) {
795 ContentResolver cr = getContentResolver();
796 String mimeType = cr.getType(uri);
797 if (mimeType.startsWith("video/")) {
798 sendBroadcast(new Intent(CameraUtil.ACTION_NEW_VIDEO, uri));
799 mDataAdapter.addNewVideo(this, uri);
800 } else if (mimeType.startsWith("image/")) {
801 CameraUtil.broadcastNewPicture(this, uri);
802 mDataAdapter.addNewPhoto(this, uri);
803 } else if (mimeType.startsWith(PlaceholderManager.PLACEHOLDER_MIME_TYPE)) {
804 mDataAdapter.addNewPhoto(this, uri);
806 android.util.Log.w(TAG, "Unknown new media with MIME type:"
807 + mimeType + ", uri:" + uri);
812 public void enableKeepScreenOn(boolean enabled) {
817 mKeepScreenOn = enabled;
819 mMainHandler.removeMessages(MSG_CLEAR_SCREEN_ON_FLAG);
820 getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
822 keepScreenOnForAWhile();
827 public CameraProvider getCameraProvider() {
828 return mCameraController;
831 private void removeData(int dataID) {
832 mDataAdapter.removeData(CameraActivity.this, dataID);
833 if (mDataAdapter.getTotalNumber() > 1) {
834 showUndoDeletionBar();
836 // If camera preview is the only view left in filmstrip,
837 // no need to show undo bar.
838 mPendingDeletion = true;
844 public boolean onOptionsItemSelected(MenuItem item) {
845 // Handle presses on the action bar items
846 switch (item.getItemId()) {
847 case android.R.id.home:
851 return super.onOptionsItemSelected(item);
855 private boolean isCaptureIntent() {
856 if (MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction())
857 || MediaStore.ACTION_IMAGE_CAPTURE.equals(getIntent().getAction())
858 || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(getIntent().getAction())) {
866 public void onCreate(Bundle state) {
867 super.onCreate(state);
868 GcamHelper.init(getContentResolver());
870 getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
871 setContentView(R.layout.activity_main);
872 mActionBar = getActionBar();
873 mActionBar.addOnMenuVisibilityListener(this);
874 mMainHandler = new MainHandler(getMainLooper());
876 new CameraController(this, this, mMainHandler,
877 CameraManagerFactory.getAndroidCameraManager());
878 mPreferences = new ComboPreferences(getAndroidContext());
879 mContentResolver = this.getContentResolver();
881 mSettingsManager = new SettingsManager(this, null, mCameraController.getNumberOfCameras());
882 // Remove this after we get rid of ComboPreferences.
883 int cameraId = Integer.parseInt(mSettingsManager.get(SettingsManager.SETTING_CAMERA_ID));
884 mPreferences.setLocalId(this, cameraId);
885 CameraSettings.upgradeGlobalPreferences(mPreferences,
886 mCameraController.getNumberOfCameras());
887 // TODO: Try to move all the resources allocation to happen as soon as
888 // possible so we can call module.init() at the earliest time.
889 mModuleManager = new ModuleManagerImpl();
890 ModulesInfo.setupModules(this, mModuleManager);
892 mModeListView = (ModeListView) findViewById(R.id.mode_list_layout);
893 mModeListView.init(mModuleManager.getSupportedModeIndexList());
894 if (ApiHelper.HAS_ROTATION_ANIMATION) {
895 setRotationAnimation();
898 // Check if this is in the secure camera mode.
899 Intent intent = getIntent();
900 String action = intent.getAction();
901 if (INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action)
902 || ACTION_IMAGE_CAPTURE_SECURE.equals(action)) {
903 mSecureCamera = true;
905 mSecureCamera = intent.getBooleanExtra(SECURE_CAMERA_EXTRA, false);
909 // Change the window flags so that secure camera can show when locked
910 Window win = getWindow();
911 WindowManager.LayoutParams params = win.getAttributes();
912 params.flags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
913 win.setAttributes(params);
915 // Filter for screen off so that we can finish secure camera activity
916 // when screen is off.
917 IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
918 registerReceiver(mScreenOffReceiver, filter);
920 mCameraAppUI = new CameraAppUI(this,
921 (MainActivityLayout) findViewById(R.id.activity_root_view),
922 isSecureCamera(), isCaptureIntent());
924 mCameraAppUI.setFilmstripBottomControlsListener(mMyFilmstripBottomControlListener);
926 mAboveFilmstripControlLayout =
927 (FrameLayout) findViewById(R.id.camera_filmstrip_content_layout);
929 // Add the session listener so we can track the session progress updates.
930 getServices().getCaptureSessionManager().addSessionListener(mSessionListener);
931 mSessionProgressPanel = findViewById(R.id.pano_session_progress_panel);
932 mBottomProgressBar = (ProgressBar) findViewById(R.id.pano_session_progress_bar);
933 mBottomProgressText = (TextView) findViewById(R.id.pano_session_progress_text);
934 mFilmstripController = ((FilmstripView) findViewById(R.id.filmstrip_view)).getController();
935 mFilmstripController.setImageGap(
936 getResources().getDimensionPixelSize(R.dimen.camera_film_strip_gap));
937 mPanoramaViewHelper = new PanoramaViewHelper(this);
938 mPanoramaViewHelper.onCreate();
939 // Set up the camera preview first so the preview shows up ASAP.
940 mDataAdapter = new CameraDataAdapter(
941 new ColorDrawable(getResources().getColor(R.color.photo_placeholder)));
942 mDataAdapter.setLocalDataListener(mLocalDataListener);
944 mCameraAppUI.getFilmstripContentPanel().setFilmstripListener(mFilmstripListener);
946 mLocationManager = new LocationManager(this);
948 mSettingsController = new SettingsController(this, mSettingsManager, mLocationManager);
951 if (MediaStore.INTENT_ACTION_VIDEO_CAMERA.equals(getIntent().getAction())
952 || MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction())) {
953 modeIndex = ModeListView.MODE_VIDEO;
954 } else if (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(getIntent().getAction())
955 || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(getIntent()
957 modeIndex = ModeListView.MODE_PHOTO;
958 if (mSettingsManager.getInt(SettingsManager.SETTING_STARTUP_MODULE_INDEX)
959 == ModeListView.MODE_GCAM && GcamHelper.hasGcamCapture()) {
960 modeIndex = ModeListView.MODE_GCAM;
962 } else if (MediaStore.ACTION_IMAGE_CAPTURE.equals(getIntent().getAction())
963 || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(getIntent().getAction())) {
964 modeIndex = ModeListView.MODE_PHOTO;
966 // If the activity has not been started using an explicit intent,
967 // read the module index from the last time the user changed modes
968 modeIndex = mSettingsManager.getInt(SettingsManager.SETTING_STARTUP_MODULE_INDEX);
969 if ((modeIndex == ModeListView.MODE_GCAM &&
970 !GcamHelper.hasGcamCapture()) || modeIndex < 0) {
971 modeIndex = ModeListView.MODE_PHOTO;
975 mOrientationManager = new OrientationManagerImpl(this);
976 mOrientationManager.addOnOrientationChangeListener(mMainHandler, this);
978 setModuleFromModeIndex(modeIndex);
980 // TODO: Remove this when refactor is done.
981 if (modeIndex == ModulesInfo.MODULE_PHOTO
982 || modeIndex == ModulesInfo.MODULE_VIDEO
983 || modeIndex == ModulesInfo.MODULE_GCAM
984 || modeIndex == ModulesInfo.MODULE_CRAFT) {
985 mCameraAppUI.prepareModuleUI();
987 mCurrentModule.init(this, isSecureCamera(), isCaptureIntent());
989 if (!mSecureCamera) {
990 mFilmstripController.setDataAdapter(mDataAdapter);
991 if (!isCaptureIntent()) {
992 mDataAdapter.requestLoad(this);
995 // Put a lock placeholder as the last image by setting its date to
997 ImageView v = (ImageView) getLayoutInflater().inflate(
998 R.layout.secure_album_placeholder, null);
999 v.setOnClickListener(new View.OnClickListener() {
1001 public void onClick(View view) {
1006 mDataAdapter = new FixedLastDataAdapter(
1010 v.getDrawable().getIntrinsicWidth(),
1011 v.getDrawable().getIntrinsicHeight(),
1013 // Flush out all the original data.
1014 mDataAdapter.flush();
1015 mFilmstripController.setDataAdapter(mDataAdapter);
1020 mLocalImagesObserver = new LocalMediaObserver();
1021 mLocalVideosObserver = new LocalMediaObserver();
1023 getContentResolver().registerContentObserver(
1024 MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true,
1025 mLocalImagesObserver);
1026 getContentResolver().registerContentObserver(
1027 MediaStore.Video.Media.EXTERNAL_CONTENT_URI, true,
1028 mLocalVideosObserver);
1029 if (FeedbackHelper.feedbackAvailable()) {
1030 mFeedbackHelper = new FeedbackHelper(this);
1034 private void setRotationAnimation() {
1035 int rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
1036 rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE;
1037 Window win = getWindow();
1038 WindowManager.LayoutParams winParams = win.getAttributes();
1039 winParams.rotationAnimation = rotationAnimation;
1040 win.setAttributes(winParams);
1044 public void onUserInteraction() {
1045 super.onUserInteraction();
1046 if (!isFinishing()) {
1047 keepScreenOnForAWhile();
1052 public boolean dispatchTouchEvent(MotionEvent ev) {
1053 boolean result = super.dispatchTouchEvent(ev);
1054 if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
1055 // Real deletion is postponed until the next user interaction after
1056 // the gesture that triggers deletion. Until real deletion is performed,
1057 // users can click the undo button to bring back the image that they
1059 if (mPendingDeletion && !mIsUndoingDeletion) {
1067 public void onPause() {
1070 // Delete photos that are pending deletion
1072 mCurrentModule.pause();
1073 mOrientationManager.pause();
1074 // Close the camera and wait for the operation done.
1075 mCameraController.closeCamera();
1077 mLocalImagesObserver.setActivityPaused(true);
1078 mLocalVideosObserver.setActivityPaused(true);
1084 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
1085 if (requestCode == REQ_CODE_DONT_SWITCH_TO_PREVIEW) {
1086 mResetToPreviewOnResume = false;
1088 super.onActivityResult(requestCode, resultCode, data);
1093 public void onResume() {
1096 mLastLayoutOrientation = getResources().getConfiguration().orientation;
1098 // TODO: Handle this in OrientationManager.
1100 if (Settings.System.getInt(getContentResolver(),
1101 Settings.System.ACCELEROMETER_ROTATION, 0) == 0) {
1102 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
1103 mAutoRotateScreen = false;
1105 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
1106 mAutoRotateScreen = true;
1109 UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
1110 UsageStatistics.ACTION_FOREGROUNDED, this.getClass().getSimpleName());
1112 mOrientationManager.resume();
1114 mCurrentModule.resume();
1115 setSwipingEnabled(true);
1117 if (mResetToPreviewOnResume) {
1118 mCameraAppUI.resume();
1120 // The share button might be disabled to avoid double tapping.
1121 mCameraAppUI.getFilmstripBottomControls().setShareEnabled(true);
1122 // Default is showing the preview, unless disabled by explicitly
1123 // starting an activity we want to return from to the filmstrip rather
1124 // than the preview.
1125 mResetToPreviewOnResume = true;
1127 if (mLocalVideosObserver.isMediaDataChangedDuringPause()
1128 || mLocalImagesObserver.isMediaDataChangedDuringPause()) {
1129 if (!mSecureCamera) {
1130 // If it's secure camera, requestLoad() should not be called
1131 // as it will load all the data.
1132 if (!mFilmstripVisible) {
1133 mDataAdapter.requestLoad(this);
1137 mLocalImagesObserver.setActivityPaused(false);
1138 mLocalVideosObserver.setActivityPaused(false);
1140 keepScreenOnForAWhile();
1144 public void onStart() {
1146 mPanoramaViewHelper.onStart();
1147 boolean recordLocation = RecordLocationPreference.get(
1148 mPreferences, mContentResolver);
1149 mLocationManager.recordLocation(recordLocation);
1153 protected void onStop() {
1154 mPanoramaViewHelper.onStop();
1155 if (mFeedbackHelper != null) {
1156 mFeedbackHelper.stopFeedback();
1159 mLocationManager.disconnect();
1160 CameraManagerFactory.recycle();
1165 public void onDestroy() {
1166 if (mSecureCamera) {
1167 unregisterReceiver(mScreenOffReceiver);
1169 getContentResolver().unregisterContentObserver(mLocalImagesObserver);
1170 getContentResolver().unregisterContentObserver(mLocalVideosObserver);
1175 public void onConfigurationChanged(Configuration config) {
1176 super.onConfigurationChanged(config);
1177 Log.v(TAG, "onConfigurationChanged");
1178 if (config.orientation == Configuration.ORIENTATION_UNDEFINED) {
1182 if (mLastLayoutOrientation != config.orientation) {
1183 mLastLayoutOrientation = config.orientation;
1184 mCurrentModule.onLayoutOrientationChanged(
1185 mLastLayoutOrientation == Configuration.ORIENTATION_LANDSCAPE);
1190 public boolean onKeyDown(int keyCode, KeyEvent event) {
1191 if (mFilmstripController.inCameraFullscreen()) {
1192 if (mCurrentModule.onKeyDown(keyCode, event)) {
1195 // Prevent software keyboard or voice search from showing up.
1196 if (keyCode == KeyEvent.KEYCODE_SEARCH
1197 || keyCode == KeyEvent.KEYCODE_MENU) {
1198 if (event.isLongPress()) {
1204 return super.onKeyDown(keyCode, event);
1208 public boolean onKeyUp(int keyCode, KeyEvent event) {
1209 if (mFilmstripController.inCameraFullscreen() && mCurrentModule.onKeyUp(keyCode, event)) {
1212 return super.onKeyUp(keyCode, event);
1216 public void onBackPressed() {
1217 if (!mCameraAppUI.onBackPressed()) {
1218 if (!mCurrentModule.onBackPressed()) {
1219 super.onBackPressed();
1224 public boolean isAutoRotateScreen() {
1225 return mAutoRotateScreen;
1228 protected void updateStorageSpace() {
1229 mStorageSpaceBytes = Storage.getAvailableSpace();
1232 protected long getStorageSpaceBytes() {
1233 return mStorageSpaceBytes;
1236 protected void updateStorageSpaceAndHint() {
1237 updateStorageSpace();
1238 updateStorageHint(mStorageSpaceBytes);
1241 protected void updateStorageHint(long storageSpace) {
1242 String message = null;
1243 if (storageSpace == Storage.UNAVAILABLE) {
1244 message = getString(R.string.no_storage);
1245 } else if (storageSpace == Storage.PREPARING) {
1246 message = getString(R.string.preparing_sd);
1247 } else if (storageSpace == Storage.UNKNOWN_SIZE) {
1248 message = getString(R.string.access_sd_fail);
1249 } else if (storageSpace <= Storage.LOW_STORAGE_THRESHOLD_BYTES) {
1250 message = getString(R.string.spaceIsLow_content);
1253 if (message != null) {
1254 if (mStorageHint == null) {
1255 mStorageHint = OnScreenHint.makeText(this, message);
1257 mStorageHint.setText(message);
1259 mStorageHint.show();
1260 } else if (mStorageHint != null) {
1261 mStorageHint.cancel();
1262 mStorageHint = null;
1266 protected void setResultEx(int resultCode) {
1267 mResultCodeForTesting = resultCode;
1268 setResult(resultCode);
1271 protected void setResultEx(int resultCode, Intent data) {
1272 mResultCodeForTesting = resultCode;
1273 mResultDataForTesting = data;
1274 setResult(resultCode, data);
1277 public int getResultCode() {
1278 return mResultCodeForTesting;
1281 public Intent getResultData() {
1282 return mResultDataForTesting;
1285 public boolean isSecureCamera() {
1286 return mSecureCamera;
1290 public boolean isPaused() {
1295 public void onModeSelected(int modeIndex) {
1296 if (mCurrentModeIndex == modeIndex) {
1300 if (modeIndex == ModeListView.MODE_SETTING) {
1301 onSettingsSelected();
1305 CameraHolder.instance().keep();
1306 closeModule(mCurrentModule);
1307 int oldModuleIndex = mCurrentModeIndex;
1308 setModuleFromModeIndex(modeIndex);
1310 // TODO: The following check is temporary for modules attached to the
1311 // generic_module layout. When the refactor is done, similar logic will
1312 // be applied to all modules.
1313 if (mCurrentModeIndex == ModulesInfo.MODULE_PHOTO
1314 || mCurrentModeIndex == ModulesInfo.MODULE_VIDEO
1315 || mCurrentModeIndex == ModulesInfo.MODULE_GCAM
1316 || mCurrentModeIndex == ModulesInfo.MODULE_CRAFT) {
1317 if (oldModuleIndex != ModulesInfo.MODULE_PHOTO
1318 && oldModuleIndex != ModulesInfo.MODULE_VIDEO
1319 && oldModuleIndex != ModulesInfo.MODULE_GCAM
1320 && oldModuleIndex != ModulesInfo.MODULE_CRAFT) {
1321 mCameraAppUI.prepareModuleUI();
1323 mCameraAppUI.clearModuleUI();
1326 // This is the old way of removing all views in CameraRootView. Will
1327 // be deprecated soon. It is here to make sure modules that haven't
1328 // been refactored can still function.
1329 mCameraAppUI.clearCameraUI();
1332 openModule(mCurrentModule);
1333 mCurrentModule.onOrientationChanged(mLastRawOrientation);
1334 // Store the module index so we can use it the next time the Camera
1336 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
1337 prefs.edit().putInt(CameraSettings.KEY_STARTUP_MODULE_INDEX, modeIndex).apply();
1340 public void onSettingsSelected() {
1341 // Temporary until we finalize the touch flow.
1342 LayoutInflater inflater = getLayoutInflater();
1343 SettingsView settingsView = (SettingsView) inflater.inflate(R.layout.settings_list_layout,
1345 settingsView.setSettingsListener(mSettingsController);
1346 if (mFeedbackHelper != null) {
1347 settingsView.setFeedbackHelper(mFeedbackHelper);
1349 PopupWindow popup = new PopupWindow(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
1350 popup.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
1351 popup.setOutsideTouchable(true);
1352 popup.setFocusable(true);
1353 popup.setContentView(settingsView);
1354 popup.showAtLocation(mModeListView.getRootView(), Gravity.CENTER, 0, 0);
1358 * Sets the mCurrentModuleIndex, creates a new module instance for the given
1359 * index an sets it as mCurrentModule.
1361 private void setModuleFromModeIndex(int modeIndex) {
1362 ModuleManagerImpl.ModuleAgent agent = mModuleManager.getModuleAgent(modeIndex);
1363 if (agent == null) {
1366 if (!agent.requestAppForCamera()) {
1367 mCameraController.closeCamera();
1369 mCurrentModeIndex = agent.getModuleId();
1370 mCurrentModule = (CameraModule) agent.createModule(this);
1374 public SettingsManager getSettingsManager() {
1375 return mSettingsManager;
1379 public CameraServices getServices() {
1380 return (CameraServices) getApplication();
1384 public SettingsController getSettingsController() {
1385 return mSettingsController;
1388 public ButtonManager getButtonManager() {
1389 if (mButtonManager == null) {
1390 mButtonManager = new ButtonManager(this);
1392 return mButtonManager;
1396 * Creates an AlertDialog appropriate for choosing whether to enable location
1397 * on the first run of the app.
1399 public AlertDialog getFirstTimeLocationAlert() {
1400 AlertDialog.Builder builder = new AlertDialog.Builder(this);
1401 builder = SettingsView.getFirstTimeLocationAlertBuilder(builder, mSettingsController);
1402 if (builder != null) {
1403 return builder.create();
1410 * Launches an ACTION_EDIT intent for the given local data item.
1412 public void launchEditor(LocalData data) {
1413 Intent intent = new Intent(Intent.ACTION_EDIT)
1414 .setDataAndType(data.getContentUri(), data.getMimeType())
1415 .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
1417 launchActivityByIntent(intent);
1418 } catch (ActivityNotFoundException e) {
1419 launchActivityByIntent(Intent.createChooser(intent, null));
1424 * Launch the tiny planet editor.
1426 * @param data The data must be a 360 degree stereographically mapped
1427 * panoramic image. It will not be modified, instead a new item
1428 * with the result will be added to the filmstrip.
1430 public void launchTinyPlanetEditor(LocalData data) {
1431 TinyPlanetFragment fragment = new TinyPlanetFragment();
1432 Bundle bundle = new Bundle();
1433 bundle.putString(TinyPlanetFragment.ARGUMENT_URI, data.getContentUri().toString());
1434 bundle.putString(TinyPlanetFragment.ARGUMENT_TITLE, data.getTitle());
1435 fragment.setArguments(bundle);
1436 fragment.show(getFragmentManager(), "tiny_planet");
1439 private void openModule(CameraModule module) {
1440 module.init(this, isSecureCamera(), isCaptureIntent());
1442 module.onPreviewVisibilityChanged(!mFilmstripVisible);
1445 private void closeModule(CameraModule module) {
1449 private void performDeletion() {
1450 if (!mPendingDeletion) {
1453 hideUndoDeletionBar(false);
1454 mDataAdapter.executeDeletion(CameraActivity.this);
1457 public void showUndoDeletionBar() {
1458 if (mPendingDeletion) {
1461 Log.v(TAG, "showing undo bar");
1462 mPendingDeletion = true;
1463 if (mUndoDeletionBar == null) {
1464 ViewGroup v = (ViewGroup) getLayoutInflater().inflate(R.layout.undo_bar,
1465 mAboveFilmstripControlLayout, true);
1466 mUndoDeletionBar = (ViewGroup) v.findViewById(R.id.camera_undo_deletion_bar);
1467 View button = mUndoDeletionBar.findViewById(R.id.camera_undo_deletion_button);
1468 button.setOnClickListener(new View.OnClickListener() {
1470 public void onClick(View view) {
1471 mDataAdapter.undoDataRemoval();
1472 hideUndoDeletionBar(true);
1475 // Setting undo bar clickable to avoid touch events going through
1476 // the bar to the buttons (eg. edit button, etc) underneath the bar.
1477 mUndoDeletionBar.setClickable(true);
1478 // When there is user interaction going on with the undo button, we
1479 // do not want to hide the undo bar.
1480 button.setOnTouchListener(new View.OnTouchListener() {
1482 public boolean onTouch(View v, MotionEvent event) {
1483 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
1484 mIsUndoingDeletion = true;
1485 } else if (event.getActionMasked() == MotionEvent.ACTION_UP) {
1486 mIsUndoingDeletion = false;
1492 mUndoDeletionBar.setAlpha(0f);
1493 mUndoDeletionBar.setVisibility(View.VISIBLE);
1494 mUndoDeletionBar.animate().setDuration(200).alpha(1f).setListener(null).start();
1497 private void hideUndoDeletionBar(boolean withAnimation) {
1498 Log.v(TAG, "Hiding undo deletion bar");
1499 mPendingDeletion = false;
1500 if (mUndoDeletionBar != null) {
1501 if (withAnimation) {
1502 mUndoDeletionBar.animate().setDuration(200).alpha(0f)
1503 .setListener(new Animator.AnimatorListener() {
1505 public void onAnimationStart(Animator animation) {
1510 public void onAnimationEnd(Animator animation) {
1511 mUndoDeletionBar.setVisibility(View.GONE);
1515 public void onAnimationCancel(Animator animation) {
1520 public void onAnimationRepeat(Animator animation) {
1525 mUndoDeletionBar.setVisibility(View.GONE);
1531 public void onOrientationChanged(int orientation) {
1532 // We keep the last known orientation. So if the user first orient
1533 // the camera then point the camera to floor or sky, we still have
1534 // the correct orientation.
1535 if (orientation == OrientationManager.ORIENTATION_UNKNOWN) {
1538 mLastRawOrientation = orientation;
1539 if (mCurrentModule != null) {
1540 mCurrentModule.onOrientationChanged(orientation);
1545 * Enable/disable swipe-to-filmstrip. Will always disable swipe if in
1548 * @param enable {@code true} to enable swipe.
1550 public void setSwipingEnabled(boolean enable) {
1551 // TODO: Bring back the functionality.
1552 if (isCaptureIntent()) {
1553 //lockPreview(true);
1555 //lockPreview(!enable);
1559 // Accessor methods for getting latency times used in performance testing
1560 public long getAutoFocusTime() {
1561 return (mCurrentModule instanceof PhotoModule) ?
1562 ((PhotoModule) mCurrentModule).mAutoFocusTime : -1;
1565 public long getShutterLag() {
1566 return (mCurrentModule instanceof PhotoModule) ?
1567 ((PhotoModule) mCurrentModule).mShutterLag : -1;
1570 public long getShutterToPictureDisplayedTime() {
1571 return (mCurrentModule instanceof PhotoModule) ?
1572 ((PhotoModule) mCurrentModule).mShutterToPictureDisplayedTime : -1;
1575 public long getPictureDisplayedToJpegCallbackTime() {
1576 return (mCurrentModule instanceof PhotoModule) ?
1577 ((PhotoModule) mCurrentModule).mPictureDisplayedToJpegCallbackTime : -1;
1580 public long getJpegCallbackFinishTime() {
1581 return (mCurrentModule instanceof PhotoModule) ?
1582 ((PhotoModule) mCurrentModule).mJpegCallbackFinishTime : -1;
1585 public long getCaptureStartTime() {
1586 return (mCurrentModule instanceof PhotoModule) ?
1587 ((PhotoModule) mCurrentModule).mCaptureStartTime : -1;
1590 public boolean isRecording() {
1591 return (mCurrentModule instanceof VideoModule) ?
1592 ((VideoModule) mCurrentModule).isRecording() : false;
1595 public CameraManager.CameraOpenCallback getCameraOpenErrorCallback() {
1596 return mCameraController;
1599 // For debugging purposes only.
1600 public CameraModule getCurrentModule() {
1601 return mCurrentModule;
1604 private void keepScreenOnForAWhile() {
1605 if (mKeepScreenOn) {
1608 mMainHandler.removeMessages(MSG_CLEAR_SCREEN_ON_FLAG);
1609 getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1610 mMainHandler.sendEmptyMessageDelayed(MSG_CLEAR_SCREEN_ON_FLAG, SCREEN_DELAY_MS);
1613 private void resetScreenOn() {
1614 mKeepScreenOn = false;
1615 mMainHandler.removeMessages(MSG_CLEAR_SCREEN_ON_FLAG);
1616 getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1619 private void startGallery() {
1621 UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
1622 UsageStatistics.ACTION_GALLERY, null);
1623 launchActivityByIntent(IntentHelper.getGalleryIntent(CameraActivity.this));
1624 } catch (ActivityNotFoundException e) {
1625 Log.w(TAG, "Failed to launch gallery activity, closing");
1629 private void setNfcBeamPushUriFromData(LocalData data) {
1630 final Uri uri = data.getContentUri();
1631 if (uri != Uri.EMPTY) {
1632 mNfcPushUris[0] = uri;
1634 mNfcPushUris[0] = null;
1639 * Updates the visibility of the filmstrip bottom controls.
1641 private void updateUiByData(final int dataId) {
1642 if (isSecureCamera()) {
1643 // We cannot show buttons in secure camera since go to other
1644 // activities might create a security hole.
1648 final LocalData currentData = mDataAdapter.getLocalData(dataId);
1649 if (currentData == null) {
1650 Log.w(TAG, "Current data ID not found.");
1651 hideSessionProgress();
1655 setNfcBeamPushUriFromData(currentData);
1657 /* Bottom controls. */
1659 updateBottomControlsByData(currentData);
1660 if (!mDataAdapter.isMetadataUpdated(dataId)) {
1661 mDataAdapter.updateMetadata(this, dataId);
1666 * Updates the bottom controls based on the data.
1668 private void updateBottomControlsByData(final LocalData currentData) {
1670 final CameraAppUI.BottomControls filmstripBottomControls =
1671 mCameraAppUI.getFilmstripBottomControls();
1672 filmstripBottomControls.setEditButtonVisibility(
1673 currentData.isDataActionSupported(LocalData.DATA_ACTION_EDIT));
1674 filmstripBottomControls.setShareButtonVisibility(
1675 currentData.isDataActionSupported(LocalData.DATA_ACTION_SHARE));
1676 filmstripBottomControls.setDeleteButtonVisibility(
1677 currentData.isDataActionSupported(LocalData.DATA_ACTION_DELETE));
1681 Uri contentUri = currentData.getContentUri();
1682 CaptureSessionManager sessionManager = getServices()
1683 .getCaptureSessionManager();
1684 int sessionProgress = sessionManager.getSessionProgress(contentUri);
1686 if (sessionProgress < 0) {
1687 hideSessionProgress();
1689 CharSequence progressMessage = sessionManager
1690 .getSessionProgressMessage(contentUri);
1691 showSessionProgress(progressMessage);
1692 updateSessionProgress(sessionProgress);
1697 // We need to add this to a separate DB.
1698 final int viewButtonVisibility;
1699 if (PanoramaMetadataLoader.isPanorama(currentData)) {
1700 viewButtonVisibility = CameraAppUI.BottomControls.VIEW_PHOTO_SPHERE;
1701 } else if (RgbzMetadataLoader.hasRGBZData(currentData)) {
1702 viewButtonVisibility = CameraAppUI.BottomControls.VIEW_RGBZ;
1704 viewButtonVisibility = CameraAppUI.BottomControls.VIEW_NONE;
1707 filmstripBottomControls.setTinyPlanetButtonVisibility(
1708 PanoramaMetadataLoader.isPanorama360(currentData));
1709 filmstripBottomControls.setViewButtonVisibility(viewButtonVisibility);