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 = "CameraActivity";
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) {
293 private int getCurrentDataId() {
294 return mFilmstripController.getCurrentId();
297 private LocalData getCurrentLocalData() {
298 return mDataAdapter.getLocalData(getCurrentDataId());
302 * Sets up the share intent and NFC properly according to the data.
304 * @param data The data to be shared.
306 private Intent getShareIntentByData(final LocalData data) {
307 Intent intent = null;
308 final Uri contentUri = data.getContentUri();
309 if (PanoramaMetadataLoader.isPanorama360(data) &&
310 data.getContentUri() != Uri.EMPTY) {
311 intent = new Intent(Intent.ACTION_SEND);
312 intent.setType("application/vnd.google.panorama360+jpg");
313 intent.putExtra(Intent.EXTRA_STREAM, contentUri);
314 } else if (data.isDataActionSupported(LocalData.DATA_ACTION_SHARE)) {
315 final String mimeType = data.getMimeType();
316 intent = getShareIntentFromType(mimeType);
317 if (intent != null) {
318 intent.putExtra(Intent.EXTRA_STREAM, contentUri);
319 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
326 * Get the share intent according to the mimeType
328 * @param mimeType The mimeType of current data.
329 * @return the video/image's ShareIntent or null if mimeType is invalid.
331 private Intent getShareIntentFromType(String mimeType) {
332 // Lazily create the intent object.
333 Intent intent = new Intent(Intent.ACTION_SEND);
334 if (mimeType.startsWith("video/")) {
335 intent.setType("video/*");
337 if (mimeType.startsWith("image/")) {
338 intent.setType("image/*");
340 Log.w(TAG, "unsupported mimeType " + mimeType);
347 private ComboPreferences mPreferences;
348 private ContentResolver mContentResolver;
351 public void onCameraOpened(CameraManager.CameraProxy camera) {
352 if (!mModuleManager.getModuleAgent(mCurrentModeIndex).requestAppForCamera()) {
353 // We shouldn't be here. Just close the camera and leave.
354 camera.release(false);
355 throw new IllegalStateException("Camera opened but the module shouldn't be " +
358 if (mCurrentModule != null) {
359 SettingsCapabilities capabilities =
360 SettingsController.getSettingsCapabilities(camera);
361 mSettingsManager.changeCamera(camera.getCameraId(), capabilities);
362 mCurrentModule.onCameraAvailable(camera);
364 mCameraAppUI.onChangeCamera();
368 public void onCameraDisabled(int cameraId) {
369 UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA, UsageStatistics.ACTION_OPEN_FAIL,
372 CameraUtil.showErrorAndFinish(this, R.string.camera_disabled);
376 public void onDeviceOpenFailure(int cameraId) {
377 UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
378 UsageStatistics.ACTION_OPEN_FAIL, "open");
380 CameraUtil.showErrorAndFinish(this, R.string.cannot_connect_camera);
384 public void onReconnectionFailure(CameraManager mgr) {
385 UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
386 UsageStatistics.ACTION_OPEN_FAIL, "reconnect");
388 CameraUtil.showErrorAndFinish(this, R.string.cannot_connect_camera);
391 private class MainHandler extends Handler {
392 public MainHandler(Looper looper) {
397 public void handleMessage(Message msg) {
399 case MSG_HIDE_ACTION_BAR: {
400 removeMessages(MSG_HIDE_ACTION_BAR);
401 CameraActivity.this.setFilmstripUiVisibility(false);
405 case MSG_CLEAR_SCREEN_ON_FLAG: {
407 getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
417 private String fileNameFromDataID(int dataID) {
418 final LocalData localData = mDataAdapter.getLocalData(dataID);
420 File localFile = new File(localData.getPath());
421 return localFile.getName();
424 private final FilmstripContentPanel.Listener mFilmstripListener =
425 new FilmstripContentPanel.Listener() {
428 public void onFilmstripHidden() {
429 mFilmstripVisible = false;
430 CameraActivity.this.setFilmstripUiVisibility(false);
431 // When the user hide the filmstrip (either swipe out or
432 // tap on back key) we move to the first item so next time
433 // when the user swipe in the filmstrip, the most recent
435 mFilmstripController.goToFirstItem();
436 if (mCurrentModule != null) {
437 mCurrentModule.onPreviewVisibilityChanged(true);
442 public void onFilmstripShown() {
443 mFilmstripVisible = true;
444 updateUiByData(mFilmstripController.getCurrentId());
445 if (mCurrentModule != null) {
446 mCurrentModule.onPreviewVisibilityChanged(false);
451 public void onDataPromoted(int dataID) {
452 UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
453 UsageStatistics.ACTION_DELETE, "promoted", 0,
454 UsageStatistics.hashFileName(fileNameFromDataID(dataID)));
460 public void onDataDemoted(int dataID) {
461 UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
462 UsageStatistics.ACTION_DELETE, "demoted", 0,
463 UsageStatistics.hashFileName(fileNameFromDataID(dataID)));
469 public void onEnterFullScreen(int dataId) {
470 if (mFilmstripVisible) {
471 CameraActivity.this.setFilmstripUiVisibility(false);
476 public void onLeaveFullScreen(int dataId) {
481 public void onEnterFilmstrip(int dataId) {
482 if (mFilmstripVisible) {
483 CameraActivity.this.setFilmstripUiVisibility(true);
488 public void onLeaveFilmstrip(int dataId) {
493 public void onDataReloaded() {
494 if (!mFilmstripVisible) {
497 updateUiByData(mFilmstripController.getCurrentId());
501 public void onDataUpdated(int dataId) {
502 if (!mFilmstripVisible) {
505 updateUiByData(mFilmstripController.getCurrentId());
509 public void onEnterZoomView(int dataID) {
510 if (mFilmstripVisible) {
511 CameraActivity.this.setFilmstripUiVisibility(false);
516 public void onDataFocusChanged(final int prevDataId, final int newDataId) {
517 if (!mFilmstripVisible) {
520 // TODO: This callback is UI event callback, should always
521 // happen on UI thread. Find the reason for this
522 // runOnUiThread() and fix it.
523 runOnUiThread(new Runnable() {
526 updateUiByData(newDataId);
532 private final LocalDataAdapter.LocalDataListener mLocalDataListener =
533 new LocalDataAdapter.LocalDataListener() {
535 public void onMetadataUpdated(List<Integer> updatedData) {
536 int currentDataId = mFilmstripController.getCurrentId();
537 for (Integer dataId : updatedData) {
538 if (dataId == currentDataId) {
539 updateBottomControlsByData(mDataAdapter.getLocalData(dataId));
545 public void gotoGallery() {
546 UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA, UsageStatistics.ACTION_FILMSTRIP,
549 mFilmstripController.goToNextItem();
553 * If {@param visible} is false, this hides the action bar and switches the
554 * filmstrip UI to lights-out mode.
556 // TODO: This should not be called outside of the activity.
557 public void setFilmstripUiVisibility(boolean visible) {
558 mMainHandler.removeMessages(MSG_HIDE_ACTION_BAR);
560 int currentSystemUIVisibility = mAboveFilmstripControlLayout.getSystemUiVisibility();
561 int newSystemUIVisibility = (visible ? View.SYSTEM_UI_FLAG_VISIBLE : View.SYSTEM_UI_FLAG_FULLSCREEN);
562 if (newSystemUIVisibility != currentSystemUIVisibility) {
563 mAboveFilmstripControlLayout.setSystemUiVisibility(newSystemUIVisibility);
566 boolean currentActionBarVisibility = mActionBar.isShowing();
567 mCameraAppUI.getFilmstripBottomControls().setVisible(visible);
568 if (visible != currentActionBarVisibility) {
577 private void hideSessionProgress() {
578 mSessionProgressPanel.setVisibility(View.GONE);
581 private void showSessionProgress(CharSequence message) {
582 mBottomProgressText.setText(message);
583 mSessionProgressPanel.setVisibility(View.VISIBLE);
586 private void updateSessionProgress(int progress) {
587 mBottomProgressBar.setProgress(progress);
590 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
591 private void setupNfcBeamPush() {
592 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(CameraActivity.this);
593 if (adapter == null) {
597 if (!ApiHelper.HAS_SET_BEAM_PUSH_URIS) {
599 adapter.setNdefPushMessage(null, CameraActivity.this);
603 adapter.setBeamPushUris(null, CameraActivity.this);
604 adapter.setBeamPushUrisCallback(new CreateBeamUrisCallback() {
606 public Uri[] createBeamUris(NfcEvent event) {
609 }, CameraActivity.this);
613 public void onMenuVisibilityChanged(boolean isVisible) {
614 // TODO: Remove this or bring back the original implementation: cancel
615 // auto-hide actionbar.
619 public boolean onShareTargetSelected(ShareActionProvider shareActionProvider, Intent intent) {
620 int currentDataId = mFilmstripController.getCurrentId();
621 if (currentDataId < 0) {
624 UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA, UsageStatistics.ACTION_SHARE,
625 intent.getComponent().getPackageName(), 0,
626 UsageStatistics.hashFileName(fileNameFromDataID(currentDataId)));
630 // Note: All callbacks come back on the main thread.
631 private final SessionListener mSessionListener =
632 new SessionListener() {
634 public void onSessionQueued(final Uri uri) {
636 int dataID = mDataAdapter.findDataByContentUri(uri);
638 // Don't allow special UI actions (swipe to
639 // delete, for example) on in-progress data.
640 LocalData d = mDataAdapter.getLocalData(dataID);
641 InProgressDataWrapper newData = new InProgressDataWrapper(d);
642 mDataAdapter.updateData(dataID, newData);
647 public void onSessionDone(final Uri uri) {
648 Log.v(TAG, "onSessionDone:" + uri);
649 int doneID = mDataAdapter.findDataByContentUri(uri);
650 int currentDataId = mFilmstripController.getCurrentId();
652 if (currentDataId == doneID) {
653 hideSessionProgress();
654 updateSessionProgress(0);
656 mDataAdapter.refresh(uri, /* isInProgress */ false);
660 public void onSessionProgress(final Uri uri, final int progress) {
662 // Do nothing, there is no task for this URI.
665 int currentDataId = mFilmstripController.getCurrentId();
666 if (currentDataId == -1) {
670 mDataAdapter.getLocalData(currentDataId).getContentUri())) {
671 updateSessionProgress(progress);
676 public void onSessionUpdated(Uri uri) {
677 mDataAdapter.refresh(uri, /* isInProgress */ true);
682 public Context getAndroidContext() {
687 public void launchActivityByIntent(Intent intent) {
688 startActivityForResult(intent, REQ_CODE_DONT_SWITCH_TO_PREVIEW);
692 public int getCurrentModuleIndex() {
693 return mCurrentModeIndex;
697 public SurfaceTexture getPreviewBuffer() {
698 // TODO: implement this
703 public void onPreviewStarted() {
704 mCameraAppUI.onPreviewStarted();
708 public void addPreviewAreaSizeChangedListener(
709 PreviewStatusListener.PreviewAreaSizeChangedListener listener) {
710 mCameraAppUI.addPreviewAreaSizeChangedListener(listener);
714 public void removePreviewAreaSizeChangedListener(
715 PreviewStatusListener.PreviewAreaSizeChangedListener listener) {
716 mCameraAppUI.removePreviewAreaSizeChangedListener(listener);
720 public void setupOneShotPreviewListener() {
721 mCameraController.setOneShotPreviewCallback(mMainHandler,
722 new CameraManager.CameraPreviewDataCallback() {
724 public void onPreviewFrame(byte[] data, CameraManager.CameraProxy camera) {
725 mCameraAppUI.onNewPreviewFrame();
731 public void updatePreviewAspectRatio(float aspectRatio) {
732 mCameraAppUI.updatePreviewAspectRatio(aspectRatio);
736 public boolean shouldShowShimmy() {
737 int remainingTimes = mSettingsManager.getInt(
738 SettingsManager.SETTING_SHIMMY_REMAINING_PLAY_TIMES_INDEX);
739 return remainingTimes > 0;
743 public void decrementShimmyPlayTimes() {
744 int remainingTimes = mSettingsManager.getInt(
745 SettingsManager.SETTING_SHIMMY_REMAINING_PLAY_TIMES_INDEX) - 1;
746 if (remainingTimes >= 0) {
747 mSettingsManager.setInt(SettingsManager.SETTING_SHIMMY_REMAINING_PLAY_TIMES_INDEX,
753 public void updatePreviewTransform(Matrix matrix) {
754 mCameraAppUI.updatePreviewTransform(matrix);
758 public void setPreviewStatusListener(PreviewStatusListener previewStatusListener) {
759 mCameraAppUI.setPreviewStatusListener(previewStatusListener);
763 public FrameLayout getModuleLayoutRoot() {
764 return mCameraAppUI.getModuleRootView();
768 public void setShutterEventsListener(ShutterEventsListener listener) {
769 // TODO: implement this
773 public void setShutterEnabled(boolean enabled) {
774 // TODO: implement this
778 public boolean isShutterEnabled() {
779 // TODO: implement this
784 public void startPreCaptureAnimation() {
785 mCameraAppUI.startPreCaptureAnimation();
789 public void cancelPreCaptureAnimation() {
790 // TODO: implement this
794 public void startPostCaptureAnimation() {
795 // TODO: implement this
799 public void startPostCaptureAnimation(Bitmap thumbnail) {
800 // TODO: implement this
804 public void cancelPostCaptureAnimation() {
805 // TODO: implement this
809 public OrientationManager getOrientationManager() {
810 return mOrientationManager;
814 public LocationManager getLocationManager() {
815 return mLocationManager;
819 public void lockOrientation() {
820 mOrientationManager.lockOrientation();
824 public void unlockOrientation() {
825 mOrientationManager.unlockOrientation();
829 public void notifyNewMedia(Uri uri) {
830 ContentResolver cr = getContentResolver();
831 String mimeType = cr.getType(uri);
832 if (mimeType.startsWith("video/")) {
833 sendBroadcast(new Intent(CameraUtil.ACTION_NEW_VIDEO, uri));
834 mDataAdapter.addNewVideo(uri);
835 } else if (mimeType.startsWith("image/")) {
836 CameraUtil.broadcastNewPicture(this, uri);
837 mDataAdapter.addNewPhoto(uri);
838 } else if (mimeType.startsWith(PlaceholderManager.PLACEHOLDER_MIME_TYPE)) {
839 mDataAdapter.addNewPhoto(uri);
841 android.util.Log.w(TAG, "Unknown new media with MIME type:"
842 + mimeType + ", uri:" + uri);
847 public void enableKeepScreenOn(boolean enabled) {
852 mKeepScreenOn = enabled;
854 mMainHandler.removeMessages(MSG_CLEAR_SCREEN_ON_FLAG);
855 getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
857 keepScreenOnForAWhile();
862 public CameraProvider getCameraProvider() {
863 return mCameraController;
866 private void removeData(int dataID) {
867 mDataAdapter.removeData(dataID);
868 if (mDataAdapter.getTotalNumber() > 1) {
869 showUndoDeletionBar();
871 // If camera preview is the only view left in filmstrip,
872 // no need to show undo bar.
873 mPendingDeletion = true;
879 public boolean onOptionsItemSelected(MenuItem item) {
880 // Handle presses on the action bar items
881 switch (item.getItemId()) {
882 case android.R.id.home:
883 if (mFilmstripVisible && IntentHelper.shouldLaunchGalleryOnUpAction()) {
890 return super.onOptionsItemSelected(item);
894 private boolean isCaptureIntent() {
895 if (MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction())
896 || MediaStore.ACTION_IMAGE_CAPTURE.equals(getIntent().getAction())
897 || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(getIntent().getAction())) {
905 public void onCreate(Bundle state) {
906 super.onCreate(state);
907 GcamHelper.init(getContentResolver());
909 getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
910 setContentView(R.layout.activity_main);
911 mActionBar = getActionBar();
912 mActionBar.addOnMenuVisibilityListener(this);
913 mMainHandler = new MainHandler(getMainLooper());
915 new CameraController(this, this, mMainHandler,
916 CameraManagerFactory.getAndroidCameraManager());
917 mPreferences = new ComboPreferences(getAndroidContext());
918 mContentResolver = this.getContentResolver();
920 mSettingsManager = new SettingsManager(this, mCameraController.getNumberOfCameras());
922 // Remove this after we get rid of ComboPreferences.
923 int cameraId = Integer.parseInt(mSettingsManager.get(SettingsManager.SETTING_CAMERA_ID));
924 mPreferences.setLocalId(this, cameraId);
925 CameraSettings.upgradeGlobalPreferences(mPreferences,
926 mCameraController.getNumberOfCameras());
927 // TODO: Try to move all the resources allocation to happen as soon as
928 // possible so we can call module.init() at the earliest time.
929 mModuleManager = new ModuleManagerImpl();
930 ModulesInfo.setupModules(this, mModuleManager);
932 mModeListView = (ModeListView) findViewById(R.id.mode_list_layout);
933 mModeListView.init(mModuleManager.getSupportedModeIndexList());
934 if (ApiHelper.HAS_ROTATION_ANIMATION) {
935 setRotationAnimation();
938 // Check if this is in the secure camera mode.
939 Intent intent = getIntent();
940 String action = intent.getAction();
941 if (INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action)
942 || ACTION_IMAGE_CAPTURE_SECURE.equals(action)) {
943 mSecureCamera = true;
945 mSecureCamera = intent.getBooleanExtra(SECURE_CAMERA_EXTRA, false);
949 // Change the window flags so that secure camera can show when locked
950 Window win = getWindow();
951 WindowManager.LayoutParams params = win.getAttributes();
952 params.flags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
953 win.setAttributes(params);
955 // Filter for screen off so that we can finish secure camera activity
956 // when screen is off.
957 IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
958 registerReceiver(mScreenOffReceiver, filter);
960 mCameraAppUI = new CameraAppUI(this,
961 (MainActivityLayout) findViewById(R.id.activity_root_view),
962 isSecureCamera(), isCaptureIntent());
964 mCameraAppUI.setFilmstripBottomControlsListener(mMyFilmstripBottomControlListener);
966 mAboveFilmstripControlLayout =
967 (FrameLayout) findViewById(R.id.camera_filmstrip_content_layout);
969 // Add the session listener so we can track the session progress updates.
970 getServices().getCaptureSessionManager().addSessionListener(mSessionListener);
971 mSessionProgressPanel = findViewById(R.id.pano_session_progress_panel);
972 mBottomProgressBar = (ProgressBar) findViewById(R.id.pano_session_progress_bar);
973 mBottomProgressText = (TextView) findViewById(R.id.pano_session_progress_text);
974 mFilmstripController = ((FilmstripView) findViewById(R.id.filmstrip_view)).getController();
975 mFilmstripController.setImageGap(
976 getResources().getDimensionPixelSize(R.dimen.camera_film_strip_gap));
977 mPanoramaViewHelper = new PanoramaViewHelper(this);
978 mPanoramaViewHelper.onCreate();
979 // Set up the camera preview first so the preview shows up ASAP.
980 mDataAdapter = new CameraDataAdapter(getApplicationContext(),
981 new ColorDrawable(getResources().getColor(R.color.photo_placeholder)));
982 mDataAdapter.setLocalDataListener(mLocalDataListener);
984 mCameraAppUI.getFilmstripContentPanel().setFilmstripListener(mFilmstripListener);
986 mLocationManager = new LocationManager(this);
988 mSettingsController = new SettingsController(this, mSettingsManager, mLocationManager);
991 if (MediaStore.INTENT_ACTION_VIDEO_CAMERA.equals(getIntent().getAction())
992 || MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction())) {
993 modeIndex = ModeListView.MODE_VIDEO;
994 } else if (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(getIntent().getAction())
995 || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(getIntent()
997 modeIndex = ModeListView.MODE_PHOTO;
998 if (mSettingsManager.getInt(SettingsManager.SETTING_STARTUP_MODULE_INDEX)
999 == ModeListView.MODE_GCAM && GcamHelper.hasGcamCapture()) {
1000 modeIndex = ModeListView.MODE_GCAM;
1002 } else if (MediaStore.ACTION_IMAGE_CAPTURE.equals(getIntent().getAction())
1003 || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(getIntent().getAction())) {
1004 modeIndex = ModeListView.MODE_PHOTO;
1006 // If the activity has not been started using an explicit intent,
1007 // read the module index from the last time the user changed modes
1008 modeIndex = mSettingsManager.getInt(SettingsManager.SETTING_STARTUP_MODULE_INDEX);
1009 if ((modeIndex == ModeListView.MODE_GCAM &&
1010 !GcamHelper.hasGcamCapture()) || modeIndex < 0) {
1011 modeIndex = ModeListView.MODE_PHOTO;
1015 mOrientationManager = new OrientationManagerImpl(this);
1016 mOrientationManager.addOnOrientationChangeListener(mMainHandler, this);
1018 setModuleFromModeIndex(modeIndex);
1020 // TODO: Remove this when refactor is done.
1021 if (modeIndex == ModulesInfo.MODULE_PHOTO
1022 || modeIndex == ModulesInfo.MODULE_VIDEO
1023 || modeIndex == ModulesInfo.MODULE_GCAM
1024 || modeIndex == ModulesInfo.MODULE_CRAFT
1025 || modeIndex == ModulesInfo.MODULE_REFOCUS) {
1026 mCameraAppUI.prepareModuleUI();
1029 syncBottomBarColor();
1030 mCurrentModule.init(this, isSecureCamera(), isCaptureIntent());
1032 if (!mSecureCamera) {
1033 mFilmstripController.setDataAdapter(mDataAdapter);
1034 if (!isCaptureIntent()) {
1035 mDataAdapter.requestLoad();
1038 // Put a lock placeholder as the last image by setting its date to
1040 ImageView v = (ImageView) getLayoutInflater().inflate(
1041 R.layout.secure_album_placeholder, null);
1042 v.setOnClickListener(new View.OnClickListener() {
1044 public void onClick(View view) {
1049 mDataAdapter = new FixedLastDataAdapter(
1050 getApplicationContext(),
1054 v.getDrawable().getIntrinsicWidth(),
1055 v.getDrawable().getIntrinsicHeight(),
1057 // Flush out all the original data.
1058 mDataAdapter.flush();
1059 mFilmstripController.setDataAdapter(mDataAdapter);
1064 mLocalImagesObserver = new LocalMediaObserver();
1065 mLocalVideosObserver = new LocalMediaObserver();
1067 getContentResolver().registerContentObserver(
1068 MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true,
1069 mLocalImagesObserver);
1070 getContentResolver().registerContentObserver(
1071 MediaStore.Video.Media.EXTERNAL_CONTENT_URI, true,
1072 mLocalVideosObserver);
1073 if (FeedbackHelper.feedbackAvailable()) {
1074 mFeedbackHelper = new FeedbackHelper(this);
1078 private void setRotationAnimation() {
1079 int rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
1080 rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE;
1081 Window win = getWindow();
1082 WindowManager.LayoutParams winParams = win.getAttributes();
1083 winParams.rotationAnimation = rotationAnimation;
1084 win.setAttributes(winParams);
1088 public void onUserInteraction() {
1089 super.onUserInteraction();
1090 if (!isFinishing()) {
1091 keepScreenOnForAWhile();
1096 public boolean dispatchTouchEvent(MotionEvent ev) {
1097 boolean result = super.dispatchTouchEvent(ev);
1098 if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
1099 // Real deletion is postponed until the next user interaction after
1100 // the gesture that triggers deletion. Until real deletion is performed,
1101 // users can click the undo button to bring back the image that they
1103 if (mPendingDeletion && !mIsUndoingDeletion) {
1111 public void onPause() {
1114 // Delete photos that are pending deletion
1116 mCurrentModule.pause();
1117 mOrientationManager.pause();
1118 // Close the camera and wait for the operation done.
1119 mCameraController.closeCamera();
1121 mLocalImagesObserver.setActivityPaused(true);
1122 mLocalVideosObserver.setActivityPaused(true);
1128 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
1129 if (requestCode == REQ_CODE_DONT_SWITCH_TO_PREVIEW) {
1130 mResetToPreviewOnResume = false;
1132 super.onActivityResult(requestCode, resultCode, data);
1137 public void onResume() {
1140 mLastLayoutOrientation = getResources().getConfiguration().orientation;
1142 // TODO: Handle this in OrientationManager.
1144 if (Settings.System.getInt(getContentResolver(),
1145 Settings.System.ACCELEROMETER_ROTATION, 0) == 0) {
1146 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
1147 mAutoRotateScreen = false;
1149 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
1150 mAutoRotateScreen = true;
1153 UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
1154 UsageStatistics.ACTION_FOREGROUNDED, this.getClass().getSimpleName());
1156 mOrientationManager.resume();
1158 mCurrentModule.resume();
1159 setSwipingEnabled(true);
1161 if (mResetToPreviewOnResume) {
1162 mCameraAppUI.resume();
1164 // The share button might be disabled to avoid double tapping.
1165 mCameraAppUI.getFilmstripBottomControls().setShareEnabled(true);
1166 // Default is showing the preview, unless disabled by explicitly
1167 // starting an activity we want to return from to the filmstrip rather
1168 // than the preview.
1169 mResetToPreviewOnResume = true;
1171 if (mLocalVideosObserver.isMediaDataChangedDuringPause()
1172 || mLocalImagesObserver.isMediaDataChangedDuringPause()) {
1173 if (!mSecureCamera) {
1174 // If it's secure camera, requestLoad() should not be called
1175 // as it will load all the data.
1176 if (!mFilmstripVisible) {
1177 mDataAdapter.requestLoad();
1181 mLocalImagesObserver.setActivityPaused(false);
1182 mLocalVideosObserver.setActivityPaused(false);
1184 keepScreenOnForAWhile();
1186 // Lights-out mode at all times.
1187 findViewById(R.id.activity_root_view).setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE);
1191 public void onStart() {
1193 mPanoramaViewHelper.onStart();
1194 boolean recordLocation = RecordLocationPreference.get(
1195 mPreferences, mContentResolver);
1196 mLocationManager.recordLocation(recordLocation);
1200 protected void onStop() {
1201 mPanoramaViewHelper.onStop();
1202 if (mFeedbackHelper != null) {
1203 mFeedbackHelper.stopFeedback();
1206 mLocationManager.disconnect();
1207 CameraManagerFactory.recycle();
1212 public void onDestroy() {
1213 if (mSecureCamera) {
1214 unregisterReceiver(mScreenOffReceiver);
1216 mSettingsManager.removeAllListeners();
1217 getContentResolver().unregisterContentObserver(mLocalImagesObserver);
1218 getContentResolver().unregisterContentObserver(mLocalVideosObserver);
1223 public void onConfigurationChanged(Configuration config) {
1224 super.onConfigurationChanged(config);
1225 Log.v(TAG, "onConfigurationChanged");
1226 if (config.orientation == Configuration.ORIENTATION_UNDEFINED) {
1230 if (mLastLayoutOrientation != config.orientation) {
1231 mLastLayoutOrientation = config.orientation;
1232 mCurrentModule.onLayoutOrientationChanged(
1233 mLastLayoutOrientation == Configuration.ORIENTATION_LANDSCAPE);
1238 public boolean onKeyDown(int keyCode, KeyEvent event) {
1239 if (!mFilmstripVisible) {
1240 if (mCurrentModule.onKeyDown(keyCode, event)) {
1243 // Prevent software keyboard or voice search from showing up.
1244 if (keyCode == KeyEvent.KEYCODE_SEARCH
1245 || keyCode == KeyEvent.KEYCODE_MENU) {
1246 if (event.isLongPress()) {
1252 return super.onKeyDown(keyCode, event);
1256 public boolean onKeyUp(int keyCode, KeyEvent event) {
1257 if (!mFilmstripVisible && mCurrentModule.onKeyUp(keyCode, event)) {
1260 return super.onKeyUp(keyCode, event);
1264 public void onBackPressed() {
1265 if (!mCameraAppUI.onBackPressed()) {
1266 if (!mCurrentModule.onBackPressed()) {
1267 super.onBackPressed();
1272 public boolean isAutoRotateScreen() {
1273 return mAutoRotateScreen;
1276 protected void updateStorageSpace() {
1277 mStorageSpaceBytes = Storage.getAvailableSpace();
1280 protected long getStorageSpaceBytes() {
1281 return mStorageSpaceBytes;
1284 protected void updateStorageSpaceAndHint() {
1285 updateStorageSpace();
1286 updateStorageHint(mStorageSpaceBytes);
1289 protected void updateStorageHint(long storageSpace) {
1290 String message = null;
1291 if (storageSpace == Storage.UNAVAILABLE) {
1292 message = getString(R.string.no_storage);
1293 } else if (storageSpace == Storage.PREPARING) {
1294 message = getString(R.string.preparing_sd);
1295 } else if (storageSpace == Storage.UNKNOWN_SIZE) {
1296 message = getString(R.string.access_sd_fail);
1297 } else if (storageSpace <= Storage.LOW_STORAGE_THRESHOLD_BYTES) {
1298 message = getString(R.string.spaceIsLow_content);
1301 if (message != null) {
1302 if (mStorageHint == null) {
1303 mStorageHint = OnScreenHint.makeText(this, message);
1305 mStorageHint.setText(message);
1307 mStorageHint.show();
1308 } else if (mStorageHint != null) {
1309 mStorageHint.cancel();
1310 mStorageHint = null;
1314 protected void setResultEx(int resultCode) {
1315 mResultCodeForTesting = resultCode;
1316 setResult(resultCode);
1319 protected void setResultEx(int resultCode, Intent data) {
1320 mResultCodeForTesting = resultCode;
1321 mResultDataForTesting = data;
1322 setResult(resultCode, data);
1325 public int getResultCode() {
1326 return mResultCodeForTesting;
1329 public Intent getResultData() {
1330 return mResultDataForTesting;
1333 public boolean isSecureCamera() {
1334 return mSecureCamera;
1338 public boolean isPaused() {
1343 public void onModeSelected(int modeIndex) {
1344 if (mCurrentModeIndex == modeIndex) {
1348 if (modeIndex == ModeListView.MODE_SETTING) {
1349 onSettingsSelected();
1353 closeModule(mCurrentModule);
1354 int oldModuleIndex = mCurrentModeIndex;
1356 // Refocus and Gcam are modes that cannot be selected
1357 // from the mode list view, because they are not list items.
1358 // Check whether we should interpret MODULE_CRAFT as either.
1359 if (modeIndex == ModulesInfo.MODULE_CRAFT) {
1360 boolean refocusOn = mSettingsManager.isRefocusOn();
1361 boolean hdrPlusOn = mSettingsManager.isHdrPlusOn();
1362 if (refocusOn && hdrPlusOn) {
1363 throw new IllegalStateException("Refocus and hdr plus cannot be on together.");
1366 modeIndex = ModulesInfo.MODULE_REFOCUS;
1367 } else if (hdrPlusOn) {
1368 modeIndex = ModulesInfo.MODULE_GCAM;
1370 // Do nothing, keep MODULE_CRAFT.
1374 setModuleFromModeIndex(modeIndex);
1376 // TODO: The following check is temporary for modules attached to the
1377 // generic_module layout. When the refactor is done, similar logic will
1378 // be applied to all modules.
1379 if (mCurrentModeIndex == ModulesInfo.MODULE_PHOTO
1380 || mCurrentModeIndex == ModulesInfo.MODULE_VIDEO
1381 || mCurrentModeIndex == ModulesInfo.MODULE_GCAM
1382 || mCurrentModeIndex == ModulesInfo.MODULE_CRAFT
1383 || mCurrentModeIndex == ModulesInfo.MODULE_REFOCUS) {
1384 if (oldModuleIndex != ModulesInfo.MODULE_PHOTO
1385 && oldModuleIndex != ModulesInfo.MODULE_VIDEO
1386 && oldModuleIndex != ModulesInfo.MODULE_GCAM
1387 && oldModuleIndex != ModulesInfo.MODULE_CRAFT
1388 && oldModuleIndex != ModulesInfo.MODULE_REFOCUS) {
1389 mCameraAppUI.prepareModuleUI();
1391 mCameraAppUI.clearModuleUI();
1394 // This is the old way of removing all views in CameraRootView. Will
1395 // be deprecated soon. It is here to make sure modules that haven't
1396 // been refactored can still function.
1397 mCameraAppUI.clearCameraUI();
1400 openModule(mCurrentModule);
1401 mCurrentModule.onOrientationChanged(mLastRawOrientation);
1402 // Store the module index so we can use it the next time the Camera
1404 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
1405 prefs.edit().putInt(CameraSettings.KEY_STARTUP_MODULE_INDEX, modeIndex).apply();
1408 public void onSettingsSelected() {
1409 // Temporary until we finalize the touch flow.
1410 LayoutInflater inflater = getLayoutInflater();
1411 SettingsView settingsView = (SettingsView) inflater.inflate(R.layout.settings_list_layout,
1413 settingsView.setSettingsListener(mSettingsController);
1414 if (mFeedbackHelper != null) {
1415 settingsView.setFeedbackHelper(mFeedbackHelper);
1417 PopupWindow popup = new PopupWindow(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
1418 popup.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
1419 popup.setOutsideTouchable(true);
1420 popup.setFocusable(true);
1421 popup.setContentView(settingsView);
1422 popup.showAtLocation(mModeListView.getRootView(), Gravity.CENTER, 0, 0);
1426 * Sets the mCurrentModuleIndex, creates a new module instance for the given
1427 * index an sets it as mCurrentModule.
1429 private void setModuleFromModeIndex(int modeIndex) {
1430 ModuleManagerImpl.ModuleAgent agent = mModuleManager.getModuleAgent(modeIndex);
1431 if (agent == null) {
1434 if (!agent.requestAppForCamera()) {
1435 mCameraController.closeCamera();
1437 mCurrentModeIndex = agent.getModuleId();
1438 mCurrentModule = (CameraModule) agent.createModule(this);
1442 public SettingsManager getSettingsManager() {
1443 return mSettingsManager;
1447 public CameraServices getServices() {
1448 return (CameraServices) getApplication();
1452 public SettingsController getSettingsController() {
1453 return mSettingsController;
1457 public ButtonManager getButtonManager() {
1458 if (mButtonManager == null) {
1459 mButtonManager = new ButtonManager(this);
1461 return mButtonManager;
1465 * Creates an AlertDialog appropriate for choosing whether to enable location
1466 * on the first run of the app.
1468 public AlertDialog getFirstTimeLocationAlert() {
1469 AlertDialog.Builder builder = new AlertDialog.Builder(this);
1470 builder = SettingsView.getFirstTimeLocationAlertBuilder(builder, mSettingsController);
1471 if (builder != null) {
1472 return builder.create();
1479 * Launches an ACTION_EDIT intent for the given local data item.
1481 public void launchEditor(LocalData data) {
1482 Intent intent = new Intent(Intent.ACTION_EDIT)
1483 .setDataAndType(data.getContentUri(), data.getMimeType())
1484 .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
1486 launchActivityByIntent(intent);
1487 } catch (ActivityNotFoundException e) {
1488 launchActivityByIntent(Intent.createChooser(intent, null));
1493 * Launch the tiny planet editor.
1495 * @param data The data must be a 360 degree stereographically mapped
1496 * panoramic image. It will not be modified, instead a new item
1497 * with the result will be added to the filmstrip.
1499 public void launchTinyPlanetEditor(LocalData data) {
1500 TinyPlanetFragment fragment = new TinyPlanetFragment();
1501 Bundle bundle = new Bundle();
1502 bundle.putString(TinyPlanetFragment.ARGUMENT_URI, data.getContentUri().toString());
1503 bundle.putString(TinyPlanetFragment.ARGUMENT_TITLE, data.getTitle());
1504 fragment.setArguments(bundle);
1505 fragment.show(getFragmentManager(), "tiny_planet");
1508 private void syncBottomBarColor() {
1509 // Currently not all modules use the generic_module UI.
1510 // TODO: once all modules have a bottom bar, remove
1511 // isUsingBottomBar check.
1512 if (mCurrentModule.isUsingBottomBar()) {
1513 int colorResId = mModeListView.getModeThemeColor(mCurrentModeIndex);
1514 mCameraAppUI.setBottomBarColor(getResources().getColor(colorResId));
1518 private void openModule(CameraModule module) {
1519 syncBottomBarColor();
1520 module.init(this, isSecureCamera(), isCaptureIntent());
1522 module.onPreviewVisibilityChanged(!mFilmstripVisible);
1525 private void closeModule(CameraModule module) {
1529 private void performDeletion() {
1530 if (!mPendingDeletion) {
1533 hideUndoDeletionBar(false);
1534 mDataAdapter.executeDeletion();
1537 public void showUndoDeletionBar() {
1538 if (mPendingDeletion) {
1541 Log.v(TAG, "showing undo bar");
1542 mPendingDeletion = true;
1543 if (mUndoDeletionBar == null) {
1544 ViewGroup v = (ViewGroup) getLayoutInflater().inflate(R.layout.undo_bar,
1545 mAboveFilmstripControlLayout, true);
1546 mUndoDeletionBar = (ViewGroup) v.findViewById(R.id.camera_undo_deletion_bar);
1547 View button = mUndoDeletionBar.findViewById(R.id.camera_undo_deletion_button);
1548 button.setOnClickListener(new View.OnClickListener() {
1550 public void onClick(View view) {
1551 mDataAdapter.undoDataRemoval();
1552 hideUndoDeletionBar(true);
1555 // Setting undo bar clickable to avoid touch events going through
1556 // the bar to the buttons (eg. edit button, etc) underneath the bar.
1557 mUndoDeletionBar.setClickable(true);
1558 // When there is user interaction going on with the undo button, we
1559 // do not want to hide the undo bar.
1560 button.setOnTouchListener(new View.OnTouchListener() {
1562 public boolean onTouch(View v, MotionEvent event) {
1563 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
1564 mIsUndoingDeletion = true;
1565 } else if (event.getActionMasked() == MotionEvent.ACTION_UP) {
1566 mIsUndoingDeletion = false;
1572 mUndoDeletionBar.setAlpha(0f);
1573 mUndoDeletionBar.setVisibility(View.VISIBLE);
1574 mUndoDeletionBar.animate().setDuration(200).alpha(1f).setListener(null).start();
1577 private void hideUndoDeletionBar(boolean withAnimation) {
1578 Log.v(TAG, "Hiding undo deletion bar");
1579 mPendingDeletion = false;
1580 if (mUndoDeletionBar != null) {
1581 if (withAnimation) {
1582 mUndoDeletionBar.animate().setDuration(200).alpha(0f)
1583 .setListener(new Animator.AnimatorListener() {
1585 public void onAnimationStart(Animator animation) {
1590 public void onAnimationEnd(Animator animation) {
1591 mUndoDeletionBar.setVisibility(View.GONE);
1595 public void onAnimationCancel(Animator animation) {
1600 public void onAnimationRepeat(Animator animation) {
1605 mUndoDeletionBar.setVisibility(View.GONE);
1611 public void onOrientationChanged(int orientation) {
1612 // We keep the last known orientation. So if the user first orient
1613 // the camera then point the camera to floor or sky, we still have
1614 // the correct orientation.
1615 if (orientation == OrientationManager.ORIENTATION_UNKNOWN) {
1618 mLastRawOrientation = orientation;
1619 if (mCurrentModule != null) {
1620 mCurrentModule.onOrientationChanged(orientation);
1625 * Enable/disable swipe-to-filmstrip. Will always disable swipe if in
1628 * @param enable {@code true} to enable swipe.
1630 public void setSwipingEnabled(boolean enable) {
1631 // TODO: Bring back the functionality.
1632 if (isCaptureIntent()) {
1633 //lockPreview(true);
1635 //lockPreview(!enable);
1639 // Accessor methods for getting latency times used in performance testing
1640 public long getAutoFocusTime() {
1641 return (mCurrentModule instanceof PhotoModule) ?
1642 ((PhotoModule) mCurrentModule).mAutoFocusTime : -1;
1645 public long getShutterLag() {
1646 return (mCurrentModule instanceof PhotoModule) ?
1647 ((PhotoModule) mCurrentModule).mShutterLag : -1;
1650 public long getShutterToPictureDisplayedTime() {
1651 return (mCurrentModule instanceof PhotoModule) ?
1652 ((PhotoModule) mCurrentModule).mShutterToPictureDisplayedTime : -1;
1655 public long getPictureDisplayedToJpegCallbackTime() {
1656 return (mCurrentModule instanceof PhotoModule) ?
1657 ((PhotoModule) mCurrentModule).mPictureDisplayedToJpegCallbackTime : -1;
1660 public long getJpegCallbackFinishTime() {
1661 return (mCurrentModule instanceof PhotoModule) ?
1662 ((PhotoModule) mCurrentModule).mJpegCallbackFinishTime : -1;
1665 public long getCaptureStartTime() {
1666 return (mCurrentModule instanceof PhotoModule) ?
1667 ((PhotoModule) mCurrentModule).mCaptureStartTime : -1;
1670 public boolean isRecording() {
1671 return (mCurrentModule instanceof VideoModule) ?
1672 ((VideoModule) mCurrentModule).isRecording() : false;
1675 public CameraManager.CameraOpenCallback getCameraOpenErrorCallback() {
1676 return mCameraController;
1679 // For debugging purposes only.
1680 public CameraModule getCurrentModule() {
1681 return mCurrentModule;
1684 private void keepScreenOnForAWhile() {
1685 if (mKeepScreenOn) {
1688 mMainHandler.removeMessages(MSG_CLEAR_SCREEN_ON_FLAG);
1689 getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1690 mMainHandler.sendEmptyMessageDelayed(MSG_CLEAR_SCREEN_ON_FLAG, SCREEN_DELAY_MS);
1693 private void resetScreenOn() {
1694 mKeepScreenOn = false;
1695 mMainHandler.removeMessages(MSG_CLEAR_SCREEN_ON_FLAG);
1696 getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1699 private void startGallery() {
1701 UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
1702 UsageStatistics.ACTION_GALLERY, null);
1703 launchActivityByIntent(IntentHelper.getGalleryIntent(CameraActivity.this));
1704 } catch (ActivityNotFoundException e) {
1705 Log.w(TAG, "Failed to launch gallery activity, closing");
1709 private void setNfcBeamPushUriFromData(LocalData data) {
1710 final Uri uri = data.getContentUri();
1711 if (uri != Uri.EMPTY) {
1712 mNfcPushUris[0] = uri;
1714 mNfcPushUris[0] = null;
1719 * Updates the visibility of the filmstrip bottom controls.
1721 private void updateUiByData(final int dataId) {
1722 if (isSecureCamera()) {
1723 // We cannot show buttons in secure camera since go to other
1724 // activities might create a security hole.
1728 final LocalData currentData = mDataAdapter.getLocalData(dataId);
1729 if (currentData == null) {
1730 Log.w(TAG, "Current data ID not found.");
1731 hideSessionProgress();
1735 setNfcBeamPushUriFromData(currentData);
1737 /* Bottom controls. */
1739 updateBottomControlsByData(currentData);
1740 if (!mDataAdapter.isMetadataUpdated(dataId)) {
1741 mDataAdapter.updateMetadata(dataId);
1746 * Updates the bottom controls based on the data.
1748 private void updateBottomControlsByData(final LocalData currentData) {
1750 final CameraAppUI.BottomControls filmstripBottomControls =
1751 mCameraAppUI.getFilmstripBottomControls();
1752 filmstripBottomControls.setEditButtonVisibility(
1753 currentData.isDataActionSupported(LocalData.DATA_ACTION_EDIT));
1754 filmstripBottomControls.setShareButtonVisibility(
1755 currentData.isDataActionSupported(LocalData.DATA_ACTION_SHARE));
1756 filmstripBottomControls.setDeleteButtonVisibility(
1757 currentData.isDataActionSupported(LocalData.DATA_ACTION_DELETE));
1761 Uri contentUri = currentData.getContentUri();
1762 CaptureSessionManager sessionManager = getServices()
1763 .getCaptureSessionManager();
1764 int sessionProgress = sessionManager.getSessionProgress(contentUri);
1766 if (sessionProgress < 0) {
1767 hideSessionProgress();
1769 CharSequence progressMessage = sessionManager
1770 .getSessionProgressMessage(contentUri);
1771 showSessionProgress(progressMessage);
1772 updateSessionProgress(sessionProgress);
1777 // We need to add this to a separate DB.
1778 final int viewButtonVisibility;
1779 if (PanoramaMetadataLoader.isPanorama(currentData)) {
1780 viewButtonVisibility = CameraAppUI.BottomControls.VIEW_PHOTO_SPHERE;
1781 } else if (RgbzMetadataLoader.hasRGBZData(currentData)) {
1782 viewButtonVisibility = CameraAppUI.BottomControls.VIEW_RGBZ;
1784 viewButtonVisibility = CameraAppUI.BottomControls.VIEW_NONE;
1787 filmstripBottomControls.setTinyPlanetButtonVisibility(
1788 PanoramaMetadataLoader.isPanorama360(currentData));
1789 filmstripBottomControls.setViewButtonVisibility(viewButtonVisibility);