OSDN Git Service

Merge "Stops playing shimmy after 10 sessions where mode drawer is swiped open" into...
[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.content.ActivityNotFoundException;
25 import android.content.BroadcastReceiver;
26 import android.content.ContentResolver;
27 import android.content.Context;
28 import android.content.Intent;
29 import android.content.IntentFilter;
30 import android.content.SharedPreferences;
31 import android.content.pm.ActivityInfo;
32 import android.content.res.Configuration;
33 import android.graphics.Bitmap;
34 import android.graphics.Color;
35 import android.graphics.Matrix;
36 import android.graphics.SurfaceTexture;
37 import android.graphics.drawable.ColorDrawable;
38 import android.net.Uri;
39 import android.nfc.NfcAdapter;
40 import android.nfc.NfcAdapter.CreateBeamUrisCallback;
41 import android.nfc.NfcEvent;
42 import android.os.Build;
43 import android.os.Bundle;
44 import android.os.Handler;
45 import android.os.Looper;
46 import android.os.Message;
47 import android.preference.PreferenceManager;
48 import android.provider.MediaStore;
49 import android.provider.Settings;
50 import android.util.Log;
51 import android.view.Gravity;
52 import android.view.KeyEvent;
53 import android.view.LayoutInflater;
54 import android.view.MenuItem;
55 import android.view.MotionEvent;
56 import android.view.View;
57 import android.view.ViewGroup;
58 import android.view.Window;
59 import android.view.WindowManager;
60 import android.widget.FrameLayout;
61 import android.widget.FrameLayout.LayoutParams;
62 import android.widget.ImageView;
63 import android.widget.PopupWindow;
64 import android.widget.ProgressBar;
65 import android.widget.ShareActionProvider;
66 import android.widget.TextView;
67
68 import com.android.camera.app.AppController;
69 import com.android.camera.app.CameraAppUI;
70 import com.android.camera.app.CameraController;
71 import com.android.camera.app.CameraManager;
72 import com.android.camera.app.CameraManagerFactory;
73 import com.android.camera.app.CameraProvider;
74 import com.android.camera.app.CameraServices;
75 import com.android.camera.app.LocationManager;
76 import com.android.camera.app.ModuleManagerImpl;
77 import com.android.camera.app.OrientationManager;
78 import com.android.camera.app.OrientationManagerImpl;
79 import com.android.camera.data.CameraDataAdapter;
80 import com.android.camera.data.FixedLastDataAdapter;
81 import com.android.camera.data.InProgressDataWrapper;
82 import com.android.camera.data.LocalData;
83 import com.android.camera.data.LocalDataAdapter;
84 import com.android.camera.data.LocalMediaObserver;
85 import com.android.camera.data.PanoramaMetadataLoader;
86 import com.android.camera.data.RgbzMetadataLoader;
87 import com.android.camera.data.SimpleViewData;
88 import com.android.camera.filmstrip.FilmstripContentPanel;
89 import com.android.camera.filmstrip.FilmstripController;
90 import com.android.camera.module.ModulesInfo;
91 import com.android.camera.session.CaptureSessionManager;
92 import com.android.camera.session.CaptureSessionManager.SessionListener;
93 import com.android.camera.session.PlaceholderManager;
94 import com.android.camera.settings.SettingsManager;
95 import com.android.camera.settings.SettingsManager.SettingsCapabilities;
96 import com.android.camera.tinyplanet.TinyPlanetFragment;
97 import com.android.camera.ui.MainActivityLayout;
98 import com.android.camera.ui.ModeListView;
99 import com.android.camera.ui.PreviewStatusListener;
100 import com.android.camera.ui.SettingsView;
101 import com.android.camera.util.ApiHelper;
102 import com.android.camera.util.CameraUtil;
103 import com.android.camera.util.FeedbackHelper;
104 import com.android.camera.util.GcamHelper;
105 import com.android.camera.util.IntentHelper;
106 import com.android.camera.util.PhotoSphereHelper.PanoramaViewHelper;
107 import com.android.camera.util.UsageStatistics;
108 import com.android.camera.widget.FilmstripView;
109 import com.android.camera2.R;
110
111 import java.io.File;
112 import java.util.List;
113
114 public class CameraActivity extends Activity
115         implements AppController, CameraManager.CameraOpenCallback,
116         ActionBar.OnMenuVisibilityListener, ShareActionProvider.OnShareTargetSelectedListener,
117         OrientationManager.OnOrientationChangeListener {
118
119     private static final String TAG = "CameraActivity";
120
121     private static final String INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE =
122             "android.media.action.STILL_IMAGE_CAMERA_SECURE";
123     public static final String ACTION_IMAGE_CAPTURE_SECURE =
124             "android.media.action.IMAGE_CAPTURE_SECURE";
125
126     // The intent extra for camera from secure lock screen. True if the gallery
127     // should only show newly captured pictures. sSecureAlbumId does not
128     // increment. This is used when switching between camera, camcorder, and
129     // panorama. If the extra is not set, it is in the normal camera mode.
130     public static final String SECURE_CAMERA_EXTRA = "secure_camera";
131
132     /**
133      * Request code from an activity we started that indicated that we do not want
134      * to reset the view to the preview in onResume.
135      */
136     public static final int REQ_CODE_DONT_SWITCH_TO_PREVIEW = 142;
137
138     public static final int REQ_CODE_GCAM_DEBUG_POSTCAPTURE = 999;
139
140     private static final int MSG_HIDE_ACTION_BAR = 1;
141     private static final int MSG_CLEAR_SCREEN_ON_FLAG = 2;
142     private static final long SCREEN_DELAY_MS = 2 * 60 * 1000;  // 2 mins.
143
144     /**
145      * Whether onResume should reset the view to the preview.
146      */
147     private boolean mResetToPreviewOnResume = true;
148
149     /**
150      * This data adapter is used by FilmStripView.
151      */
152     private LocalDataAdapter mDataAdapter;
153
154     /**
155      * TODO: This should be moved to the app level.
156      */
157     private SettingsManager mSettingsManager;
158
159     /**
160      * TODO: This should be moved to the app level.
161      */
162     private SettingsController mSettingsController;
163     private ModeListView mModeListView;
164     private int mCurrentModeIndex;
165     private CameraModule mCurrentModule;
166     private ModuleManagerImpl mModuleManager;
167     private FrameLayout mAboveFilmstripControlLayout;
168     private FilmstripController mFilmstripController;
169     private boolean mFilmstripVisible;
170     private TextView mBottomProgressText;
171     private ProgressBar mBottomProgressBar;
172     private View mSessionProgressPanel;
173     private int mResultCodeForTesting;
174     private Intent mResultDataForTesting;
175     private OnScreenHint mStorageHint;
176     private long mStorageSpaceBytes = Storage.LOW_STORAGE_THRESHOLD_BYTES;
177     private boolean mAutoRotateScreen;
178     private boolean mSecureCamera;
179     private int mLastRawOrientation;
180     private OrientationManagerImpl mOrientationManager;
181     private LocationManager mLocationManager;
182     private ButtonManager mButtonManager;
183     private Handler mMainHandler;
184     private PanoramaViewHelper mPanoramaViewHelper;
185     private ActionBar mActionBar;
186     private ViewGroup mUndoDeletionBar;
187     private boolean mIsUndoingDeletion = false;
188
189     private final Uri[] mNfcPushUris = new Uri[1];
190
191     private LocalMediaObserver mLocalImagesObserver;
192     private LocalMediaObserver mLocalVideosObserver;
193
194     private boolean mPendingDeletion = false;
195
196     private CameraController mCameraController;
197     private boolean mPaused;
198     private CameraAppUI mCameraAppUI;
199
200     private FeedbackHelper mFeedbackHelper;
201
202     @Override
203     public CameraAppUI getCameraAppUI() {
204         return mCameraAppUI;
205     }
206
207     // close activity when screen turns off
208     private final BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() {
209         @Override
210         public void onReceive(Context context, Intent intent) {
211             finish();
212         }
213     };
214
215     /**
216      * Whether the screen is kept turned on.
217      */
218     private boolean mKeepScreenOn;
219     private int mLastLayoutOrientation;
220     private final CameraAppUI.BottomControls.Listener mMyFilmstripBottomControlListener =
221             new CameraAppUI.BottomControls.Listener() {
222
223                 /**
224                  * If the current photo is a photo sphere, this will launch the Photo Sphere
225                  * panorama viewer.
226                  */
227                 @Override
228                 public void onExternalViewer() {
229                     if (mPanoramaViewHelper == null) {
230                         return;
231                     }
232                     final LocalData data = getCurrentLocalData();
233                     if (data == null) {
234                         return;
235                     }
236                     final Uri contentUri = data.getContentUri();
237                     if (contentUri == Uri.EMPTY) {
238                         return;
239                     }
240
241                     if (PanoramaMetadataLoader.isPanorama(data)) {
242                         mPanoramaViewHelper.showPanorama(contentUri);
243                     } else if (RgbzMetadataLoader.hasRGBZData(data)) {
244                         mPanoramaViewHelper.showRgbz(contentUri);
245                     }
246                 }
247
248                 @Override
249                 public void onEdit() {
250                     LocalData data = getCurrentLocalData();
251                     if (data == null) {
252                         return;
253                     }
254                     launchEditor(data);
255                 }
256
257                 @Override
258                 public void onTinyPlanet() {
259                     LocalData data = getCurrentLocalData();
260                     if (data == null) {
261                         return;
262                     }
263                     final int currentDataId = getCurrentDataId();
264                     UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
265                             UsageStatistics.ACTION_EDIT, null, 0,
266                             UsageStatistics.hashFileName(fileNameFromDataID(currentDataId)));
267                     launchTinyPlanetEditor(data);
268                 }
269
270                 @Override
271                 public void onDelete() {
272                     final int currentDataId = getCurrentDataId();
273                     UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
274                             UsageStatistics.ACTION_DELETE, null, 0,
275                             UsageStatistics.hashFileName(fileNameFromDataID(currentDataId)));
276                     removeData(currentDataId);
277                 }
278
279                 @Override
280                 public void onShare() {
281                     final LocalData data = getCurrentLocalData();
282                     Intent shareIntent = getShareIntentByData(data);
283                     if (shareIntent != null) {
284                         try {
285                             launchActivityByIntent(shareIntent);
286                             mCameraAppUI.getFilmstripBottomControls().setShareEnabled(false);
287                         } catch (ActivityNotFoundException ex) {
288                             // Nothing.
289                         }
290                     }
291                 }
292
293                 private int getCurrentDataId() {
294                     return mFilmstripController.getCurrentId();
295                 }
296
297                 private LocalData getCurrentLocalData() {
298                     return mDataAdapter.getLocalData(getCurrentDataId());
299                 }
300
301                 /**
302                  * Sets up the share intent and NFC properly according to the data.
303                  *
304                  * @param data The data to be shared.
305                  */
306                 private Intent getShareIntentByData(final LocalData data) {
307                     Intent intent = null;
308                     final Uri contentUri = data.getContentUri();
309                     if (PanoramaMetadataLoader.isPanorama360(data) &&
310                             data.getContentUri() != Uri.EMPTY) {
311                         intent = new Intent(Intent.ACTION_SEND);
312                         intent.setType("application/vnd.google.panorama360+jpg");
313                         intent.putExtra(Intent.EXTRA_STREAM, contentUri);
314                     } else if (data.isDataActionSupported(LocalData.DATA_ACTION_SHARE)) {
315                         final String mimeType = data.getMimeType();
316                         intent = getShareIntentFromType(mimeType);
317                         if (intent != null) {
318                             intent.putExtra(Intent.EXTRA_STREAM, contentUri);
319                             intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
320                         }
321                     }
322                     return intent;
323                 }
324
325                 /**
326                  * Get the share intent according to the mimeType
327                  *
328                  * @param mimeType The mimeType of current data.
329                  * @return the video/image's ShareIntent or null if mimeType is invalid.
330                  */
331                 private Intent getShareIntentFromType(String mimeType) {
332                     // Lazily create the intent object.
333                     Intent intent = new Intent(Intent.ACTION_SEND);
334                     if (mimeType.startsWith("video/")) {
335                         intent.setType("video/*");
336                     } else {
337                         if (mimeType.startsWith("image/")) {
338                             intent.setType("image/*");
339                         } else {
340                             Log.w(TAG, "unsupported mimeType " + mimeType);
341                         }
342                     }
343                     return intent;
344                 }
345             };
346
347     private ComboPreferences mPreferences;
348     private ContentResolver mContentResolver;
349
350     @Override
351     public void onCameraOpened(CameraManager.CameraProxy camera) {
352         if (!mModuleManager.getModuleAgent(mCurrentModeIndex).requestAppForCamera()) {
353             // We shouldn't be here. Just close the camera and leave.
354             camera.release(false);
355             throw new IllegalStateException("Camera opened but the module shouldn't be " +
356                     "requesting");
357         }
358         if (mCurrentModule != null) {
359             SettingsCapabilities capabilities =
360                 SettingsController.getSettingsCapabilities(camera);
361             mSettingsManager.changeCamera(camera.getCameraId(), capabilities);
362             mCurrentModule.onCameraAvailable(camera);
363         }
364         mCameraAppUI.onChangeCamera();
365     }
366
367     @Override
368     public void onCameraDisabled(int cameraId) {
369         UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA, UsageStatistics.ACTION_OPEN_FAIL,
370                 "security");
371
372         CameraUtil.showErrorAndFinish(this, R.string.camera_disabled);
373     }
374
375     @Override
376     public void onDeviceOpenFailure(int cameraId) {
377         UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
378                 UsageStatistics.ACTION_OPEN_FAIL, "open");
379
380         CameraUtil.showErrorAndFinish(this, R.string.cannot_connect_camera);
381     }
382
383     @Override
384     public void onReconnectionFailure(CameraManager mgr) {
385         UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
386                 UsageStatistics.ACTION_OPEN_FAIL, "reconnect");
387
388         CameraUtil.showErrorAndFinish(this, R.string.cannot_connect_camera);
389     }
390
391     private class MainHandler extends Handler {
392         public MainHandler(Looper looper) {
393             super(looper);
394         }
395
396         @Override
397         public void handleMessage(Message msg) {
398             switch (msg.what) {
399                 case MSG_HIDE_ACTION_BAR: {
400                     removeMessages(MSG_HIDE_ACTION_BAR);
401                     CameraActivity.this.setFilmstripUiVisibility(false);
402                     break;
403                 }
404
405                 case MSG_CLEAR_SCREEN_ON_FLAG:  {
406                     if (!mPaused) {
407                         getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
408                     }
409                     break;
410                 }
411
412                 default:
413             }
414         }
415     }
416
417     private String fileNameFromDataID(int dataID) {
418         final LocalData localData = mDataAdapter.getLocalData(dataID);
419
420         File localFile = new File(localData.getPath());
421         return localFile.getName();
422     }
423
424     private final FilmstripContentPanel.Listener mFilmstripListener =
425             new FilmstripContentPanel.Listener() {
426
427                 @Override
428                 public void onFilmstripHidden() {
429                     mFilmstripVisible = false;
430                     CameraActivity.this.setFilmstripUiVisibility(false);
431                     // When the user hide the filmstrip (either swipe out or
432                     // tap on back key) we move to the first item so next time
433                     // when the user swipe in the filmstrip, the most recent
434                     // one is shown.
435                     mFilmstripController.goToFirstItem();
436                     if (mCurrentModule != null) {
437                         mCurrentModule.onPreviewVisibilityChanged(true);
438                     }
439                 }
440
441                 @Override
442                 public void onFilmstripShown() {
443                     mFilmstripVisible = true;
444                     updateUiByData(mFilmstripController.getCurrentId());
445                     if (mCurrentModule != null) {
446                         mCurrentModule.onPreviewVisibilityChanged(false);
447                     }
448                 }
449
450                 @Override
451                 public void onDataPromoted(int dataID) {
452                     UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
453                             UsageStatistics.ACTION_DELETE, "promoted", 0,
454                             UsageStatistics.hashFileName(fileNameFromDataID(dataID)));
455
456                     removeData(dataID);
457                 }
458
459                 @Override
460                 public void onDataDemoted(int dataID) {
461                     UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
462                             UsageStatistics.ACTION_DELETE, "demoted", 0,
463                             UsageStatistics.hashFileName(fileNameFromDataID(dataID)));
464
465                     removeData(dataID);
466                 }
467
468                 @Override
469                 public void onEnterFullScreen(int dataId) {
470                     if (mFilmstripVisible) {
471                         CameraActivity.this.setFilmstripUiVisibility(false);
472                     }
473                 }
474
475                 @Override
476                 public void onLeaveFullScreen(int dataId) {
477                     // Do nothing.
478                 }
479
480                 @Override
481                 public void onEnterFilmstrip(int dataId) {
482                     if (mFilmstripVisible) {
483                         CameraActivity.this.setFilmstripUiVisibility(true);
484                     }
485                 }
486
487                 @Override
488                 public void onLeaveFilmstrip(int dataId) {
489                     // Do nothing.
490                 }
491
492                 @Override
493                 public void onDataReloaded() {
494                     if (!mFilmstripVisible) {
495                         return;
496                     }
497                     updateUiByData(mFilmstripController.getCurrentId());
498                 }
499
500                 @Override
501                 public void onDataUpdated(int dataId) {
502                     if (!mFilmstripVisible) {
503                         return;
504                     }
505                     updateUiByData(mFilmstripController.getCurrentId());
506                 }
507
508                 @Override
509                 public void onEnterZoomView(int dataID) {
510                     if (mFilmstripVisible) {
511                         CameraActivity.this.setFilmstripUiVisibility(false);
512                     }
513                 }
514
515                 @Override
516                 public void onDataFocusChanged(final int prevDataId, final int newDataId) {
517                     if (!mFilmstripVisible) {
518                         return;
519                     }
520                     // TODO: This callback is UI event callback, should always
521                     // happen on UI thread. Find the reason for this
522                     // runOnUiThread() and fix it.
523                     runOnUiThread(new Runnable() {
524                         @Override
525                         public void run() {
526                             updateUiByData(newDataId);
527                         }
528                     });
529                 }
530             };
531
532     private final LocalDataAdapter.LocalDataListener mLocalDataListener =
533             new LocalDataAdapter.LocalDataListener() {
534                 @Override
535                 public void onMetadataUpdated(List<Integer> updatedData) {
536                     int currentDataId = mFilmstripController.getCurrentId();
537                     for (Integer dataId : updatedData) {
538                         if (dataId == currentDataId) {
539                             updateBottomControlsByData(mDataAdapter.getLocalData(dataId));
540                         }
541                     }
542                 }
543             };
544
545     public void gotoGallery() {
546         UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA, UsageStatistics.ACTION_FILMSTRIP,
547                 "thumbnailTap");
548
549         mFilmstripController.goToNextItem();
550     }
551
552     /**
553      * If {@param visible} is false, this hides the action bar and switches the
554      * filmstrip UI to lights-out mode.
555      */
556     // TODO: This should not be called outside of the activity.
557     public void setFilmstripUiVisibility(boolean visible) {
558         mMainHandler.removeMessages(MSG_HIDE_ACTION_BAR);
559
560         int currentSystemUIVisibility = mAboveFilmstripControlLayout.getSystemUiVisibility();
561         int newSystemUIVisibility = (visible ? View.SYSTEM_UI_FLAG_VISIBLE : View.SYSTEM_UI_FLAG_FULLSCREEN);
562         if (newSystemUIVisibility != currentSystemUIVisibility) {
563             mAboveFilmstripControlLayout.setSystemUiVisibility(newSystemUIVisibility);
564         }
565
566         boolean currentActionBarVisibility = mActionBar.isShowing();
567         mCameraAppUI.getFilmstripBottomControls().setVisible(visible);
568         if (visible != currentActionBarVisibility) {
569             if (visible) {
570                 mActionBar.show();
571             } else {
572                 mActionBar.hide();
573             }
574         }
575     }
576
577     private void hideSessionProgress() {
578         mSessionProgressPanel.setVisibility(View.GONE);
579     }
580
581     private void showSessionProgress(CharSequence message) {
582         mBottomProgressText.setText(message);
583         mSessionProgressPanel.setVisibility(View.VISIBLE);
584     }
585
586     private void updateSessionProgress(int progress) {
587         mBottomProgressBar.setProgress(progress);
588     }
589
590     @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
591     private void setupNfcBeamPush() {
592         NfcAdapter adapter = NfcAdapter.getDefaultAdapter(CameraActivity.this);
593         if (adapter == null) {
594             return;
595         }
596
597         if (!ApiHelper.HAS_SET_BEAM_PUSH_URIS) {
598             // Disable beaming
599             adapter.setNdefPushMessage(null, CameraActivity.this);
600             return;
601         }
602
603         adapter.setBeamPushUris(null, CameraActivity.this);
604         adapter.setBeamPushUrisCallback(new CreateBeamUrisCallback() {
605             @Override
606             public Uri[] createBeamUris(NfcEvent event) {
607                 return mNfcPushUris;
608             }
609         }, CameraActivity.this);
610     }
611
612     @Override
613     public void onMenuVisibilityChanged(boolean isVisible) {
614         // TODO: Remove this or bring back the original implementation: cancel
615         // auto-hide actionbar.
616     }
617
618     @Override
619     public boolean onShareTargetSelected(ShareActionProvider shareActionProvider, Intent intent) {
620         int currentDataId = mFilmstripController.getCurrentId();
621         if (currentDataId < 0) {
622             return false;
623         }
624         UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA, UsageStatistics.ACTION_SHARE,
625                 intent.getComponent().getPackageName(), 0,
626                 UsageStatistics.hashFileName(fileNameFromDataID(currentDataId)));
627         return true;
628     }
629
630     // Note: All callbacks come back on the main thread.
631     private final SessionListener mSessionListener =
632             new SessionListener() {
633                 @Override
634                 public void onSessionQueued(final Uri uri) {
635                     notifyNewMedia(uri);
636                     int dataID = mDataAdapter.findDataByContentUri(uri);
637                     if (dataID != -1) {
638                         // Don't allow special UI actions (swipe to
639                         // delete, for example) on in-progress data.
640                         LocalData d = mDataAdapter.getLocalData(dataID);
641                         InProgressDataWrapper newData = new InProgressDataWrapper(d);
642                         mDataAdapter.updateData(dataID, newData);
643                     }
644                 }
645
646                 @Override
647                 public void onSessionDone(final Uri uri) {
648                     Log.v(TAG, "onSessionDone:" + uri);
649                     int doneID = mDataAdapter.findDataByContentUri(uri);
650                     int currentDataId = mFilmstripController.getCurrentId();
651
652                     if (currentDataId == doneID) {
653                         hideSessionProgress();
654                         updateSessionProgress(0);
655                     }
656                     mDataAdapter.refresh(uri, /* isInProgress */ false);
657                 }
658
659                 @Override
660                 public void onSessionProgress(final Uri uri, final int progress) {
661                     if (progress < 0) {
662                         // Do nothing, there is no task for this URI.
663                         return;
664                     }
665                     int currentDataId = mFilmstripController.getCurrentId();
666                     if (currentDataId == -1) {
667                         return;
668                     }
669                     if (uri.equals(
670                             mDataAdapter.getLocalData(currentDataId).getContentUri())) {
671                         updateSessionProgress(progress);
672                     }
673                 }
674
675                 @Override
676                 public void onSessionUpdated(Uri uri) {
677                     mDataAdapter.refresh(uri, /* isInProgress */ true);
678                 }
679             };
680
681     @Override
682     public Context getAndroidContext() {
683         return this;
684     }
685
686     @Override
687     public void launchActivityByIntent(Intent intent) {
688         startActivityForResult(intent, REQ_CODE_DONT_SWITCH_TO_PREVIEW);
689     }
690
691     @Override
692     public int getCurrentModuleIndex() {
693         return mCurrentModeIndex;
694     }
695
696     @Override
697     public SurfaceTexture getPreviewBuffer() {
698         // TODO: implement this
699         return null;
700     }
701
702     @Override
703     public void onPreviewStarted() {
704         mCameraAppUI.onPreviewStarted();
705     }
706
707     @Override
708     public void addPreviewAreaSizeChangedListener(
709             PreviewStatusListener.PreviewAreaSizeChangedListener listener) {
710         mCameraAppUI.addPreviewAreaSizeChangedListener(listener);
711     }
712
713     @Override
714     public void removePreviewAreaSizeChangedListener(
715             PreviewStatusListener.PreviewAreaSizeChangedListener listener) {
716         mCameraAppUI.removePreviewAreaSizeChangedListener(listener);
717     }
718
719     @Override
720     public void setupOneShotPreviewListener() {
721         mCameraController.setOneShotPreviewCallback(mMainHandler,
722                 new CameraManager.CameraPreviewDataCallback() {
723                     @Override
724                     public void onPreviewFrame(byte[] data, CameraManager.CameraProxy camera) {
725                         mCameraAppUI.onNewPreviewFrame();
726                     }
727                 });
728     }
729
730     @Override
731     public void updatePreviewAspectRatio(float aspectRatio) {
732         mCameraAppUI.updatePreviewAspectRatio(aspectRatio);
733     }
734
735     @Override
736     public boolean shouldShowShimmy() {
737         int remainingTimes = mSettingsManager.getInt(
738                 SettingsManager.SETTING_SHIMMY_REMAINING_PLAY_TIMES_INDEX);
739         return remainingTimes > 0;
740     }
741
742     @Override
743     public void decrementShimmyPlayTimes() {
744         int remainingTimes = mSettingsManager.getInt(
745                 SettingsManager.SETTING_SHIMMY_REMAINING_PLAY_TIMES_INDEX) - 1;
746         if (remainingTimes >= 0) {
747             mSettingsManager.setInt(SettingsManager.SETTING_SHIMMY_REMAINING_PLAY_TIMES_INDEX,
748                     remainingTimes);
749         }
750      }
751
752     @Override
753     public void updatePreviewTransform(Matrix matrix) {
754         mCameraAppUI.updatePreviewTransform(matrix);
755     }
756
757     @Override
758     public void setPreviewStatusListener(PreviewStatusListener previewStatusListener) {
759         mCameraAppUI.setPreviewStatusListener(previewStatusListener);
760     }
761
762     @Override
763     public FrameLayout getModuleLayoutRoot() {
764         return mCameraAppUI.getModuleRootView();
765     }
766
767     @Override
768     public void setShutterEventsListener(ShutterEventsListener listener) {
769         // TODO: implement this
770     }
771
772     @Override
773     public void setShutterEnabled(boolean enabled) {
774         // TODO: implement this
775     }
776
777     @Override
778     public boolean isShutterEnabled() {
779         // TODO: implement this
780         return false;
781     }
782
783     @Override
784     public void startPreCaptureAnimation() {
785         mCameraAppUI.startPreCaptureAnimation();
786     }
787
788     @Override
789     public void cancelPreCaptureAnimation() {
790         // TODO: implement this
791     }
792
793     @Override
794     public void startPostCaptureAnimation() {
795         // TODO: implement this
796     }
797
798     @Override
799     public void startPostCaptureAnimation(Bitmap thumbnail) {
800         // TODO: implement this
801     }
802
803     @Override
804     public void cancelPostCaptureAnimation() {
805         // TODO: implement this
806     }
807
808     @Override
809     public OrientationManager getOrientationManager() {
810         return mOrientationManager;
811     }
812
813     @Override
814     public LocationManager getLocationManager() {
815         return mLocationManager;
816     }
817
818     @Override
819     public void lockOrientation() {
820         mOrientationManager.lockOrientation();
821     }
822
823     @Override
824     public void unlockOrientation() {
825         mOrientationManager.unlockOrientation();
826     }
827
828     @Override
829     public void notifyNewMedia(Uri uri) {
830         ContentResolver cr = getContentResolver();
831         String mimeType = cr.getType(uri);
832         if (mimeType.startsWith("video/")) {
833             sendBroadcast(new Intent(CameraUtil.ACTION_NEW_VIDEO, uri));
834             mDataAdapter.addNewVideo(uri);
835         } else if (mimeType.startsWith("image/")) {
836             CameraUtil.broadcastNewPicture(this, uri);
837             mDataAdapter.addNewPhoto(uri);
838         } else if (mimeType.startsWith(PlaceholderManager.PLACEHOLDER_MIME_TYPE)) {
839             mDataAdapter.addNewPhoto(uri);
840         } else {
841             android.util.Log.w(TAG, "Unknown new media with MIME type:"
842                     + mimeType + ", uri:" + uri);
843         }
844     }
845
846     @Override
847     public void enableKeepScreenOn(boolean enabled) {
848         if (mPaused) {
849             return;
850         }
851
852         mKeepScreenOn = enabled;
853         if (mKeepScreenOn) {
854             mMainHandler.removeMessages(MSG_CLEAR_SCREEN_ON_FLAG);
855             getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
856         } else {
857             keepScreenOnForAWhile();
858         }
859     }
860
861     @Override
862     public CameraProvider getCameraProvider() {
863         return mCameraController;
864     }
865
866     private void removeData(int dataID) {
867         mDataAdapter.removeData(dataID);
868         if (mDataAdapter.getTotalNumber() > 1) {
869             showUndoDeletionBar();
870         } else {
871             // If camera preview is the only view left in filmstrip,
872             // no need to show undo bar.
873             mPendingDeletion = true;
874             performDeletion();
875         }
876     }
877
878     @Override
879     public boolean onOptionsItemSelected(MenuItem item) {
880         // Handle presses on the action bar items
881         switch (item.getItemId()) {
882             case android.R.id.home:
883                 if (mFilmstripVisible && IntentHelper.shouldLaunchGalleryOnUpAction()) {
884                     startGallery();
885                 } else {
886                     onBackPressed();
887                 }
888                 return true;
889             default:
890                 return super.onOptionsItemSelected(item);
891         }
892     }
893
894     private boolean isCaptureIntent() {
895         if (MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction())
896                 || MediaStore.ACTION_IMAGE_CAPTURE.equals(getIntent().getAction())
897                 || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(getIntent().getAction())) {
898             return true;
899         } else {
900             return false;
901         }
902     }
903
904     @Override
905     public void onCreate(Bundle state) {
906         super.onCreate(state);
907         GcamHelper.init(getContentResolver());
908
909         getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
910         setContentView(R.layout.activity_main);
911         mActionBar = getActionBar();
912         mActionBar.addOnMenuVisibilityListener(this);
913         mMainHandler = new MainHandler(getMainLooper());
914         mCameraController =
915                 new CameraController(this, this, mMainHandler,
916                         CameraManagerFactory.getAndroidCameraManager());
917         mPreferences = new ComboPreferences(getAndroidContext());
918         mContentResolver = this.getContentResolver();
919
920         mSettingsManager = new SettingsManager(this, mCameraController.getNumberOfCameras());
921
922         // Remove this after we get rid of ComboPreferences.
923         int cameraId = Integer.parseInt(mSettingsManager.get(SettingsManager.SETTING_CAMERA_ID));
924         mPreferences.setLocalId(this, cameraId);
925         CameraSettings.upgradeGlobalPreferences(mPreferences,
926                 mCameraController.getNumberOfCameras());
927         // TODO: Try to move all the resources allocation to happen as soon as
928         // possible so we can call module.init() at the earliest time.
929         mModuleManager = new ModuleManagerImpl();
930         ModulesInfo.setupModules(this, mModuleManager);
931
932         mModeListView = (ModeListView) findViewById(R.id.mode_list_layout);
933         mModeListView.init(mModuleManager.getSupportedModeIndexList());
934         if (ApiHelper.HAS_ROTATION_ANIMATION) {
935             setRotationAnimation();
936         }
937
938         // Check if this is in the secure camera mode.
939         Intent intent = getIntent();
940         String action = intent.getAction();
941         if (INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action)
942                 || ACTION_IMAGE_CAPTURE_SECURE.equals(action)) {
943             mSecureCamera = true;
944         } else {
945             mSecureCamera = intent.getBooleanExtra(SECURE_CAMERA_EXTRA, false);
946         }
947
948         if (mSecureCamera) {
949             // Change the window flags so that secure camera can show when locked
950             Window win = getWindow();
951             WindowManager.LayoutParams params = win.getAttributes();
952             params.flags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
953             win.setAttributes(params);
954
955             // Filter for screen off so that we can finish secure camera activity
956             // when screen is off.
957             IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
958             registerReceiver(mScreenOffReceiver, filter);
959         }
960         mCameraAppUI = new CameraAppUI(this,
961                 (MainActivityLayout) findViewById(R.id.activity_root_view),
962                 isSecureCamera(), isCaptureIntent());
963
964         mCameraAppUI.setFilmstripBottomControlsListener(mMyFilmstripBottomControlListener);
965
966         mAboveFilmstripControlLayout =
967                 (FrameLayout) findViewById(R.id.camera_filmstrip_content_layout);
968
969         // Add the session listener so we can track the session progress updates.
970         getServices().getCaptureSessionManager().addSessionListener(mSessionListener);
971         mSessionProgressPanel = findViewById(R.id.pano_session_progress_panel);
972         mBottomProgressBar = (ProgressBar) findViewById(R.id.pano_session_progress_bar);
973         mBottomProgressText = (TextView) findViewById(R.id.pano_session_progress_text);
974         mFilmstripController = ((FilmstripView) findViewById(R.id.filmstrip_view)).getController();
975         mFilmstripController.setImageGap(
976                 getResources().getDimensionPixelSize(R.dimen.camera_film_strip_gap));
977         mPanoramaViewHelper = new PanoramaViewHelper(this);
978         mPanoramaViewHelper.onCreate();
979         // Set up the camera preview first so the preview shows up ASAP.
980         mDataAdapter = new CameraDataAdapter(getApplicationContext(),
981                 new ColorDrawable(getResources().getColor(R.color.photo_placeholder)));
982         mDataAdapter.setLocalDataListener(mLocalDataListener);
983
984         mCameraAppUI.getFilmstripContentPanel().setFilmstripListener(mFilmstripListener);
985
986         mLocationManager = new LocationManager(this);
987
988         mSettingsController = new SettingsController(this, mSettingsManager, mLocationManager);
989
990         int modeIndex = -1;
991         if (MediaStore.INTENT_ACTION_VIDEO_CAMERA.equals(getIntent().getAction())
992                 || MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction())) {
993             modeIndex = ModeListView.MODE_VIDEO;
994         } else if (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(getIntent().getAction())
995                 || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(getIntent()
996                         .getAction())) {
997             modeIndex = ModeListView.MODE_PHOTO;
998             if (mSettingsManager.getInt(SettingsManager.SETTING_STARTUP_MODULE_INDEX)
999                         == ModeListView.MODE_GCAM && GcamHelper.hasGcamCapture()) {
1000                 modeIndex = ModeListView.MODE_GCAM;
1001             }
1002         } else if (MediaStore.ACTION_IMAGE_CAPTURE.equals(getIntent().getAction())
1003                 || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(getIntent().getAction())) {
1004             modeIndex = ModeListView.MODE_PHOTO;
1005         } else {
1006             // If the activity has not been started using an explicit intent,
1007             // read the module index from the last time the user changed modes
1008             modeIndex = mSettingsManager.getInt(SettingsManager.SETTING_STARTUP_MODULE_INDEX);
1009             if ((modeIndex == ModeListView.MODE_GCAM &&
1010                     !GcamHelper.hasGcamCapture()) || modeIndex < 0) {
1011                 modeIndex = ModeListView.MODE_PHOTO;
1012             }
1013         }
1014
1015         mOrientationManager = new OrientationManagerImpl(this);
1016         mOrientationManager.addOnOrientationChangeListener(mMainHandler, this);
1017
1018         setModuleFromModeIndex(modeIndex);
1019
1020         // TODO: Remove this when refactor is done.
1021         if (modeIndex == ModulesInfo.MODULE_PHOTO
1022                 || modeIndex == ModulesInfo.MODULE_VIDEO
1023                 || modeIndex == ModulesInfo.MODULE_GCAM
1024                 || modeIndex == ModulesInfo.MODULE_CRAFT
1025                 || modeIndex == ModulesInfo.MODULE_REFOCUS) {
1026             mCameraAppUI.prepareModuleUI();
1027         }
1028
1029         syncBottomBarColor();
1030         mCurrentModule.init(this, isSecureCamera(), isCaptureIntent());
1031
1032         if (!mSecureCamera) {
1033             mFilmstripController.setDataAdapter(mDataAdapter);
1034             if (!isCaptureIntent()) {
1035                 mDataAdapter.requestLoad();
1036             }
1037         } else {
1038             // Put a lock placeholder as the last image by setting its date to
1039             // 0.
1040             ImageView v = (ImageView) getLayoutInflater().inflate(
1041                     R.layout.secure_album_placeholder, null);
1042             v.setOnClickListener(new View.OnClickListener() {
1043                 @Override
1044                 public void onClick(View view) {
1045                     startGallery();
1046                     finish();
1047                 }
1048             });
1049             mDataAdapter = new FixedLastDataAdapter(
1050                     getApplicationContext(),
1051                     mDataAdapter,
1052                     new SimpleViewData(
1053                             v,
1054                             v.getDrawable().getIntrinsicWidth(),
1055                             v.getDrawable().getIntrinsicHeight(),
1056                             0, 0));
1057             // Flush out all the original data.
1058             mDataAdapter.flush();
1059             mFilmstripController.setDataAdapter(mDataAdapter);
1060         }
1061
1062         setupNfcBeamPush();
1063
1064         mLocalImagesObserver = new LocalMediaObserver();
1065         mLocalVideosObserver = new LocalMediaObserver();
1066
1067         getContentResolver().registerContentObserver(
1068                 MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true,
1069                 mLocalImagesObserver);
1070         getContentResolver().registerContentObserver(
1071                 MediaStore.Video.Media.EXTERNAL_CONTENT_URI, true,
1072                 mLocalVideosObserver);
1073         if (FeedbackHelper.feedbackAvailable()) {
1074             mFeedbackHelper = new FeedbackHelper(this);
1075         }
1076     }
1077
1078     private void setRotationAnimation() {
1079         int rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
1080         rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE;
1081         Window win = getWindow();
1082         WindowManager.LayoutParams winParams = win.getAttributes();
1083         winParams.rotationAnimation = rotationAnimation;
1084         win.setAttributes(winParams);
1085     }
1086
1087     @Override
1088     public void onUserInteraction() {
1089         super.onUserInteraction();
1090         if (!isFinishing()) {
1091             keepScreenOnForAWhile();
1092         }
1093     }
1094
1095     @Override
1096     public boolean dispatchTouchEvent(MotionEvent ev) {
1097         boolean result = super.dispatchTouchEvent(ev);
1098         if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
1099             // Real deletion is postponed until the next user interaction after
1100             // the gesture that triggers deletion. Until real deletion is performed,
1101             // users can click the undo button to bring back the image that they
1102             // chose to delete.
1103             if (mPendingDeletion && !mIsUndoingDeletion) {
1104                 performDeletion();
1105             }
1106         }
1107         return result;
1108     }
1109
1110     @Override
1111     public void onPause() {
1112         mPaused = true;
1113
1114         // Delete photos that are pending deletion
1115         performDeletion();
1116         mCurrentModule.pause();
1117         mOrientationManager.pause();
1118         // Close the camera and wait for the operation done.
1119         mCameraController.closeCamera();
1120
1121         mLocalImagesObserver.setActivityPaused(true);
1122         mLocalVideosObserver.setActivityPaused(true);
1123         resetScreenOn();
1124         super.onPause();
1125     }
1126
1127     @Override
1128     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
1129         if (requestCode == REQ_CODE_DONT_SWITCH_TO_PREVIEW) {
1130             mResetToPreviewOnResume = false;
1131         } else {
1132             super.onActivityResult(requestCode, resultCode, data);
1133         }
1134     }
1135
1136     @Override
1137     public void onResume() {
1138         mPaused = false;
1139
1140         mLastLayoutOrientation = getResources().getConfiguration().orientation;
1141
1142         // TODO: Handle this in OrientationManager.
1143         // Auto-rotate off
1144         if (Settings.System.getInt(getContentResolver(),
1145                 Settings.System.ACCELEROMETER_ROTATION, 0) == 0) {
1146             setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
1147             mAutoRotateScreen = false;
1148         } else {
1149             setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
1150             mAutoRotateScreen = true;
1151         }
1152
1153         UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
1154                 UsageStatistics.ACTION_FOREGROUNDED, this.getClass().getSimpleName());
1155
1156         mOrientationManager.resume();
1157         super.onResume();
1158         mCurrentModule.resume();
1159         setSwipingEnabled(true);
1160
1161         if (mResetToPreviewOnResume) {
1162             mCameraAppUI.resume();
1163         }
1164         // The share button might be disabled to avoid double tapping.
1165         mCameraAppUI.getFilmstripBottomControls().setShareEnabled(true);
1166         // Default is showing the preview, unless disabled by explicitly
1167         // starting an activity we want to return from to the filmstrip rather
1168         // than the preview.
1169         mResetToPreviewOnResume = true;
1170
1171         if (mLocalVideosObserver.isMediaDataChangedDuringPause()
1172                 || mLocalImagesObserver.isMediaDataChangedDuringPause()) {
1173             if (!mSecureCamera) {
1174                 // If it's secure camera, requestLoad() should not be called
1175                 // as it will load all the data.
1176                 if (!mFilmstripVisible) {
1177                     mDataAdapter.requestLoad();
1178                 }
1179             }
1180         }
1181         mLocalImagesObserver.setActivityPaused(false);
1182         mLocalVideosObserver.setActivityPaused(false);
1183
1184         keepScreenOnForAWhile();
1185
1186         // Lights-out mode at all times.
1187         findViewById(R.id.activity_root_view).setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE);
1188     }
1189
1190     @Override
1191     public void onStart() {
1192         super.onStart();
1193         mPanoramaViewHelper.onStart();
1194         boolean recordLocation = RecordLocationPreference.get(
1195                 mPreferences, mContentResolver);
1196         mLocationManager.recordLocation(recordLocation);
1197     }
1198
1199     @Override
1200     protected void onStop() {
1201         mPanoramaViewHelper.onStop();
1202         if (mFeedbackHelper != null) {
1203             mFeedbackHelper.stopFeedback();
1204         }
1205
1206         mLocationManager.disconnect();
1207         CameraManagerFactory.recycle();
1208         super.onStop();
1209     }
1210
1211     @Override
1212     public void onDestroy() {
1213         if (mSecureCamera) {
1214             unregisterReceiver(mScreenOffReceiver);
1215         }
1216         mSettingsManager.removeAllListeners();
1217         getContentResolver().unregisterContentObserver(mLocalImagesObserver);
1218         getContentResolver().unregisterContentObserver(mLocalVideosObserver);
1219         super.onDestroy();
1220     }
1221
1222     @Override
1223     public void onConfigurationChanged(Configuration config) {
1224         super.onConfigurationChanged(config);
1225         Log.v(TAG, "onConfigurationChanged");
1226         if (config.orientation == Configuration.ORIENTATION_UNDEFINED) {
1227             return;
1228         }
1229
1230         if (mLastLayoutOrientation != config.orientation) {
1231             mLastLayoutOrientation = config.orientation;
1232             mCurrentModule.onLayoutOrientationChanged(
1233                     mLastLayoutOrientation == Configuration.ORIENTATION_LANDSCAPE);
1234         }
1235     }
1236
1237     @Override
1238     public boolean onKeyDown(int keyCode, KeyEvent event) {
1239         if (!mFilmstripVisible) {
1240             if (mCurrentModule.onKeyDown(keyCode, event)) {
1241                 return true;
1242             }
1243             // Prevent software keyboard or voice search from showing up.
1244             if (keyCode == KeyEvent.KEYCODE_SEARCH
1245                     || keyCode == KeyEvent.KEYCODE_MENU) {
1246                 if (event.isLongPress()) {
1247                     return true;
1248                 }
1249             }
1250         }
1251
1252         return super.onKeyDown(keyCode, event);
1253     }
1254
1255     @Override
1256     public boolean onKeyUp(int keyCode, KeyEvent event) {
1257         if (!mFilmstripVisible && mCurrentModule.onKeyUp(keyCode, event)) {
1258             return true;
1259         }
1260         return super.onKeyUp(keyCode, event);
1261     }
1262
1263     @Override
1264     public void onBackPressed() {
1265         if (!mCameraAppUI.onBackPressed()) {
1266             if (!mCurrentModule.onBackPressed()) {
1267                 super.onBackPressed();
1268             }
1269         }
1270     }
1271
1272     public boolean isAutoRotateScreen() {
1273         return mAutoRotateScreen;
1274     }
1275
1276     protected void updateStorageSpace() {
1277         mStorageSpaceBytes = Storage.getAvailableSpace();
1278     }
1279
1280     protected long getStorageSpaceBytes() {
1281         return mStorageSpaceBytes;
1282     }
1283
1284     protected void updateStorageSpaceAndHint() {
1285         updateStorageSpace();
1286         updateStorageHint(mStorageSpaceBytes);
1287     }
1288
1289     protected void updateStorageHint(long storageSpace) {
1290         String message = null;
1291         if (storageSpace == Storage.UNAVAILABLE) {
1292             message = getString(R.string.no_storage);
1293         } else if (storageSpace == Storage.PREPARING) {
1294             message = getString(R.string.preparing_sd);
1295         } else if (storageSpace == Storage.UNKNOWN_SIZE) {
1296             message = getString(R.string.access_sd_fail);
1297         } else if (storageSpace <= Storage.LOW_STORAGE_THRESHOLD_BYTES) {
1298             message = getString(R.string.spaceIsLow_content);
1299         }
1300
1301         if (message != null) {
1302             if (mStorageHint == null) {
1303                 mStorageHint = OnScreenHint.makeText(this, message);
1304             } else {
1305                 mStorageHint.setText(message);
1306             }
1307             mStorageHint.show();
1308         } else if (mStorageHint != null) {
1309             mStorageHint.cancel();
1310             mStorageHint = null;
1311         }
1312     }
1313
1314     protected void setResultEx(int resultCode) {
1315         mResultCodeForTesting = resultCode;
1316         setResult(resultCode);
1317     }
1318
1319     protected void setResultEx(int resultCode, Intent data) {
1320         mResultCodeForTesting = resultCode;
1321         mResultDataForTesting = data;
1322         setResult(resultCode, data);
1323     }
1324
1325     public int getResultCode() {
1326         return mResultCodeForTesting;
1327     }
1328
1329     public Intent getResultData() {
1330         return mResultDataForTesting;
1331     }
1332
1333     public boolean isSecureCamera() {
1334         return mSecureCamera;
1335     }
1336
1337     @Override
1338     public boolean isPaused() {
1339         return mPaused;
1340     }
1341
1342     @Override
1343     public void onModeSelected(int modeIndex) {
1344         if (mCurrentModeIndex == modeIndex) {
1345             return;
1346         }
1347
1348         if (modeIndex == ModeListView.MODE_SETTING) {
1349             onSettingsSelected();
1350             return;
1351         }
1352
1353         closeModule(mCurrentModule);
1354         int oldModuleIndex = mCurrentModeIndex;
1355
1356         // Refocus and Gcam are modes that cannot be selected
1357         // from the mode list view, because they are not list items.
1358         // Check whether we should interpret MODULE_CRAFT as either.
1359         if (modeIndex == ModulesInfo.MODULE_CRAFT) {
1360             boolean refocusOn = mSettingsManager.isRefocusOn();
1361             boolean hdrPlusOn = mSettingsManager.isHdrPlusOn();
1362             if (refocusOn && hdrPlusOn) {
1363                 throw new IllegalStateException("Refocus and hdr plus cannot be on together.");
1364             }
1365             if (refocusOn) {
1366                 modeIndex = ModulesInfo.MODULE_REFOCUS;
1367             } else if (hdrPlusOn) {
1368                 modeIndex = ModulesInfo.MODULE_GCAM;
1369             } else {
1370                 // Do nothing, keep MODULE_CRAFT.
1371             }
1372         }
1373
1374         setModuleFromModeIndex(modeIndex);
1375
1376         // TODO: The following check is temporary for modules attached to the
1377         // generic_module layout. When the refactor is done, similar logic will
1378         // be applied to all modules.
1379         if (mCurrentModeIndex == ModulesInfo.MODULE_PHOTO
1380                 || mCurrentModeIndex == ModulesInfo.MODULE_VIDEO
1381                 || mCurrentModeIndex == ModulesInfo.MODULE_GCAM
1382                 || mCurrentModeIndex == ModulesInfo.MODULE_CRAFT
1383                 || mCurrentModeIndex == ModulesInfo.MODULE_REFOCUS) {
1384             if (oldModuleIndex != ModulesInfo.MODULE_PHOTO
1385                     && oldModuleIndex != ModulesInfo.MODULE_VIDEO
1386                     && oldModuleIndex != ModulesInfo.MODULE_GCAM
1387                     && oldModuleIndex != ModulesInfo.MODULE_CRAFT
1388                     && oldModuleIndex != ModulesInfo.MODULE_REFOCUS) {
1389                 mCameraAppUI.prepareModuleUI();
1390             } else {
1391                 mCameraAppUI.clearModuleUI();
1392             }
1393         } else {
1394             // This is the old way of removing all views in CameraRootView. Will
1395             // be deprecated soon. It is here to make sure modules that haven't
1396             // been refactored can still function.
1397             mCameraAppUI.clearCameraUI();
1398         }
1399
1400         openModule(mCurrentModule);
1401         mCurrentModule.onOrientationChanged(mLastRawOrientation);
1402         // Store the module index so we can use it the next time the Camera
1403         // starts up.
1404         SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
1405         prefs.edit().putInt(CameraSettings.KEY_STARTUP_MODULE_INDEX, modeIndex).apply();
1406     }
1407
1408     public void onSettingsSelected() {
1409         // Temporary until we finalize the touch flow.
1410         LayoutInflater inflater = getLayoutInflater();
1411         SettingsView settingsView = (SettingsView) inflater.inflate(R.layout.settings_list_layout,
1412             null, false);
1413         settingsView.setSettingsListener(mSettingsController);
1414         if (mFeedbackHelper != null) {
1415             settingsView.setFeedbackHelper(mFeedbackHelper);
1416         }
1417         PopupWindow popup = new PopupWindow(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
1418         popup.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
1419         popup.setOutsideTouchable(true);
1420         popup.setFocusable(true);
1421         popup.setContentView(settingsView);
1422         popup.showAtLocation(mModeListView.getRootView(), Gravity.CENTER, 0, 0);
1423     }
1424
1425     /**
1426      * Sets the mCurrentModuleIndex, creates a new module instance for the given
1427      * index an sets it as mCurrentModule.
1428      */
1429     private void setModuleFromModeIndex(int modeIndex) {
1430         ModuleManagerImpl.ModuleAgent agent = mModuleManager.getModuleAgent(modeIndex);
1431         if (agent == null) {
1432             return;
1433         }
1434         if (!agent.requestAppForCamera()) {
1435             mCameraController.closeCamera();
1436         }
1437         mCurrentModeIndex = agent.getModuleId();
1438         mCurrentModule = (CameraModule)  agent.createModule(this);
1439     }
1440
1441     @Override
1442     public SettingsManager getSettingsManager() {
1443         return mSettingsManager;
1444     }
1445
1446     @Override
1447     public CameraServices getServices() {
1448         return (CameraServices) getApplication();
1449     }
1450
1451     @Override
1452     public SettingsController getSettingsController() {
1453         return mSettingsController;
1454     }
1455
1456     @Override
1457     public ButtonManager getButtonManager() {
1458         if (mButtonManager == null) {
1459             mButtonManager = new ButtonManager(this);
1460         }
1461         return mButtonManager;
1462     }
1463
1464     /**
1465      * Creates an AlertDialog appropriate for choosing whether to enable location
1466      * on the first run of the app.
1467      */
1468     public AlertDialog getFirstTimeLocationAlert() {
1469         AlertDialog.Builder builder = new AlertDialog.Builder(this);
1470         builder = SettingsView.getFirstTimeLocationAlertBuilder(builder, mSettingsController);
1471         if (builder != null) {
1472             return builder.create();
1473         } else {
1474             return null;
1475         }
1476     }
1477
1478     /**
1479      * Launches an ACTION_EDIT intent for the given local data item.
1480      */
1481     public void launchEditor(LocalData data) {
1482         Intent intent = new Intent(Intent.ACTION_EDIT)
1483                 .setDataAndType(data.getContentUri(), data.getMimeType())
1484                 .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
1485         try {
1486             launchActivityByIntent(intent);
1487         } catch (ActivityNotFoundException e) {
1488             launchActivityByIntent(Intent.createChooser(intent, null));
1489         }
1490     }
1491
1492     /**
1493      * Launch the tiny planet editor.
1494      *
1495      * @param data The data must be a 360 degree stereographically mapped
1496      *             panoramic image. It will not be modified, instead a new item
1497      *             with the result will be added to the filmstrip.
1498      */
1499     public void launchTinyPlanetEditor(LocalData data) {
1500         TinyPlanetFragment fragment = new TinyPlanetFragment();
1501         Bundle bundle = new Bundle();
1502         bundle.putString(TinyPlanetFragment.ARGUMENT_URI, data.getContentUri().toString());
1503         bundle.putString(TinyPlanetFragment.ARGUMENT_TITLE, data.getTitle());
1504         fragment.setArguments(bundle);
1505         fragment.show(getFragmentManager(), "tiny_planet");
1506     }
1507
1508     private void syncBottomBarColor() {
1509         // Currently not all modules use the generic_module UI.
1510         // TODO: once all modules have a bottom bar, remove
1511         // isUsingBottomBar check.
1512         if (mCurrentModule.isUsingBottomBar()) {
1513             int colorResId = mModeListView.getModeThemeColor(mCurrentModeIndex);
1514             mCameraAppUI.setBottomBarColor(getResources().getColor(colorResId));
1515         }
1516     }
1517
1518     private void openModule(CameraModule module) {
1519         syncBottomBarColor();
1520         module.init(this, isSecureCamera(), isCaptureIntent());
1521         module.resume();
1522         module.onPreviewVisibilityChanged(!mFilmstripVisible);
1523     }
1524
1525     private void closeModule(CameraModule module) {
1526         module.pause();
1527     }
1528
1529     private void performDeletion() {
1530         if (!mPendingDeletion) {
1531             return;
1532         }
1533         hideUndoDeletionBar(false);
1534         mDataAdapter.executeDeletion();
1535     }
1536
1537     public void showUndoDeletionBar() {
1538         if (mPendingDeletion) {
1539             performDeletion();
1540         }
1541         Log.v(TAG, "showing undo bar");
1542         mPendingDeletion = true;
1543         if (mUndoDeletionBar == null) {
1544             ViewGroup v = (ViewGroup) getLayoutInflater().inflate(R.layout.undo_bar,
1545                     mAboveFilmstripControlLayout, true);
1546             mUndoDeletionBar = (ViewGroup) v.findViewById(R.id.camera_undo_deletion_bar);
1547             View button = mUndoDeletionBar.findViewById(R.id.camera_undo_deletion_button);
1548             button.setOnClickListener(new View.OnClickListener() {
1549                 @Override
1550                 public void onClick(View view) {
1551                     mDataAdapter.undoDataRemoval();
1552                     hideUndoDeletionBar(true);
1553                 }
1554             });
1555             // Setting undo bar clickable to avoid touch events going through
1556             // the bar to the buttons (eg. edit button, etc) underneath the bar.
1557             mUndoDeletionBar.setClickable(true);
1558             // When there is user interaction going on with the undo button, we
1559             // do not want to hide the undo bar.
1560             button.setOnTouchListener(new View.OnTouchListener() {
1561                 @Override
1562                 public boolean onTouch(View v, MotionEvent event) {
1563                     if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
1564                         mIsUndoingDeletion = true;
1565                     } else if (event.getActionMasked() == MotionEvent.ACTION_UP) {
1566                         mIsUndoingDeletion = false;
1567                     }
1568                     return false;
1569                 }
1570             });
1571         }
1572         mUndoDeletionBar.setAlpha(0f);
1573         mUndoDeletionBar.setVisibility(View.VISIBLE);
1574         mUndoDeletionBar.animate().setDuration(200).alpha(1f).setListener(null).start();
1575     }
1576
1577     private void hideUndoDeletionBar(boolean withAnimation) {
1578         Log.v(TAG, "Hiding undo deletion bar");
1579         mPendingDeletion = false;
1580         if (mUndoDeletionBar != null) {
1581             if (withAnimation) {
1582                 mUndoDeletionBar.animate().setDuration(200).alpha(0f)
1583                         .setListener(new Animator.AnimatorListener() {
1584                             @Override
1585                             public void onAnimationStart(Animator animation) {
1586                                 // Do nothing.
1587                             }
1588
1589                             @Override
1590                             public void onAnimationEnd(Animator animation) {
1591                                 mUndoDeletionBar.setVisibility(View.GONE);
1592                             }
1593
1594                             @Override
1595                             public void onAnimationCancel(Animator animation) {
1596                                 // Do nothing.
1597                             }
1598
1599                             @Override
1600                             public void onAnimationRepeat(Animator animation) {
1601                                 // Do nothing.
1602                             }
1603                         }).start();
1604             } else {
1605                 mUndoDeletionBar.setVisibility(View.GONE);
1606             }
1607         }
1608     }
1609
1610     @Override
1611     public void onOrientationChanged(int orientation) {
1612         // We keep the last known orientation. So if the user first orient
1613         // the camera then point the camera to floor or sky, we still have
1614         // the correct orientation.
1615         if (orientation == OrientationManager.ORIENTATION_UNKNOWN) {
1616             return;
1617         }
1618         mLastRawOrientation = orientation;
1619         if (mCurrentModule != null) {
1620             mCurrentModule.onOrientationChanged(orientation);
1621         }
1622     }
1623
1624     /**
1625      * Enable/disable swipe-to-filmstrip. Will always disable swipe if in
1626      * capture intent.
1627      *
1628      * @param enable {@code true} to enable swipe.
1629      */
1630     public void setSwipingEnabled(boolean enable) {
1631         // TODO: Bring back the functionality.
1632         if (isCaptureIntent()) {
1633             //lockPreview(true);
1634         } else {
1635             //lockPreview(!enable);
1636         }
1637     }
1638
1639     // Accessor methods for getting latency times used in performance testing
1640     public long getAutoFocusTime() {
1641         return (mCurrentModule instanceof PhotoModule) ?
1642                 ((PhotoModule) mCurrentModule).mAutoFocusTime : -1;
1643     }
1644
1645     public long getShutterLag() {
1646         return (mCurrentModule instanceof PhotoModule) ?
1647                 ((PhotoModule) mCurrentModule).mShutterLag : -1;
1648     }
1649
1650     public long getShutterToPictureDisplayedTime() {
1651         return (mCurrentModule instanceof PhotoModule) ?
1652                 ((PhotoModule) mCurrentModule).mShutterToPictureDisplayedTime : -1;
1653     }
1654
1655     public long getPictureDisplayedToJpegCallbackTime() {
1656         return (mCurrentModule instanceof PhotoModule) ?
1657                 ((PhotoModule) mCurrentModule).mPictureDisplayedToJpegCallbackTime : -1;
1658     }
1659
1660     public long getJpegCallbackFinishTime() {
1661         return (mCurrentModule instanceof PhotoModule) ?
1662                 ((PhotoModule) mCurrentModule).mJpegCallbackFinishTime : -1;
1663     }
1664
1665     public long getCaptureStartTime() {
1666         return (mCurrentModule instanceof PhotoModule) ?
1667                 ((PhotoModule) mCurrentModule).mCaptureStartTime : -1;
1668     }
1669
1670     public boolean isRecording() {
1671         return (mCurrentModule instanceof VideoModule) ?
1672                 ((VideoModule) mCurrentModule).isRecording() : false;
1673     }
1674
1675     public CameraManager.CameraOpenCallback getCameraOpenErrorCallback() {
1676         return mCameraController;
1677     }
1678
1679     // For debugging purposes only.
1680     public CameraModule getCurrentModule() {
1681         return mCurrentModule;
1682     }
1683
1684     private void keepScreenOnForAWhile() {
1685         if (mKeepScreenOn) {
1686             return;
1687         }
1688         mMainHandler.removeMessages(MSG_CLEAR_SCREEN_ON_FLAG);
1689         getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1690         mMainHandler.sendEmptyMessageDelayed(MSG_CLEAR_SCREEN_ON_FLAG, SCREEN_DELAY_MS);
1691     }
1692
1693     private void resetScreenOn() {
1694         mKeepScreenOn = false;
1695         mMainHandler.removeMessages(MSG_CLEAR_SCREEN_ON_FLAG);
1696         getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1697     }
1698
1699     private void startGallery() {
1700         try {
1701             UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
1702                     UsageStatistics.ACTION_GALLERY, null);
1703             launchActivityByIntent(IntentHelper.getGalleryIntent(CameraActivity.this));
1704         } catch (ActivityNotFoundException e) {
1705             Log.w(TAG, "Failed to launch gallery activity, closing");
1706         }
1707     }
1708
1709     private void setNfcBeamPushUriFromData(LocalData data) {
1710         final Uri uri = data.getContentUri();
1711         if (uri != Uri.EMPTY) {
1712             mNfcPushUris[0] = uri;
1713         } else {
1714             mNfcPushUris[0] = null;
1715         }
1716     }
1717
1718     /**
1719      * Updates the visibility of the filmstrip bottom controls.
1720      */
1721     private void updateUiByData(final int dataId) {
1722         if (isSecureCamera()) {
1723             // We cannot show buttons in secure camera since go to other
1724             // activities might create a security hole.
1725             return;
1726         }
1727
1728         final LocalData currentData = mDataAdapter.getLocalData(dataId);
1729         if (currentData == null) {
1730             Log.w(TAG, "Current data ID not found.");
1731             hideSessionProgress();
1732             return;
1733         }
1734
1735         setNfcBeamPushUriFromData(currentData);
1736
1737         /* Bottom controls. */
1738
1739         updateBottomControlsByData(currentData);
1740         if (!mDataAdapter.isMetadataUpdated(dataId)) {
1741             mDataAdapter.updateMetadata(dataId);
1742         }
1743     }
1744
1745     /**
1746      * Updates the bottom controls based on the data.
1747      */
1748     private void updateBottomControlsByData(final LocalData currentData) {
1749
1750         final CameraAppUI.BottomControls filmstripBottomControls =
1751                 mCameraAppUI.getFilmstripBottomControls();
1752         filmstripBottomControls.setEditButtonVisibility(
1753                 currentData.isDataActionSupported(LocalData.DATA_ACTION_EDIT));
1754         filmstripBottomControls.setShareButtonVisibility(
1755                 currentData.isDataActionSupported(LocalData.DATA_ACTION_SHARE));
1756         filmstripBottomControls.setDeleteButtonVisibility(
1757                 currentData.isDataActionSupported(LocalData.DATA_ACTION_DELETE));
1758
1759         /* Progress bar */
1760
1761         Uri contentUri = currentData.getContentUri();
1762         CaptureSessionManager sessionManager = getServices()
1763                 .getCaptureSessionManager();
1764         int sessionProgress = sessionManager.getSessionProgress(contentUri);
1765
1766         if (sessionProgress < 0) {
1767             hideSessionProgress();
1768         } else {
1769             CharSequence progressMessage = sessionManager
1770                     .getSessionProgressMessage(contentUri);
1771             showSessionProgress(progressMessage);
1772             updateSessionProgress(sessionProgress);
1773         }
1774
1775         /* View button */
1776
1777         // We need to add this to a separate DB.
1778         final int viewButtonVisibility;
1779         if (PanoramaMetadataLoader.isPanorama(currentData)) {
1780             viewButtonVisibility = CameraAppUI.BottomControls.VIEW_PHOTO_SPHERE;
1781         } else if (RgbzMetadataLoader.hasRGBZData(currentData)) {
1782             viewButtonVisibility = CameraAppUI.BottomControls.VIEW_RGBZ;
1783         } else {
1784             viewButtonVisibility = CameraAppUI.BottomControls.VIEW_NONE;
1785         }
1786
1787         filmstripBottomControls.setTinyPlanetButtonVisibility(
1788                 PanoramaMetadataLoader.isPanorama360(currentData));
1789         filmstripBottomControls.setViewButtonVisibility(viewButtonVisibility);
1790     }
1791 }