Home | History | Annotate | Download | only in camera
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 
     18 package com.android.camera;
     19 
     20 import android.Manifest;
     21 import android.animation.Animator;
     22 import android.app.ActionBar;
     23 import android.app.Activity;
     24 import android.app.Dialog;
     25 import android.content.ActivityNotFoundException;
     26 import android.content.BroadcastReceiver;
     27 import android.content.ContentResolver;
     28 import android.content.Context;
     29 import android.content.Intent;
     30 import android.content.IntentFilter;
     31 import android.content.pm.ActivityInfo;
     32 import android.content.pm.PackageManager;
     33 import android.content.res.Configuration;
     34 import android.graphics.Bitmap;
     35 import android.graphics.Matrix;
     36 import android.graphics.RectF;
     37 import android.graphics.SurfaceTexture;
     38 import android.graphics.drawable.ColorDrawable;
     39 import android.graphics.drawable.Drawable;
     40 import android.net.Uri;
     41 import android.nfc.NfcAdapter;
     42 import android.nfc.NfcAdapter.CreateBeamUrisCallback;
     43 import android.nfc.NfcEvent;
     44 import android.os.AsyncTask;
     45 import android.os.Build;
     46 import android.os.Bundle;
     47 import android.os.Handler;
     48 import android.os.Looper;
     49 import android.os.Message;
     50 import android.provider.MediaStore;
     51 import android.provider.Settings;
     52 import android.text.TextUtils;
     53 import android.util.CameraPerformanceTracker;
     54 import android.view.ContextMenu;
     55 import android.view.ContextMenu.ContextMenuInfo;
     56 import android.view.KeyEvent;
     57 import android.view.Menu;
     58 import android.view.MenuInflater;
     59 import android.view.MenuItem;
     60 import android.view.MotionEvent;
     61 import android.view.View;
     62 import android.view.View.OnSystemUiVisibilityChangeListener;
     63 import android.view.ViewGroup;
     64 import android.view.Window;
     65 import android.view.WindowManager;
     66 import android.widget.FrameLayout;
     67 import android.widget.ImageView;
     68 import android.widget.ShareActionProvider;
     69 
     70 import com.android.camera.app.AppController;
     71 import com.android.camera.app.CameraAppUI;
     72 import com.android.camera.app.CameraController;
     73 import com.android.camera.app.CameraProvider;
     74 import com.android.camera.app.CameraServices;
     75 import com.android.camera.app.CameraServicesImpl;
     76 import com.android.camera.app.FirstRunDialog;
     77 import com.android.camera.app.LocationManager;
     78 import com.android.camera.app.MemoryManager;
     79 import com.android.camera.app.MemoryQuery;
     80 import com.android.camera.app.ModuleManager;
     81 import com.android.camera.app.ModuleManager.ModuleAgent;
     82 import com.android.camera.app.ModuleManagerImpl;
     83 import com.android.camera.app.MotionManager;
     84 import com.android.camera.app.OrientationManager;
     85 import com.android.camera.app.OrientationManagerImpl;
     86 import com.android.camera.data.CameraFilmstripDataAdapter;
     87 import com.android.camera.data.FilmstripContentObserver;
     88 import com.android.camera.data.FilmstripItem;
     89 import com.android.camera.data.FilmstripItemData;
     90 import com.android.camera.data.FilmstripItemType;
     91 import com.android.camera.data.FilmstripItemUtils;
     92 import com.android.camera.data.FixedLastProxyAdapter;
     93 import com.android.camera.data.GlideFilmstripManager;
     94 import com.android.camera.data.LocalFilmstripDataAdapter;
     95 import com.android.camera.data.LocalFilmstripDataAdapter.FilmstripItemListener;
     96 import com.android.camera.data.MediaDetails;
     97 import com.android.camera.data.MetadataLoader;
     98 import com.android.camera.data.PhotoDataFactory;
     99 import com.android.camera.data.PhotoItem;
    100 import com.android.camera.data.PhotoItemFactory;
    101 import com.android.camera.data.PlaceholderItem;
    102 import com.android.camera.data.SessionItem;
    103 import com.android.camera.data.VideoDataFactory;
    104 import com.android.camera.data.VideoItemFactory;
    105 import com.android.camera.debug.Log;
    106 import com.android.camera.device.ActiveCameraDeviceTracker;
    107 import com.android.camera.device.CameraId;
    108 import com.android.camera.filmstrip.FilmstripContentPanel;
    109 import com.android.camera.filmstrip.FilmstripController;
    110 import com.android.camera.module.ModuleController;
    111 import com.android.camera.module.ModulesInfo;
    112 import com.android.camera.one.OneCameraException;
    113 import com.android.camera.one.OneCameraManager;
    114 import com.android.camera.one.OneCameraModule;
    115 import com.android.camera.one.OneCameraOpener;
    116 import com.android.camera.one.config.OneCameraFeatureConfig;
    117 import com.android.camera.one.config.OneCameraFeatureConfigCreator;
    118 import com.android.camera.session.CaptureSession;
    119 import com.android.camera.session.CaptureSessionManager;
    120 import com.android.camera.session.CaptureSessionManager.SessionListener;
    121 import com.android.camera.settings.AppUpgrader;
    122 import com.android.camera.settings.CameraSettingsActivity;
    123 import com.android.camera.settings.Keys;
    124 import com.android.camera.settings.PictureSizeLoader;
    125 import com.android.camera.settings.ResolutionSetting;
    126 import com.android.camera.settings.ResolutionUtil;
    127 import com.android.camera.settings.SettingsManager;
    128 import com.android.camera.stats.UsageStatistics;
    129 import com.android.camera.stats.profiler.Profile;
    130 import com.android.camera.stats.profiler.Profiler;
    131 import com.android.camera.stats.profiler.Profilers;
    132 import com.android.camera.tinyplanet.TinyPlanetFragment;
    133 import com.android.camera.ui.AbstractTutorialOverlay;
    134 import com.android.camera.ui.DetailsDialog;
    135 import com.android.camera.ui.MainActivityLayout;
    136 import com.android.camera.ui.ModeListView;
    137 import com.android.camera.ui.ModeListView.ModeListVisibilityChangedListener;
    138 import com.android.camera.ui.PreviewStatusListener;
    139 import com.android.camera.util.ApiHelper;
    140 import com.android.camera.util.Callback;
    141 import com.android.camera.util.CameraUtil;
    142 import com.android.camera.util.GalleryHelper;
    143 import com.android.camera.util.GcamHelper;
    144 import com.android.camera.util.GoogleHelpHelper;
    145 import com.android.camera.util.IntentHelper;
    146 import com.android.camera.util.PhotoSphereHelper.PanoramaViewHelper;
    147 import com.android.camera.util.QuickActivity;
    148 import com.android.camera.util.ReleaseHelper;
    149 import com.android.camera.widget.FilmstripView;
    150 import com.android.camera.widget.Preloader;
    151 import com.android.camera2.R;
    152 import com.android.ex.camera2.portability.CameraAgent;
    153 import com.android.ex.camera2.portability.CameraAgentFactory;
    154 import com.android.ex.camera2.portability.CameraExceptionHandler;
    155 import com.android.ex.camera2.portability.CameraSettings;
    156 import com.bumptech.glide.Glide;
    157 import com.bumptech.glide.GlideBuilder;
    158 import com.bumptech.glide.MemoryCategory;
    159 import com.bumptech.glide.load.DecodeFormat;
    160 import com.bumptech.glide.load.engine.executor.FifoPriorityThreadPoolExecutor;
    161 
    162 import com.google.common.base.Optional;
    163 import com.google.common.logging.eventprotos;
    164 import com.google.common.logging.eventprotos.ForegroundEvent.ForegroundSource;
    165 import com.google.common.logging.eventprotos.MediaInteraction;
    166 import com.google.common.logging.eventprotos.NavigationChange;
    167 
    168 import java.io.File;
    169 import java.lang.ref.WeakReference;
    170 import java.util.ArrayList;
    171 import java.util.HashMap;
    172 import java.util.List;
    173 
    174 public class CameraActivity extends QuickActivity
    175         implements AppController, CameraAgent.CameraOpenCallback,
    176         ShareActionProvider.OnShareTargetSelectedListener {
    177 
    178     private static final Log.Tag TAG = new Log.Tag("CameraActivity");
    179 
    180     private static final String INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE =
    181             "android.media.action.STILL_IMAGE_CAMERA_SECURE";
    182     public static final String ACTION_IMAGE_CAPTURE_SECURE =
    183             "android.media.action.IMAGE_CAPTURE_SECURE";
    184 
    185     // The intent extra for camera from secure lock screen. True if the gallery
    186     // should only show newly captured pictures. sSecureAlbumId does not
    187     // increment. This is used when switching between camera, camcorder, and
    188     // panorama. If the extra is not set, it is in the normal camera mode.
    189     public static final String SECURE_CAMERA_EXTRA = "secure_camera";
    190 
    191     private static final int MSG_CLEAR_SCREEN_ON_FLAG = 2;
    192     private static final long SCREEN_DELAY_MS = 2 * 60 * 1000; // 2 mins.
    193     /** Load metadata for 10 items ahead of our current. */
    194     private static final int FILMSTRIP_PRELOAD_AHEAD_ITEMS = 10;
    195     private static final int PERMISSIONS_ACTIVITY_REQUEST_CODE = 1;
    196     private static final int PERMISSIONS_RESULT_CODE_OK = 1;
    197     private static final int PERMISSIONS_RESULT_CODE_FAILED = 2;
    198 
    199     /** Should be used wherever a context is needed. */
    200     private Context mAppContext;
    201 
    202     /**
    203      * Camera fatal error handling:
    204      * 1) Present error dialog to guide users to exit the app.
    205      * 2) If users hit home button, onPause should just call finish() to exit the app.
    206      */
    207     private boolean mCameraFatalError = false;
    208 
    209     /**
    210      * Whether onResume should reset the view to the preview.
    211      */
    212     private boolean mResetToPreviewOnResume = true;
    213 
    214     /**
    215      * This data adapter is used by FilmStripView.
    216      */
    217     private VideoItemFactory mVideoItemFactory;
    218     private PhotoItemFactory mPhotoItemFactory;
    219     private LocalFilmstripDataAdapter mDataAdapter;
    220 
    221     private ActiveCameraDeviceTracker mActiveCameraDeviceTracker;
    222     private OneCameraOpener mOneCameraOpener;
    223     private OneCameraManager mOneCameraManager;
    224     private SettingsManager mSettingsManager;
    225     private ResolutionSetting mResolutionSetting;
    226     private ModeListView mModeListView;
    227     private boolean mModeListVisible = false;
    228     private int mCurrentModeIndex;
    229     private CameraModule mCurrentModule;
    230     private ModuleManagerImpl mModuleManager;
    231     private FrameLayout mAboveFilmstripControlLayout;
    232     private FilmstripController mFilmstripController;
    233     private boolean mFilmstripVisible;
    234     /** Whether the filmstrip fully covers the preview. */
    235     private boolean mFilmstripCoversPreview = false;
    236     private int mResultCodeForTesting;
    237     private Intent mResultDataForTesting;
    238     private OnScreenHint mStorageHint;
    239     private final Object mStorageSpaceLock = new Object();
    240     private long mStorageSpaceBytes = Storage.LOW_STORAGE_THRESHOLD_BYTES;
    241     private boolean mAutoRotateScreen;
    242     private boolean mSecureCamera;
    243     private OrientationManagerImpl mOrientationManager;
    244     private LocationManager mLocationManager;
    245     private ButtonManager mButtonManager;
    246     private Handler mMainHandler;
    247     private PanoramaViewHelper mPanoramaViewHelper;
    248     private ActionBar mActionBar;
    249     private ViewGroup mUndoDeletionBar;
    250     private boolean mIsUndoingDeletion = false;
    251     private boolean mIsActivityRunning = false;
    252     private FatalErrorHandler mFatalErrorHandler;
    253     private boolean mHasCriticalPermissions;
    254 
    255     private final Uri[] mNfcPushUris = new Uri[1];
    256 
    257     private FilmstripContentObserver mLocalImagesObserver;
    258     private FilmstripContentObserver mLocalVideosObserver;
    259 
    260     private boolean mPendingDeletion = false;
    261 
    262     private CameraController mCameraController;
    263     private boolean mPaused;
    264     private CameraAppUI mCameraAppUI;
    265 
    266     private Intent mGalleryIntent;
    267     private long mOnCreateTime;
    268 
    269     private Menu mActionBarMenu;
    270     private Preloader<Integer, AsyncTask> mPreloader;
    271 
    272     /** Can be used to play custom sounds. */
    273     private SoundPlayer mSoundPlayer;
    274 
    275     /** Holds configuration for various OneCamera features. */
    276     private OneCameraFeatureConfig mFeatureConfig;
    277 
    278     private static final int LIGHTS_OUT_DELAY_MS = 4000;
    279     private final int BASE_SYS_UI_VISIBILITY =
    280             View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
    281             | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
    282     private final Runnable mLightsOutRunnable = new Runnable() {
    283         @Override
    284         public void run() {
    285             getWindow().getDecorView().setSystemUiVisibility(
    286                     BASE_SYS_UI_VISIBILITY | View.SYSTEM_UI_FLAG_LOW_PROFILE);
    287         }
    288     };
    289     private MemoryManager mMemoryManager;
    290     private MotionManager mMotionManager;
    291     private final Profiler mProfiler = Profilers.instance().guard();
    292 
    293     /** First run dialog */
    294     private FirstRunDialog mFirstRunDialog;
    295 
    296     @Override
    297     public CameraAppUI getCameraAppUI() {
    298         return mCameraAppUI;
    299     }
    300 
    301     @Override
    302     public ModuleManager getModuleManager() {
    303         return mModuleManager;
    304     }
    305 
    306     /**
    307      * Close activity when secure app passes lock screen or screen turns
    308      * off.
    309      */
    310     private final BroadcastReceiver mShutdownReceiver = new BroadcastReceiver() {
    311         @Override
    312         public void onReceive(Context context, Intent intent) {
    313             finish();
    314         }
    315     };
    316 
    317     /**
    318      * Whether the screen is kept turned on.
    319      */
    320     private boolean mKeepScreenOn;
    321     private int mLastLayoutOrientation;
    322     private final CameraAppUI.BottomPanel.Listener mMyFilmstripBottomControlListener =
    323             new CameraAppUI.BottomPanel.Listener() {
    324 
    325                 /**
    326                  * If the current photo is a photo sphere, this will launch the
    327                  * Photo Sphere panorama viewer.
    328                  */
    329                 @Override
    330                 public void onExternalViewer() {
    331                     if (mPanoramaViewHelper == null) {
    332                         return;
    333                     }
    334                     final FilmstripItem data = getCurrentLocalData();
    335                     if (data == null) {
    336                         Log.w(TAG, "Cannot open null data.");
    337                         return;
    338                     }
    339                     final Uri contentUri = data.getData().getUri();
    340                     if (contentUri == Uri.EMPTY) {
    341                         Log.w(TAG, "Cannot open empty URL.");
    342                         return;
    343                     }
    344 
    345                     if (data.getMetadata().isUsePanoramaViewer()) {
    346                         mPanoramaViewHelper.showPanorama(CameraActivity.this, contentUri);
    347                     } else if (data.getMetadata().isHasRgbzData()) {
    348                         mPanoramaViewHelper.showRgbz(contentUri);
    349                         if (mSettingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL,
    350                                 Keys.KEY_SHOULD_SHOW_REFOCUS_VIEWER_CLING)) {
    351                             mSettingsManager.set(SettingsManager.SCOPE_GLOBAL,
    352                                     Keys.KEY_SHOULD_SHOW_REFOCUS_VIEWER_CLING, false);
    353                             mCameraAppUI.clearClingForViewer(
    354                                     CameraAppUI.BottomPanel.VIEWER_REFOCUS);
    355                         }
    356                     }
    357                 }
    358 
    359                 @Override
    360                 public void onEdit() {
    361                     FilmstripItem data = getCurrentLocalData();
    362                     if (data == null) {
    363                         Log.w(TAG, "Cannot edit null data.");
    364                         return;
    365                     }
    366                     final int currentDataId = getCurrentDataId();
    367                     UsageStatistics.instance().mediaInteraction(fileNameFromAdapterAtIndex(
    368                                 currentDataId),
    369                             MediaInteraction.InteractionType.EDIT,
    370                             NavigationChange.InteractionCause.BUTTON,
    371                             fileAgeFromAdapterAtIndex(currentDataId));
    372                     launchEditor(data);
    373                 }
    374 
    375                 @Override
    376                 public void onTinyPlanet() {
    377                     FilmstripItem data = getCurrentLocalData();
    378                     if (data == null) {
    379                         Log.w(TAG, "Cannot edit tiny planet on null data.");
    380                         return;
    381                     }
    382                     launchTinyPlanetEditor(data);
    383                 }
    384 
    385                 @Override
    386                 public void onDelete() {
    387                     final int currentDataId = getCurrentDataId();
    388                     UsageStatistics.instance().mediaInteraction(fileNameFromAdapterAtIndex(
    389                                 currentDataId),
    390                             MediaInteraction.InteractionType.DELETE,
    391                             NavigationChange.InteractionCause.BUTTON,
    392                             fileAgeFromAdapterAtIndex(currentDataId));
    393                     removeItemAt(currentDataId);
    394                 }
    395 
    396                 @Override
    397                 public void onShare() {
    398                     final FilmstripItem data = getCurrentLocalData();
    399                     if (data == null) {
    400                         Log.w(TAG, "Cannot share null data.");
    401                         return;
    402                     }
    403 
    404                     final int currentDataId = getCurrentDataId();
    405                     UsageStatistics.instance().mediaInteraction(fileNameFromAdapterAtIndex(
    406                                 currentDataId),
    407                             MediaInteraction.InteractionType.SHARE,
    408                             NavigationChange.InteractionCause.BUTTON,
    409                             fileAgeFromAdapterAtIndex(currentDataId));
    410                     // If applicable, show release information before this item
    411                     // is shared.
    412                     if (ReleaseHelper.shouldShowReleaseInfoDialogOnShare(data)) {
    413                         ReleaseHelper.showReleaseInfoDialog(CameraActivity.this,
    414                                 new Callback<Void>() {
    415                                     @Override
    416                                     public void onCallback(Void result) {
    417                                         share(data);
    418                                     }
    419                                 });
    420                     } else {
    421                         share(data);
    422                     }
    423                 }
    424 
    425                 private void share(FilmstripItem data) {
    426                     Intent shareIntent = getShareIntentByData(data);
    427                     if (shareIntent != null) {
    428                         try {
    429                             launchActivityByIntent(shareIntent);
    430                             mCameraAppUI.getFilmstripBottomControls().setShareEnabled(false);
    431                         } catch (ActivityNotFoundException ex) {
    432                             // Nothing.
    433                         }
    434                     }
    435                 }
    436 
    437                 private int getCurrentDataId() {
    438                     return mFilmstripController.getCurrentAdapterIndex();
    439                 }
    440 
    441                 private FilmstripItem getCurrentLocalData() {
    442                     return mDataAdapter.getItemAt(getCurrentDataId());
    443                 }
    444 
    445                 /**
    446                  * Sets up the share intent and NFC properly according to the
    447                  * data.
    448                  *
    449                  * @param item The data to be shared.
    450                  */
    451                 private Intent getShareIntentByData(final FilmstripItem item) {
    452                     Intent intent = null;
    453                     final Uri contentUri = item.getData().getUri();
    454                     final String msgShareTo = getResources().getString(R.string.share_to);
    455 
    456                     if (item.getMetadata().isPanorama360() &&
    457                           item.getData().getUri() != Uri.EMPTY) {
    458                         intent = new Intent(Intent.ACTION_SEND);
    459                         intent.setType(FilmstripItemData.MIME_TYPE_PHOTOSPHERE);
    460                         intent.putExtra(Intent.EXTRA_STREAM, contentUri);
    461                     } else if (item.getAttributes().canShare()) {
    462                         final String mimeType = item.getData().getMimeType();
    463                         intent = getShareIntentFromType(mimeType);
    464                         if (intent != null) {
    465                             intent.putExtra(Intent.EXTRA_STREAM, contentUri);
    466                             intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    467                         }
    468                         intent = Intent.createChooser(intent, msgShareTo);
    469                     }
    470                     return intent;
    471                 }
    472 
    473                 /**
    474                  * Get the share intent according to the mimeType
    475                  *
    476                  * @param mimeType The mimeType of current data.
    477                  * @return the video/image's ShareIntent or null if mimeType is
    478                  *         invalid.
    479                  */
    480                 private Intent getShareIntentFromType(String mimeType) {
    481                     // Lazily create the intent object.
    482                     Intent intent = new Intent(Intent.ACTION_SEND);
    483                     if (mimeType.startsWith("video/")) {
    484                         intent.setType("video/*");
    485                     } else {
    486                         if (mimeType.startsWith("image/")) {
    487                             intent.setType("image/*");
    488                         } else {
    489                             Log.w(TAG, "unsupported mimeType " + mimeType);
    490                         }
    491                     }
    492                     return intent;
    493                 }
    494 
    495                 @Override
    496                 public void onProgressErrorClicked() {
    497                     FilmstripItem data = getCurrentLocalData();
    498                     getServices().getCaptureSessionManager().removeErrorMessage(
    499                             data.getData().getUri());
    500                     updateBottomControlsByData(data);
    501                 }
    502             };
    503 
    504     @Override
    505     public void onCameraOpened(CameraAgent.CameraProxy camera) {
    506         Log.v(TAG, "onCameraOpened");
    507         if (mPaused) {
    508             // We've paused, but just asynchronously opened the camera. Close it
    509             // because we should be releasing the camera when paused to allow
    510             // other apps to access it.
    511             Log.v(TAG, "received onCameraOpened but activity is paused, closing Camera");
    512             mCameraController.closeCamera(false);
    513             return;
    514         }
    515 
    516         if (!mModuleManager.getModuleAgent(mCurrentModeIndex).requestAppForCamera()) {
    517             // We shouldn't be here. Just close the camera and leave.
    518             mCameraController.closeCamera(false);
    519             throw new IllegalStateException("Camera opened but the module shouldn't be " +
    520                     "requesting");
    521         }
    522         if (mCurrentModule != null) {
    523             resetExposureCompensationToDefault(camera);
    524             try {
    525                 mCurrentModule.onCameraAvailable(camera);
    526             } catch (RuntimeException ex) {
    527                 Log.e(TAG, "Error connecting to camera", ex);
    528                 mFatalErrorHandler.onCameraOpenFailure();
    529             }
    530         } else {
    531             Log.v(TAG, "mCurrentModule null, not invoking onCameraAvailable");
    532         }
    533         Log.v(TAG, "invoking onChangeCamera");
    534         mCameraAppUI.onChangeCamera();
    535     }
    536 
    537     private void resetExposureCompensationToDefault(CameraAgent.CameraProxy camera) {
    538         // Reset the exposure compensation before handing the camera to module.
    539         CameraSettings cameraSettings = camera.getSettings();
    540         cameraSettings.setExposureCompensationIndex(0);
    541         camera.applySettings(cameraSettings);
    542     }
    543 
    544     @Override
    545     public void onCameraDisabled(int cameraId) {
    546         Log.w(TAG, "Camera disabled: " + cameraId);
    547         mFatalErrorHandler.onCameraDisabledFailure();
    548     }
    549 
    550     @Override
    551     public void onDeviceOpenFailure(int cameraId, String info) {
    552         Log.w(TAG, "Camera open failure: " + info);
    553         mFatalErrorHandler.onCameraOpenFailure();
    554     }
    555 
    556     @Override
    557     public void onDeviceOpenedAlready(int cameraId, String info) {
    558         Log.w(TAG, "Camera open already: " + cameraId + "," + info);
    559         mFatalErrorHandler.onGenericCameraAccessFailure();
    560     }
    561 
    562     @Override
    563     public void onReconnectionFailure(CameraAgent mgr, String info) {
    564         Log.w(TAG, "Camera reconnection failure:" + info);
    565         mFatalErrorHandler.onCameraReconnectFailure();
    566     }
    567 
    568     private static class MainHandler extends Handler {
    569         final WeakReference<CameraActivity> mActivity;
    570 
    571         public MainHandler(CameraActivity activity, Looper looper) {
    572             super(looper);
    573             mActivity = new WeakReference<CameraActivity>(activity);
    574         }
    575 
    576         @Override
    577         public void handleMessage(Message msg) {
    578             CameraActivity activity = mActivity.get();
    579             if (activity == null) {
    580                 return;
    581             }
    582             switch (msg.what) {
    583 
    584                 case MSG_CLEAR_SCREEN_ON_FLAG: {
    585                     if (!activity.mPaused) {
    586                         activity.getWindow().clearFlags(
    587                                 WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
    588                     }
    589                     break;
    590                 }
    591             }
    592         }
    593     }
    594 
    595     private String fileNameFromAdapterAtIndex(int index) {
    596         final FilmstripItem filmstripItem = mDataAdapter.getItemAt(index);
    597         if (filmstripItem == null) {
    598             return "";
    599         }
    600 
    601         File localFile = new File(filmstripItem.getData().getFilePath());
    602         return localFile.getName();
    603     }
    604 
    605     private float fileAgeFromAdapterAtIndex(int index) {
    606         final FilmstripItem filmstripItem = mDataAdapter.getItemAt(index);
    607         if (filmstripItem == null) {
    608             return 0;
    609         }
    610 
    611         File localFile = new File(filmstripItem.getData().getFilePath());
    612         return 0.001f * (System.currentTimeMillis() - localFile.lastModified());
    613     }
    614 
    615     private final FilmstripContentPanel.Listener mFilmstripListener =
    616             new FilmstripContentPanel.Listener() {
    617 
    618                 @Override
    619                 public void onSwipeOut() {
    620                 }
    621 
    622                 @Override
    623                 public void onSwipeOutBegin() {
    624                     mActionBar.hide();
    625                     mCameraAppUI.hideBottomControls();
    626                     mFilmstripCoversPreview = false;
    627                     updatePreviewVisibility();
    628                 }
    629 
    630                 @Override
    631                 public void onFilmstripHidden() {
    632                     mFilmstripVisible = false;
    633                     UsageStatistics.instance().changeScreen(currentUserInterfaceMode(),
    634                             NavigationChange.InteractionCause.SWIPE_RIGHT);
    635                     CameraActivity.this.setFilmstripUiVisibility(false);
    636                     // When the user hide the filmstrip (either swipe out or
    637                     // tap on back key) we move to the first item so next time
    638                     // when the user swipe in the filmstrip, the most recent
    639                     // one is shown.
    640                     mFilmstripController.goToFirstItem();
    641                 }
    642 
    643                 @Override
    644                 public void onFilmstripShown() {
    645                     mFilmstripVisible = true;
    646                     mCameraAppUI.hideCaptureIndicator();
    647                     UsageStatistics.instance().changeScreen(currentUserInterfaceMode(),
    648                             NavigationChange.InteractionCause.SWIPE_LEFT);
    649                     updateUiByData(mFilmstripController.getCurrentAdapterIndex());
    650                 }
    651 
    652                 @Override
    653                 public void onFocusedDataLongPressed(int adapterIndex) {
    654                     // Do nothing.
    655                 }
    656 
    657                 @Override
    658                 public void onFocusedDataPromoted(int adapterIndex) {
    659                     UsageStatistics.instance().mediaInteraction(fileNameFromAdapterAtIndex(
    660                                 adapterIndex),
    661                             MediaInteraction.InteractionType.DELETE,
    662                             NavigationChange.InteractionCause.SWIPE_UP, fileAgeFromAdapterAtIndex(
    663                                 adapterIndex));
    664                     removeItemAt(adapterIndex);
    665                 }
    666 
    667                 @Override
    668                 public void onFocusedDataDemoted(int adapterIndex) {
    669                     UsageStatistics.instance().mediaInteraction(fileNameFromAdapterAtIndex(
    670                                 adapterIndex),
    671                             MediaInteraction.InteractionType.DELETE,
    672                             NavigationChange.InteractionCause.SWIPE_DOWN,
    673                             fileAgeFromAdapterAtIndex(adapterIndex));
    674                     removeItemAt(adapterIndex);
    675                 }
    676 
    677                 @Override
    678                 public void onEnterFullScreenUiShown(int adapterIndex) {
    679                     if (mFilmstripVisible) {
    680                         CameraActivity.this.setFilmstripUiVisibility(true);
    681                     }
    682                 }
    683 
    684                 @Override
    685                 public void onLeaveFullScreenUiShown(int adapterIndex) {
    686                     // Do nothing.
    687                 }
    688 
    689                 @Override
    690                 public void onEnterFullScreenUiHidden(int adapterIndex) {
    691                     if (mFilmstripVisible) {
    692                         CameraActivity.this.setFilmstripUiVisibility(false);
    693                     }
    694                 }
    695 
    696                 @Override
    697                 public void onLeaveFullScreenUiHidden(int adapterIndex) {
    698                     // Do nothing.
    699                 }
    700 
    701                 @Override
    702                 public void onEnterFilmstrip(int adapterIndex) {
    703                     if (mFilmstripVisible) {
    704                         CameraActivity.this.setFilmstripUiVisibility(true);
    705                     }
    706                 }
    707 
    708                 @Override
    709                 public void onLeaveFilmstrip(int adapterIndex) {
    710                     // Do nothing.
    711                 }
    712 
    713                 @Override
    714                 public void onDataReloaded() {
    715                     if (!mFilmstripVisible) {
    716                         return;
    717                     }
    718                     updateUiByData(mFilmstripController.getCurrentAdapterIndex());
    719                 }
    720 
    721                 @Override
    722                 public void onDataUpdated(int adapterIndex) {
    723                     if (!mFilmstripVisible) {
    724                         return;
    725                     }
    726                     updateUiByData(mFilmstripController.getCurrentAdapterIndex());
    727                 }
    728 
    729                 @Override
    730                 public void onEnterZoomView(int adapterIndex) {
    731                     if (mFilmstripVisible) {
    732                         CameraActivity.this.setFilmstripUiVisibility(false);
    733                     }
    734                 }
    735 
    736                 @Override
    737                 public void onZoomAtIndexChanged(int adapterIndex, float zoom) {
    738                     final FilmstripItem filmstripItem = mDataAdapter.getItemAt(adapterIndex);
    739                     long ageMillis = System.currentTimeMillis()
    740                           - filmstripItem.getData().getLastModifiedDate().getTime();
    741 
    742                     // Do not log if items is to old or does not have a path (which is
    743                     // being used as a key).
    744                     if (TextUtils.isEmpty(filmstripItem.getData().getFilePath()) ||
    745                             ageMillis > UsageStatistics.VIEW_TIMEOUT_MILLIS) {
    746                         return;
    747                     }
    748                     File localFile = new File(filmstripItem.getData().getFilePath());
    749                     UsageStatistics.instance().mediaView(localFile.getName(),
    750                           filmstripItem.getData().getLastModifiedDate().getTime(), zoom);
    751                }
    752 
    753                 @Override
    754                 public void onDataFocusChanged(final int prevIndex, final int newIndex) {
    755                     if (!mFilmstripVisible) {
    756                         return;
    757                     }
    758                     // TODO: This callback is UI event callback, should always
    759                     // happen on UI thread. Find the reason for this
    760                     // runOnUiThread() and fix it.
    761                     runOnUiThread(new Runnable() {
    762                         @Override
    763                         public void run() {
    764                             updateUiByData(newIndex);
    765                         }
    766                     });
    767                 }
    768 
    769                 @Override
    770                 public void onScroll(int firstVisiblePosition, int visibleItemCount, int totalItemCount) {
    771                     mPreloader.onScroll(null /*absListView*/, firstVisiblePosition, visibleItemCount, totalItemCount);
    772                 }
    773             };
    774 
    775     private final FilmstripItemListener mFilmstripItemListener =
    776             new FilmstripItemListener() {
    777                 @Override
    778                 public void onMetadataUpdated(List<Integer> indexes) {
    779                     if (mPaused) {
    780                         // Callback after the activity is paused.
    781                         return;
    782                     }
    783                     int currentIndex = mFilmstripController.getCurrentAdapterIndex();
    784                     for (Integer index : indexes) {
    785                         if (index == currentIndex) {
    786                             updateUiByData(index);
    787                             // Currently we have only 1 data can be matched.
    788                             // No need to look for more, break.
    789                             break;
    790                         }
    791                     }
    792                 }
    793             };
    794 
    795     public void gotoGallery() {
    796         UsageStatistics.instance().changeScreen(NavigationChange.Mode.FILMSTRIP,
    797                 NavigationChange.InteractionCause.BUTTON);
    798 
    799         mFilmstripController.goToNextItem();
    800     }
    801 
    802     /**
    803      * If 'visible' is false, this hides the action bar. Also maintains
    804      * lights-out at all times.
    805      *
    806      * @param visible is false, this hides the action bar and filmstrip bottom
    807      *            controls.
    808      */
    809     private void setFilmstripUiVisibility(boolean visible) {
    810         mLightsOutRunnable.run();
    811         mCameraAppUI.getFilmstripBottomControls().setVisible(visible);
    812         if (visible != mActionBar.isShowing()) {
    813             if (visible) {
    814                 mActionBar.show();
    815                 mCameraAppUI.showBottomControls();
    816             } else {
    817                 mActionBar.hide();
    818                 mCameraAppUI.hideBottomControls();
    819             }
    820         }
    821         mFilmstripCoversPreview = visible;
    822         updatePreviewVisibility();
    823     }
    824 
    825     private void hideSessionProgress() {
    826         mCameraAppUI.getFilmstripBottomControls().hideProgress();
    827     }
    828 
    829     private void showSessionProgress(int messageId) {
    830         CameraAppUI.BottomPanel controls = mCameraAppUI.getFilmstripBottomControls();
    831         controls.setProgressText(messageId > 0 ? getString(messageId) : "");
    832         controls.hideControls();
    833         controls.hideProgressError();
    834         controls.showProgress();
    835     }
    836 
    837     private void showProcessError(int messageId) {
    838         mCameraAppUI.getFilmstripBottomControls().showProgressError(
    839                 messageId > 0 ? getString(messageId) : "");
    840     }
    841 
    842     private void updateSessionProgress(int progress) {
    843         mCameraAppUI.getFilmstripBottomControls().setProgress(progress);
    844     }
    845 
    846     private void updateSessionProgressText(int messageId) {
    847         mCameraAppUI.getFilmstripBottomControls().setProgressText(
    848                 messageId > 0 ? getString(messageId) : "");
    849     }
    850 
    851     private void setupNfcBeamPush() {
    852         NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mAppContext);
    853         if (adapter == null) {
    854             return;
    855         }
    856 
    857         if (!ApiHelper.HAS_SET_BEAM_PUSH_URIS) {
    858             // Disable beaming
    859             adapter.setNdefPushMessage(null, CameraActivity.this);
    860             return;
    861         }
    862 
    863         adapter.setBeamPushUris(null, CameraActivity.this);
    864         adapter.setBeamPushUrisCallback(new CreateBeamUrisCallback() {
    865             @Override
    866             public Uri[] createBeamUris(NfcEvent event) {
    867                 return mNfcPushUris;
    868             }
    869         }, CameraActivity.this);
    870     }
    871 
    872     @Override
    873     public boolean onShareTargetSelected(ShareActionProvider shareActionProvider, Intent intent) {
    874         int currentIndex = mFilmstripController.getCurrentAdapterIndex();
    875         if (currentIndex < 0) {
    876             return false;
    877         }
    878         UsageStatistics.instance().mediaInteraction(fileNameFromAdapterAtIndex(currentIndex),
    879                 MediaInteraction.InteractionType.SHARE,
    880                 NavigationChange.InteractionCause.BUTTON, fileAgeFromAdapterAtIndex(currentIndex));
    881         // TODO add intent.getComponent().getPackageName()
    882         return true;
    883     }
    884 
    885     // Note: All callbacks come back on the main thread.
    886     private final SessionListener mSessionListener =
    887             new SessionListener() {
    888                 @Override
    889                 public void onSessionQueued(final Uri uri) {
    890                     Log.v(TAG, "onSessionQueued: " + uri);
    891                     if (!Storage.isSessionUri(uri)) {
    892                         return;
    893                     }
    894                     Optional<SessionItem> newData = SessionItem.create(getApplicationContext(), uri);
    895                     if (newData.isPresent()) {
    896                         mDataAdapter.addOrUpdate(newData.get());
    897                     }
    898                 }
    899 
    900                 @Override
    901                 public void onSessionUpdated(Uri uri) {
    902                     Log.v(TAG, "onSessionUpdated: " + uri);
    903                     mDataAdapter.refresh(uri);
    904                 }
    905 
    906                 @Override
    907                 public void onSessionDone(final Uri sessionUri) {
    908                     Log.v(TAG, "onSessionDone:" + sessionUri);
    909                     Uri contentUri = Storage.getContentUriForSessionUri(sessionUri);
    910                     if (contentUri == null) {
    911                         mDataAdapter.refresh(sessionUri);
    912                         return;
    913                     }
    914                     PhotoItem newData = mPhotoItemFactory.queryContentUri(contentUri);
    915 
    916                     // This can be null if e.g. a session is canceled (e.g.
    917                     // through discard panorama). It might be worth adding
    918                     // onSessionCanceled or the like this interface.
    919                     if (newData == null) {
    920                         Log.i(TAG, "onSessionDone: Could not find LocalData for URI: " + contentUri);
    921                         return;
    922                     }
    923 
    924                     final int pos = mDataAdapter.findByContentUri(sessionUri);
    925                     if (pos == -1) {
    926                         // We do not have a placeholder for this image, perhaps
    927                         // due to the activity crashing or being killed.
    928                         mDataAdapter.addOrUpdate(newData);
    929                     } else {
    930                         // Make the PhotoItem aware of the session placeholder, to
    931                         // allow it to make a smooth transition to its content if it
    932                         // the session item is currently visible.
    933                         FilmstripItem oldSessionData = mDataAdapter.getFilmstripItemAt(pos);
    934                         if (mCameraAppUI.getFilmstripVisibility() == View.VISIBLE
    935                                 && mFilmstripController.isVisible(oldSessionData)) {
    936                             Log.v(TAG, "session item visible, setting transition placeholder");
    937                             newData.setSessionPlaceholderBitmap(
    938                                     Storage.getPlaceholderForSession(sessionUri));
    939                         }
    940                         mDataAdapter.updateItemAt(pos, newData);
    941                     }
    942                 }
    943 
    944                 @Override
    945                 public void onSessionProgress(final Uri uri, final int progress) {
    946                     if (progress < 0) {
    947                         // Do nothing, there is no task for this URI.
    948                         return;
    949                     }
    950                     int currentIndex = mFilmstripController.getCurrentAdapterIndex();
    951                     if (currentIndex == -1) {
    952                         return;
    953                     }
    954                     if (uri.equals(
    955                             mDataAdapter.getItemAt(currentIndex).getData().getUri())) {
    956                         updateSessionProgress(progress);
    957                     }
    958                 }
    959 
    960                 @Override
    961                 public void onSessionProgressText(final Uri uri, final int messageId) {
    962                     int currentIndex = mFilmstripController.getCurrentAdapterIndex();
    963                     if (currentIndex == -1) {
    964                         return;
    965                     }
    966                     if (uri.equals(
    967                             mDataAdapter.getItemAt(currentIndex).getData().getUri())) {
    968                         updateSessionProgressText(messageId);
    969                     }
    970                 }
    971 
    972                 @Override
    973                 public void onSessionCaptureIndicatorUpdate(Bitmap indicator, int rotationDegrees) {
    974                     // Don't show capture indicator in Photo Sphere.
    975                     final int photosphereModuleId = getApplicationContext().getResources()
    976                             .getInteger(
    977                                     R.integer.camera_mode_photosphere);
    978                     if (mCurrentModeIndex == photosphereModuleId) {
    979                         return;
    980                     }
    981                     indicateCapture(indicator, rotationDegrees);
    982                 }
    983 
    984                 @Override
    985                 public void onSessionFailed(Uri uri, int failureMessageId,
    986                         boolean removeFromFilmstrip) {
    987                     Log.v(TAG, "onSessionFailed:" + uri);
    988 
    989                     int failedIndex = mDataAdapter.findByContentUri(uri);
    990                     int currentIndex = mFilmstripController.getCurrentAdapterIndex();
    991 
    992                     if (currentIndex == failedIndex) {
    993                         updateSessionProgress(0);
    994                         showProcessError(failureMessageId);
    995                         mDataAdapter.refresh(uri);
    996                     }
    997                     if (removeFromFilmstrip) {
    998                         mFatalErrorHandler.onMediaStorageFailure();
    999                         mDataAdapter.removeAt(failedIndex);
   1000                     }
   1001                 }
   1002 
   1003                 @Override
   1004                 public void onSessionCanceled(Uri uri) {
   1005                     Log.v(TAG, "onSessionCanceled:" + uri);
   1006                     int failedIndex = mDataAdapter.findByContentUri(uri);
   1007                     mDataAdapter.removeAt(failedIndex);
   1008                 }
   1009 
   1010                 @Override
   1011                 public void onSessionThumbnailUpdate(Bitmap bitmap) {
   1012                 }
   1013 
   1014                 @Override
   1015                 public void onSessionPictureDataUpdate(byte[] pictureData, int orientation) {
   1016                 }
   1017             };
   1018 
   1019     @Override
   1020     public Context getAndroidContext() {
   1021         return mAppContext;
   1022     }
   1023 
   1024     @Override
   1025     public OneCameraFeatureConfig getCameraFeatureConfig() {
   1026         return mFeatureConfig;
   1027     }
   1028 
   1029     @Override
   1030     public Dialog createDialog() {
   1031         return new Dialog(this, android.R.style.Theme_Black_NoTitleBar_Fullscreen);
   1032     }
   1033 
   1034     @Override
   1035     public void launchActivityByIntent(Intent intent) {
   1036         // Starting from L, we prefer not to start edit activity within camera's task.
   1037         mResetToPreviewOnResume = false;
   1038         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
   1039 
   1040         startActivity(intent);
   1041     }
   1042 
   1043     @Override
   1044     public int getCurrentModuleIndex() {
   1045         return mCurrentModeIndex;
   1046     }
   1047 
   1048     @Override
   1049     public String getModuleScope() {
   1050         ModuleAgent agent = mModuleManager.getModuleAgent(mCurrentModeIndex);
   1051         return SettingsManager.getModuleSettingScope(agent.getScopeNamespace());
   1052     }
   1053 
   1054     @Override
   1055     public String getCameraScope() {
   1056         // if an unopen camera i.e. negative ID is returned, which we've observed in
   1057         // some automated scenarios, just return it as a valid separate scope
   1058         // this could cause user issues, so log a stack trace noting the call path
   1059         // which resulted in this scenario.
   1060 
   1061         CameraId cameraId =  mCameraController.getCurrentCameraId();
   1062 
   1063         if(cameraId == null) {
   1064             Log.e(TAG,  "Retrieving Camera Setting Scope with -1");
   1065             return SettingsManager.getCameraSettingScope("-1");
   1066         }
   1067 
   1068         return SettingsManager.getCameraSettingScope(cameraId.getValue());
   1069     }
   1070 
   1071     @Override
   1072     public ModuleController getCurrentModuleController() {
   1073         return mCurrentModule;
   1074     }
   1075 
   1076     @Override
   1077     public int getQuickSwitchToModuleId(int currentModuleIndex) {
   1078         return mModuleManager.getQuickSwitchToModuleId(currentModuleIndex, mSettingsManager,
   1079                 mAppContext);
   1080     }
   1081 
   1082     @Override
   1083     public SurfaceTexture getPreviewBuffer() {
   1084         // TODO: implement this
   1085         return null;
   1086     }
   1087 
   1088     @Override
   1089     public void onPreviewReadyToStart() {
   1090         mCameraAppUI.onPreviewReadyToStart();
   1091     }
   1092 
   1093     @Override
   1094     public void onPreviewStarted() {
   1095         mCameraAppUI.onPreviewStarted();
   1096     }
   1097 
   1098     @Override
   1099     public void addPreviewAreaSizeChangedListener(
   1100             PreviewStatusListener.PreviewAreaChangedListener listener) {
   1101         mCameraAppUI.addPreviewAreaChangedListener(listener);
   1102     }
   1103 
   1104     @Override
   1105     public void removePreviewAreaSizeChangedListener(
   1106             PreviewStatusListener.PreviewAreaChangedListener listener) {
   1107         mCameraAppUI.removePreviewAreaChangedListener(listener);
   1108     }
   1109 
   1110     @Override
   1111     public void setupOneShotPreviewListener() {
   1112         mCameraController.setOneShotPreviewCallback(mMainHandler,
   1113                 new CameraAgent.CameraPreviewDataCallback() {
   1114                     @Override
   1115                     public void onPreviewFrame(byte[] data, CameraAgent.CameraProxy camera) {
   1116                         mCurrentModule.onPreviewInitialDataReceived();
   1117                         mCameraAppUI.onNewPreviewFrame();
   1118                     }
   1119                 }
   1120         );
   1121     }
   1122 
   1123     @Override
   1124     public void updatePreviewAspectRatio(float aspectRatio) {
   1125         mCameraAppUI.updatePreviewAspectRatio(aspectRatio);
   1126     }
   1127 
   1128     @Override
   1129     public void updatePreviewTransformFullscreen(Matrix matrix, float aspectRatio) {
   1130         mCameraAppUI.updatePreviewTransformFullscreen(matrix, aspectRatio);
   1131     }
   1132 
   1133     @Override
   1134     public RectF getFullscreenRect() {
   1135         return mCameraAppUI.getFullscreenRect();
   1136     }
   1137 
   1138     @Override
   1139     public void updatePreviewTransform(Matrix matrix) {
   1140         mCameraAppUI.updatePreviewTransform(matrix);
   1141     }
   1142 
   1143     @Override
   1144     public void setPreviewStatusListener(PreviewStatusListener previewStatusListener) {
   1145         mCameraAppUI.setPreviewStatusListener(previewStatusListener);
   1146     }
   1147 
   1148     @Override
   1149     public FrameLayout getModuleLayoutRoot() {
   1150         return mCameraAppUI.getModuleRootView();
   1151     }
   1152 
   1153     @Override
   1154     public void setShutterEventsListener(ShutterEventsListener listener) {
   1155         // TODO: implement this
   1156     }
   1157 
   1158     @Override
   1159     public void setShutterEnabled(boolean enabled) {
   1160         mCameraAppUI.setShutterButtonEnabled(enabled);
   1161     }
   1162 
   1163     @Override
   1164     public boolean isShutterEnabled() {
   1165         return mCameraAppUI.isShutterButtonEnabled();
   1166     }
   1167 
   1168     @Override
   1169     public void startFlashAnimation(boolean shortFlash) {
   1170         mCameraAppUI.startFlashAnimation(shortFlash);
   1171     }
   1172 
   1173     @Override
   1174     public void startPreCaptureAnimation() {
   1175         // TODO: implement this
   1176     }
   1177 
   1178     @Override
   1179     public void cancelPreCaptureAnimation() {
   1180         // TODO: implement this
   1181     }
   1182 
   1183     @Override
   1184     public void startPostCaptureAnimation() {
   1185         // TODO: implement this
   1186     }
   1187 
   1188     @Override
   1189     public void startPostCaptureAnimation(Bitmap thumbnail) {
   1190         // TODO: implement this
   1191     }
   1192 
   1193     @Override
   1194     public void cancelPostCaptureAnimation() {
   1195         // TODO: implement this
   1196     }
   1197 
   1198     @Override
   1199     public OrientationManager getOrientationManager() {
   1200         return mOrientationManager;
   1201     }
   1202 
   1203     @Override
   1204     public LocationManager getLocationManager() {
   1205         return mLocationManager;
   1206     }
   1207 
   1208     @Override
   1209     public void lockOrientation() {
   1210         if (mOrientationManager != null) {
   1211             mOrientationManager.lockOrientation();
   1212         }
   1213     }
   1214 
   1215     @Override
   1216     public void unlockOrientation() {
   1217         if (mOrientationManager != null) {
   1218             mOrientationManager.unlockOrientation();
   1219         }
   1220     }
   1221 
   1222     /**
   1223      * If not in filmstrip, this shows the capture indicator.
   1224      */
   1225     private void indicateCapture(final Bitmap indicator, final int rotationDegrees) {
   1226         if (mFilmstripVisible) {
   1227             return;
   1228         }
   1229 
   1230         // Don't show capture indicator in Photo Sphere.
   1231         // TODO: Don't reach into resources to figure out the current mode.
   1232         final int photosphereModuleId = getApplicationContext().getResources().getInteger(
   1233                 R.integer.camera_mode_photosphere);
   1234         if (mCurrentModeIndex == photosphereModuleId) {
   1235             return;
   1236         }
   1237 
   1238         mMainHandler.post(new Runnable() {
   1239             @Override
   1240             public void run() {
   1241                 mCameraAppUI.startCaptureIndicatorRevealAnimation(mCurrentModule
   1242                         .getPeekAccessibilityString());
   1243                 mCameraAppUI.updateCaptureIndicatorThumbnail(indicator, rotationDegrees);
   1244             }
   1245         });
   1246     }
   1247 
   1248     @Override
   1249     public void notifyNewMedia(Uri uri) {
   1250         // TODO: This method is running on the main thread. Also we should get
   1251         // rid of that AsyncTask.
   1252 
   1253         updateStorageSpaceAndHint(null);
   1254         ContentResolver cr = getContentResolver();
   1255         String mimeType = cr.getType(uri);
   1256         FilmstripItem newData = null;
   1257         if (FilmstripItemUtils.isMimeTypeVideo(mimeType)) {
   1258             sendBroadcast(new Intent(CameraUtil.ACTION_NEW_VIDEO, uri));
   1259             newData = mVideoItemFactory.queryContentUri(uri);
   1260             if (newData == null) {
   1261                 Log.e(TAG, "Can't find video data in content resolver:" + uri);
   1262                 return;
   1263             }
   1264         } else if (FilmstripItemUtils.isMimeTypeImage(mimeType)) {
   1265             CameraUtil.broadcastNewPicture(mAppContext, uri);
   1266             newData = mPhotoItemFactory.queryContentUri(uri);
   1267             if (newData == null) {
   1268                 Log.e(TAG, "Can't find photo data in content resolver:" + uri);
   1269                 return;
   1270             }
   1271         } else {
   1272             Log.w(TAG, "Unknown new media with MIME type:" + mimeType + ", uri:" + uri);
   1273             return;
   1274         }
   1275 
   1276         // We are preloading the metadata for new video since we need the
   1277         // rotation info for the thumbnail.
   1278         new AsyncTask<FilmstripItem, Void, FilmstripItem>() {
   1279             @Override
   1280             protected FilmstripItem doInBackground(FilmstripItem... params) {
   1281                 FilmstripItem data = params[0];
   1282                 MetadataLoader.loadMetadata(getAndroidContext(), data);
   1283                 return data;
   1284             }
   1285 
   1286             @Override
   1287             protected void onPostExecute(final FilmstripItem data) {
   1288                 // TODO: Figure out why sometimes the data is aleady there.
   1289                 mDataAdapter.addOrUpdate(data);
   1290 
   1291                 // Legacy modules don't use CaptureSession, so we show the capture indicator when
   1292                 // the item was safed.
   1293                 if (mCurrentModule instanceof PhotoModule ||
   1294                         mCurrentModule instanceof VideoModule) {
   1295                     AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
   1296                         @Override
   1297                         public void run() {
   1298                             final Optional<Bitmap> bitmap = data.generateThumbnail(
   1299                                     mAboveFilmstripControlLayout.getWidth(),
   1300                                     mAboveFilmstripControlLayout.getMeasuredHeight());
   1301                             if (bitmap.isPresent()) {
   1302                                 indicateCapture(bitmap.get(), 0);
   1303                             }
   1304                         }
   1305                     });
   1306                 }
   1307             }
   1308         }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, newData);
   1309     }
   1310 
   1311     @Override
   1312     public void enableKeepScreenOn(boolean enabled) {
   1313         if (mPaused) {
   1314             return;
   1315         }
   1316 
   1317         mKeepScreenOn = enabled;
   1318         if (mKeepScreenOn) {
   1319             mMainHandler.removeMessages(MSG_CLEAR_SCREEN_ON_FLAG);
   1320             getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
   1321         } else {
   1322             keepScreenOnForAWhile();
   1323         }
   1324     }
   1325 
   1326     @Override
   1327     public CameraProvider getCameraProvider() {
   1328         return mCameraController;
   1329     }
   1330 
   1331     @Override
   1332     public OneCameraOpener getCameraOpener() {
   1333         return mOneCameraOpener;
   1334     }
   1335 
   1336     private void removeItemAt(int index) {
   1337         mDataAdapter.removeAt(index);
   1338         if (mDataAdapter.getTotalNumber() > 1) {
   1339             showUndoDeletionBar();
   1340         } else {
   1341             // If camera preview is the only view left in filmstrip,
   1342             // no need to show undo bar.
   1343             mPendingDeletion = true;
   1344             performDeletion();
   1345             if (mFilmstripVisible) {
   1346                 mCameraAppUI.getFilmstripContentPanel().animateHide();
   1347             }
   1348         }
   1349     }
   1350 
   1351     @Override
   1352     public boolean onOptionsItemSelected(MenuItem item) {
   1353         // Handle presses on the action bar items
   1354         switch (item.getItemId()) {
   1355             case android.R.id.home:
   1356                 onBackPressed();
   1357                 return true;
   1358             case R.id.action_details:
   1359                 showDetailsDialog(mFilmstripController.getCurrentAdapterIndex());
   1360                 return true;
   1361             case R.id.action_help_and_feedback:
   1362                 mResetToPreviewOnResume = false;
   1363                 new GoogleHelpHelper(this).launchGoogleHelp();
   1364                 return true;
   1365             default:
   1366                 return super.onOptionsItemSelected(item);
   1367         }
   1368     }
   1369 
   1370     private boolean isCaptureIntent() {
   1371         if (MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction())
   1372                 || MediaStore.ACTION_IMAGE_CAPTURE.equals(getIntent().getAction())
   1373                 || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(getIntent().getAction())) {
   1374             return true;
   1375         } else {
   1376             return false;
   1377         }
   1378     }
   1379 
   1380     /**
   1381      * Note: Make sure this callback is unregistered properly when the activity
   1382      * is destroyed since we're otherwise leaking the Activity reference.
   1383      */
   1384     private final CameraExceptionHandler.CameraExceptionCallback mCameraExceptionCallback
   1385         = new CameraExceptionHandler.CameraExceptionCallback() {
   1386                 @Override
   1387                 public void onCameraError(int errorCode) {
   1388                     // Not a fatal error. only do Log.e().
   1389                     Log.e(TAG, "Camera error callback. error=" + errorCode);
   1390                 }
   1391                 @Override
   1392                 public void onCameraException(
   1393                         RuntimeException ex, String commandHistory, int action, int state) {
   1394                     Log.e(TAG, "Camera Exception", ex);
   1395                     UsageStatistics.instance().cameraFailure(
   1396                             eventprotos.CameraFailure.FailureReason.API_RUNTIME_EXCEPTION,
   1397                             commandHistory, action, state);
   1398                     onFatalError();
   1399                 }
   1400                 @Override
   1401                 public void onDispatchThreadException(RuntimeException ex) {
   1402                     Log.e(TAG, "DispatchThread Exception", ex);
   1403                     UsageStatistics.instance().cameraFailure(
   1404                             eventprotos.CameraFailure.FailureReason.API_TIMEOUT,
   1405                             null, UsageStatistics.NONE, UsageStatistics.NONE);
   1406                     onFatalError();
   1407                 }
   1408                 private void onFatalError() {
   1409                     if (mCameraFatalError) {
   1410                         return;
   1411                     }
   1412                     mCameraFatalError = true;
   1413 
   1414                     // If the activity receives exception during onPause, just exit the app.
   1415                     if (mPaused && !isFinishing()) {
   1416                         Log.e(TAG, "Fatal error during onPause, call Activity.finish()");
   1417                         finish();
   1418                     } else {
   1419                         mFatalErrorHandler.handleFatalError(FatalErrorHandler.Reason.CANNOT_CONNECT_TO_CAMERA);
   1420                     }
   1421                 }
   1422             };
   1423 
   1424     @Override
   1425     public void onNewIntentTasks(Intent intent) {
   1426         onModeSelected(getModeIndex());
   1427     }
   1428 
   1429     @Override
   1430     public void onCreateTasks(Bundle state) {
   1431         Profile profile = mProfiler.create("CameraActivity.onCreateTasks").start();
   1432         CameraPerformanceTracker.onEvent(CameraPerformanceTracker.ACTIVITY_START);
   1433         mOnCreateTime = System.currentTimeMillis();
   1434         mAppContext = getApplicationContext();
   1435         mMainHandler = new MainHandler(this, getMainLooper());
   1436         mLocationManager = new LocationManager(mAppContext);
   1437         mOrientationManager = new OrientationManagerImpl(this, mMainHandler);
   1438         mSettingsManager = getServices().getSettingsManager();
   1439         mSoundPlayer = new SoundPlayer(mAppContext);
   1440         mFeatureConfig = OneCameraFeatureConfigCreator.createDefault(getContentResolver(),
   1441                 getServices().getMemoryManager());
   1442         mFatalErrorHandler = new FatalErrorHandlerImpl(this);
   1443         checkPermissions();
   1444         if (!mHasCriticalPermissions) {
   1445             Log.v(TAG, "onCreate: Missing critical permissions.");
   1446             finish();
   1447             return;
   1448         }
   1449         profile.mark();
   1450         if (!Glide.isSetup()) {
   1451             Context context = getAndroidContext();
   1452             Glide.setup(new GlideBuilder(context)
   1453                 .setDecodeFormat(DecodeFormat.ALWAYS_ARGB_8888)
   1454                 .setResizeService(new FifoPriorityThreadPoolExecutor(2)));
   1455 
   1456             Glide glide = Glide.get(context);
   1457 
   1458             // As a camera we will use a large amount of memory
   1459             // for displaying images.
   1460             glide.setMemoryCategory(MemoryCategory.HIGH);
   1461         }
   1462         profile.mark("Glide.setup");
   1463 
   1464         mActiveCameraDeviceTracker = ActiveCameraDeviceTracker.instance();
   1465         try {
   1466             mOneCameraOpener = OneCameraModule.provideOneCameraOpener(
   1467                     mFeatureConfig,
   1468                     mAppContext,
   1469                     mActiveCameraDeviceTracker,
   1470                     ResolutionUtil.getDisplayMetrics(this));
   1471             mOneCameraManager = OneCameraModule.provideOneCameraManager();
   1472         } catch (OneCameraException e) {
   1473             // Log error and continue start process while showing error dialog..
   1474             Log.e(TAG, "Creating camera manager failed.", e);
   1475             mFatalErrorHandler.onGenericCameraAccessFailure();
   1476         }
   1477         profile.mark("OneCameraManager.get");
   1478 
   1479         try {
   1480             mCameraController = new CameraController(mAppContext, this, mMainHandler,
   1481                     CameraAgentFactory.getAndroidCameraAgent(mAppContext,
   1482                             CameraAgentFactory.CameraApi.API_1),
   1483                     CameraAgentFactory.getAndroidCameraAgent(mAppContext,
   1484                             CameraAgentFactory.CameraApi.AUTO),
   1485                     mActiveCameraDeviceTracker);
   1486             mCameraController.setCameraExceptionHandler(
   1487                     new CameraExceptionHandler(mCameraExceptionCallback, mMainHandler));
   1488         } catch (AssertionError e) {
   1489             Log.e(TAG, "Creating camera controller failed.", e);
   1490             mFatalErrorHandler.onGenericCameraAccessFailure();
   1491         }
   1492 
   1493         // TODO: Try to move all the resources allocation to happen as soon as
   1494         // possible so we can call module.init() at the earliest time.
   1495         mModuleManager = new ModuleManagerImpl();
   1496 
   1497         ModulesInfo.setupModules(mAppContext, mModuleManager, mFeatureConfig);
   1498 
   1499         AppUpgrader appUpgrader = new AppUpgrader(this);
   1500         appUpgrader.upgrade(mSettingsManager);
   1501 
   1502         // Make sure the picture sizes are correctly cached for the current OS
   1503         // version.
   1504         profile.mark();
   1505         try {
   1506             (new PictureSizeLoader(mAppContext)).computePictureSizes();
   1507         } catch (AssertionError e) {
   1508             Log.e(TAG, "Creating camera controller failed.", e);
   1509             mFatalErrorHandler.onGenericCameraAccessFailure();
   1510         }
   1511         profile.mark("computePictureSizes");
   1512         Keys.setDefaults(mSettingsManager, mAppContext);
   1513 
   1514         mResolutionSetting = new ResolutionSetting(mSettingsManager, mOneCameraManager,
   1515                 getContentResolver());
   1516 
   1517         getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
   1518         // We suppress this flag via theme when drawing the system preview
   1519         // background, but once we create activity here, reactivate to the
   1520         // default value. The default is important for L, we don't want to
   1521         // change app behavior, just starting background drawable layout.
   1522         if (ApiHelper.isLOrHigher()) {
   1523             getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
   1524         }
   1525 
   1526         profile.mark();
   1527         setContentView(R.layout.activity_main);
   1528         profile.mark("setContentView()");
   1529         // A window background is set in styles.xml for the system to show a
   1530         // drawable background with gray color and camera icon before the
   1531         // activity is created. We set the background to null here to prevent
   1532         // overdraw, all views must take care of drawing backgrounds if
   1533         // necessary. This call to setBackgroundDrawable must occur after
   1534         // setContentView, otherwise a background may be set again from the
   1535         // style.
   1536         getWindow().setBackgroundDrawable(null);
   1537 
   1538         mActionBar = getActionBar();
   1539         // set actionbar background to 100% or 50% transparent
   1540         if (ApiHelper.isLOrHigher()) {
   1541             mActionBar.setBackgroundDrawable(new ColorDrawable(0x00000000));
   1542         } else {
   1543             mActionBar.setBackgroundDrawable(new ColorDrawable(0x80000000));
   1544         }
   1545 
   1546         mModeListView = (ModeListView) findViewById(R.id.mode_list_layout);
   1547         mModeListView.init(mModuleManager.getSupportedModeIndexList());
   1548         if (ApiHelper.HAS_ROTATION_ANIMATION) {
   1549             setRotationAnimation();
   1550         }
   1551         mModeListView.setVisibilityChangedListener(new ModeListVisibilityChangedListener() {
   1552             @Override
   1553             public void onVisibilityChanged(boolean visible) {
   1554                 mModeListVisible = visible;
   1555                 mCameraAppUI.setShutterButtonImportantToA11y(!visible);
   1556                 updatePreviewVisibility();
   1557             }
   1558         });
   1559 
   1560         // Check if this is in the secure camera mode.
   1561         Intent intent = getIntent();
   1562         String action = intent.getAction();
   1563         if (INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action)
   1564                 || ACTION_IMAGE_CAPTURE_SECURE.equals(action)) {
   1565             mSecureCamera = true;
   1566         } else {
   1567             mSecureCamera = intent.getBooleanExtra(SECURE_CAMERA_EXTRA, false);
   1568         }
   1569 
   1570         if (mSecureCamera) {
   1571             // Change the window flags so that secure camera can show when
   1572             // locked
   1573             Window win = getWindow();
   1574             WindowManager.LayoutParams params = win.getAttributes();
   1575             params.flags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
   1576             win.setAttributes(params);
   1577 
   1578             // Filter for screen off so that we can finish secure camera
   1579             // activity when screen is off.
   1580             IntentFilter filter_screen_off = new IntentFilter(Intent.ACTION_SCREEN_OFF);
   1581             registerReceiver(mShutdownReceiver, filter_screen_off);
   1582 
   1583             // Filter for phone unlock so that we can finish secure camera
   1584             // via this UI path:
   1585             //    1. from secure lock screen, user starts secure camera
   1586             //    2. user presses home button
   1587             //    3. user unlocks phone
   1588             IntentFilter filter_user_unlock = new IntentFilter(Intent.ACTION_USER_PRESENT);
   1589             registerReceiver(mShutdownReceiver, filter_user_unlock);
   1590         }
   1591         mCameraAppUI = new CameraAppUI(this,
   1592                 (MainActivityLayout) findViewById(R.id.activity_root_view), isCaptureIntent());
   1593 
   1594         mCameraAppUI.setFilmstripBottomControlsListener(mMyFilmstripBottomControlListener);
   1595 
   1596         mAboveFilmstripControlLayout =
   1597                 (FrameLayout) findViewById(R.id.camera_filmstrip_content_layout);
   1598 
   1599         // Add the session listener so we can track the session progress
   1600         // updates.
   1601         getServices().getCaptureSessionManager().addSessionListener(mSessionListener);
   1602         mFilmstripController = ((FilmstripView) findViewById(R.id.filmstrip_view)).getController();
   1603         mFilmstripController.setImageGap(
   1604                 getResources().getDimensionPixelSize(R.dimen.camera_film_strip_gap));
   1605         profile.mark("Configure Camera UI");
   1606 
   1607         mPanoramaViewHelper = new PanoramaViewHelper(this);
   1608         mPanoramaViewHelper.onCreate();
   1609 
   1610         ContentResolver appContentResolver = mAppContext.getContentResolver();
   1611         GlideFilmstripManager glideManager = new GlideFilmstripManager(mAppContext);
   1612         mPhotoItemFactory = new PhotoItemFactory(mAppContext, glideManager, appContentResolver,
   1613               new PhotoDataFactory());
   1614         mVideoItemFactory = new VideoItemFactory(mAppContext, glideManager, appContentResolver,
   1615               new VideoDataFactory());
   1616         mCameraAppUI.getFilmstripContentPanel().setFilmstripListener(mFilmstripListener);
   1617         if (mSettingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL,
   1618                                         Keys.KEY_SHOULD_SHOW_REFOCUS_VIEWER_CLING)) {
   1619             mCameraAppUI.setupClingForViewer(CameraAppUI.BottomPanel.VIEWER_REFOCUS);
   1620         }
   1621 
   1622         setModuleFromModeIndex(getModeIndex());
   1623 
   1624         profile.mark();
   1625         mCameraAppUI.prepareModuleUI();
   1626         profile.mark("Init Current Module UI");
   1627         mCurrentModule.init(this, isSecureCamera(), isCaptureIntent());
   1628         profile.mark("Init CurrentModule");
   1629 
   1630         preloadFilmstripItems();
   1631 
   1632         setupNfcBeamPush();
   1633 
   1634         mLocalImagesObserver = new FilmstripContentObserver();
   1635         mLocalVideosObserver = new FilmstripContentObserver();
   1636 
   1637         getContentResolver().registerContentObserver(
   1638                 MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true,
   1639                 mLocalImagesObserver);
   1640         getContentResolver().registerContentObserver(
   1641               MediaStore.Video.Media.EXTERNAL_CONTENT_URI, true,
   1642               mLocalVideosObserver);
   1643 
   1644         mMemoryManager = getServices().getMemoryManager();
   1645 
   1646         AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
   1647             @Override
   1648             public void run() {
   1649                 HashMap memoryData = mMemoryManager.queryMemory();
   1650                 UsageStatistics.instance().reportMemoryConsumed(memoryData,
   1651                       MemoryQuery.REPORT_LABEL_LAUNCH);
   1652             }
   1653         });
   1654 
   1655         mMotionManager = getServices().getMotionManager();
   1656 
   1657         mFirstRunDialog = new FirstRunDialog(this,
   1658               this /* as context */,
   1659               mResolutionSetting,
   1660               mSettingsManager,
   1661               mOneCameraManager,
   1662               new FirstRunDialog.FirstRunDialogListener() {
   1663             @Override
   1664             public void onFirstRunStateReady() {
   1665                 // Run normal resume tasks.
   1666                 resume();
   1667             }
   1668 
   1669             @Override
   1670             public void onFirstRunDialogCancelled() {
   1671                 // App isn't functional until users finish first run dialog.
   1672                 // We need to finish here since users hit back button during
   1673                 // first run dialog (b/19593942).
   1674                 finish();
   1675             }
   1676 
   1677             @Override
   1678             public void onCameraAccessException() {
   1679                 mFatalErrorHandler.onGenericCameraAccessFailure();
   1680             }
   1681         });
   1682         profile.stop();
   1683     }
   1684 
   1685     /**
   1686      * Get the current mode index from the Intent or from persistent
   1687      * settings.
   1688      */
   1689     private int getModeIndex() {
   1690         int modeIndex = -1;
   1691         int photoIndex = getResources().getInteger(R.integer.camera_mode_photo);
   1692         int videoIndex = getResources().getInteger(R.integer.camera_mode_video);
   1693         int gcamIndex = getResources().getInteger(R.integer.camera_mode_gcam);
   1694         int captureIntentIndex =
   1695                 getResources().getInteger(R.integer.camera_mode_capture_intent);
   1696         String intentAction = getIntent().getAction();
   1697         if (MediaStore.INTENT_ACTION_VIDEO_CAMERA.equals(intentAction)
   1698                 || MediaStore.ACTION_VIDEO_CAPTURE.equals(intentAction)) {
   1699             modeIndex = videoIndex;
   1700         } else if (MediaStore.ACTION_IMAGE_CAPTURE.equals(intentAction)
   1701                 || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(intentAction)) {
   1702             // Capture intent.
   1703             modeIndex = captureIntentIndex;
   1704         } else if (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(intentAction)
   1705                 ||MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(intentAction)
   1706                 || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(intentAction)) {
   1707             modeIndex = mSettingsManager.getInteger(SettingsManager.SCOPE_GLOBAL,
   1708                 Keys.KEY_CAMERA_MODULE_LAST_USED);
   1709 
   1710             // For upgraders who have not seen the aspect ratio selection screen,
   1711             // we need to drop them back in the photo module and have them select
   1712             // aspect ratio.
   1713             // TODO: Move this to SettingsManager as an upgrade procedure.
   1714             if (!mSettingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL,
   1715                     Keys.KEY_USER_SELECTED_ASPECT_RATIO)) {
   1716                 modeIndex = photoIndex;
   1717             }
   1718         } else {
   1719             // If the activity has not been started using an explicit intent,
   1720             // read the module index from the last time the user changed modes
   1721             modeIndex = mSettingsManager.getInteger(SettingsManager.SCOPE_GLOBAL,
   1722                                                     Keys.KEY_STARTUP_MODULE_INDEX);
   1723             if ((modeIndex == gcamIndex &&
   1724                     !GcamHelper.hasGcamAsSeparateModule(mFeatureConfig)) || modeIndex < 0) {
   1725                 modeIndex = photoIndex;
   1726             }
   1727         }
   1728         return modeIndex;
   1729     }
   1730 
   1731     /**
   1732      * Call this whenever the mode drawer or filmstrip change the visibility
   1733      * state.
   1734      */
   1735     private void updatePreviewVisibility() {
   1736         if (mCurrentModule == null) {
   1737             return;
   1738         }
   1739 
   1740         int visibility = getPreviewVisibility();
   1741         mCameraAppUI.onPreviewVisiblityChanged(visibility);
   1742         updatePreviewRendering(visibility);
   1743         mCurrentModule.onPreviewVisibilityChanged(visibility);
   1744     }
   1745 
   1746     private void updatePreviewRendering(int visibility) {
   1747         if (visibility == ModuleController.VISIBILITY_HIDDEN) {
   1748             mCameraAppUI.pausePreviewRendering();
   1749         } else {
   1750             mCameraAppUI.resumePreviewRendering();
   1751         }
   1752     }
   1753 
   1754     private int getPreviewVisibility() {
   1755         if (mFilmstripCoversPreview) {
   1756             return ModuleController.VISIBILITY_HIDDEN;
   1757         } else if (mModeListVisible){
   1758             return ModuleController.VISIBILITY_COVERED;
   1759         } else {
   1760             return ModuleController.VISIBILITY_VISIBLE;
   1761         }
   1762     }
   1763 
   1764     private void setRotationAnimation() {
   1765         int rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
   1766         rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE;
   1767         Window win = getWindow();
   1768         WindowManager.LayoutParams winParams = win.getAttributes();
   1769         winParams.rotationAnimation = rotationAnimation;
   1770         win.setAttributes(winParams);
   1771     }
   1772 
   1773     @Override
   1774     public void onUserInteraction() {
   1775         super.onUserInteraction();
   1776         if (!isFinishing()) {
   1777             keepScreenOnForAWhile();
   1778         }
   1779     }
   1780 
   1781     @Override
   1782     public boolean dispatchTouchEvent(MotionEvent ev) {
   1783         boolean result = super.dispatchTouchEvent(ev);
   1784         if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
   1785             // Real deletion is postponed until the next user interaction after
   1786             // the gesture that triggers deletion. Until real deletion is
   1787             // performed, users can click the undo button to bring back the
   1788             // image that they chose to delete.
   1789             if (mPendingDeletion && !mIsUndoingDeletion) {
   1790                 performDeletion();
   1791             }
   1792         }
   1793         return result;
   1794     }
   1795 
   1796     @Override
   1797     public void onPauseTasks() {
   1798         CameraPerformanceTracker.onEvent(CameraPerformanceTracker.ACTIVITY_PAUSE);
   1799         Profile profile = mProfiler.create("CameraActivity.onPause").start();
   1800 
   1801         /*
   1802          * Save the last module index after all secure camera and icon launches,
   1803          * not just on mode switches.
   1804          *
   1805          * Right now we exclude capture intents from this logic, because we also
   1806          * ignore the cross-Activity recovery logic in onStart for capture intents.
   1807          */
   1808         if (!isCaptureIntent()) {
   1809             mSettingsManager.set(SettingsManager.SCOPE_GLOBAL,
   1810                                  Keys.KEY_STARTUP_MODULE_INDEX,
   1811                 mCurrentModeIndex);
   1812         }
   1813 
   1814         mPaused = true;
   1815         mCameraAppUI.hideCaptureIndicator();
   1816         mFirstRunDialog.dismiss();
   1817 
   1818         // Delete photos that are pending deletion
   1819         performDeletion();
   1820         mCurrentModule.pause();
   1821         mOrientationManager.pause();
   1822         mPanoramaViewHelper.onPause();
   1823 
   1824         mLocalImagesObserver.setForegroundChangeListener(null);
   1825         mLocalImagesObserver.setActivityPaused(true);
   1826         mLocalVideosObserver.setActivityPaused(true);
   1827         if (mPreloader != null) {
   1828             mPreloader.cancelAllLoads();
   1829         }
   1830         resetScreenOn();
   1831 
   1832         mMotionManager.stop();
   1833 
   1834         // Always stop recording location when paused. Resume will start
   1835         // location recording again if the location setting is on.
   1836         mLocationManager.recordLocation(false);
   1837 
   1838         UsageStatistics.instance().backgrounded();
   1839 
   1840         // Camera is in fatal state. A fatal dialog is presented to users, but users just hit home
   1841         // button. Let's just kill the process.
   1842         if (mCameraFatalError && !isFinishing()) {
   1843             Log.v(TAG, "onPause when camera is in fatal state, call Activity.finish()");
   1844             finish();
   1845         } else {
   1846             // Close the camera and wait for the operation done.
   1847             Log.v(TAG, "onPause closing camera");
   1848             if (mCameraController != null) {
   1849                 mCameraController.closeCamera(true);
   1850             }
   1851         }
   1852 
   1853         profile.stop();
   1854     }
   1855 
   1856     @Override
   1857     public void onResumeTasks() {
   1858         mPaused = false;
   1859         checkPermissions();
   1860         if (!mHasCriticalPermissions) {
   1861             Log.v(TAG, "onResume: Missing critical permissions.");
   1862             finish();
   1863             return;
   1864         }
   1865         if (!isSecureCamera() && !isCaptureIntent()) {
   1866             // Show the dialog if necessary. The rest resume logic will be invoked
   1867             // at the onFirstRunStateReady() callback.
   1868             try {
   1869                 mFirstRunDialog.showIfNecessary();
   1870             } catch (AssertionError e) {
   1871                 Log.e(TAG, "Creating camera controller failed.", e);
   1872                 mFatalErrorHandler.onGenericCameraAccessFailure();
   1873             }
   1874         } else {
   1875             // In secure mode from lockscreen, we go straight to camera and will
   1876             // show first run dialog next time user enters launcher.
   1877             Log.v(TAG, "in secure mode, skipping first run dialog check");
   1878             resume();
   1879         }
   1880     }
   1881 
   1882     /**
   1883      * Checks if any of the needed Android runtime permissions are missing.
   1884      * If they are, then launch the permissions activity under one of the following conditions:
   1885      * a) The permissions dialogs have not run yet. We will ask for permission only once.
   1886      * b) If the missing permissions are critical to the app running, we will display a fatal error dialog.
   1887      * Critical permissions are: camera, microphone and storage. The app cannot run without them.
   1888      * Non-critical permission is location.
   1889      */
   1890     private void checkPermissions() {
   1891         if (!ApiHelper.isMOrHigher()) {
   1892             Log.v(TAG, "not running on M, skipping permission checks");
   1893             mHasCriticalPermissions = true;
   1894             return;
   1895         }
   1896 
   1897         if (checkSelfPermission(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED &&
   1898                 checkSelfPermission(Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED &&
   1899                 checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
   1900             mHasCriticalPermissions = true;
   1901         } else {
   1902             mHasCriticalPermissions = false;
   1903         }
   1904 
   1905         if ((checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
   1906                 !mSettingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL, Keys.KEY_HAS_SEEN_PERMISSIONS_DIALOGS)) ||
   1907                 !mHasCriticalPermissions) {
   1908             Intent intent = new Intent(this, PermissionsActivity.class);
   1909             startActivity(intent);
   1910             finish();
   1911         }
   1912     }
   1913 
   1914     private void preloadFilmstripItems() {
   1915         if (mDataAdapter == null) {
   1916             mDataAdapter = new CameraFilmstripDataAdapter(mAppContext,
   1917                     mPhotoItemFactory, mVideoItemFactory);
   1918             mDataAdapter.setLocalDataListener(mFilmstripItemListener);
   1919             mPreloader = new Preloader<Integer, AsyncTask>(FILMSTRIP_PRELOAD_AHEAD_ITEMS, mDataAdapter,
   1920                     mDataAdapter);
   1921             if (!mSecureCamera) {
   1922                 mFilmstripController.setDataAdapter(mDataAdapter);
   1923                 if (!isCaptureIntent()) {
   1924                     mDataAdapter.requestLoad(new Callback<Void>() {
   1925                         @Override
   1926                         public void onCallback(Void result) {
   1927                             fillTemporarySessions();
   1928                         }
   1929                     });
   1930                 }
   1931             } else {
   1932                 // Put a lock placeholder as the last image by setting its date to
   1933                 // 0.
   1934                 ImageView v = (ImageView) getLayoutInflater().inflate(
   1935                         R.layout.secure_album_placeholder, null);
   1936                 v.setTag(R.id.mediadata_tag_viewtype, FilmstripItemType.SECURE_ALBUM_PLACEHOLDER.ordinal());
   1937                 v.setOnClickListener(new View.OnClickListener() {
   1938                     @Override
   1939                     public void onClick(View view) {
   1940                         UsageStatistics.instance().changeScreen(NavigationChange.Mode.GALLERY,
   1941                                 NavigationChange.InteractionCause.BUTTON);
   1942                         startGallery();
   1943                         finish();
   1944                     }
   1945                 });
   1946                 v.setContentDescription(getString(R.string.accessibility_unlock_to_camera));
   1947                 mDataAdapter = new FixedLastProxyAdapter(
   1948                         mAppContext,
   1949                         mDataAdapter,
   1950                         new PlaceholderItem(
   1951                                 v,
   1952                                 FilmstripItemType.SECURE_ALBUM_PLACEHOLDER,
   1953                                 v.getDrawable().getIntrinsicWidth(),
   1954                                 v.getDrawable().getIntrinsicHeight()));
   1955                 // Flush out all the original data.
   1956                 mDataAdapter.clear();
   1957                 mFilmstripController.setDataAdapter(mDataAdapter);
   1958             }
   1959         }
   1960     }
   1961 
   1962     private void resume() {
   1963         Profile profile = mProfiler.create("CameraActivity.resume").start();
   1964         CameraPerformanceTracker.onEvent(CameraPerformanceTracker.ACTIVITY_RESUME);
   1965         Log.v(TAG, "Build info: " + Build.DISPLAY);
   1966         updateStorageSpaceAndHint(null);
   1967 
   1968         mLastLayoutOrientation = getResources().getConfiguration().orientation;
   1969 
   1970         // TODO: Handle this in OrientationManager.
   1971         // Auto-rotate off
   1972         if (Settings.System.getInt(getContentResolver(),
   1973                 Settings.System.ACCELEROMETER_ROTATION, 0) == 0) {
   1974             setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
   1975             mAutoRotateScreen = false;
   1976         } else {
   1977             setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
   1978             mAutoRotateScreen = true;
   1979         }
   1980 
   1981         // Foreground event logging.  ACTION_STILL_IMAGE_CAMERA and
   1982         // INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE are double logged due to
   1983         // lockscreen onResume->onPause->onResume sequence.
   1984         int source;
   1985         String action = getIntent().getAction();
   1986         if (action == null) {
   1987             source = ForegroundSource.UNKNOWN_SOURCE;
   1988         } else {
   1989             switch (action) {
   1990                 case MediaStore.ACTION_IMAGE_CAPTURE:
   1991                     source = ForegroundSource.ACTION_IMAGE_CAPTURE;
   1992                     break;
   1993                 case MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA:
   1994                     // was UNKNOWN_SOURCE in Fishlake.
   1995                     source = ForegroundSource.ACTION_STILL_IMAGE_CAMERA;
   1996                     break;
   1997                 case MediaStore.INTENT_ACTION_VIDEO_CAMERA:
   1998                     // was UNKNOWN_SOURCE in Fishlake.
   1999                     source = ForegroundSource.ACTION_VIDEO_CAMERA;
   2000                     break;
   2001                 case MediaStore.ACTION_VIDEO_CAPTURE:
   2002                     source = ForegroundSource.ACTION_VIDEO_CAPTURE;
   2003                     break;
   2004                 case MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE:
   2005                     // was ACTION_IMAGE_CAPTURE_SECURE in Fishlake.
   2006                     source = ForegroundSource.ACTION_STILL_IMAGE_CAMERA_SECURE;
   2007                     break;
   2008                 case MediaStore.ACTION_IMAGE_CAPTURE_SECURE:
   2009                     source = ForegroundSource.ACTION_IMAGE_CAPTURE_SECURE;
   2010                     break;
   2011                 case Intent.ACTION_MAIN:
   2012                     source = ForegroundSource.ACTION_MAIN;
   2013                     break;
   2014                 default:
   2015                     source = ForegroundSource.UNKNOWN_SOURCE;
   2016                     break;
   2017             }
   2018         }
   2019         UsageStatistics.instance().foregrounded(source, currentUserInterfaceMode(),
   2020                 isKeyguardSecure(), isKeyguardLocked(),
   2021                 mStartupOnCreate, mExecutionStartNanoTime);
   2022 
   2023         mGalleryIntent = IntentHelper.getGalleryIntent(mAppContext);
   2024         if (ApiHelper.isLOrHigher()) {
   2025             // hide the up affordance for L devices, it's not very Materially
   2026             mActionBar.setDisplayShowHomeEnabled(false);
   2027         }
   2028 
   2029         mOrientationManager.resume();
   2030 
   2031         mCurrentModule.hardResetSettings(mSettingsManager);
   2032 
   2033         profile.mark();
   2034         mCurrentModule.resume();
   2035         UsageStatistics.instance().changeScreen(currentUserInterfaceMode(),
   2036                 NavigationChange.InteractionCause.BUTTON);
   2037         setSwipingEnabled(true);
   2038         profile.mark("mCurrentModule.resume");
   2039 
   2040         if (!mResetToPreviewOnResume) {
   2041             FilmstripItem item = mDataAdapter.getItemAt(
   2042                   mFilmstripController.getCurrentAdapterIndex());
   2043             if (item != null) {
   2044                 mDataAdapter.refresh(item.getData().getUri());
   2045             }
   2046         }
   2047 
   2048         // The share button might be disabled to avoid double tapping.
   2049         mCameraAppUI.getFilmstripBottomControls().setShareEnabled(true);
   2050         // Default is showing the preview, unless disabled by explicitly
   2051         // starting an activity we want to return from to the filmstrip rather
   2052         // than the preview.
   2053         mResetToPreviewOnResume = true;
   2054 
   2055         if (mLocalVideosObserver.isMediaDataChangedDuringPause()
   2056                 || mLocalImagesObserver.isMediaDataChangedDuringPause()) {
   2057             if (!mSecureCamera) {
   2058                 // If it's secure camera, requestLoad() should not be called
   2059                 // as it will load all the data.
   2060                 if (!mFilmstripVisible) {
   2061                     mDataAdapter.requestLoad(new Callback<Void>() {
   2062                         @Override
   2063                         public void onCallback(Void result) {
   2064                             fillTemporarySessions();
   2065                         }
   2066                     });
   2067                 } else {
   2068                     mDataAdapter.requestLoadNewPhotos();
   2069                 }
   2070             }
   2071         }
   2072         mLocalImagesObserver.setActivityPaused(false);
   2073         mLocalVideosObserver.setActivityPaused(false);
   2074         if (!mSecureCamera) {
   2075             mLocalImagesObserver.setForegroundChangeListener(
   2076                     new FilmstripContentObserver.ChangeListener() {
   2077                 @Override
   2078                 public void onChange() {
   2079                     mDataAdapter.requestLoadNewPhotos();
   2080                 }
   2081             });
   2082         }
   2083 
   2084         keepScreenOnForAWhile();
   2085 
   2086         // Lights-out mode at all times.
   2087         final View rootView = findViewById(R.id.activity_root_view);
   2088         mLightsOutRunnable.run();
   2089         getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(
   2090               new OnSystemUiVisibilityChangeListener() {
   2091                   @Override
   2092                   public void onSystemUiVisibilityChange(int visibility) {
   2093                       mMainHandler.removeCallbacks(mLightsOutRunnable);
   2094                       mMainHandler.postDelayed(mLightsOutRunnable, LIGHTS_OUT_DELAY_MS);
   2095                   }
   2096               });
   2097 
   2098         profile.mark();
   2099         mPanoramaViewHelper.onResume();
   2100         profile.mark("mPanoramaViewHelper.onResume()");
   2101 
   2102         ReleaseHelper.showReleaseInfoDialogOnStart(this, mSettingsManager);
   2103         // Enable location recording if the setting is on.
   2104         final boolean locationRecordingEnabled =
   2105                 mSettingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL, Keys.KEY_RECORD_LOCATION);
   2106         mLocationManager.recordLocation(locationRecordingEnabled);
   2107 
   2108         final int previewVisibility = getPreviewVisibility();
   2109         updatePreviewRendering(previewVisibility);
   2110 
   2111         mMotionManager.start();
   2112         profile.stop();
   2113     }
   2114 
   2115     private void fillTemporarySessions() {
   2116         if (mSecureCamera) {
   2117             return;
   2118         }
   2119         // There might be sessions still in flight (processed by our service).
   2120         // Make sure they're added to the filmstrip.
   2121         getServices().getCaptureSessionManager().fillTemporarySession(mSessionListener);
   2122     }
   2123 
   2124     @Override
   2125     public void onStartTasks() {
   2126         mIsActivityRunning = true;
   2127         mPanoramaViewHelper.onStart();
   2128 
   2129         /*
   2130          * If we're starting after launching a different Activity (lockscreen),
   2131          * we need to use the last mode used in the other Activity, and
   2132          * not the old one from this Activity.
   2133          *
   2134          * This needs to happen before CameraAppUI.resume() in order to set the
   2135          * mode cover icon to the actual last mode used.
   2136          *
   2137          * Right now we exclude capture intents from this logic.
   2138          */
   2139         int modeIndex = getModeIndex();
   2140         if (!isCaptureIntent() && mCurrentModeIndex != modeIndex) {
   2141             onModeSelected(modeIndex);
   2142         }
   2143 
   2144         if (mResetToPreviewOnResume) {
   2145             mCameraAppUI.resume();
   2146             mResetToPreviewOnResume = false;
   2147         }
   2148     }
   2149 
   2150     @Override
   2151     protected void onStopTasks() {
   2152         mIsActivityRunning = false;
   2153         mPanoramaViewHelper.onStop();
   2154 
   2155         mLocationManager.disconnect();
   2156     }
   2157 
   2158     @Override
   2159     public void onDestroyTasks() {
   2160         if (mSecureCamera) {
   2161             unregisterReceiver(mShutdownReceiver);
   2162         }
   2163 
   2164         // Ensure anything that checks for "isPaused" returns true.
   2165         mPaused = true;
   2166 
   2167         mSettingsManager.removeAllListeners();
   2168         if (mCameraController != null) {
   2169             mCameraController.removeCallbackReceiver();
   2170             mCameraController.setCameraExceptionHandler(null);
   2171         }
   2172         if (mLocalImagesObserver != null) {
   2173             getContentResolver().unregisterContentObserver(mLocalImagesObserver);
   2174         }
   2175         if (mLocalVideosObserver != null) {
   2176             getContentResolver().unregisterContentObserver(mLocalVideosObserver);
   2177         }
   2178         getServices().getCaptureSessionManager().removeSessionListener(mSessionListener);
   2179         if (mCameraAppUI != null) {
   2180             mCameraAppUI.onDestroy();
   2181         }
   2182         if (mModeListView != null) {
   2183             mModeListView.setVisibilityChangedListener(null);
   2184         }
   2185         mCameraController = null;
   2186         mSettingsManager = null;
   2187         mOrientationManager = null;
   2188         mButtonManager = null;
   2189         if (mSoundPlayer != null) {
   2190           mSoundPlayer.release();
   2191         }
   2192         if (mFirstRunDialog != null) {
   2193             mFirstRunDialog.dismiss();
   2194         }
   2195         CameraAgentFactory.recycle(CameraAgentFactory.CameraApi.API_1);
   2196         CameraAgentFactory.recycle(CameraAgentFactory.CameraApi.AUTO);
   2197     }
   2198 
   2199     @Override
   2200     public void onConfigurationChanged(Configuration config) {
   2201         super.onConfigurationChanged(config);
   2202         Log.v(TAG, "onConfigurationChanged");
   2203         if (config.orientation == Configuration.ORIENTATION_UNDEFINED) {
   2204             return;
   2205         }
   2206 
   2207         if (mLastLayoutOrientation != config.orientation) {
   2208             mLastLayoutOrientation = config.orientation;
   2209             mCurrentModule.onLayoutOrientationChanged(
   2210                     mLastLayoutOrientation == Configuration.ORIENTATION_LANDSCAPE);
   2211         }
   2212     }
   2213 
   2214     @Override
   2215     public boolean onKeyDown(int keyCode, KeyEvent event) {
   2216         if (!mFilmstripVisible) {
   2217             if (mCurrentModule.onKeyDown(keyCode, event)) {
   2218                 return true;
   2219             }
   2220             // Prevent software keyboard or voice search from showing up.
   2221             if (keyCode == KeyEvent.KEYCODE_SEARCH
   2222                     || keyCode == KeyEvent.KEYCODE_MENU) {
   2223                 if (event.isLongPress()) {
   2224                     return true;
   2225                 }
   2226             }
   2227         }
   2228 
   2229         return super.onKeyDown(keyCode, event);
   2230     }
   2231 
   2232     @Override
   2233     public boolean onKeyUp(int keyCode, KeyEvent event) {
   2234         if (!mFilmstripVisible) {
   2235             // If a module is in the middle of capture, it should
   2236             // consume the key event.
   2237             if (mCurrentModule.onKeyUp(keyCode, event)) {
   2238                 return true;
   2239             } else if (keyCode == KeyEvent.KEYCODE_MENU
   2240                     || keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {
   2241                 // Let the mode list view consume the event.
   2242                 mCameraAppUI.openModeList();
   2243                 return true;
   2244             } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
   2245                 mCameraAppUI.showFilmstrip();
   2246                 return true;
   2247             }
   2248         } else {
   2249             if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
   2250                 mFilmstripController.goToNextItem();
   2251                 return true;
   2252             } else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {
   2253                 boolean wentToPrevious = mFilmstripController.goToPreviousItem();
   2254                 if (!wentToPrevious) {
   2255                   // at beginning of filmstrip, hide and go back to preview
   2256                   mCameraAppUI.hideFilmstrip();
   2257                 }
   2258                 return true;
   2259             }
   2260         }
   2261         return super.onKeyUp(keyCode, event);
   2262     }
   2263 
   2264     @Override
   2265     public void onBackPressed() {
   2266         if (!mCameraAppUI.onBackPressed()) {
   2267             if (!mCurrentModule.onBackPressed()) {
   2268                 super.onBackPressed();
   2269             }
   2270         }
   2271     }
   2272 
   2273     @Override
   2274     public boolean isAutoRotateScreen() {
   2275         // TODO: Move to OrientationManager.
   2276         return mAutoRotateScreen;
   2277     }
   2278 
   2279     @Override
   2280     public boolean onCreateOptionsMenu(Menu menu) {
   2281         MenuInflater inflater = getMenuInflater();
   2282         inflater.inflate(R.menu.filmstrip_menu, menu);
   2283         mActionBarMenu = menu;
   2284 
   2285         // add a button for launching the gallery
   2286         if (mGalleryIntent != null) {
   2287             CharSequence appName =  IntentHelper.getGalleryAppName(mAppContext, mGalleryIntent);
   2288             if (appName != null) {
   2289                 MenuItem menuItem = menu.add(appName);
   2290                 menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
   2291                 menuItem.setIntent(mGalleryIntent);
   2292 
   2293                 Drawable galleryLogo = IntentHelper.getGalleryIcon(mAppContext, mGalleryIntent);
   2294                 if (galleryLogo != null) {
   2295                     menuItem.setIcon(galleryLogo);
   2296                 }
   2297             }
   2298         }
   2299 
   2300         return super.onCreateOptionsMenu(menu);
   2301     }
   2302 
   2303     @Override
   2304     public boolean onPrepareOptionsMenu(Menu menu) {
   2305         if (isSecureCamera() && !ApiHelper.isLOrHigher()) {
   2306             // Compatibility pre-L: launching new activities right above
   2307             // lockscreen does not reliably work, only show help if not secure
   2308             menu.removeItem(R.id.action_help_and_feedback);
   2309         }
   2310 
   2311         return super.onPrepareOptionsMenu(menu);
   2312     }
   2313 
   2314     protected long getStorageSpaceBytes() {
   2315         synchronized (mStorageSpaceLock) {
   2316             return mStorageSpaceBytes;
   2317         }
   2318     }
   2319 
   2320     protected interface OnStorageUpdateDoneListener {
   2321         public void onStorageUpdateDone(long bytes);
   2322     }
   2323 
   2324     protected void updateStorageSpaceAndHint(final OnStorageUpdateDoneListener callback) {
   2325         /*
   2326          * We execute disk operations on a background thread in order to
   2327          * free up the UI thread.  Synchronizing on the lock below ensures
   2328          * that when getStorageSpaceBytes is called, the main thread waits
   2329          * until this method has completed.
   2330          *
   2331          * However, .execute() does not ensure this execution block will be
   2332          * run right away (.execute() schedules this AsyncTask for sometime
   2333          * in the future. executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
   2334          * tries to execute the task in parellel with other AsyncTasks, but
   2335          * there's still no guarantee).
   2336          * e.g. don't call this then immediately call getStorageSpaceBytes().
   2337          * Instead, pass in an OnStorageUpdateDoneListener.
   2338          */
   2339         (new AsyncTask<Void, Void, Long>() {
   2340             @Override
   2341             protected Long doInBackground(Void ... arg) {
   2342                 synchronized (mStorageSpaceLock) {
   2343                     mStorageSpaceBytes = Storage.getAvailableSpace();
   2344                     return mStorageSpaceBytes;
   2345                 }
   2346             }
   2347 
   2348             @Override
   2349             protected void onPostExecute(Long bytes) {
   2350                 updateStorageHint(bytes);
   2351                 // This callback returns after I/O to check disk, so we could be
   2352                 // pausing and shutting down. If so, don't bother invoking.
   2353                 if (callback != null && !mPaused) {
   2354                     callback.onStorageUpdateDone(bytes);
   2355                 } else {
   2356                     Log.v(TAG, "ignoring storage callback after activity pause");
   2357                 }
   2358             }
   2359         }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
   2360     }
   2361 
   2362     protected void updateStorageHint(long storageSpace) {
   2363         if (!mIsActivityRunning) {
   2364             return;
   2365         }
   2366 
   2367         String message = null;
   2368         if (storageSpace == Storage.UNAVAILABLE) {
   2369             message = getString(R.string.no_storage);
   2370         } else if (storageSpace == Storage.PREPARING) {
   2371             message = getString(R.string.preparing_sd);
   2372         } else if (storageSpace == Storage.UNKNOWN_SIZE) {
   2373             message = getString(R.string.access_sd_fail);
   2374         } else if (storageSpace <= Storage.LOW_STORAGE_THRESHOLD_BYTES) {
   2375             message = getString(R.string.spaceIsLow_content);
   2376         }
   2377 
   2378         if (message != null) {
   2379             Log.w(TAG, "Storage warning: " + message);
   2380             if (mStorageHint == null) {
   2381                 mStorageHint = OnScreenHint.makeText(CameraActivity.this, message);
   2382             } else {
   2383                 mStorageHint.setText(message);
   2384             }
   2385             mStorageHint.show();
   2386             UsageStatistics.instance().storageWarning(storageSpace);
   2387 
   2388             // Disable all user interactions,
   2389             mCameraAppUI.setDisableAllUserInteractions(true);
   2390         } else if (mStorageHint != null) {
   2391             mStorageHint.cancel();
   2392             mStorageHint = null;
   2393 
   2394             // Re-enable all user interactions.
   2395             mCameraAppUI.setDisableAllUserInteractions(false);
   2396         }
   2397     }
   2398 
   2399     protected void setResultEx(int resultCode) {
   2400         mResultCodeForTesting = resultCode;
   2401         setResult(resultCode);
   2402     }
   2403 
   2404     protected void setResultEx(int resultCode, Intent data) {
   2405         mResultCodeForTesting = resultCode;
   2406         mResultDataForTesting = data;
   2407         setResult(resultCode, data);
   2408     }
   2409 
   2410     public int getResultCode() {
   2411         return mResultCodeForTesting;
   2412     }
   2413 
   2414     public Intent getResultData() {
   2415         return mResultDataForTesting;
   2416     }
   2417 
   2418     public boolean isSecureCamera() {
   2419         return mSecureCamera;
   2420     }
   2421 
   2422     @Override
   2423     public boolean isPaused() {
   2424         return mPaused;
   2425     }
   2426 
   2427     @Override
   2428     public int getPreferredChildModeIndex(int modeIndex) {
   2429         if (modeIndex == getResources().getInteger(R.integer.camera_mode_photo)) {
   2430             boolean hdrPlusOn = Keys.isHdrPlusOn(mSettingsManager);
   2431             if (hdrPlusOn && GcamHelper.hasGcamAsSeparateModule(mFeatureConfig)) {
   2432                 modeIndex = getResources().getInteger(R.integer.camera_mode_gcam);
   2433             }
   2434         }
   2435         return modeIndex;
   2436     }
   2437 
   2438     @Override
   2439     public void onModeSelected(int modeIndex) {
   2440         if (mCurrentModeIndex == modeIndex) {
   2441             return;
   2442         }
   2443 
   2444         CameraPerformanceTracker.onEvent(CameraPerformanceTracker.MODE_SWITCH_START);
   2445         // Record last used camera mode for quick switching
   2446         if (modeIndex == getResources().getInteger(R.integer.camera_mode_photo)
   2447                 || modeIndex == getResources().getInteger(R.integer.camera_mode_gcam)) {
   2448             mSettingsManager.set(SettingsManager.SCOPE_GLOBAL,
   2449                                  Keys.KEY_CAMERA_MODULE_LAST_USED,
   2450                                  modeIndex);
   2451         }
   2452 
   2453         closeModule(mCurrentModule);
   2454 
   2455         // Select the correct module index from the mode switcher index.
   2456         modeIndex = getPreferredChildModeIndex(modeIndex);
   2457         setModuleFromModeIndex(modeIndex);
   2458 
   2459         mCameraAppUI.resetBottomControls(mCurrentModule, modeIndex);
   2460         mCameraAppUI.addShutterListener(mCurrentModule);
   2461         openModule(mCurrentModule);
   2462         // Store the module index so we can use it the next time the Camera
   2463         // starts up.
   2464         mSettingsManager.set(SettingsManager.SCOPE_GLOBAL,
   2465                              Keys.KEY_STARTUP_MODULE_INDEX, modeIndex);
   2466     }
   2467 
   2468     /**
   2469      * Shows the settings dialog.
   2470      */
   2471     @Override
   2472     public void onSettingsSelected() {
   2473         UsageStatistics.instance().controlUsed(
   2474                 eventprotos.ControlEvent.ControlType.OVERALL_SETTINGS);
   2475         Intent intent = new Intent(this, CameraSettingsActivity.class);
   2476         startActivity(intent);
   2477     }
   2478 
   2479     @Override
   2480     public void freezeScreenUntilPreviewReady() {
   2481         mCameraAppUI.freezeScreenUntilPreviewReady();
   2482     }
   2483 
   2484     @Override
   2485     public int getModuleId(int modeIndex) {
   2486         ModuleManagerImpl.ModuleAgent agent = mModuleManager.getModuleAgent(modeIndex);
   2487         if (agent == null) {
   2488             return -1;
   2489         }
   2490         return agent.getModuleId();
   2491     }
   2492 
   2493     /**
   2494      * Sets the mCurrentModuleIndex, creates a new module instance for the given
   2495      * index an sets it as mCurrentModule.
   2496      */
   2497     private void setModuleFromModeIndex(int modeIndex) {
   2498         ModuleManagerImpl.ModuleAgent agent = mModuleManager.getModuleAgent(modeIndex);
   2499         if (agent == null) {
   2500             return;
   2501         }
   2502         if (!agent.requestAppForCamera()) {
   2503             mCameraController.closeCamera(true);
   2504         }
   2505         mCurrentModeIndex = agent.getModuleId();
   2506         mCurrentModule = (CameraModule) agent.createModule(this, getIntent());
   2507     }
   2508 
   2509     @Override
   2510     public SettingsManager getSettingsManager() {
   2511         return mSettingsManager;
   2512     }
   2513 
   2514     @Override
   2515     public ResolutionSetting getResolutionSetting() {
   2516         return mResolutionSetting;
   2517     }
   2518 
   2519     @Override
   2520     public CameraServices getServices() {
   2521         return CameraServicesImpl.instance();
   2522     }
   2523 
   2524     @Override
   2525     public FatalErrorHandler getFatalErrorHandler() {
   2526         return mFatalErrorHandler;
   2527     }
   2528 
   2529     public List<String> getSupportedModeNames() {
   2530         List<Integer> indices = mModuleManager.getSupportedModeIndexList();
   2531         List<String> supported = new ArrayList<String>();
   2532 
   2533         for (Integer modeIndex : indices) {
   2534             String name = CameraUtil.getCameraModeText(modeIndex, mAppContext);
   2535             if (name != null && !name.equals("")) {
   2536                 supported.add(name);
   2537             }
   2538         }
   2539         return supported;
   2540     }
   2541 
   2542     @Override
   2543     public ButtonManager getButtonManager() {
   2544         if (mButtonManager == null) {
   2545             mButtonManager = new ButtonManager(this);
   2546         }
   2547         return mButtonManager;
   2548     }
   2549 
   2550     @Override
   2551     public SoundPlayer getSoundPlayer() {
   2552         return mSoundPlayer;
   2553     }
   2554 
   2555     /**
   2556      * Launches an ACTION_EDIT intent for the given local data item. If
   2557      * 'withTinyPlanet' is set, this will show a disambig dialog first to let
   2558      * the user start either the tiny planet editor or another photo editor.
   2559      *
   2560      * @param data The data item to edit.
   2561      */
   2562     public void launchEditor(FilmstripItem data) {
   2563         Intent intent = new Intent(Intent.ACTION_EDIT)
   2564                 .setDataAndType(data.getData().getUri(), data.getData().getMimeType())
   2565                 .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
   2566         try {
   2567             launchActivityByIntent(intent);
   2568         } catch (ActivityNotFoundException e) {
   2569             final String msgEditWith = getResources().getString(R.string.edit_with);
   2570             launchActivityByIntent(Intent.createChooser(intent, msgEditWith));
   2571         }
   2572     }
   2573 
   2574     @Override
   2575     public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
   2576         super.onCreateContextMenu(menu, v, menuInfo);
   2577 
   2578         MenuInflater inflater = getMenuInflater();
   2579         inflater.inflate(R.menu.filmstrip_context_menu, menu);
   2580     }
   2581 
   2582     @Override
   2583     public boolean onContextItemSelected(MenuItem item) {
   2584         switch (item.getItemId()) {
   2585             case R.id.tiny_planet_editor:
   2586                 mMyFilmstripBottomControlListener.onTinyPlanet();
   2587                 return true;
   2588             case R.id.photo_editor:
   2589                 mMyFilmstripBottomControlListener.onEdit();
   2590                 return true;
   2591         }
   2592         return false;
   2593     }
   2594 
   2595     /**
   2596      * Launch the tiny planet editor.
   2597      *
   2598      * @param data The data must be a 360 degree stereographically mapped
   2599      *            panoramic image. It will not be modified, instead a new item
   2600      *            with the result will be added to the filmstrip.
   2601      */
   2602     public void launchTinyPlanetEditor(FilmstripItem data) {
   2603         TinyPlanetFragment fragment = new TinyPlanetFragment();
   2604         Bundle bundle = new Bundle();
   2605         bundle.putString(TinyPlanetFragment.ARGUMENT_URI, data.getData().getUri().toString());
   2606         bundle.putString(TinyPlanetFragment.ARGUMENT_TITLE, data.getData().getTitle());
   2607         fragment.setArguments(bundle);
   2608         fragment.show(getFragmentManager(), "tiny_planet");
   2609     }
   2610 
   2611     /**
   2612      * Returns what UI mode (capture mode or filmstrip) we are in.
   2613      * Returned number one of {@link com.google.common.logging.eventprotos.NavigationChange.Mode}
   2614      */
   2615     private int currentUserInterfaceMode() {
   2616         int mode = NavigationChange.Mode.UNKNOWN_MODE;
   2617         if (mCurrentModeIndex == getResources().getInteger(R.integer.camera_mode_photo)) {
   2618             mode = NavigationChange.Mode.PHOTO_CAPTURE;
   2619         }
   2620         if (mCurrentModeIndex == getResources().getInteger(R.integer.camera_mode_video)) {
   2621             mode = NavigationChange.Mode.VIDEO_CAPTURE;
   2622         }
   2623         if (mCurrentModeIndex == getResources().getInteger(R.integer.camera_mode_refocus)) {
   2624             mode = NavigationChange.Mode.LENS_BLUR;
   2625         }
   2626         if (mCurrentModeIndex == getResources().getInteger(R.integer.camera_mode_gcam)) {
   2627             mode = NavigationChange.Mode.HDR_PLUS;
   2628         }
   2629         if (mCurrentModeIndex == getResources().getInteger(R.integer.camera_mode_photosphere)) {
   2630             mode = NavigationChange.Mode.PHOTO_SPHERE;
   2631         }
   2632         if (mCurrentModeIndex == getResources().getInteger(R.integer.camera_mode_panorama)) {
   2633             mode = NavigationChange.Mode.PANORAMA;
   2634         }
   2635         if (mFilmstripVisible) {
   2636             mode = NavigationChange.Mode.FILMSTRIP;
   2637         }
   2638         return mode;
   2639     }
   2640 
   2641     private void openModule(CameraModule module) {
   2642         module.init(this, isSecureCamera(), isCaptureIntent());
   2643         module.hardResetSettings(mSettingsManager);
   2644         // Hide accessibility zoom UI by default. Modules will enable it themselves if required.
   2645         getCameraAppUI().hideAccessibilityZoomUI();
   2646         if (!mPaused) {
   2647             module.resume();
   2648             UsageStatistics.instance().changeScreen(currentUserInterfaceMode(),
   2649                     NavigationChange.InteractionCause.BUTTON);
   2650             updatePreviewVisibility();
   2651         }
   2652     }
   2653 
   2654     private void closeModule(CameraModule module) {
   2655         module.pause();
   2656         mCameraAppUI.clearModuleUI();
   2657     }
   2658 
   2659     private void performDeletion() {
   2660         if (!mPendingDeletion) {
   2661             return;
   2662         }
   2663         hideUndoDeletionBar(false);
   2664         mDataAdapter.executeDeletion();
   2665     }
   2666 
   2667     public void showUndoDeletionBar() {
   2668         if (mPendingDeletion) {
   2669             performDeletion();
   2670         }
   2671         Log.v(TAG, "showing undo bar");
   2672         mPendingDeletion = true;
   2673         if (mUndoDeletionBar == null) {
   2674             ViewGroup v = (ViewGroup) getLayoutInflater().inflate(R.layout.undo_bar,
   2675                     mAboveFilmstripControlLayout, true);
   2676             mUndoDeletionBar = (ViewGroup) v.findViewById(R.id.camera_undo_deletion_bar);
   2677             View button = mUndoDeletionBar.findViewById(R.id.camera_undo_deletion_button);
   2678             button.setOnClickListener(new View.OnClickListener() {
   2679                 @Override
   2680                 public void onClick(View view) {
   2681                     mDataAdapter.undoDeletion();
   2682                     // Fix for b/21666018: When undoing a delete in Fullscreen
   2683                     // mode, just flip
   2684                     // back to the filmstrip to force a refresh.
   2685                     if (mFilmstripController.inFullScreen()) {
   2686                         mFilmstripController.goToFilmstrip();
   2687                     }
   2688                     hideUndoDeletionBar(true);
   2689                 }
   2690             });
   2691             // Setting undo bar clickable to avoid touch events going through
   2692             // the bar to the buttons (eg. edit button, etc) underneath the bar.
   2693             mUndoDeletionBar.setClickable(true);
   2694             // When there is user interaction going on with the undo button, we
   2695             // do not want to hide the undo bar.
   2696             button.setOnTouchListener(new View.OnTouchListener() {
   2697                 @Override
   2698                 public boolean onTouch(View v, MotionEvent event) {
   2699                     if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
   2700                         mIsUndoingDeletion = true;
   2701                     } else if (event.getActionMasked() == MotionEvent.ACTION_UP) {
   2702                         mIsUndoingDeletion = false;
   2703                     }
   2704                     return false;
   2705                 }
   2706             });
   2707         }
   2708         mUndoDeletionBar.setAlpha(0f);
   2709         mUndoDeletionBar.setVisibility(View.VISIBLE);
   2710         mUndoDeletionBar.animate().setDuration(200).alpha(1f).setListener(null).start();
   2711     }
   2712 
   2713     private void hideUndoDeletionBar(boolean withAnimation) {
   2714         Log.v(TAG, "Hiding undo deletion bar");
   2715         mPendingDeletion = false;
   2716         if (mUndoDeletionBar != null) {
   2717             if (withAnimation) {
   2718                 mUndoDeletionBar.animate().setDuration(200).alpha(0f)
   2719                         .setListener(new Animator.AnimatorListener() {
   2720                             @Override
   2721                             public void onAnimationStart(Animator animation) {
   2722                                 // Do nothing.
   2723                             }
   2724 
   2725                             @Override
   2726                             public void onAnimationEnd(Animator animation) {
   2727                                 mUndoDeletionBar.setVisibility(View.GONE);
   2728                             }
   2729 
   2730                             @Override
   2731                             public void onAnimationCancel(Animator animation) {
   2732                                 // Do nothing.
   2733                             }
   2734 
   2735                             @Override
   2736                             public void onAnimationRepeat(Animator animation) {
   2737                                 // Do nothing.
   2738                             }
   2739                         }).start();
   2740             } else {
   2741                 mUndoDeletionBar.setVisibility(View.GONE);
   2742             }
   2743         }
   2744     }
   2745 
   2746     /**
   2747      * Enable/disable swipe-to-filmstrip. Will always disable swipe if in
   2748      * capture intent.
   2749      *
   2750      * @param enable {@code true} to enable swipe.
   2751      */
   2752     public void setSwipingEnabled(boolean enable) {
   2753         // TODO: Bring back the functionality.
   2754         if (isCaptureIntent()) {
   2755             // lockPreview(true);
   2756         } else {
   2757             // lockPreview(!enable);
   2758         }
   2759     }
   2760 
   2761     // Accessor methods for getting latency times used in performance testing
   2762     public long getFirstPreviewTime() {
   2763         if (mCurrentModule instanceof PhotoModule) {
   2764             long coverHiddenTime = getCameraAppUI().getCoverHiddenTime();
   2765             if (coverHiddenTime != -1) {
   2766                 return coverHiddenTime - mOnCreateTime;
   2767             }
   2768         }
   2769         return -1;
   2770     }
   2771 
   2772     public long getAutoFocusTime() {
   2773         return (mCurrentModule instanceof PhotoModule) ?
   2774                 ((PhotoModule) mCurrentModule).mAutoFocusTime : -1;
   2775     }
   2776 
   2777     public long getShutterLag() {
   2778         return (mCurrentModule instanceof PhotoModule) ?
   2779                 ((PhotoModule) mCurrentModule).mShutterLag : -1;
   2780     }
   2781 
   2782     public long getShutterToPictureDisplayedTime() {
   2783         return (mCurrentModule instanceof PhotoModule) ?
   2784                 ((PhotoModule) mCurrentModule).mShutterToPictureDisplayedTime : -1;
   2785     }
   2786 
   2787     public long getPictureDisplayedToJpegCallbackTime() {
   2788         return (mCurrentModule instanceof PhotoModule) ?
   2789                 ((PhotoModule) mCurrentModule).mPictureDisplayedToJpegCallbackTime : -1;
   2790     }
   2791 
   2792     public long getJpegCallbackFinishTime() {
   2793         return (mCurrentModule instanceof PhotoModule) ?
   2794                 ((PhotoModule) mCurrentModule).mJpegCallbackFinishTime : -1;
   2795     }
   2796 
   2797     public long getCaptureStartTime() {
   2798         return (mCurrentModule instanceof PhotoModule) ?
   2799                 ((PhotoModule) mCurrentModule).mCaptureStartTime : -1;
   2800     }
   2801 
   2802     public boolean isRecording() {
   2803         return (mCurrentModule instanceof VideoModule) ?
   2804                 ((VideoModule) mCurrentModule).isRecording() : false;
   2805     }
   2806 
   2807     public CameraAgent.CameraOpenCallback getCameraOpenErrorCallback() {
   2808         return mCameraController;
   2809     }
   2810 
   2811     // For debugging purposes only.
   2812     public CameraModule getCurrentModule() {
   2813         return mCurrentModule;
   2814     }
   2815 
   2816     @Override
   2817     public void showTutorial(AbstractTutorialOverlay tutorial) {
   2818         mCameraAppUI.showTutorial(tutorial, getLayoutInflater());
   2819     }
   2820 
   2821     @Override
   2822     public void finishActivityWithIntentCompleted(Intent resultIntent) {
   2823         finishActivityWithIntentResult(Activity.RESULT_OK, resultIntent);
   2824     }
   2825 
   2826     @Override
   2827     public void finishActivityWithIntentCanceled() {
   2828         finishActivityWithIntentResult(Activity.RESULT_CANCELED, new Intent());
   2829     }
   2830 
   2831     private void finishActivityWithIntentResult(int resultCode, Intent resultIntent) {
   2832         mResultCodeForTesting = resultCode;
   2833         mResultDataForTesting = resultIntent;
   2834         setResult(resultCode, resultIntent);
   2835         finish();
   2836     }
   2837 
   2838     private void keepScreenOnForAWhile() {
   2839         if (mKeepScreenOn) {
   2840             return;
   2841         }
   2842         mMainHandler.removeMessages(MSG_CLEAR_SCREEN_ON_FLAG);
   2843         getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
   2844         mMainHandler.sendEmptyMessageDelayed(MSG_CLEAR_SCREEN_ON_FLAG, SCREEN_DELAY_MS);
   2845     }
   2846 
   2847     private void resetScreenOn() {
   2848         mKeepScreenOn = false;
   2849         mMainHandler.removeMessages(MSG_CLEAR_SCREEN_ON_FLAG);
   2850         getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
   2851     }
   2852 
   2853     /**
   2854      * @return {@code true} if the Gallery is launched successfully.
   2855      */
   2856     private boolean startGallery() {
   2857         if (mGalleryIntent == null) {
   2858             return false;
   2859         }
   2860         try {
   2861             UsageStatistics.instance().changeScreen(NavigationChange.Mode.GALLERY,
   2862                     NavigationChange.InteractionCause.BUTTON);
   2863             Intent startGalleryIntent = new Intent(mGalleryIntent);
   2864             int currentIndex = mFilmstripController.getCurrentAdapterIndex();
   2865             FilmstripItem currentFilmstripItem = mDataAdapter.getItemAt(currentIndex);
   2866             if (currentFilmstripItem != null) {
   2867                 GalleryHelper.setContentUri(startGalleryIntent,
   2868                       currentFilmstripItem.getData().getUri());
   2869             }
   2870             launchActivityByIntent(startGalleryIntent);
   2871         } catch (ActivityNotFoundException e) {
   2872             Log.w(TAG, "Failed to launch gallery activity, closing");
   2873         }
   2874         return false;
   2875     }
   2876 
   2877     private void setNfcBeamPushUriFromData(FilmstripItem data) {
   2878         final Uri uri = data.getData().getUri();
   2879         if (uri != Uri.EMPTY) {
   2880             mNfcPushUris[0] = uri;
   2881         } else {
   2882             mNfcPushUris[0] = null;
   2883         }
   2884     }
   2885 
   2886     /**
   2887      * Updates the visibility of the filmstrip bottom controls and action bar.
   2888      */
   2889     private void updateUiByData(final int index) {
   2890         final FilmstripItem currentData = mDataAdapter.getItemAt(index);
   2891         if (currentData == null) {
   2892             Log.w(TAG, "Current data ID not found.");
   2893             hideSessionProgress();
   2894             return;
   2895         }
   2896         updateActionBarMenu(currentData);
   2897 
   2898         /* Bottom controls. */
   2899         updateBottomControlsByData(currentData);
   2900 
   2901         if (isSecureCamera()) {
   2902             // We cannot show buttons in secure camera since go to other
   2903             // activities might create a security hole.
   2904             mCameraAppUI.getFilmstripBottomControls().hideControls();
   2905             return;
   2906         }
   2907 
   2908         setNfcBeamPushUriFromData(currentData);
   2909 
   2910         if (!mDataAdapter.isMetadataUpdatedAt(index)) {
   2911             mDataAdapter.updateMetadataAt(index);
   2912         }
   2913     }
   2914 
   2915     /**
   2916      * Updates the bottom controls based on the data.
   2917      */
   2918     private void updateBottomControlsByData(final FilmstripItem currentData) {
   2919 
   2920         final CameraAppUI.BottomPanel filmstripBottomPanel =
   2921                 mCameraAppUI.getFilmstripBottomControls();
   2922         filmstripBottomPanel.showControls();
   2923         filmstripBottomPanel.setEditButtonVisibility(
   2924                 currentData.getAttributes().canEdit());
   2925         filmstripBottomPanel.setShareButtonVisibility(
   2926               currentData.getAttributes().canShare());
   2927         filmstripBottomPanel.setDeleteButtonVisibility(
   2928                 currentData.getAttributes().canDelete());
   2929 
   2930         /* Progress bar */
   2931 
   2932         Uri contentUri = currentData.getData().getUri();
   2933         CaptureSessionManager sessionManager = getServices()
   2934                 .getCaptureSessionManager();
   2935 
   2936         if (sessionManager.hasErrorMessage(contentUri)) {
   2937             showProcessError(sessionManager.getErrorMessageId(contentUri));
   2938         } else {
   2939             filmstripBottomPanel.hideProgressError();
   2940             CaptureSession session = sessionManager.getSession(contentUri);
   2941 
   2942             if (session != null) {
   2943                 int sessionProgress = session.getProgress();
   2944 
   2945                 if (sessionProgress < 0) {
   2946                     hideSessionProgress();
   2947                 } else {
   2948                     int progressMessageId = session.getProgressMessageId();
   2949                     showSessionProgress(progressMessageId);
   2950                     updateSessionProgress(sessionProgress);
   2951                 }
   2952             } else {
   2953                 hideSessionProgress();
   2954             }
   2955         }
   2956 
   2957         /* View button */
   2958 
   2959         // We need to add this to a separate DB.
   2960         final int viewButtonVisibility;
   2961         if (currentData.getMetadata().isUsePanoramaViewer()) {
   2962             viewButtonVisibility = CameraAppUI.BottomPanel.VIEWER_PHOTO_SPHERE;
   2963         } else if (currentData.getMetadata().isHasRgbzData()) {
   2964             viewButtonVisibility = CameraAppUI.BottomPanel.VIEWER_REFOCUS;
   2965         } else {
   2966             viewButtonVisibility = CameraAppUI.BottomPanel.VIEWER_NONE;
   2967         }
   2968 
   2969         filmstripBottomPanel.setTinyPlanetEnabled(
   2970                 currentData.getMetadata().isPanorama360());
   2971         filmstripBottomPanel.setViewerButtonVisibility(viewButtonVisibility);
   2972     }
   2973 
   2974     private void showDetailsDialog(int index) {
   2975         final FilmstripItem data = mDataAdapter.getItemAt(index);
   2976         if (data == null) {
   2977             return;
   2978         }
   2979         Optional<MediaDetails> details = data.getMediaDetails();
   2980         if (!details.isPresent()) {
   2981             return;
   2982         }
   2983         Dialog detailDialog = DetailsDialog.create(CameraActivity.this, details.get());
   2984         detailDialog.show();
   2985         UsageStatistics.instance().mediaInteraction(
   2986                 fileNameFromAdapterAtIndex(index), MediaInteraction.InteractionType.DETAILS,
   2987                 NavigationChange.InteractionCause.BUTTON, fileAgeFromAdapterAtIndex(index));
   2988     }
   2989 
   2990     /**
   2991      * Show or hide action bar items depending on current data type.
   2992      */
   2993     private void updateActionBarMenu(FilmstripItem data) {
   2994         if (mActionBarMenu == null) {
   2995             return;
   2996         }
   2997 
   2998         MenuItem detailsMenuItem = mActionBarMenu.findItem(R.id.action_details);
   2999         if (detailsMenuItem == null) {
   3000             return;
   3001         }
   3002 
   3003         boolean showDetails = data.getAttributes().hasDetailedCaptureInfo();
   3004         detailsMenuItem.setVisible(showDetails);
   3005     }
   3006 }
   3007