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