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