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