OSDN Git Service

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