OSDN Git Service

c5c120a8c4b326f5cc76575d8d1d7e9e325a3440
[android-x86/packages-apps-Camera2.git] / src / com / android / camera / CameraActivity.java
1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.android.camera;
18
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.app.Dialog;
25 import android.content.ActivityNotFoundException;
26 import android.content.BroadcastReceiver;
27 import android.content.ContentResolver;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.IntentFilter;
31 import android.content.SharedPreferences;
32 import android.content.pm.ActivityInfo;
33 import android.content.pm.PackageManager;
34 import android.content.res.Configuration;
35 import android.graphics.Bitmap;
36 import android.graphics.Matrix;
37 import android.graphics.Point;
38 import android.graphics.SurfaceTexture;
39 import android.graphics.drawable.ColorDrawable;
40 import android.graphics.drawable.Drawable;
41 import android.net.Uri;
42 import android.nfc.NfcAdapter;
43 import android.nfc.NfcAdapter.CreateBeamUrisCallback;
44 import android.nfc.NfcEvent;
45 import android.os.Build;
46 import android.os.Bundle;
47 import android.os.Handler;
48 import android.os.HandlerThread;
49 import android.os.Looper;
50 import android.os.Message;
51 import android.preference.PreferenceManager;
52 import android.provider.MediaStore;
53 import android.provider.Settings;
54 import android.util.CameraPerformanceTracker;
55 import android.util.Log;
56 import android.view.ContextMenu;
57 import android.view.ContextMenu.ContextMenuInfo;
58 import android.view.KeyEvent;
59 import android.view.Menu;
60 import android.view.MenuInflater;
61 import android.view.MenuItem;
62 import android.view.MotionEvent;
63 import android.view.View;
64 import android.view.ViewGroup;
65 import android.view.Window;
66 import android.view.WindowManager;
67 import android.widget.FrameLayout;
68 import android.widget.ImageView;
69 import android.widget.ShareActionProvider;
70
71 import com.android.camera.app.AppController;
72 import com.android.camera.app.CameraAppUI;
73 import com.android.camera.app.CameraController;
74 import com.android.camera.app.CameraManager;
75 import com.android.camera.app.CameraManagerFactory;
76 import com.android.camera.app.CameraProvider;
77 import com.android.camera.app.CameraServices;
78 import com.android.camera.app.LocationManager;
79 import com.android.camera.app.ModuleManagerImpl;
80 import com.android.camera.app.OrientationManager;
81 import com.android.camera.app.OrientationManagerImpl;
82 import com.android.camera.data.CameraDataAdapter;
83 import com.android.camera.data.FixedLastDataAdapter;
84 import com.android.camera.data.InProgressDataWrapper;
85 import com.android.camera.data.LocalData;
86 import com.android.camera.data.LocalDataAdapter;
87 import com.android.camera.data.LocalDataUtil;
88 import com.android.camera.data.LocalMediaObserver;
89 import com.android.camera.data.MediaDetails;
90 import com.android.camera.data.PanoramaMetadataLoader;
91 import com.android.camera.data.RgbzMetadataLoader;
92 import com.android.camera.data.SimpleViewData;
93 import com.android.camera.filmstrip.FilmstripContentPanel;
94 import com.android.camera.filmstrip.FilmstripController;
95 import com.android.camera.hardware.HardwareSpec;
96 import com.android.camera.hardware.HardwareSpecImpl;
97 import com.android.camera.module.ModuleController;
98 import com.android.camera.module.ModulesInfo;
99 import com.android.camera.session.CaptureSessionManager;
100 import com.android.camera.session.CaptureSessionManager.SessionListener;
101 import com.android.camera.settings.CameraSettingsActivity;
102 import com.android.camera.settings.SettingsManager;
103 import com.android.camera.settings.SettingsManager.SettingsCapabilities;
104 import com.android.camera.settings.SettingsUtil;
105 import com.android.camera.tinyplanet.TinyPlanetFragment;
106 import com.android.camera.ui.DetailsDialog;
107 import com.android.camera.ui.MainActivityLayout;
108 import com.android.camera.ui.ModeListView;
109 import com.android.camera.ui.ModeListView.ModeListVisibilityChangedListener;
110 import com.android.camera.ui.PreviewStatusListener;
111 import com.android.camera.util.ApiHelper;
112 import com.android.camera.util.Callback;
113 import com.android.camera.util.CameraUtil;
114 import com.android.camera.util.FeedbackHelper;
115 import com.android.camera.util.GalleryHelper;
116 import com.android.camera.util.GcamHelper;
117 import com.android.camera.util.IntentHelper;
118 import com.android.camera.util.PhotoSphereHelper.PanoramaViewHelper;
119 import com.android.camera.util.ReleaseDialogHelper;
120 import com.android.camera.util.UsageStatistics;
121 import com.android.camera.widget.FilmstripView;
122 import com.android.camera2.R;
123 import com.google.common.logging.eventprotos;
124 import com.google.common.logging.eventprotos.CameraEvent.InteractionCause;
125 import com.google.common.logging.eventprotos.NavigationChange;
126
127 import java.io.File;
128 import java.io.FileInputStream;
129 import java.io.FileNotFoundException;
130 import java.lang.ref.WeakReference;
131 import java.util.ArrayList;
132 import java.util.List;
133
134 public class CameraActivity extends Activity
135         implements AppController, CameraManager.CameraOpenCallback,
136         ActionBar.OnMenuVisibilityListener, ShareActionProvider.OnShareTargetSelectedListener,
137         OrientationManager.OnOrientationChangeListener {
138
139     private static final String TAG = "CameraActivity";
140
141     private static final String INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE =
142             "android.media.action.STILL_IMAGE_CAMERA_SECURE";
143     public static final String ACTION_IMAGE_CAPTURE_SECURE =
144             "android.media.action.IMAGE_CAPTURE_SECURE";
145
146     // The intent extra for camera from secure lock screen. True if the gallery
147     // should only show newly captured pictures. sSecureAlbumId does not
148     // increment. This is used when switching between camera, camcorder, and
149     // panorama. If the extra is not set, it is in the normal camera mode.
150     public static final String SECURE_CAMERA_EXTRA = "secure_camera";
151
152     /**
153      * Request code from an activity we started that indicated that we do not
154      * want to reset the view to the preview in onResume.
155      */
156     public static final int REQ_CODE_DONT_SWITCH_TO_PREVIEW = 142;
157
158     public static final int REQ_CODE_GCAM_DEBUG_POSTCAPTURE = 999;
159
160     private static final int MSG_CLEAR_SCREEN_ON_FLAG = 2;
161     private static final long SCREEN_DELAY_MS = 2 * 60 * 1000; // 2 mins.
162     private static final int MAX_PEEK_BITMAP_PIXELS = 1600000; // 1.6 * 4 MBs.
163
164     /** Should be used wherever a context is needed. */
165     private Context mAppContext;
166
167     /**
168      * Whether onResume should reset the view to the preview.
169      */
170     private boolean mResetToPreviewOnResume = true;
171
172     /**
173      * This data adapter is used by FilmStripView.
174      */
175     private LocalDataAdapter mDataAdapter;
176
177     /**
178      * TODO: This should be moved to the app level.
179      */
180     private SettingsManager mSettingsManager;
181
182     private ModeListView mModeListView;
183     private int mCurrentModeIndex;
184     private CameraModule mCurrentModule;
185     private ModuleManagerImpl mModuleManager;
186     private FrameLayout mAboveFilmstripControlLayout;
187     private FilmstripController mFilmstripController;
188     private boolean mFilmstripVisible;
189     private int mResultCodeForTesting;
190     private Intent mResultDataForTesting;
191     private OnScreenHint mStorageHint;
192     private long mStorageSpaceBytes = Storage.LOW_STORAGE_THRESHOLD_BYTES;
193     private boolean mAutoRotateScreen;
194     private boolean mSecureCamera;
195     private int mLastRawOrientation;
196     private OrientationManagerImpl mOrientationManager;
197     private LocationManager mLocationManager;
198     private ButtonManager mButtonManager;
199     private Handler mMainHandler;
200     private PanoramaViewHelper mPanoramaViewHelper;
201     private ActionBar mActionBar;
202     private ViewGroup mUndoDeletionBar;
203     private boolean mIsUndoingDeletion = false;
204
205     private final Uri[] mNfcPushUris = new Uri[1];
206
207     private LocalMediaObserver mLocalImagesObserver;
208     private LocalMediaObserver mLocalVideosObserver;
209
210     private boolean mPendingDeletion = false;
211
212     private CameraController mCameraController;
213     private boolean mPaused;
214     private CameraAppUI mCameraAppUI;
215
216     private PeekAnimationHandler mPeekAnimationHandler;
217     private HandlerThread mPeekAnimationThread;
218
219     private FeedbackHelper mFeedbackHelper;
220
221     private Intent mGalleryIntent;
222     private long mOnCreateTime;
223
224     private Menu mActionBarMenu;
225
226     @Override
227     public CameraAppUI getCameraAppUI() {
228         return mCameraAppUI;
229     }
230
231     // close activity when screen turns off
232     private final BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() {
233         @Override
234         public void onReceive(Context context, Intent intent) {
235             finish();
236         }
237     };
238
239     /**
240      * Whether the screen is kept turned on.
241      */
242     private boolean mKeepScreenOn;
243     private int mLastLayoutOrientation;
244     private final CameraAppUI.BottomPanel.Listener mMyFilmstripBottomControlListener =
245             new CameraAppUI.BottomPanel.Listener() {
246
247                 /**
248                  * If the current photo is a photo sphere, this will launch the
249                  * Photo Sphere panorama viewer.
250                  */
251                 @Override
252                 public void onExternalViewer() {
253                     if (mPanoramaViewHelper == null) {
254                         return;
255                     }
256                     final LocalData data = getCurrentLocalData();
257                     if (data == null) {
258                         return;
259                     }
260                     final Uri contentUri = data.getContentUri();
261                     if (contentUri == Uri.EMPTY) {
262                         return;
263                     }
264
265                     if (PanoramaMetadataLoader.isPanoramaAndUseViewer(data)) {
266                         mPanoramaViewHelper.showPanorama(CameraActivity.this, contentUri);
267                     } else if (RgbzMetadataLoader.hasRGBZData(data)) {
268                         mPanoramaViewHelper.showRgbz(contentUri);
269                     }
270                 }
271
272                 @Override
273                 public void onEdit() {
274                     LocalData data = getCurrentLocalData();
275                     if (data == null) {
276                         return;
277                     }
278                     launchEditor(data);
279                 }
280
281                 @Override
282                 public void onTinyPlanet() {
283                     LocalData data = getCurrentLocalData();
284                     if (data == null) {
285                         return;
286                     }
287                     launchTinyPlanetEditor(data);
288                 }
289
290                 @Override
291                 public void onDelete() {
292                     final int currentDataId = getCurrentDataId();
293                     UsageStatistics.photoInteraction(
294                             UsageStatistics.hashFileName(fileNameFromDataID(currentDataId)),
295                             eventprotos.CameraEvent.InteractionType.DELETE,
296                             InteractionCause.BUTTON);
297                     removeData(currentDataId);
298                 }
299
300                 @Override
301                 public void onShare() {
302                     final LocalData data = getCurrentLocalData();
303
304                     // If applicable, show release information before this item
305                     // is shared.
306                     if (PanoramaMetadataLoader.isPanorama(data)
307                             || RgbzMetadataLoader.hasRGBZData(data)) {
308                         ReleaseDialogHelper.showReleaseInfoDialog(CameraActivity.this,
309                                 new Callback<Void>() {
310                                     @Override
311                                     public void onCallback(Void result) {
312                                         share(data);
313                                     }
314                                 });
315                     } else {
316                         share(data);
317                     }
318                 }
319
320                 private void share(LocalData data) {
321                     Intent shareIntent = getShareIntentByData(data);
322                     if (shareIntent != null) {
323                         try {
324                             launchActivityByIntent(shareIntent);
325                             mCameraAppUI.getFilmstripBottomControls().setShareEnabled(false);
326                         } catch (ActivityNotFoundException ex) {
327                             // Nothing.
328                         }
329                     }
330                 }
331
332                 private int getCurrentDataId() {
333                     return mFilmstripController.getCurrentId();
334                 }
335
336                 private LocalData getCurrentLocalData() {
337                     return mDataAdapter.getLocalData(getCurrentDataId());
338                 }
339
340                 /**
341                  * Sets up the share intent and NFC properly according to the
342                  * data.
343                  *
344                  * @param data The data to be shared.
345                  */
346                 private Intent getShareIntentByData(final LocalData data) {
347                     Intent intent = null;
348                     final Uri contentUri = data.getContentUri();
349                     if (PanoramaMetadataLoader.isPanorama360(data) &&
350                             data.getContentUri() != Uri.EMPTY) {
351                         intent = new Intent(Intent.ACTION_SEND);
352                         intent.setType("application/vnd.google.panorama360+jpg");
353                         intent.putExtra(Intent.EXTRA_STREAM, contentUri);
354                     } else if (data.isDataActionSupported(LocalData.DATA_ACTION_SHARE)) {
355                         final String mimeType = data.getMimeType();
356                         intent = getShareIntentFromType(mimeType);
357                         if (intent != null) {
358                             intent.putExtra(Intent.EXTRA_STREAM, contentUri);
359                             intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
360                         }
361                         intent = Intent.createChooser(intent, null);
362                     }
363                     return intent;
364                 }
365
366                 /**
367                  * Get the share intent according to the mimeType
368                  *
369                  * @param mimeType The mimeType of current data.
370                  * @return the video/image's ShareIntent or null if mimeType is
371                  *         invalid.
372                  */
373                 private Intent getShareIntentFromType(String mimeType) {
374                     // Lazily create the intent object.
375                     Intent intent = new Intent(Intent.ACTION_SEND);
376                     if (mimeType.startsWith("video/")) {
377                         intent.setType("video/*");
378                     } else {
379                         if (mimeType.startsWith("image/")) {
380                             intent.setType("image/*");
381                         } else {
382                             Log.w(TAG, "unsupported mimeType " + mimeType);
383                         }
384                     }
385                     return intent;
386                 }
387             };
388
389     private ComboPreferences mPreferences;
390
391     @Override
392     public void onCameraOpened(CameraManager.CameraProxy camera) {
393         /**
394          * The current UI requires that the flash option visibility in front-facing
395          * camera be
396          *   * disabled if back facing camera supports flash
397          *   * hidden if back facing camera does not support flash
398          * We save whether back facing camera supports flash because we cannot get
399          * this in front facing camera without a camera switch.
400          *
401          * If this preference is cleared, we also need to clear the camera facing
402          * setting so we default to opening the camera in back facing camera, and
403          * can save this flash support value again.
404          */
405         if (!mSettingsManager.isSet(SettingsManager.SETTING_FLASH_SUPPORTED_BACK_CAMERA)) {
406             HardwareSpec hardware = new HardwareSpecImpl(camera.getParameters());
407             mSettingsManager.setBoolean(SettingsManager.SETTING_FLASH_SUPPORTED_BACK_CAMERA,
408                 hardware.isFlashSupported());
409         }
410
411         if (!mModuleManager.getModuleAgent(mCurrentModeIndex).requestAppForCamera()) {
412             // We shouldn't be here. Just close the camera and leave.
413             camera.release(false);
414             throw new IllegalStateException("Camera opened but the module shouldn't be " +
415                     "requesting");
416         }
417         if (mCurrentModule != null) {
418             SettingsCapabilities capabilities =
419                     SettingsUtil.getSettingsCapabilities(camera);
420             mSettingsManager.changeCamera(camera.getCameraId(), capabilities);
421             mCurrentModule.onCameraAvailable(camera);
422         }
423         mCameraAppUI.onChangeCamera();
424     }
425
426     @Override
427     public void onCameraDisabled(int cameraId) {
428         UsageStatistics.cameraFailure(eventprotos.CameraFailure.FailureReason.SECURITY);
429
430         CameraUtil.showErrorAndFinish(this, R.string.camera_disabled);
431     }
432
433     @Override
434     public void onDeviceOpenFailure(int cameraId) {
435         UsageStatistics.cameraFailure(eventprotos.CameraFailure.FailureReason.OPEN_FAILURE);
436
437         CameraUtil.showErrorAndFinish(this, R.string.cannot_connect_camera);
438     }
439
440     @Override
441     public void onDeviceOpenedAlready(int cameraId) {
442         CameraUtil.showErrorAndFinish(this, R.string.cannot_connect_camera);
443     }
444
445     @Override
446     public void onReconnectionFailure(CameraManager mgr) {
447         UsageStatistics.cameraFailure(eventprotos.CameraFailure.FailureReason.RECONNECT_FAILURE);
448
449         CameraUtil.showErrorAndFinish(this, R.string.cannot_connect_camera);
450     }
451
452     private static class MainHandler extends Handler {
453         final WeakReference<CameraActivity> mActivity;
454
455         public MainHandler(CameraActivity activity, Looper looper) {
456             super(looper);
457             mActivity = new WeakReference<CameraActivity>(activity);
458         }
459
460         @Override
461         public void handleMessage(Message msg) {
462             CameraActivity activity = mActivity.get();
463             if (activity == null) {
464                 return;
465             }
466             switch (msg.what) {
467
468                 case MSG_CLEAR_SCREEN_ON_FLAG: {
469                     if (!activity.mPaused) {
470                         activity.getWindow().clearFlags(
471                                 WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
472                     }
473                     break;
474                 }
475             }
476         }
477     }
478
479     private String fileNameFromDataID(int dataID) {
480         final LocalData localData = mDataAdapter.getLocalData(dataID);
481
482         File localFile = new File(localData.getPath());
483         return localFile.getName();
484     }
485
486     private final FilmstripContentPanel.Listener mFilmstripListener =
487             new FilmstripContentPanel.Listener() {
488
489                 @Override
490                 public void onSwipeOut() {
491                     UsageStatistics.changeScreen(eventprotos.NavigationChange.Mode.PHOTO_CAPTURE,
492                             eventprotos.CameraEvent.InteractionCause.SWIPE_RIGHT);
493                 }
494
495                 @Override
496                 public void onSwipeOutBegin() {
497                     mActionBar.hide();
498                 }
499
500                 @Override
501                 public void onFilmstripHidden() {
502                     mFilmstripVisible = false;
503                     CameraActivity.this.setFilmstripUiVisibility(false);
504                     // When the user hide the filmstrip (either swipe out or
505                     // tap on back key) we move to the first item so next time
506                     // when the user swipe in the filmstrip, the most recent
507                     // one is shown.
508                     mFilmstripController.goToFirstItem();
509                     if (mCurrentModule != null) {
510                         mCurrentModule
511                                 .onPreviewVisibilityChanged(ModuleController.VISIBILITY_VISIBLE);
512                     }
513                 }
514
515                 @Override
516                 public void onFilmstripShown() {
517                     mFilmstripVisible = true;
518                     decrementPeekAnimPlayTimes();
519                     updateUiByData(mFilmstripController.getCurrentId());
520                     if (mCurrentModule != null) {
521                         mCurrentModule
522                                 .onPreviewVisibilityChanged(ModuleController.VISIBILITY_HIDDEN);
523                     }
524                 }
525
526                 @Override
527                 public void onFocusedDataLongPressed(int dataId) {
528                     // Do nothing.
529                 }
530
531                 @Override
532                 public void onFocusedDataPromoted(int dataID) {
533                     UsageStatistics.photoInteraction(
534                             UsageStatistics.hashFileName(fileNameFromDataID(dataID)),
535                             eventprotos.CameraEvent.InteractionType.DELETE,
536                             InteractionCause.SWIPE_UP);
537
538                     removeData(dataID);
539                 }
540
541                 @Override
542                 public void onFocusedDataDemoted(int dataID) {
543                     UsageStatistics.photoInteraction(
544                             UsageStatistics.hashFileName(fileNameFromDataID(dataID)),
545                             eventprotos.CameraEvent.InteractionType.DELETE,
546                             InteractionCause.SWIPE_DOWN);
547
548                     removeData(dataID);
549                 }
550
551                 @Override
552                 public void onEnterFullScreenUiShown(int dataId) {
553                     if (mFilmstripVisible) {
554                         CameraActivity.this.setFilmstripUiVisibility(true);
555                     }
556                 }
557
558                 @Override
559                 public void onLeaveFullScreenUiShown(int dataId) {
560                     // Do nothing.
561                 }
562
563                 @Override
564                 public void onEnterFullScreenUiHidden(int dataId) {
565                     if (mFilmstripVisible) {
566                         CameraActivity.this.setFilmstripUiVisibility(false);
567                     }
568                 }
569
570                 @Override
571                 public void onLeaveFullScreenUiHidden(int dataId) {
572                     // Do nothing.
573                 }
574
575                 @Override
576                 public void onEnterFilmstrip(int dataId) {
577                     if (mFilmstripVisible) {
578                         CameraActivity.this.setFilmstripUiVisibility(true);
579                     }
580                 }
581
582                 @Override
583                 public void onLeaveFilmstrip(int dataId) {
584                     // Do nothing.
585                 }
586
587                 @Override
588                 public void onDataReloaded() {
589                     if (!mFilmstripVisible) {
590                         return;
591                     }
592                     updateUiByData(mFilmstripController.getCurrentId());
593                 }
594
595                 @Override
596                 public void onDataUpdated(int dataId) {
597                     if (!mFilmstripVisible) {
598                         return;
599                     }
600                     updateUiByData(mFilmstripController.getCurrentId());
601                 }
602
603                 @Override
604                 public void onEnterZoomView(int dataID) {
605                     if (mFilmstripVisible) {
606                         CameraActivity.this.setFilmstripUiVisibility(false);
607                     }
608                 }
609
610                 @Override
611                 public void onDataFocusChanged(final int prevDataId, final int newDataId) {
612                     if (!mFilmstripVisible) {
613                         return;
614                     }
615                     // TODO: This callback is UI event callback, should always
616                     // happen on UI thread. Find the reason for this
617                     // runOnUiThread() and fix it.
618                     runOnUiThread(new Runnable() {
619                         @Override
620                         public void run() {
621                             updateUiByData(newDataId);
622                         }
623                     });
624                 }
625             };
626
627     private final LocalDataAdapter.LocalDataListener mLocalDataListener =
628             new LocalDataAdapter.LocalDataListener() {
629                 @Override
630                 public void onMetadataUpdated(List<Integer> updatedData) {
631                     int currentDataId = mFilmstripController.getCurrentId();
632                     for (Integer dataId : updatedData) {
633                         if (dataId == currentDataId) {
634                             updateBottomControlsByData(mDataAdapter.getLocalData(dataId));
635                         }
636                     }
637                 }
638
639                 @Override
640                 public void onNewDataAdded(LocalData data) {
641                     startPeekAnimation(data);
642                 }
643             };
644
645     public void gotoGallery() {
646         UsageStatistics.changeScreen(NavigationChange.Mode.FILMSTRIP,
647                 InteractionCause.BUTTON);
648
649         mFilmstripController.goToNextItem();
650     }
651
652     /**
653      * If 'visible' is false, this hides the action bar and switches the
654      * filmstrip UI to lights-out mode.
655      *
656      * @param visible is false, this hides the action bar and switches the
657      *            filmstrip UI to lights-out mode.
658      */
659     private void setFilmstripUiVisibility(boolean visible) {
660         int currentSystemUIVisibility = mAboveFilmstripControlLayout.getSystemUiVisibility();
661         int newSystemUIVisibility = (visible ? View.SYSTEM_UI_FLAG_VISIBLE
662                 : View.SYSTEM_UI_FLAG_FULLSCREEN);
663         if (newSystemUIVisibility != currentSystemUIVisibility) {
664             mAboveFilmstripControlLayout.setSystemUiVisibility(newSystemUIVisibility);
665         }
666
667         mCameraAppUI.getFilmstripBottomControls().setVisible(visible);
668         if (visible != mActionBar.isShowing()) {
669             if (visible) {
670                 mActionBar.show();
671             } else {
672                 mActionBar.hide();
673             }
674         }
675     }
676
677     private void hideSessionProgress() {
678         mCameraAppUI.getFilmstripBottomControls().hideProgress();
679     }
680
681     private void showSessionProgress(CharSequence message) {
682         CameraAppUI.BottomPanel controls =  mCameraAppUI.getFilmstripBottomControls();
683         controls.setProgressText(message);
684         controls.hideControls();
685         controls.showProgress();
686     }
687
688     private void updateSessionProgress(int progress) {
689         mCameraAppUI.getFilmstripBottomControls().setProgress(progress);
690     }
691
692     @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
693     private void setupNfcBeamPush() {
694         NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mAppContext);
695         if (adapter == null) {
696             return;
697         }
698
699         if (!ApiHelper.HAS_SET_BEAM_PUSH_URIS) {
700             // Disable beaming
701             adapter.setNdefPushMessage(null, CameraActivity.this);
702             return;
703         }
704
705         adapter.setBeamPushUris(null, CameraActivity.this);
706         adapter.setBeamPushUrisCallback(new CreateBeamUrisCallback() {
707             @Override
708             public Uri[] createBeamUris(NfcEvent event) {
709                 return mNfcPushUris;
710             }
711         }, CameraActivity.this);
712     }
713
714     @Override
715     public void onMenuVisibilityChanged(boolean isVisible) {
716         // TODO: Remove this or bring back the original implementation: cancel
717         // auto-hide actionbar.
718     }
719
720     @Override
721     public boolean onShareTargetSelected(ShareActionProvider shareActionProvider, Intent intent) {
722         int currentDataId = mFilmstripController.getCurrentId();
723         if (currentDataId < 0) {
724             return false;
725         }
726         UsageStatistics.photoInteraction(
727                 UsageStatistics.hashFileName(fileNameFromDataID(currentDataId)),
728                 eventprotos.CameraEvent.InteractionType.SHARE,
729                 InteractionCause.BUTTON);
730         // TODO add intent.getComponent().getPackageName()
731         return true;
732     }
733
734     // Note: All callbacks come back on the main thread.
735     private final SessionListener mSessionListener =
736             new SessionListener() {
737                 @Override
738                 public void onSessionQueued(final Uri uri) {
739                     notifyNewMedia(uri);
740                     int dataID = mDataAdapter.findDataByContentUri(uri);
741                     if (dataID != -1) {
742                         // Don't allow special UI actions (swipe to
743                         // delete, for example) on in-progress data.
744                         LocalData d = mDataAdapter.getLocalData(dataID);
745                         InProgressDataWrapper newData = new InProgressDataWrapper(d);
746                         mDataAdapter.updateData(dataID, newData);
747                     }
748                 }
749
750                 @Override
751                 public void onSessionDone(final Uri uri) {
752                     Log.v(TAG, "onSessionDone:" + uri);
753                     int doneID = mDataAdapter.findDataByContentUri(uri);
754                     int currentDataId = mFilmstripController.getCurrentId();
755
756                     if (currentDataId == doneID) {
757                         hideSessionProgress();
758                         updateSessionProgress(0);
759                     }
760                     mDataAdapter.refresh(uri, /* isInProgress */false);
761                 }
762
763                 @Override
764                 public void onSessionProgress(final Uri uri, final int progress) {
765                     if (progress < 0) {
766                         // Do nothing, there is no task for this URI.
767                         return;
768                     }
769                     int currentDataId = mFilmstripController.getCurrentId();
770                     if (currentDataId == -1) {
771                         return;
772                     }
773                     if (uri.equals(
774                             mDataAdapter.getLocalData(currentDataId).getContentUri())) {
775                         updateSessionProgress(progress);
776                     }
777                 }
778
779                 @Override
780                 public void onSessionUpdated(Uri uri) {
781                     mDataAdapter.refresh(uri, /* isInProgress */true);
782                 }
783             };
784
785     @Override
786     public Context getAndroidContext() {
787         return mAppContext;
788     }
789
790     @Override
791     public void launchActivityByIntent(Intent intent) {
792         startActivityForResult(intent, REQ_CODE_DONT_SWITCH_TO_PREVIEW);
793     }
794
795     @Override
796     public int getCurrentModuleIndex() {
797         return mCurrentModeIndex;
798     }
799
800     @Override
801     public ModuleController getCurrentModuleController() {
802         return mCurrentModule;
803     }
804
805     @Override
806     public int getQuickSwitchToModuleId(int currentModuleIndex) {
807         return mModuleManager.getQuickSwitchToModuleId(currentModuleIndex, mSettingsManager,
808                 mAppContext);
809     }
810
811     @Override
812     public SurfaceTexture getPreviewBuffer() {
813         // TODO: implement this
814         return null;
815     }
816
817     @Override
818     public void onPreviewReadyToStart() {
819         mCameraAppUI.onPreviewReadyToStart();
820     }
821
822     @Override
823     public void onPreviewStarted() {
824         mCameraAppUI.onPreviewStarted();
825     }
826
827     @Override
828     public void addPreviewAreaSizeChangedListener(
829             PreviewStatusListener.PreviewAreaSizeChangedListener listener) {
830         mCameraAppUI.addPreviewAreaSizeChangedListener(listener);
831     }
832
833     @Override
834     public void removePreviewAreaSizeChangedListener(
835             PreviewStatusListener.PreviewAreaSizeChangedListener listener) {
836         mCameraAppUI.removePreviewAreaSizeChangedListener(listener);
837     }
838
839     @Override
840     public void setupOneShotPreviewListener() {
841         mCameraController.setOneShotPreviewCallback(mMainHandler,
842                 new CameraManager.CameraPreviewDataCallback() {
843                     @Override
844                     public void onPreviewFrame(byte[] data, CameraManager.CameraProxy camera) {
845                         mCurrentModule.onPreviewInitialDataReceived();
846                         mCameraAppUI.onNewPreviewFrame();
847                     }
848                 });
849     }
850
851     @Override
852     public void updatePreviewAspectRatio(float aspectRatio) {
853         mCameraAppUI.updatePreviewAspectRatio(aspectRatio);
854     }
855
856     @Override
857     public boolean shouldShowShimmy() {
858         int remainingTimes = mSettingsManager.getInt(
859                 SettingsManager.SETTING_SHIMMY_REMAINING_PLAY_TIMES_INDEX);
860         return remainingTimes > 0;
861     }
862
863     @Override
864     public void decrementShimmyPlayTimes() {
865         int remainingTimes = mSettingsManager.getInt(
866                 SettingsManager.SETTING_SHIMMY_REMAINING_PLAY_TIMES_INDEX) - 1;
867         if (remainingTimes >= 0) {
868             mSettingsManager.setInt(SettingsManager.SETTING_SHIMMY_REMAINING_PLAY_TIMES_INDEX,
869                     remainingTimes);
870         }
871     }
872
873     @Override
874     public void updatePreviewTransform(Matrix matrix) {
875         mCameraAppUI.updatePreviewTransform(matrix);
876     }
877
878     @Override
879     public void setPreviewStatusListener(PreviewStatusListener previewStatusListener) {
880         mCameraAppUI.setPreviewStatusListener(previewStatusListener);
881     }
882
883     @Override
884     public FrameLayout getModuleLayoutRoot() {
885         return mCameraAppUI.getModuleRootView();
886     }
887
888     @Override
889     public void setShutterEventsListener(ShutterEventsListener listener) {
890         // TODO: implement this
891     }
892
893     @Override
894     public void setShutterEnabled(boolean enabled) {
895         // TODO: implement this
896     }
897
898     @Override
899     public boolean isShutterEnabled() {
900         // TODO: implement this
901         return false;
902     }
903
904     @Override
905     public void startPreCaptureAnimation() {
906         mCameraAppUI.startPreCaptureAnimation();
907     }
908
909     @Override
910     public void cancelPreCaptureAnimation() {
911         // TODO: implement this
912     }
913
914     @Override
915     public void startPostCaptureAnimation() {
916         // TODO: implement this
917     }
918
919     @Override
920     public void startPostCaptureAnimation(Bitmap thumbnail) {
921         // TODO: implement this
922     }
923
924     @Override
925     public void cancelPostCaptureAnimation() {
926         // TODO: implement this
927     }
928
929     @Override
930     public OrientationManager getOrientationManager() {
931         return mOrientationManager;
932     }
933
934     @Override
935     public LocationManager getLocationManager() {
936         return mLocationManager;
937     }
938
939     @Override
940     public void lockOrientation() {
941         if (mOrientationManager != null) {
942             mOrientationManager.lockOrientation();
943         }
944     }
945
946     @Override
947     public void unlockOrientation() {
948         if (mOrientationManager != null) {
949             mOrientationManager.unlockOrientation();
950         }
951     }
952
953     /**
954      * Decrement the remaining play times for peek animation.
955      */
956     private void decrementPeekAnimPlayTimes() {
957         int remainingTimes = mSettingsManager.getInt(
958                 SettingsManager.SETTING_FILMSTRIP_PEEK_ANIM_REMAINING_PLAY_TIMES_INDEX) - 1;
959         if (remainingTimes < 0) {
960             return;
961         }
962         mSettingsManager
963                 .setInt(SettingsManager.SETTING_FILMSTRIP_PEEK_ANIM_REMAINING_PLAY_TIMES_INDEX,
964                         remainingTimes);
965     }
966
967     /**
968      * Starts the filmstrip peek animation if the filmstrip is not visible.
969      * Only {@link LocalData#LOCAL_IMAGE}, {@link
970      * LocalData#LOCAL_IN_PROGRESS_DATA} and {@link
971      * LocalData#LOCAL_VIDEO} are supported.
972      *
973      * @param data The data to peek.
974      */
975     private void startPeekAnimation(final LocalData data) {
976         if (mFilmstripVisible || mPeekAnimationHandler == null) {
977             return;
978         }
979
980         int dataType = data.getLocalDataType();
981         if (dataType != LocalData.LOCAL_IMAGE && dataType != LocalData.LOCAL_IN_PROGRESS_DATA &&
982                 dataType != LocalData.LOCAL_VIDEO) {
983             return;
984         }
985
986         int remainingTimes = mSettingsManager.getInt(
987                 SettingsManager.SETTING_FILMSTRIP_PEEK_ANIM_REMAINING_PLAY_TIMES_INDEX);
988         if (remainingTimes <= 0) {
989             return;
990         }
991         mPeekAnimationHandler.startDecodingJob(data, new Callback<Bitmap>() {
992             @Override
993             public void onCallback(Bitmap result) {
994                 mCameraAppUI.startPeekAnimation(result, true);
995             }
996         });
997     }
998
999     @Override
1000     public void notifyNewMedia(Uri uri) {
1001         ContentResolver cr = getContentResolver();
1002         String mimeType = cr.getType(uri);
1003         if (LocalDataUtil.isMimeTypeVideo(mimeType)) {
1004             sendBroadcast(new Intent(CameraUtil.ACTION_NEW_VIDEO, uri));
1005             mDataAdapter.addNewVideo(uri);
1006         } else if (LocalDataUtil.isMimeTypeImage(mimeType)) {
1007             CameraUtil.broadcastNewPicture(mAppContext, uri);
1008             mDataAdapter.addNewPhoto(uri);
1009         } else if (LocalDataUtil.isMimeTypePlaceHolder(mimeType)) {
1010             mDataAdapter.addNewPhoto(uri);
1011         } else {
1012             android.util.Log.w(TAG, "Unknown new media with MIME type:"
1013                     + mimeType + ", uri:" + uri);
1014         }
1015     }
1016
1017     @Override
1018     public void enableKeepScreenOn(boolean enabled) {
1019         if (mPaused) {
1020             return;
1021         }
1022
1023         mKeepScreenOn = enabled;
1024         if (mKeepScreenOn) {
1025             mMainHandler.removeMessages(MSG_CLEAR_SCREEN_ON_FLAG);
1026             getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1027         } else {
1028             keepScreenOnForAWhile();
1029         }
1030     }
1031
1032     @Override
1033     public CameraProvider getCameraProvider() {
1034         return mCameraController;
1035     }
1036
1037     private void removeData(int dataID) {
1038         mDataAdapter.removeData(dataID);
1039         if (mDataAdapter.getTotalNumber() > 1) {
1040             showUndoDeletionBar();
1041         } else {
1042             // If camera preview is the only view left in filmstrip,
1043             // no need to show undo bar.
1044             mPendingDeletion = true;
1045             performDeletion();
1046             if (mFilmstripVisible) {
1047                 mCameraAppUI.getFilmstripContentPanel().animateHide();
1048             }
1049         }
1050     }
1051
1052     @Override
1053     public boolean onOptionsItemSelected(MenuItem item) {
1054         // Handle presses on the action bar items
1055         switch (item.getItemId()) {
1056             case android.R.id.home:
1057                 if (mFilmstripVisible && startGallery()) {
1058                     return true;
1059                 }
1060                 onBackPressed();
1061                 return true;
1062             case R.id.action_details:
1063                 showDetailsDialog(mFilmstripController.getCurrentId());
1064                 return true;
1065             default:
1066                 return super.onOptionsItemSelected(item);
1067         }
1068     }
1069
1070     private boolean isCaptureIntent() {
1071         if (MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction())
1072                 || MediaStore.ACTION_IMAGE_CAPTURE.equals(getIntent().getAction())
1073                 || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(getIntent().getAction())) {
1074             return true;
1075         } else {
1076             return false;
1077         }
1078     }
1079
1080     private final SettingsManager.StrictUpgradeCallback mStrictUpgradeCallback
1081         = new SettingsManager.StrictUpgradeCallback() {
1082                 @Override
1083                 public void upgrade(SettingsManager settingsManager, int version) {
1084                     // Show the location dialog on upgrade if
1085                     //  (a) the user has never set this option (status quo).
1086                     //  (b) the user opt'ed out previously.
1087                     if (settingsManager.isSet(SettingsManager.SETTING_RECORD_LOCATION) &&
1088                             !settingsManager.getBoolean(SettingsManager.SETTING_RECORD_LOCATION)) {
1089                         settingsManager.remove(SettingsManager.SETTING_RECORD_LOCATION);
1090                     }
1091                 }
1092             };
1093
1094
1095     @Override
1096     public void onCreate(Bundle state) {
1097         super.onCreate(state);
1098         CameraPerformanceTracker.onEvent(CameraPerformanceTracker.ACTIVITY_START);
1099         mOnCreateTime = System.currentTimeMillis();
1100         mAppContext = getApplicationContext();
1101         GcamHelper.init(getContentResolver());
1102
1103         getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
1104         setContentView(R.layout.activity_main);
1105         mActionBar = getActionBar();
1106         mActionBar.addOnMenuVisibilityListener(this);
1107         mMainHandler = new MainHandler(this, getMainLooper());
1108         mCameraController =
1109                 new CameraController(mAppContext, this, mMainHandler,
1110                         CameraManagerFactory.getAndroidCameraManager());
1111         mPreferences = new ComboPreferences(mAppContext);
1112
1113         mSettingsManager = new SettingsManager(mAppContext, this,
1114                 mCameraController.getNumberOfCameras(), mStrictUpgradeCallback);
1115
1116         // Remove this after we get rid of ComboPreferences.
1117         int cameraId = Integer.parseInt(mSettingsManager.get(SettingsManager.SETTING_CAMERA_ID));
1118         mPreferences.setLocalId(mAppContext, cameraId);
1119         CameraSettings.upgradeGlobalPreferences(mPreferences,
1120                 mCameraController.getNumberOfCameras());
1121         // TODO: Try to move all the resources allocation to happen as soon as
1122         // possible so we can call module.init() at the earliest time.
1123         mModuleManager = new ModuleManagerImpl();
1124         ModulesInfo.setupModules(mAppContext, mModuleManager);
1125
1126         mModeListView = (ModeListView) findViewById(R.id.mode_list_layout);
1127         mModeListView.init(mModuleManager.getSupportedModeIndexList());
1128         if (ApiHelper.HAS_ROTATION_ANIMATION) {
1129             setRotationAnimation();
1130         }
1131         mModeListView.setVisibilityChangedListener(new ModeListVisibilityChangedListener() {
1132             @Override
1133             public void onVisibilityChanged(boolean visible) {
1134                 if (mCurrentModule != null) {
1135                     int visibility = visible ? ModuleController.VISIBILITY_COVERED
1136                             : ModuleController.VISIBILITY_VISIBLE;
1137                     mCurrentModule.onPreviewVisibilityChanged(visibility);
1138                 }
1139             }
1140         });
1141
1142         // Check if this is in the secure camera mode.
1143         Intent intent = getIntent();
1144         String action = intent.getAction();
1145         if (INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action)
1146                 || ACTION_IMAGE_CAPTURE_SECURE.equals(action)) {
1147             mSecureCamera = true;
1148         } else {
1149             mSecureCamera = intent.getBooleanExtra(SECURE_CAMERA_EXTRA, false);
1150         }
1151
1152         if (mSecureCamera) {
1153             // Foreground event caused by lock screen startup.
1154             // It is necessary to log this in onCreate, to avoid the
1155             // onResume->onPause->onResume sequence.
1156             UsageStatistics.foregrounded(
1157                     eventprotos.ForegroundEvent.ForegroundSource.LOCK_SCREEN);
1158
1159             // Change the window flags so that secure camera can show when
1160             // locked
1161             Window win = getWindow();
1162             WindowManager.LayoutParams params = win.getAttributes();
1163             params.flags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
1164             win.setAttributes(params);
1165
1166             // Filter for screen off so that we can finish secure camera
1167             // activity
1168             // when screen is off.
1169             IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
1170             registerReceiver(mScreenOffReceiver, filter);
1171         }
1172         mCameraAppUI = new CameraAppUI(this,
1173                 (MainActivityLayout) findViewById(R.id.activity_root_view), isCaptureIntent());
1174
1175         mCameraAppUI.setFilmstripBottomControlsListener(mMyFilmstripBottomControlListener);
1176
1177         mAboveFilmstripControlLayout =
1178                 (FrameLayout) findViewById(R.id.camera_filmstrip_content_layout);
1179
1180         // Add the session listener so we can track the session progress
1181         // updates.
1182         getServices().getCaptureSessionManager().addSessionListener(mSessionListener);
1183         mFilmstripController = ((FilmstripView) findViewById(R.id.filmstrip_view)).getController();
1184         mFilmstripController.setImageGap(
1185                 getResources().getDimensionPixelSize(R.dimen.camera_film_strip_gap));
1186         mPanoramaViewHelper = new PanoramaViewHelper(this);
1187         mPanoramaViewHelper.onCreate();
1188         // Set up the camera preview first so the preview shows up ASAP.
1189         mDataAdapter = new CameraDataAdapter(mAppContext,
1190                 new ColorDrawable(getResources().getColor(R.color.photo_placeholder)));
1191         mDataAdapter.setLocalDataListener(mLocalDataListener);
1192
1193         mCameraAppUI.getFilmstripContentPanel().setFilmstripListener(mFilmstripListener);
1194
1195         mLocationManager = new LocationManager(mAppContext);
1196
1197         int modeIndex = -1;
1198         int photoIndex = getResources().getInteger(R.integer.camera_mode_photo);
1199         int videoIndex = getResources().getInteger(R.integer.camera_mode_video);
1200         int gcamIndex = getResources().getInteger(R.integer.camera_mode_gcam);
1201         if (MediaStore.INTENT_ACTION_VIDEO_CAMERA.equals(getIntent().getAction())
1202                 || MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction())) {
1203             modeIndex = videoIndex;
1204         } else if (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(getIntent().getAction())
1205                 || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(getIntent()
1206                         .getAction())) {
1207             modeIndex = photoIndex;
1208             if (mSettingsManager.getInt(SettingsManager.SETTING_STARTUP_MODULE_INDEX)
1209                         == gcamIndex && GcamHelper.hasGcamCapture()) {
1210                 modeIndex = gcamIndex;
1211             }
1212         } else if (MediaStore.ACTION_IMAGE_CAPTURE.equals(getIntent().getAction())
1213                 || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(getIntent().getAction())) {
1214             modeIndex = photoIndex;
1215         } else {
1216             // If the activity has not been started using an explicit intent,
1217             // read the module index from the last time the user changed modes
1218             modeIndex = mSettingsManager.getInt(SettingsManager.SETTING_STARTUP_MODULE_INDEX);
1219             if ((modeIndex == gcamIndex &&
1220                     !GcamHelper.hasGcamCapture()) || modeIndex < 0) {
1221                 modeIndex = photoIndex;
1222             }
1223         }
1224
1225         mOrientationManager = new OrientationManagerImpl(this);
1226         mOrientationManager.addOnOrientationChangeListener(mMainHandler, this);
1227
1228         setModuleFromModeIndex(modeIndex);
1229         mCameraAppUI.prepareModuleUI();
1230         mCurrentModule.init(this, isSecureCamera(), isCaptureIntent());
1231
1232         if (!mSecureCamera) {
1233             mFilmstripController.setDataAdapter(mDataAdapter);
1234             if (!isCaptureIntent()) {
1235                 mDataAdapter.requestLoad();
1236             }
1237         } else {
1238             // Put a lock placeholder as the last image by setting its date to
1239             // 0.
1240             ImageView v = (ImageView) getLayoutInflater().inflate(
1241                     R.layout.secure_album_placeholder, null);
1242             v.setOnClickListener(new View.OnClickListener() {
1243                 @Override
1244                 public void onClick(View view) {
1245                     UsageStatistics.changeScreen(NavigationChange.Mode.GALLERY,
1246                             InteractionCause.BUTTON);
1247                     startGallery();
1248                     finish();
1249                 }
1250             });
1251             mDataAdapter = new FixedLastDataAdapter(
1252                     mAppContext,
1253                     mDataAdapter,
1254                     new SimpleViewData(
1255                             v,
1256                             v.getDrawable().getIntrinsicWidth(),
1257                             v.getDrawable().getIntrinsicHeight(),
1258                             0, 0));
1259             // Flush out all the original data.
1260             mDataAdapter.flush();
1261             mFilmstripController.setDataAdapter(mDataAdapter);
1262         }
1263
1264         setupNfcBeamPush();
1265
1266         mLocalImagesObserver = new LocalMediaObserver();
1267         mLocalVideosObserver = new LocalMediaObserver();
1268
1269         getContentResolver().registerContentObserver(
1270                 MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true,
1271                 mLocalImagesObserver);
1272         getContentResolver().registerContentObserver(
1273                 MediaStore.Video.Media.EXTERNAL_CONTENT_URI, true,
1274                 mLocalVideosObserver);
1275         if (FeedbackHelper.feedbackAvailable()) {
1276             mFeedbackHelper = new FeedbackHelper(mAppContext);
1277         }
1278     }
1279
1280     private void setRotationAnimation() {
1281         int rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
1282         rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE;
1283         Window win = getWindow();
1284         WindowManager.LayoutParams winParams = win.getAttributes();
1285         winParams.rotationAnimation = rotationAnimation;
1286         win.setAttributes(winParams);
1287     }
1288
1289     @Override
1290     public void onUserInteraction() {
1291         super.onUserInteraction();
1292         if (!isFinishing()) {
1293             keepScreenOnForAWhile();
1294         }
1295     }
1296
1297     @Override
1298     public boolean dispatchTouchEvent(MotionEvent ev) {
1299         boolean result = super.dispatchTouchEvent(ev);
1300         if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
1301             // Real deletion is postponed until the next user interaction after
1302             // the gesture that triggers deletion. Until real deletion is
1303             // performed, users can click the undo button to bring back the
1304             // image that they chose to delete.
1305             if (mPendingDeletion && !mIsUndoingDeletion) {
1306                 performDeletion();
1307             }
1308         }
1309         return result;
1310     }
1311
1312     @Override
1313     public void onPause() {
1314         mPaused = true;
1315         mPeekAnimationHandler = null;
1316         mPeekAnimationThread.quitSafely();
1317         mPeekAnimationThread = null;
1318         CameraPerformanceTracker.onEvent(CameraPerformanceTracker.ACTIVITY_PAUSE);
1319
1320         // Delete photos that are pending deletion
1321         performDeletion();
1322         mCurrentModule.pause();
1323         mOrientationManager.pause();
1324         // Close the camera and wait for the operation done.
1325         mCameraController.closeCamera();
1326         mPanoramaViewHelper.onPause();
1327
1328         mLocalImagesObserver.setActivityPaused(true);
1329         mLocalVideosObserver.setActivityPaused(true);
1330         resetScreenOn();
1331         super.onPause();
1332     }
1333
1334     @Override
1335     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
1336         if (requestCode == REQ_CODE_DONT_SWITCH_TO_PREVIEW) {
1337             mResetToPreviewOnResume = false;
1338         } else {
1339             super.onActivityResult(requestCode, resultCode, data);
1340         }
1341     }
1342
1343     @Override
1344     public void onResume() {
1345         mPaused = false;
1346         CameraPerformanceTracker.onEvent(CameraPerformanceTracker.ACTIVITY_RESUME);
1347
1348         mLastLayoutOrientation = getResources().getConfiguration().orientation;
1349
1350         // TODO: Handle this in OrientationManager.
1351         // Auto-rotate off
1352         if (Settings.System.getInt(getContentResolver(),
1353                 Settings.System.ACCELEROMETER_ROTATION, 0) == 0) {
1354             setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
1355             mAutoRotateScreen = false;
1356         } else {
1357             setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
1358             mAutoRotateScreen = true;
1359         }
1360
1361         if (isCaptureIntent()) {
1362             // Foreground event caused by photo or video capure intent.
1363             UsageStatistics.foregrounded(
1364                     eventprotos.ForegroundEvent.ForegroundSource.INTENT_PICKER);
1365         } else if (!mSecureCamera) {
1366             // Foreground event that is not caused by an intent.
1367             UsageStatistics.foregrounded(
1368                     eventprotos.ForegroundEvent.ForegroundSource.ICON_LAUNCHER);
1369         }
1370
1371         Drawable galleryLogo;
1372         if (mSecureCamera) {
1373             mGalleryIntent = null;
1374             galleryLogo = null;
1375         } else {
1376             mGalleryIntent = IntentHelper.getDefaultGalleryIntent(mAppContext);
1377             galleryLogo = IntentHelper.getGalleryIcon(mAppContext, mGalleryIntent);
1378         }
1379         if (galleryLogo == null) {
1380             try {
1381                 galleryLogo = getPackageManager().getActivityLogo(getComponentName());
1382             } catch (PackageManager.NameNotFoundException e) {
1383                 Log.e(TAG, "Can't get the activity logo");
1384             }
1385         }
1386         if (mGalleryIntent != null) {
1387             mActionBar.setDisplayUseLogoEnabled(true);
1388         }
1389         mActionBar.setLogo(galleryLogo);
1390         mOrientationManager.resume();
1391         super.onResume();
1392         mPeekAnimationThread = new HandlerThread("Peek animation");
1393         mPeekAnimationThread.start();
1394         mPeekAnimationHandler = new PeekAnimationHandler(mPeekAnimationThread.getLooper());
1395         mCurrentModule.resume();
1396         setSwipingEnabled(true);
1397
1398         if (mResetToPreviewOnResume) {
1399             mCameraAppUI.resume();
1400         } else {
1401             LocalData data = mDataAdapter.getLocalData(mFilmstripController.getCurrentId());
1402             if (data != null) {
1403                 mDataAdapter.refresh(data.getContentUri(), false);
1404             }
1405         }
1406         // The share button might be disabled to avoid double tapping.
1407         mCameraAppUI.getFilmstripBottomControls().setShareEnabled(true);
1408         // Default is showing the preview, unless disabled by explicitly
1409         // starting an activity we want to return from to the filmstrip rather
1410         // than the preview.
1411         mResetToPreviewOnResume = true;
1412
1413         if (mLocalVideosObserver.isMediaDataChangedDuringPause()
1414                 || mLocalImagesObserver.isMediaDataChangedDuringPause()) {
1415             if (!mSecureCamera) {
1416                 // If it's secure camera, requestLoad() should not be called
1417                 // as it will load all the data.
1418                 if (!mFilmstripVisible) {
1419                     mDataAdapter.requestLoad();
1420                 }
1421             }
1422         }
1423         mLocalImagesObserver.setActivityPaused(false);
1424         mLocalVideosObserver.setActivityPaused(false);
1425
1426         keepScreenOnForAWhile();
1427
1428         // Lights-out mode at all times.
1429         findViewById(R.id.activity_root_view)
1430                 .setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE);
1431         mPanoramaViewHelper.onResume();
1432         ReleaseDialogHelper.showReleaseInfoDialogOnStart(this, mSettingsManager);
1433         syncLocationManagerSetting();
1434     }
1435
1436     @Override
1437     public void onStart() {
1438         super.onStart();
1439         mPanoramaViewHelper.onStart();
1440     }
1441
1442     @Override
1443     protected void onStop() {
1444         mPanoramaViewHelper.onStop();
1445         if (mFeedbackHelper != null) {
1446             mFeedbackHelper.stopFeedback();
1447         }
1448
1449         mLocationManager.disconnect();
1450         super.onStop();
1451     }
1452
1453     @Override
1454     public void onDestroy() {
1455         if (mSecureCamera) {
1456             unregisterReceiver(mScreenOffReceiver);
1457         }
1458         mActionBar.removeOnMenuVisibilityListener(this);
1459         mSettingsManager.removeAllListeners();
1460         mCameraController.removeCallbackReceiver();
1461         getContentResolver().unregisterContentObserver(mLocalImagesObserver);
1462         getContentResolver().unregisterContentObserver(mLocalVideosObserver);
1463         getServices().getCaptureSessionManager().removeSessionListener(mSessionListener);
1464         mCameraAppUI.onDestroy();
1465         mCameraController = null;
1466         mSettingsManager = null;
1467         mCameraAppUI = null;
1468         mOrientationManager = null;
1469         mButtonManager = null;
1470         CameraManagerFactory.recycle();
1471         super.onDestroy();
1472     }
1473
1474     @Override
1475     public void onConfigurationChanged(Configuration config) {
1476         super.onConfigurationChanged(config);
1477         Log.v(TAG, "onConfigurationChanged");
1478         if (config.orientation == Configuration.ORIENTATION_UNDEFINED) {
1479             return;
1480         }
1481
1482         if (mLastLayoutOrientation != config.orientation) {
1483             mLastLayoutOrientation = config.orientation;
1484             mCurrentModule.onLayoutOrientationChanged(
1485                     mLastLayoutOrientation == Configuration.ORIENTATION_LANDSCAPE);
1486         }
1487     }
1488
1489     @Override
1490     public boolean onKeyDown(int keyCode, KeyEvent event) {
1491         if (!mFilmstripVisible) {
1492             if (mCurrentModule.onKeyDown(keyCode, event)) {
1493                 return true;
1494             }
1495             // Prevent software keyboard or voice search from showing up.
1496             if (keyCode == KeyEvent.KEYCODE_SEARCH
1497                     || keyCode == KeyEvent.KEYCODE_MENU) {
1498                 if (event.isLongPress()) {
1499                     return true;
1500                 }
1501             }
1502         }
1503
1504         return super.onKeyDown(keyCode, event);
1505     }
1506
1507     @Override
1508     public boolean onKeyUp(int keyCode, KeyEvent event) {
1509         if (!mFilmstripVisible && mCurrentModule.onKeyUp(keyCode, event)) {
1510             return true;
1511         }
1512         return super.onKeyUp(keyCode, event);
1513     }
1514
1515     @Override
1516     public void onBackPressed() {
1517         if (!mCameraAppUI.onBackPressed()) {
1518             if (!mCurrentModule.onBackPressed()) {
1519                 super.onBackPressed();
1520             }
1521         }
1522     }
1523
1524     @Override
1525     public boolean isAutoRotateScreen() {
1526         // TODO: Move to OrientationManager.
1527         return mAutoRotateScreen;
1528     }
1529
1530     @Override
1531     public boolean onCreateOptionsMenu(Menu menu) {
1532         MenuInflater inflater = getMenuInflater();
1533         inflater.inflate(R.menu.filmstrip_menu, menu);
1534         mActionBarMenu = menu;
1535         return super.onCreateOptionsMenu(menu);
1536     }
1537
1538     protected void updateStorageSpace() {
1539         mStorageSpaceBytes = Storage.getAvailableSpace();
1540     }
1541
1542     protected long getStorageSpaceBytes() {
1543         return mStorageSpaceBytes;
1544     }
1545
1546     protected void updateStorageSpaceAndHint() {
1547         updateStorageSpace();
1548         updateStorageHint(mStorageSpaceBytes);
1549     }
1550
1551     protected void updateStorageHint(long storageSpace) {
1552         String message = null;
1553         if (storageSpace == Storage.UNAVAILABLE) {
1554             message = getString(R.string.no_storage);
1555         } else if (storageSpace == Storage.PREPARING) {
1556             message = getString(R.string.preparing_sd);
1557         } else if (storageSpace == Storage.UNKNOWN_SIZE) {
1558             message = getString(R.string.access_sd_fail);
1559         } else if (storageSpace <= Storage.LOW_STORAGE_THRESHOLD_BYTES) {
1560             message = getString(R.string.spaceIsLow_content);
1561         }
1562
1563         if (message != null) {
1564             if (mStorageHint == null) {
1565                 mStorageHint = OnScreenHint.makeText(mAppContext, message);
1566             } else {
1567                 mStorageHint.setText(message);
1568             }
1569             mStorageHint.show();
1570         } else if (mStorageHint != null) {
1571             mStorageHint.cancel();
1572             mStorageHint = null;
1573         }
1574     }
1575
1576     protected void setResultEx(int resultCode) {
1577         mResultCodeForTesting = resultCode;
1578         setResult(resultCode);
1579     }
1580
1581     protected void setResultEx(int resultCode, Intent data) {
1582         mResultCodeForTesting = resultCode;
1583         mResultDataForTesting = data;
1584         setResult(resultCode, data);
1585     }
1586
1587     public int getResultCode() {
1588         return mResultCodeForTesting;
1589     }
1590
1591     public Intent getResultData() {
1592         return mResultDataForTesting;
1593     }
1594
1595     public boolean isSecureCamera() {
1596         return mSecureCamera;
1597     }
1598
1599     @Override
1600     public boolean isPaused() {
1601         return mPaused;
1602     }
1603
1604     @Override
1605     public void onModeSelected(int modeIndex) {
1606         if (mCurrentModeIndex == modeIndex) {
1607             return;
1608         }
1609
1610         CameraPerformanceTracker.onEvent(CameraPerformanceTracker.MODE_SWITCH_START);
1611         // Record last used camera mode for quick switching
1612         if (modeIndex == getResources().getInteger(R.integer.camera_mode_photo)
1613                 || modeIndex == getResources().getInteger(R.integer.camera_mode_gcam)) {
1614             mSettingsManager.setInt(SettingsManager.SETTING_KEY_CAMERA_MODULE_LAST_USED_INDEX,
1615                     modeIndex);
1616         }
1617
1618         closeModule(mCurrentModule);
1619         int oldModuleIndex = mCurrentModeIndex;
1620
1621         // Refocus and Gcam are modes that cannot be selected
1622         // from the mode list view, because they are not list items.
1623         // Check whether we should interpret MODULE_CRAFT as either.
1624         if (modeIndex == getResources().getInteger(R.integer.camera_mode_photo)) {
1625             boolean hdrPlusOn = mSettingsManager.isHdrPlusOn();
1626             if (hdrPlusOn && GcamHelper.hasGcamCapture()) {
1627                 modeIndex = getResources().getInteger(R.integer.camera_mode_gcam);
1628             }
1629         }
1630
1631         setModuleFromModeIndex(modeIndex);
1632
1633         mCameraAppUI.resetBottomControls(mCurrentModule, modeIndex);
1634         mCameraAppUI.addShutterListener(mCurrentModule);
1635         openModule(mCurrentModule);
1636         mCurrentModule.onOrientationChanged(mLastRawOrientation);
1637         // Store the module index so we can use it the next time the Camera
1638         // starts up.
1639         SharedPreferences prefs = PreferenceManager
1640                 .getDefaultSharedPreferences(mAppContext);
1641         prefs.edit().putInt(CameraSettings.KEY_STARTUP_MODULE_INDEX, modeIndex).apply();
1642     }
1643
1644     /**
1645      * Shows the settings dialog.
1646      */
1647     @Override
1648     public void onSettingsSelected() {
1649         Intent intent = new Intent(this, CameraSettingsActivity.class);
1650         startActivity(intent);
1651     }
1652
1653     /**
1654      * Sets the mCurrentModuleIndex, creates a new module instance for the given
1655      * index an sets it as mCurrentModule.
1656      */
1657     private void setModuleFromModeIndex(int modeIndex) {
1658         ModuleManagerImpl.ModuleAgent agent = mModuleManager.getModuleAgent(modeIndex);
1659         if (agent == null) {
1660             return;
1661         }
1662         if (!agent.requestAppForCamera()) {
1663             mCameraController.closeCamera();
1664         }
1665         mCurrentModeIndex = agent.getModuleId();
1666         mCurrentModule = (CameraModule) agent.createModule(this);
1667     }
1668
1669     @Override
1670     public SettingsManager getSettingsManager() {
1671         return mSettingsManager;
1672     }
1673
1674     @Override
1675     public CameraServices getServices() {
1676         return (CameraServices) getApplication();
1677     }
1678
1679     public List<String> getSupportedModeNames() {
1680         List<Integer> indices = mModuleManager.getSupportedModeIndexList();
1681         List<String> supported = new ArrayList<String>();
1682
1683         for (Integer modeIndex : indices) {
1684             String name = CameraUtil.getCameraModeText(modeIndex, mAppContext);
1685             if (name != null && !name.equals("")) {
1686                 supported.add(name);
1687             }
1688         }
1689         return supported;
1690     }
1691
1692     @Override
1693     public ButtonManager getButtonManager() {
1694         if (mButtonManager == null) {
1695             mButtonManager = new ButtonManager(this);
1696         }
1697         return mButtonManager;
1698     }
1699
1700     /**
1701      * Creates an AlertDialog appropriate for choosing whether to enable
1702      * location on the first run of the app.
1703      */
1704     public AlertDialog getFirstTimeLocationAlert() {
1705         AlertDialog.Builder builder = new AlertDialog.Builder(this);
1706         builder = SettingsUtil.getFirstTimeLocationAlertBuilder(builder, new Callback<Boolean>() {
1707             @Override
1708             public void onCallback(Boolean locationOn) {
1709                 mSettingsManager.setLocation(locationOn, mLocationManager);
1710             }
1711         });
1712         if (builder != null) {
1713             return builder.create();
1714         } else {
1715             return null;
1716         }
1717     }
1718
1719     /**
1720      * Launches an ACTION_EDIT intent for the given local data item. If
1721      * 'withTinyPlanet' is set, this will show a disambig dialog first to let
1722      * the user start either the tiny planet editor or another photo edior.
1723      *
1724      * @param data The data item to edit.
1725      */
1726     public void launchEditor(LocalData data) {
1727         Intent intent = new Intent(Intent.ACTION_EDIT)
1728                 .setDataAndType(data.getContentUri(), data.getMimeType())
1729                 .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
1730         try {
1731             launchActivityByIntent(intent);
1732         } catch (ActivityNotFoundException e) {
1733             launchActivityByIntent(Intent.createChooser(intent, null));
1734         }
1735     }
1736
1737     @Override
1738     public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
1739         super.onCreateContextMenu(menu, v, menuInfo);
1740
1741         MenuInflater inflater = getMenuInflater();
1742         inflater.inflate(R.menu.filmstrip_context_menu, menu);
1743     }
1744
1745     @Override
1746     public boolean onContextItemSelected(MenuItem item) {
1747         switch (item.getItemId()) {
1748             case R.id.tiny_planet_editor:
1749                 mMyFilmstripBottomControlListener.onTinyPlanet();
1750                 return true;
1751             case R.id.photo_editor:
1752                 mMyFilmstripBottomControlListener.onEdit();
1753                 return true;
1754         }
1755         return false;
1756     }
1757
1758     /**
1759      * Launch the tiny planet editor.
1760      *
1761      * @param data The data must be a 360 degree stereographically mapped
1762      *            panoramic image. It will not be modified, instead a new item
1763      *            with the result will be added to the filmstrip.
1764      */
1765     public void launchTinyPlanetEditor(LocalData data) {
1766         TinyPlanetFragment fragment = new TinyPlanetFragment();
1767         Bundle bundle = new Bundle();
1768         bundle.putString(TinyPlanetFragment.ARGUMENT_URI, data.getContentUri().toString());
1769         bundle.putString(TinyPlanetFragment.ARGUMENT_TITLE, data.getTitle());
1770         fragment.setArguments(bundle);
1771         fragment.show(getFragmentManager(), "tiny_planet");
1772     }
1773
1774     private void openModule(CameraModule module) {
1775         module.init(this, isSecureCamera(), isCaptureIntent());
1776         module.resume();
1777         int visibility = mFilmstripVisible ? ModuleController.VISIBILITY_HIDDEN
1778                 : ModuleController.VISIBILITY_VISIBLE;
1779         module.onPreviewVisibilityChanged(visibility);
1780     }
1781
1782     private void closeModule(CameraModule module) {
1783         module.pause();
1784         mCameraAppUI.clearModuleUI();
1785     }
1786
1787     private void performDeletion() {
1788         if (!mPendingDeletion) {
1789             return;
1790         }
1791         hideUndoDeletionBar(false);
1792         mDataAdapter.executeDeletion();
1793     }
1794
1795     public void showUndoDeletionBar() {
1796         if (mPendingDeletion) {
1797             performDeletion();
1798         }
1799         Log.v(TAG, "showing undo bar");
1800         mPendingDeletion = true;
1801         if (mUndoDeletionBar == null) {
1802             ViewGroup v = (ViewGroup) getLayoutInflater().inflate(R.layout.undo_bar,
1803                     mAboveFilmstripControlLayout, true);
1804             mUndoDeletionBar = (ViewGroup) v.findViewById(R.id.camera_undo_deletion_bar);
1805             View button = mUndoDeletionBar.findViewById(R.id.camera_undo_deletion_button);
1806             button.setOnClickListener(new View.OnClickListener() {
1807                 @Override
1808                 public void onClick(View view) {
1809                     mDataAdapter.undoDataRemoval();
1810                     hideUndoDeletionBar(true);
1811                 }
1812             });
1813             // Setting undo bar clickable to avoid touch events going through
1814             // the bar to the buttons (eg. edit button, etc) underneath the bar.
1815             mUndoDeletionBar.setClickable(true);
1816             // When there is user interaction going on with the undo button, we
1817             // do not want to hide the undo bar.
1818             button.setOnTouchListener(new View.OnTouchListener() {
1819                 @Override
1820                 public boolean onTouch(View v, MotionEvent event) {
1821                     if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
1822                         mIsUndoingDeletion = true;
1823                     } else if (event.getActionMasked() == MotionEvent.ACTION_UP) {
1824                         mIsUndoingDeletion = false;
1825                     }
1826                     return false;
1827                 }
1828             });
1829         }
1830         mUndoDeletionBar.setAlpha(0f);
1831         mUndoDeletionBar.setVisibility(View.VISIBLE);
1832         mUndoDeletionBar.animate().setDuration(200).alpha(1f).setListener(null).start();
1833     }
1834
1835     private void hideUndoDeletionBar(boolean withAnimation) {
1836         Log.v(TAG, "Hiding undo deletion bar");
1837         mPendingDeletion = false;
1838         if (mUndoDeletionBar != null) {
1839             if (withAnimation) {
1840                 mUndoDeletionBar.animate().setDuration(200).alpha(0f)
1841                         .setListener(new Animator.AnimatorListener() {
1842                             @Override
1843                             public void onAnimationStart(Animator animation) {
1844                                 // Do nothing.
1845                             }
1846
1847                             @Override
1848                             public void onAnimationEnd(Animator animation) {
1849                                 mUndoDeletionBar.setVisibility(View.GONE);
1850                             }
1851
1852                             @Override
1853                             public void onAnimationCancel(Animator animation) {
1854                                 // Do nothing.
1855                             }
1856
1857                             @Override
1858                             public void onAnimationRepeat(Animator animation) {
1859                                 // Do nothing.
1860                             }
1861                         }).start();
1862             } else {
1863                 mUndoDeletionBar.setVisibility(View.GONE);
1864             }
1865         }
1866     }
1867
1868     @Override
1869     public void onOrientationChanged(int orientation) {
1870         // We keep the last known orientation. So if the user first orient
1871         // the camera then point the camera to floor or sky, we still have
1872         // the correct orientation.
1873         if (orientation == OrientationManager.ORIENTATION_UNKNOWN) {
1874             return;
1875         }
1876         mLastRawOrientation = orientation;
1877         if (mCurrentModule != null) {
1878             mCurrentModule.onOrientationChanged(orientation);
1879         }
1880     }
1881
1882     /**
1883      * Enable/disable swipe-to-filmstrip. Will always disable swipe if in
1884      * capture intent.
1885      *
1886      * @param enable {@code true} to enable swipe.
1887      */
1888     public void setSwipingEnabled(boolean enable) {
1889         // TODO: Bring back the functionality.
1890         if (isCaptureIntent()) {
1891             // lockPreview(true);
1892         } else {
1893             // lockPreview(!enable);
1894         }
1895     }
1896
1897     // Accessor methods for getting latency times used in performance testing
1898     public long getFirstPreviewTime() {
1899         if (mCurrentModule instanceof PhotoModule) {
1900             long coverHiddenTime = getCameraAppUI().getCoverHiddenTime();
1901             if (coverHiddenTime != -1) {
1902                 return coverHiddenTime - mOnCreateTime;
1903             }
1904         }
1905         return -1;
1906     }
1907
1908     public long getAutoFocusTime() {
1909         return (mCurrentModule instanceof PhotoModule) ?
1910                 ((PhotoModule) mCurrentModule).mAutoFocusTime : -1;
1911     }
1912
1913     public long getShutterLag() {
1914         return (mCurrentModule instanceof PhotoModule) ?
1915                 ((PhotoModule) mCurrentModule).mShutterLag : -1;
1916     }
1917
1918     public long getShutterToPictureDisplayedTime() {
1919         return (mCurrentModule instanceof PhotoModule) ?
1920                 ((PhotoModule) mCurrentModule).mShutterToPictureDisplayedTime : -1;
1921     }
1922
1923     public long getPictureDisplayedToJpegCallbackTime() {
1924         return (mCurrentModule instanceof PhotoModule) ?
1925                 ((PhotoModule) mCurrentModule).mPictureDisplayedToJpegCallbackTime : -1;
1926     }
1927
1928     public long getJpegCallbackFinishTime() {
1929         return (mCurrentModule instanceof PhotoModule) ?
1930                 ((PhotoModule) mCurrentModule).mJpegCallbackFinishTime : -1;
1931     }
1932
1933     public long getCaptureStartTime() {
1934         return (mCurrentModule instanceof PhotoModule) ?
1935                 ((PhotoModule) mCurrentModule).mCaptureStartTime : -1;
1936     }
1937
1938     public boolean isRecording() {
1939         return (mCurrentModule instanceof VideoModule) ?
1940                 ((VideoModule) mCurrentModule).isRecording() : false;
1941     }
1942
1943     public CameraManager.CameraOpenCallback getCameraOpenErrorCallback() {
1944         return mCameraController;
1945     }
1946
1947     // For debugging purposes only.
1948     public CameraModule getCurrentModule() {
1949         return mCurrentModule;
1950     }
1951
1952     /**
1953      * Reads the current location recording settings and passes it on to the
1954      * location manager.
1955      */
1956     public void syncLocationManagerSetting() {
1957         mSettingsManager.syncLocationManager(mLocationManager);
1958     }
1959
1960     private void keepScreenOnForAWhile() {
1961         if (mKeepScreenOn) {
1962             return;
1963         }
1964         mMainHandler.removeMessages(MSG_CLEAR_SCREEN_ON_FLAG);
1965         getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1966         mMainHandler.sendEmptyMessageDelayed(MSG_CLEAR_SCREEN_ON_FLAG, SCREEN_DELAY_MS);
1967     }
1968
1969     private void resetScreenOn() {
1970         mKeepScreenOn = false;
1971         mMainHandler.removeMessages(MSG_CLEAR_SCREEN_ON_FLAG);
1972         getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1973     }
1974
1975     /**
1976      * @return {@code true} if the Gallery is launched successfully.
1977      */
1978     private boolean startGallery() {
1979         if (mGalleryIntent == null) {
1980             return false;
1981         }
1982         try {
1983             UsageStatistics.changeScreen(NavigationChange.Mode.GALLERY, InteractionCause.BUTTON);
1984             Intent startGalleryIntent = new Intent(mGalleryIntent);
1985             int currentDataId = mFilmstripController.getCurrentId();
1986             LocalData currentLocalData = mDataAdapter.getLocalData(currentDataId);
1987             if (currentLocalData != null) {
1988                 GalleryHelper.setContentUri(startGalleryIntent, currentLocalData.getContentUri());
1989             }
1990             launchActivityByIntent(startGalleryIntent);
1991         } catch (ActivityNotFoundException e) {
1992             Log.w(TAG, "Failed to launch gallery activity, closing");
1993         }
1994         return false;
1995     }
1996
1997     private void setNfcBeamPushUriFromData(LocalData data) {
1998         final Uri uri = data.getContentUri();
1999         if (uri != Uri.EMPTY) {
2000             mNfcPushUris[0] = uri;
2001         } else {
2002             mNfcPushUris[0] = null;
2003         }
2004     }
2005
2006     /**
2007      * Updates the visibility of the filmstrip bottom controls and action bar.
2008      */
2009     private void updateUiByData(final int dataId) {
2010         final LocalData currentData = mDataAdapter.getLocalData(dataId);
2011         if (currentData == null) {
2012             Log.w(TAG, "Current data ID not found.");
2013             hideSessionProgress();
2014             return;
2015         }
2016         updateActionBarMenu(currentData);
2017
2018         /* Bottom controls. */
2019         updateBottomControlsByData(currentData);
2020
2021         if (isSecureCamera()) {
2022             // We cannot show buttons in secure camera since go to other
2023             // activities might create a security hole.
2024             mCameraAppUI.getFilmstripBottomControls().hideControls();
2025             return;
2026         }
2027
2028
2029         setNfcBeamPushUriFromData(currentData);
2030
2031         if (!mDataAdapter.isMetadataUpdated(dataId)) {
2032             mDataAdapter.updateMetadata(dataId);
2033         }
2034     }
2035
2036     /**
2037      * Updates the bottom controls based on the data.
2038      */
2039     private void updateBottomControlsByData(final LocalData currentData) {
2040
2041         final CameraAppUI.BottomPanel filmstripBottomPanel =
2042                 mCameraAppUI.getFilmstripBottomControls();
2043         filmstripBottomPanel.showControls();
2044         filmstripBottomPanel.setEditButtonVisibility(
2045                 currentData.isDataActionSupported(LocalData.DATA_ACTION_EDIT));
2046         filmstripBottomPanel.setShareButtonVisibility(
2047                 currentData.isDataActionSupported(LocalData.DATA_ACTION_SHARE));
2048         filmstripBottomPanel.setDeleteButtonVisibility(
2049                 currentData.isDataActionSupported(LocalData.DATA_ACTION_DELETE));
2050
2051         /* Progress bar */
2052
2053         Uri contentUri = currentData.getContentUri();
2054         CaptureSessionManager sessionManager = getServices()
2055                 .getCaptureSessionManager();
2056         int sessionProgress = sessionManager.getSessionProgress(contentUri);
2057
2058         if (sessionProgress < 0) {
2059             hideSessionProgress();
2060         } else {
2061             CharSequence progressMessage = sessionManager
2062                     .getSessionProgressMessage(contentUri);
2063             showSessionProgress(progressMessage);
2064             updateSessionProgress(sessionProgress);
2065         }
2066
2067         /* View button */
2068
2069         // We need to add this to a separate DB.
2070         final int viewButtonVisibility;
2071         if (PanoramaMetadataLoader.isPanoramaAndUseViewer(currentData)) {
2072             viewButtonVisibility = CameraAppUI.BottomPanel.VIEWER_PHOTO_SPHERE;
2073         } else if (RgbzMetadataLoader.hasRGBZData(currentData)) {
2074             viewButtonVisibility = CameraAppUI.BottomPanel.VIEWER_REFOCUS;
2075         } else {
2076             viewButtonVisibility = CameraAppUI.BottomPanel.VIEWER_NONE;
2077         }
2078
2079         filmstripBottomPanel.setTinyPlanetEnabled(
2080                 PanoramaMetadataLoader.isPanorama360(currentData));
2081         filmstripBottomPanel.setViewerButtonVisibility(viewButtonVisibility);
2082     }
2083
2084     private class PeekAnimationHandler extends Handler {
2085         private class DataAndCallback {
2086             LocalData mData;
2087             com.android.camera.util.Callback<Bitmap> mCallback;
2088
2089             public DataAndCallback(LocalData data, com.android.camera.util.Callback<Bitmap>
2090                     callback) {
2091                 mData = data;
2092                 mCallback = callback;
2093             }
2094         }
2095
2096         public PeekAnimationHandler(Looper looper) {
2097             super(looper);
2098         }
2099
2100         /**
2101          * Starts the animation decoding job and posts a {@code Runnable} back
2102          * when when the decoding is done.
2103          *
2104          * @param data The data item to decode the thumbnail for.
2105          * @param callback {@link com.android.camera.util.Callback} after the
2106          *                 decoding is done.
2107          */
2108         public void startDecodingJob(final LocalData data,
2109                 final com.android.camera.util.Callback<Bitmap> callback) {
2110             PeekAnimationHandler.this.obtainMessage(0 /** dummy integer **/,
2111                     new DataAndCallback(data, callback)).sendToTarget();
2112         }
2113
2114         @Override
2115         public void handleMessage(Message msg) {
2116             final LocalData data = ((DataAndCallback) msg.obj).mData;
2117             final com.android.camera.util.Callback<Bitmap> callback =
2118                     ((DataAndCallback) msg.obj).mCallback;
2119             if (data == null || callback == null) {
2120                 return;
2121             }
2122
2123             final Bitmap bitmap;
2124             switch (data.getLocalDataType()) {
2125                 case LocalData.LOCAL_IMAGE:
2126                 case LocalData.LOCAL_IN_PROGRESS_DATA:
2127                     FileInputStream stream;
2128                     try {
2129                         stream = new FileInputStream(data.getPath());
2130                     } catch (FileNotFoundException e) {
2131                         Log.e(TAG, "File not found:" + data.getPath());
2132                         return;
2133                     }
2134                     Point dim = CameraUtil.resizeToFill(data.getWidth(), data.getHeight(),
2135                             data.getRotation(), mAboveFilmstripControlLayout.getWidth(),
2136                             mAboveFilmstripControlLayout.getMeasuredHeight());
2137                     if (data.getRotation() % 180 != 0) {
2138                         int dummy = dim.x;
2139                         dim.x = dim.y;
2140                         dim.y = dummy;
2141                     }
2142                     bitmap = LocalDataUtil
2143                             .loadImageThumbnailFromStream(stream, data.getWidth(), data.getHeight(),
2144                                     (int) (dim.x * 0.7f), (int) (dim.y * 0.7),
2145                                     data.getRotation(), MAX_PEEK_BITMAP_PIXELS);
2146                     break;
2147
2148                 case LocalData.LOCAL_VIDEO:
2149                     bitmap = LocalDataUtil.loadVideoThumbnail(data.getPath());
2150                     break;
2151
2152                 default:
2153                     bitmap = null;
2154                     break;
2155             }
2156
2157             if (bitmap == null) {
2158                 return;
2159             }
2160
2161             mMainHandler.post(new Runnable() {
2162                 @Override
2163                 public void run() {
2164                     callback.onCallback(bitmap);
2165                     mCameraAppUI.startPeekAnimation(bitmap, true);
2166                 }
2167             });
2168         }
2169     }
2170
2171     private void showDetailsDialog(int dataId) {
2172         final LocalData data = mDataAdapter.getLocalData(dataId);
2173         if (data == null) {
2174             return;
2175         }
2176         MediaDetails details = data.getMediaDetails(getAndroidContext());
2177         if (details == null) {
2178             return;
2179         }
2180         Dialog detailDialog = DetailsDialog.create(CameraActivity.this, details);
2181         detailDialog.show();
2182
2183     }
2184
2185     /**
2186      * Show or hide action bar items depending on current data type.
2187      */
2188     private void updateActionBarMenu(LocalData data) {
2189         if (mActionBarMenu == null) {
2190             return;
2191         }
2192
2193         MenuItem detailsMenuItem = mActionBarMenu.findItem(R.id.action_details);
2194         if (detailsMenuItem == null) {
2195             return;
2196         }
2197
2198         int type = data.getLocalDataType();
2199         boolean showDetails = (type == LocalData.LOCAL_IMAGE) || (type == LocalData.LOCAL_VIDEO);
2200         detailsMenuItem.setVisible(showDetails);
2201     }
2202 }