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