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