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 package com.android.camera;
     18 
     19 import android.animation.Animator;
     20 import android.annotation.TargetApi;
     21 import android.app.ActionBar;
     22 import android.app.Activity;
     23 import android.content.ActivityNotFoundException;
     24 import android.content.BroadcastReceiver;
     25 import android.content.ComponentName;
     26 import android.content.ContentResolver;
     27 import android.content.Context;
     28 import android.content.Intent;
     29 import android.content.IntentFilter;
     30 import android.content.ServiceConnection;
     31 import android.content.SharedPreferences;
     32 import android.content.pm.ActivityInfo;
     33 import android.content.res.Configuration;
     34 import android.graphics.drawable.ColorDrawable;
     35 import android.net.Uri;
     36 import android.nfc.NfcAdapter;
     37 import android.nfc.NfcAdapter.CreateBeamUrisCallback;
     38 import android.nfc.NfcEvent;
     39 import android.os.AsyncTask;
     40 import android.os.Build;
     41 import android.os.Bundle;
     42 import android.os.Handler;
     43 import android.os.IBinder;
     44 import android.os.Looper;
     45 import android.os.Message;
     46 import android.preference.PreferenceManager;
     47 import android.provider.MediaStore;
     48 import android.provider.Settings;
     49 import android.util.Log;
     50 import android.view.KeyEvent;
     51 import android.view.LayoutInflater;
     52 import android.view.Menu;
     53 import android.view.MenuInflater;
     54 import android.view.MenuItem;
     55 import android.view.MotionEvent;
     56 import android.view.OrientationEventListener;
     57 import android.view.View;
     58 import android.view.ViewGroup;
     59 import android.view.Window;
     60 import android.view.WindowManager;
     61 import android.widget.FrameLayout;
     62 import android.widget.ImageView;
     63 import android.widget.ProgressBar;
     64 import android.widget.ShareActionProvider;
     65 
     66 import com.android.camera.app.AppManagerFactory;
     67 import com.android.camera.app.PanoramaStitchingManager;
     68 import com.android.camera.crop.CropActivity;
     69 import com.android.camera.data.CameraDataAdapter;
     70 import com.android.camera.data.CameraPreviewData;
     71 import com.android.camera.data.FixedFirstDataAdapter;
     72 import com.android.camera.data.FixedLastDataAdapter;
     73 import com.android.camera.data.InProgressDataWrapper;
     74 import com.android.camera.data.LocalData;
     75 import com.android.camera.data.LocalDataAdapter;
     76 import com.android.camera.data.LocalMediaObserver;
     77 import com.android.camera.data.MediaDetails;
     78 import com.android.camera.data.SimpleViewData;
     79 import com.android.camera.tinyplanet.TinyPlanetFragment;
     80 import com.android.camera.ui.ModuleSwitcher;
     81 import com.android.camera.ui.DetailsDialog;
     82 import com.android.camera.ui.FilmStripView;
     83 import com.android.camera.util.ApiHelper;
     84 import com.android.camera.util.CameraUtil;
     85 import com.android.camera.util.GcamHelper;
     86 import com.android.camera.util.PhotoSphereHelper;
     87 import com.android.camera.util.PhotoSphereHelper.PanoramaViewHelper;
     88 import com.android.camera2.R;
     89 
     90 import static com.android.camera.CameraManager.CameraOpenErrorCallback;
     91 
     92 public class CameraActivity extends Activity
     93         implements ModuleSwitcher.ModuleSwitchListener,
     94         ActionBar.OnMenuVisibilityListener {
     95 
     96     private static final String TAG = "CAM_Activity";
     97 
     98     private static final String INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE =
     99             "android.media.action.STILL_IMAGE_CAMERA_SECURE";
    100     public static final String ACTION_IMAGE_CAPTURE_SECURE =
    101             "android.media.action.IMAGE_CAPTURE_SECURE";
    102     public static final String ACTION_TRIM_VIDEO =
    103             "com.android.camera.action.TRIM";
    104     public static final String MEDIA_ITEM_PATH = "media-item-path";
    105 
    106     // The intent extra for camera from secure lock screen. True if the gallery
    107     // should only show newly captured pictures. sSecureAlbumId does not
    108     // increment. This is used when switching between camera, camcorder, and
    109     // panorama. If the extra is not set, it is in the normal camera mode.
    110     public static final String SECURE_CAMERA_EXTRA = "secure_camera";
    111 
    112     /**
    113      * Request code from an activity we started that indicated that we do not
    114      * want to reset the view to the preview in onResume.
    115      */
    116     public static final int REQ_CODE_DONT_SWITCH_TO_PREVIEW = 142;
    117 
    118     public static final int REQ_CODE_GCAM_DEBUG_POSTCAPTURE = 999;
    119 
    120     private static final int HIDE_ACTION_BAR = 1;
    121     private static final long SHOW_ACTION_BAR_TIMEOUT_MS = 3000;
    122 
    123     /** Whether onResume should reset the view to the preview. */
    124     private boolean mResetToPreviewOnResume = true;
    125 
    126     // Supported operations at FilmStripView. Different data has different
    127     // set of supported operations.
    128     private static final int SUPPORT_DELETE = 1 << 0;
    129     private static final int SUPPORT_ROTATE = 1 << 1;
    130     private static final int SUPPORT_INFO = 1 << 2;
    131     private static final int SUPPORT_CROP = 1 << 3;
    132     private static final int SUPPORT_SETAS = 1 << 4;
    133     private static final int SUPPORT_EDIT = 1 << 5;
    134     private static final int SUPPORT_TRIM = 1 << 6;
    135     private static final int SUPPORT_SHARE = 1 << 7;
    136     private static final int SUPPORT_SHARE_PANORAMA360 = 1 << 8;
    137     private static final int SUPPORT_SHOW_ON_MAP = 1 << 9;
    138     private static final int SUPPORT_ALL = 0xffffffff;
    139 
    140     /** This data adapter is used by FilmStripView. */
    141     private LocalDataAdapter mDataAdapter;
    142     /** This data adapter represents the real local camera data. */
    143     private LocalDataAdapter mWrappedDataAdapter;
    144 
    145     private PanoramaStitchingManager mPanoramaManager;
    146     private int mCurrentModuleIndex;
    147     private CameraModule mCurrentModule;
    148     private FrameLayout mAboveFilmstripControlLayout;
    149     private View mCameraModuleRootView;
    150     private FilmStripView mFilmStripView;
    151     private ProgressBar mBottomProgress;
    152     private View mPanoStitchingPanel;
    153     private int mResultCodeForTesting;
    154     private Intent mResultDataForTesting;
    155     private OnScreenHint mStorageHint;
    156     private long mStorageSpaceBytes = Storage.LOW_STORAGE_THRESHOLD_BYTES;
    157     private boolean mAutoRotateScreen;
    158     private boolean mSecureCamera;
    159     // This is a hack to speed up the start of SecureCamera.
    160     private static boolean sFirstStartAfterScreenOn = true;
    161     private int mLastRawOrientation;
    162     private MyOrientationEventListener mOrientationListener;
    163     private Handler mMainHandler;
    164     private PanoramaViewHelper mPanoramaViewHelper;
    165     private CameraPreviewData mCameraPreviewData;
    166     private ActionBar mActionBar;
    167     private OnActionBarVisibilityListener mOnActionBarVisibilityListener = null;
    168     private Menu mActionBarMenu;
    169     private ViewGroup mUndoDeletionBar;
    170     private boolean mIsUndoingDeletion = false;
    171 
    172     private Uri[] mNfcPushUris = new Uri[1];
    173 
    174     private ShareActionProvider mStandardShareActionProvider;
    175     private Intent mStandardShareIntent;
    176     private ShareActionProvider mPanoramaShareActionProvider;
    177     private Intent mPanoramaShareIntent;
    178     private LocalMediaObserver mLocalImagesObserver;
    179     private LocalMediaObserver mLocalVideosObserver;
    180 
    181     private final int DEFAULT_SYSTEM_UI_VISIBILITY = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
    182             | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
    183     private boolean mPendingDeletion = false;
    184 
    185     private Intent mVideoShareIntent;
    186     private Intent mImageShareIntent;
    187 
    188     private class MyOrientationEventListener
    189             extends OrientationEventListener {
    190         public MyOrientationEventListener(Context context) {
    191             super(context);
    192         }
    193 
    194         @Override
    195         public void onOrientationChanged(int orientation) {
    196             // We keep the last known orientation. So if the user first orient
    197             // the camera then point the camera to floor or sky, we still have
    198             // the correct orientation.
    199             if (orientation == ORIENTATION_UNKNOWN) {
    200                 return;
    201             }
    202             mLastRawOrientation = orientation;
    203             mCurrentModule.onOrientationChanged(orientation);
    204         }
    205     }
    206 
    207     private MediaSaveService mMediaSaveService;
    208     private ServiceConnection mConnection = new ServiceConnection() {
    209         @Override
    210         public void onServiceConnected(ComponentName className, IBinder b) {
    211             mMediaSaveService = ((MediaSaveService.LocalBinder) b).getService();
    212             mCurrentModule.onMediaSaveServiceConnected(mMediaSaveService);
    213         }
    214 
    215         @Override
    216         public void onServiceDisconnected(ComponentName className) {
    217             if (mMediaSaveService != null) {
    218                 mMediaSaveService.setListener(null);
    219                 mMediaSaveService = null;
    220             }
    221         }
    222     };
    223 
    224     private CameraOpenErrorCallback mCameraOpenErrorCallback =
    225             new CameraOpenErrorCallback() {
    226                 @Override
    227                 public void onCameraDisabled(int cameraId) {
    228                     CameraUtil.showErrorAndFinish(CameraActivity.this,
    229                             R.string.camera_disabled);
    230                 }
    231 
    232                 @Override
    233                 public void onDeviceOpenFailure(int cameraId) {
    234                     CameraUtil.showErrorAndFinish(CameraActivity.this,
    235                             R.string.cannot_connect_camera);
    236                 }
    237 
    238                 @Override
    239                 public void onReconnectionFailure(CameraManager mgr) {
    240                     CameraUtil.showErrorAndFinish(CameraActivity.this,
    241                             R.string.cannot_connect_camera);
    242                 }
    243             };
    244 
    245     // close activity when screen turns off
    246     private BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() {
    247         @Override
    248         public void onReceive(Context context, Intent intent) {
    249             finish();
    250         }
    251     };
    252 
    253     private static BroadcastReceiver sScreenOffReceiver;
    254 
    255     private static class ScreenOffReceiver extends BroadcastReceiver {
    256         @Override
    257         public void onReceive(Context context, Intent intent) {
    258             sFirstStartAfterScreenOn = true;
    259         }
    260     }
    261 
    262     private class MainHandler extends Handler {
    263         public MainHandler(Looper looper) {
    264             super(looper);
    265         }
    266 
    267         @Override
    268         public void handleMessage(Message msg) {
    269             if (msg.what == HIDE_ACTION_BAR) {
    270                 removeMessages(HIDE_ACTION_BAR);
    271                 CameraActivity.this.setSystemBarsVisibility(false);
    272             }
    273         }
    274     }
    275 
    276     public interface OnActionBarVisibilityListener {
    277         public void onActionBarVisibilityChanged(boolean isVisible);
    278     }
    279 
    280     public void setOnActionBarVisibilityListener(OnActionBarVisibilityListener listener) {
    281         mOnActionBarVisibilityListener = listener;
    282     }
    283 
    284     public static boolean isFirstStartAfterScreenOn() {
    285         return sFirstStartAfterScreenOn;
    286     }
    287 
    288     public static void resetFirstStartAfterScreenOn() {
    289         sFirstStartAfterScreenOn = false;
    290     }
    291 
    292     private FilmStripView.Listener mFilmStripListener =
    293             new FilmStripView.Listener() {
    294                 @Override
    295                 public void onDataPromoted(int dataID) {
    296                     removeData(dataID);
    297                 }
    298 
    299                 @Override
    300                 public void onDataDemoted(int dataID) {
    301                     removeData(dataID);
    302                 }
    303 
    304                 @Override
    305                 public void onDataFullScreenChange(int dataID, boolean full) {
    306                     boolean isCameraID = isCameraPreview(dataID);
    307                     if (!isCameraID) {
    308                         if (!full) {
    309                             // Always show action bar in filmstrip mode
    310                             CameraActivity.this.setSystemBarsVisibility(true, false);
    311                         } else if (mActionBar.isShowing()) {
    312                             // Hide action bar after time out in full screen mode
    313                             mMainHandler.sendEmptyMessageDelayed(HIDE_ACTION_BAR,
    314                                     SHOW_ACTION_BAR_TIMEOUT_MS);
    315                         }
    316                     }
    317                 }
    318 
    319                 /**
    320                  * Check if the local data corresponding to dataID is the camera
    321                  * preview.
    322                  *
    323                  * @param dataID the ID of the local data
    324                  * @return true if the local data is not null and it is the
    325                  *         camera preview.
    326                  */
    327                 private boolean isCameraPreview(int dataID) {
    328                     LocalData localData = mDataAdapter.getLocalData(dataID);
    329                     if (localData == null) {
    330                         Log.w(TAG, "Current data ID not found.");
    331                         return false;
    332                     }
    333                     return localData.getLocalDataType() == LocalData.LOCAL_CAMERA_PREVIEW;
    334                 }
    335 
    336                 @Override
    337                 public void onReload() {
    338                     setPreviewControlsVisibility(true);
    339                 }
    340 
    341                 @Override
    342                 public void onCurrentDataCentered(int dataID) {
    343                     if (dataID != 0 && !mFilmStripView.isCameraPreview()) {
    344                         // For now, We ignore all items that are not the camera preview.
    345                         return;
    346                     }
    347 
    348                     if(!arePreviewControlsVisible()) {
    349                         setPreviewControlsVisibility(true);
    350                     }
    351                 }
    352 
    353                 @Override
    354                 public void onCurrentDataOffCentered(int dataID) {
    355                     if (dataID != 0 && !mFilmStripView.isCameraPreview()) {
    356                         // For now, We ignore all items that are not the camera preview.
    357                         return;
    358                     }
    359 
    360                     if (arePreviewControlsVisible()) {
    361                         setPreviewControlsVisibility(false);
    362                     }
    363                 }
    364 
    365                 @Override
    366                 public void onDataFocusChanged(final int dataID, final boolean focused) {
    367                     // Delay hiding action bar if there is any user interaction
    368                     if (mMainHandler.hasMessages(HIDE_ACTION_BAR)) {
    369                         mMainHandler.removeMessages(HIDE_ACTION_BAR);
    370                         mMainHandler.sendEmptyMessageDelayed(HIDE_ACTION_BAR,
    371                                 SHOW_ACTION_BAR_TIMEOUT_MS);
    372                     }
    373                     // TODO: This callback is UI event callback, should always
    374                     // happen on UI thread. Find the reason for this
    375                     // runOnUiThread() and fix it.
    376                     runOnUiThread(new Runnable() {
    377                         @Override
    378                         public void run() {
    379                             LocalData currentData = mDataAdapter.getLocalData(dataID);
    380                             if (currentData == null) {
    381                                 Log.w(TAG, "Current data ID not found.");
    382                                 hidePanoStitchingProgress();
    383                                 return;
    384                             }
    385                             boolean isCameraID = currentData.getLocalDataType() ==
    386                                     LocalData.LOCAL_CAMERA_PREVIEW;
    387                             if (!focused) {
    388                                 if (isCameraID) {
    389                                     mCurrentModule.onPreviewFocusChanged(false);
    390                                     CameraActivity.this.setSystemBarsVisibility(true);
    391                                 }
    392                                 hidePanoStitchingProgress();
    393                             } else {
    394                                 if (isCameraID) {
    395                                     // Don't show the action bar in Camera
    396                                     // preview.
    397                                     CameraActivity.this.setSystemBarsVisibility(false);
    398 
    399                                     if (mPendingDeletion) {
    400                                         performDeletion();
    401                                     }
    402                                 } else {
    403                                     updateActionBarMenu(dataID);
    404                                 }
    405 
    406                                 Uri contentUri = currentData.getContentUri();
    407                                 if (contentUri == null) {
    408                                     hidePanoStitchingProgress();
    409                                     return;
    410                                 }
    411                                 int panoStitchingProgress = mPanoramaManager.getTaskProgress(
    412                                         contentUri);
    413                                 if (panoStitchingProgress < 0) {
    414                                     hidePanoStitchingProgress();
    415                                     return;
    416                                 }
    417                                 showPanoStitchingProgress();
    418                                 updateStitchingProgress(panoStitchingProgress);
    419                             }
    420                         }
    421                     });
    422                 }
    423 
    424                 @Override
    425                 public void onToggleSystemDecorsVisibility(int dataID) {
    426                     // If action bar is showing, hide it immediately, otherwise
    427                     // show action bar and hide it later
    428                     if (mActionBar.isShowing()) {
    429                         CameraActivity.this.setSystemBarsVisibility(false);
    430                     } else {
    431                         // Don't show the action bar if that is the camera preview.
    432                         boolean isCameraID = isCameraPreview(dataID);
    433                         if (!isCameraID) {
    434                             CameraActivity.this.setSystemBarsVisibility(true, true);
    435                         }
    436                     }
    437                 }
    438 
    439                 @Override
    440                 public void setSystemDecorsVisibility(boolean visible) {
    441                     CameraActivity.this.setSystemBarsVisibility(visible);
    442                 }
    443             };
    444 
    445     public void gotoGallery() {
    446         mFilmStripView.getController().goToNextItem();
    447     }
    448 
    449     /**
    450      * If {@param visible} is false, this hides the action bar and switches the system UI
    451      * to lights-out mode.
    452      */
    453     // TODO: This should not be called outside of the activity.
    454     public void setSystemBarsVisibility(boolean visible) {
    455         setSystemBarsVisibility(visible, false);
    456     }
    457 
    458     /**
    459      * If {@param visible} is false, this hides the action bar and switches the
    460      * system UI to lights-out mode. If {@param hideLater} is true, a delayed message
    461      * will be sent after a timeout to hide the action bar.
    462      */
    463     private void setSystemBarsVisibility(boolean visible, boolean hideLater) {
    464         mMainHandler.removeMessages(HIDE_ACTION_BAR);
    465         boolean currentlyVisible = mActionBar.isShowing();
    466 
    467         if (visible != currentlyVisible) {
    468             int visibility = DEFAULT_SYSTEM_UI_VISIBILITY | (visible ? View.SYSTEM_UI_FLAG_VISIBLE
    469                     : View.SYSTEM_UI_FLAG_LOW_PROFILE | View.SYSTEM_UI_FLAG_FULLSCREEN);
    470             mAboveFilmstripControlLayout.setSystemUiVisibility(visibility);
    471 
    472             if (visible) {
    473                 mActionBar.show();
    474             } else {
    475                 mActionBar.hide();
    476             }
    477             if (mOnActionBarVisibilityListener != null) {
    478                 mOnActionBarVisibilityListener.onActionBarVisibilityChanged(visible);
    479             }
    480         }
    481 
    482         // Now delay hiding the bars
    483         if (visible && hideLater) {
    484             mMainHandler.sendEmptyMessageDelayed(HIDE_ACTION_BAR, SHOW_ACTION_BAR_TIMEOUT_MS);
    485         }
    486     }
    487 
    488     private void hidePanoStitchingProgress() {
    489         mPanoStitchingPanel.setVisibility(View.GONE);
    490     }
    491 
    492     private void showPanoStitchingProgress() {
    493         mPanoStitchingPanel.setVisibility(View.VISIBLE);
    494     }
    495 
    496     private void updateStitchingProgress(int progress) {
    497         mBottomProgress.setProgress(progress);
    498     }
    499 
    500     @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    501     private void setupNfcBeamPush() {
    502         NfcAdapter adapter = NfcAdapter.getDefaultAdapter(CameraActivity.this);
    503         if (adapter == null) {
    504             return;
    505         }
    506 
    507         if (!ApiHelper.HAS_SET_BEAM_PUSH_URIS) {
    508             // Disable beaming
    509             adapter.setNdefPushMessage(null, CameraActivity.this);
    510             return;
    511         }
    512 
    513         adapter.setBeamPushUris(null, CameraActivity.this);
    514         adapter.setBeamPushUrisCallback(new CreateBeamUrisCallback() {
    515             @Override
    516             public Uri[] createBeamUris(NfcEvent event) {
    517                 return mNfcPushUris;
    518             }
    519         }, CameraActivity.this);
    520     }
    521 
    522     private void setNfcBeamPushUri(Uri uri) {
    523         mNfcPushUris[0] = uri;
    524     }
    525 
    526     private void setStandardShareIntent(Uri contentUri, String mimeType) {
    527         mStandardShareIntent = getShareIntentFromType(mimeType);
    528         if (mStandardShareIntent != null) {
    529             mStandardShareIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
    530             mStandardShareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    531             if (mStandardShareActionProvider != null) {
    532                 mStandardShareActionProvider.setShareIntent(mStandardShareIntent);
    533             }
    534         }
    535     }
    536 
    537     /**
    538      * Get the share intent according to the mimeType
    539      *
    540      * @param mimeType The mimeType of current data.
    541      * @return the video/image's ShareIntent or null if mimeType is invalid.
    542      */
    543     private Intent getShareIntentFromType(String mimeType) {
    544         // Lazily create the intent object.
    545         if (mimeType.startsWith("video/")) {
    546             if (mVideoShareIntent == null) {
    547                 mVideoShareIntent = new Intent(Intent.ACTION_SEND);
    548                 mVideoShareIntent.setType("video/*");
    549             }
    550             return mVideoShareIntent;
    551         } else if (mimeType.startsWith("image/")) {
    552             if (mImageShareIntent == null) {
    553                 mImageShareIntent = new Intent(Intent.ACTION_SEND);
    554                 mImageShareIntent.setType("image/*");
    555             }
    556             return mImageShareIntent;
    557         }
    558         Log.w(TAG, "unsupported mimeType " + mimeType);
    559         return null;
    560     }
    561 
    562     private void setPanoramaShareIntent(Uri contentUri) {
    563         if (mPanoramaShareIntent == null) {
    564             mPanoramaShareIntent = new Intent(Intent.ACTION_SEND);
    565         }
    566         mPanoramaShareIntent.setType("application/vnd.google.panorama360+jpg");
    567         mPanoramaShareIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
    568         if (mPanoramaShareActionProvider != null) {
    569             mPanoramaShareActionProvider.setShareIntent(mPanoramaShareIntent);
    570         }
    571     }
    572 
    573     @Override
    574     public void onMenuVisibilityChanged(boolean isVisible) {
    575         // If menu is showing, we need to make sure action bar does not go away.
    576         mMainHandler.removeMessages(HIDE_ACTION_BAR);
    577         if (!isVisible) {
    578             mMainHandler.sendEmptyMessageDelayed(HIDE_ACTION_BAR, SHOW_ACTION_BAR_TIMEOUT_MS);
    579         }
    580     }
    581 
    582     /**
    583      * According to the data type, make the menu items for supported operations
    584      * visible.
    585      *
    586      * @param dataID the data ID of the current item.
    587      */
    588     private void updateActionBarMenu(int dataID) {
    589         LocalData currentData = mDataAdapter.getLocalData(dataID);
    590         if (currentData == null) {
    591             return;
    592         }
    593         int type = currentData.getLocalDataType();
    594 
    595         if (mActionBarMenu == null) {
    596             return;
    597         }
    598 
    599         int supported = 0;
    600 
    601         switch (type) {
    602             case LocalData.LOCAL_IMAGE:
    603                 supported |= SUPPORT_DELETE | SUPPORT_ROTATE | SUPPORT_INFO
    604                         | SUPPORT_CROP | SUPPORT_SETAS | SUPPORT_EDIT
    605                         | SUPPORT_SHARE | SUPPORT_SHOW_ON_MAP;
    606                 break;
    607             case LocalData.LOCAL_VIDEO:
    608                 supported |= SUPPORT_DELETE | SUPPORT_INFO | SUPPORT_TRIM
    609                         | SUPPORT_SHARE;
    610                 break;
    611             case LocalData.LOCAL_PHOTO_SPHERE:
    612                 supported |= SUPPORT_DELETE | SUPPORT_ROTATE | SUPPORT_INFO
    613                         | SUPPORT_CROP | SUPPORT_SETAS | SUPPORT_EDIT
    614                         | SUPPORT_SHARE | SUPPORT_SHOW_ON_MAP;
    615                 break;
    616             case LocalData.LOCAL_360_PHOTO_SPHERE:
    617                 supported |= SUPPORT_DELETE | SUPPORT_ROTATE | SUPPORT_INFO
    618                         | SUPPORT_CROP | SUPPORT_SETAS | SUPPORT_EDIT
    619                         | SUPPORT_SHARE | SUPPORT_SHARE_PANORAMA360
    620                         | SUPPORT_SHOW_ON_MAP;
    621                 break;
    622             default:
    623                 break;
    624         }
    625 
    626         // In secure camera mode, we only support delete operation.
    627         if (isSecureCamera()) {
    628             supported &= SUPPORT_DELETE;
    629         }
    630 
    631         setMenuItemVisible(mActionBarMenu, R.id.action_delete,
    632                 (supported & SUPPORT_DELETE) != 0);
    633         setMenuItemVisible(mActionBarMenu, R.id.action_rotate_ccw,
    634                 (supported & SUPPORT_ROTATE) != 0);
    635         setMenuItemVisible(mActionBarMenu, R.id.action_rotate_cw,
    636                 (supported & SUPPORT_ROTATE) != 0);
    637         setMenuItemVisible(mActionBarMenu, R.id.action_details,
    638                 (supported & SUPPORT_INFO) != 0);
    639         setMenuItemVisible(mActionBarMenu, R.id.action_crop,
    640                 (supported & SUPPORT_CROP) != 0);
    641         setMenuItemVisible(mActionBarMenu, R.id.action_setas,
    642                 (supported & SUPPORT_SETAS) != 0);
    643         setMenuItemVisible(mActionBarMenu, R.id.action_edit,
    644                 (supported & SUPPORT_EDIT) != 0);
    645         setMenuItemVisible(mActionBarMenu, R.id.action_trim,
    646                 (supported & SUPPORT_TRIM) != 0);
    647 
    648         boolean standardShare = (supported & SUPPORT_SHARE) != 0;
    649         boolean panoramaShare = (supported & SUPPORT_SHARE_PANORAMA360) != 0;
    650         setMenuItemVisible(mActionBarMenu, R.id.action_share, standardShare);
    651         setMenuItemVisible(mActionBarMenu, R.id.action_share_panorama, panoramaShare);
    652 
    653         if (panoramaShare) {
    654             // For 360 PhotoSphere, relegate standard share to the overflow menu
    655             MenuItem item = mActionBarMenu.findItem(R.id.action_share);
    656             if (item != null) {
    657                 item.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
    658                 item.setTitle(getResources().getString(R.string.share_as_photo));
    659             }
    660             // And, promote "share as panorama" to action bar
    661             item = mActionBarMenu.findItem(R.id.action_share_panorama);
    662             if (item != null) {
    663                 item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
    664             }
    665             setPanoramaShareIntent(currentData.getContentUri());
    666         }
    667         if (standardShare) {
    668             if (!panoramaShare) {
    669                 MenuItem item = mActionBarMenu.findItem(R.id.action_share);
    670                 if (item != null) {
    671                     item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
    672                     item.setTitle(getResources().getString(R.string.share));
    673                 }
    674             }
    675             setStandardShareIntent(currentData.getContentUri(), currentData.getMimeType());
    676             setNfcBeamPushUri(currentData.getContentUri());
    677         }
    678 
    679         boolean itemHasLocation = currentData.getLatLong() != null;
    680         setMenuItemVisible(mActionBarMenu, R.id.action_show_on_map,
    681                 itemHasLocation && (supported & SUPPORT_SHOW_ON_MAP) != 0);
    682     }
    683 
    684     private void setMenuItemVisible(Menu menu, int itemId, boolean visible) {
    685         MenuItem item = menu.findItem(itemId);
    686         if (item != null)
    687             item.setVisible(visible);
    688     }
    689 
    690     private ImageTaskManager.TaskListener mStitchingListener =
    691             new ImageTaskManager.TaskListener() {
    692                 @Override
    693                 public void onTaskQueued(String filePath, final Uri imageUri) {
    694                     mMainHandler.post(new Runnable() {
    695                         @Override
    696                         public void run() {
    697                             notifyNewMedia(imageUri);
    698                             int dataID = mDataAdapter.findDataByContentUri(imageUri);
    699                             if (dataID != -1) {
    700                                 // Don't allow special UI actions (swipe to
    701                                 // delete, for example) on in-progress data.
    702                                 LocalData d = mDataAdapter.getLocalData(dataID);
    703                                 InProgressDataWrapper newData = new InProgressDataWrapper(d);
    704                                 mDataAdapter.updateData(dataID, newData);
    705                             }
    706                         }
    707                     });
    708                 }
    709 
    710                 @Override
    711                 public void onTaskDone(String filePath, final Uri imageUri) {
    712                     Log.v(TAG, "onTaskDone:" + filePath);
    713                     mMainHandler.post(new Runnable() {
    714                         @Override
    715                         public void run() {
    716                             int doneID = mDataAdapter.findDataByContentUri(imageUri);
    717                             int currentDataId = mFilmStripView.getCurrentId();
    718 
    719                             if (currentDataId == doneID) {
    720                                 hidePanoStitchingProgress();
    721                                 updateStitchingProgress(0);
    722                             }
    723 
    724                             mDataAdapter.refresh(getContentResolver(), imageUri);
    725                         }
    726                     });
    727                 }
    728 
    729                 @Override
    730                 public void onTaskProgress(
    731                         String filePath, final Uri imageUri, final int progress) {
    732                     mMainHandler.post(new Runnable() {
    733                         @Override
    734                         public void run() {
    735                             int currentDataId = mFilmStripView.getCurrentId();
    736                             if (currentDataId == -1) {
    737                                 return;
    738                             }
    739                             if (imageUri.equals(
    740                                     mDataAdapter.getLocalData(currentDataId).getContentUri())) {
    741                                 updateStitchingProgress(progress);
    742                             }
    743                         }
    744                     });
    745                 }
    746             };
    747 
    748     public MediaSaveService getMediaSaveService() {
    749         return mMediaSaveService;
    750     }
    751 
    752     public void notifyNewMedia(Uri uri) {
    753         ContentResolver cr = getContentResolver();
    754         String mimeType = cr.getType(uri);
    755         if (mimeType.startsWith("video/")) {
    756             sendBroadcast(new Intent(CameraUtil.ACTION_NEW_VIDEO, uri));
    757             mDataAdapter.addNewVideo(cr, uri);
    758         } else if (mimeType.startsWith("image/")) {
    759             CameraUtil.broadcastNewPicture(this, uri);
    760             mDataAdapter.addNewPhoto(cr, uri);
    761         } else if (mimeType.startsWith("application/stitching-preview")) {
    762             mDataAdapter.addNewPhoto(cr, uri);
    763         } else {
    764             android.util.Log.w(TAG, "Unknown new media with MIME type:"
    765                     + mimeType + ", uri:" + uri);
    766         }
    767     }
    768 
    769     private void removeData(int dataID) {
    770         mDataAdapter.removeData(CameraActivity.this, dataID);
    771         if (mDataAdapter.getTotalNumber() > 1) {
    772             showUndoDeletionBar();
    773         } else {
    774             // If camera preview is the only view left in filmstrip,
    775             // no need to show undo bar.
    776             mPendingDeletion = true;
    777             performDeletion();
    778         }
    779     }
    780 
    781     private void bindMediaSaveService() {
    782         Intent intent = new Intent(this, MediaSaveService.class);
    783         bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    784     }
    785 
    786     private void unbindMediaSaveService() {
    787         if (mConnection != null) {
    788             unbindService(mConnection);
    789         }
    790     }
    791 
    792     @Override
    793     public boolean onCreateOptionsMenu(Menu menu) {
    794         // Inflate the menu items for use in the action bar
    795         MenuInflater inflater = getMenuInflater();
    796         inflater.inflate(R.menu.operations, menu);
    797         mActionBarMenu = menu;
    798 
    799         // Configure the standard share action provider
    800         MenuItem item = menu.findItem(R.id.action_share);
    801         mStandardShareActionProvider = (ShareActionProvider) item.getActionProvider();
    802         mStandardShareActionProvider.setShareHistoryFileName("standard_share_history.xml");
    803         if (mStandardShareIntent != null) {
    804             mStandardShareActionProvider.setShareIntent(mStandardShareIntent);
    805         }
    806 
    807         // Configure the panorama share action provider
    808         item = menu.findItem(R.id.action_share_panorama);
    809         mPanoramaShareActionProvider = (ShareActionProvider) item.getActionProvider();
    810         mPanoramaShareActionProvider.setShareHistoryFileName("panorama_share_history.xml");
    811         if (mPanoramaShareIntent != null) {
    812             mPanoramaShareActionProvider.setShareIntent(mPanoramaShareIntent);
    813         }
    814 
    815         return super.onCreateOptionsMenu(menu);
    816     }
    817 
    818     @Override
    819     public boolean onOptionsItemSelected(MenuItem item) {
    820         int currentDataId = mFilmStripView.getCurrentId();
    821         if (currentDataId < 0) {
    822             return false;
    823         }
    824         final LocalData localData = mDataAdapter.getLocalData(currentDataId);
    825 
    826         // Handle presses on the action bar items
    827         switch (item.getItemId()) {
    828             case android.R.id.home:
    829                 // ActionBar's Up/Home button was clicked
    830                 try {
    831                     if (!CameraUtil.launchGallery(CameraActivity.this)) {
    832                         mFilmStripView.getController().goToFirstItem();
    833                     }
    834                     return true;
    835                 } catch (ActivityNotFoundException e) {
    836                     Log.w(TAG, "No activity found to handle APP_GALLERY category!");
    837                     finish();
    838                 }
    839             case R.id.action_delete:
    840                 removeData(currentDataId);
    841                 return true;
    842             case R.id.action_edit:
    843                 launchEditor(localData);
    844                 return true;
    845             case R.id.action_trim: {
    846                 // This is going to be handled by the Gallery app.
    847                 Intent intent = new Intent(ACTION_TRIM_VIDEO);
    848                 LocalData currentData = mDataAdapter.getLocalData(
    849                         mFilmStripView.getCurrentId());
    850                 intent.setData(currentData.getContentUri());
    851                 // We need the file path to wrap this into a RandomAccessFile.
    852                 intent.putExtra(MEDIA_ITEM_PATH, currentData.getPath());
    853                 startActivityForResult(intent, REQ_CODE_DONT_SWITCH_TO_PREVIEW);
    854                 return true;
    855             }
    856             case R.id.action_rotate_ccw:
    857                 localData.rotate90Degrees(this, mDataAdapter, currentDataId, false);
    858                 return true;
    859             case R.id.action_rotate_cw:
    860                 localData.rotate90Degrees(this, mDataAdapter, currentDataId, true);
    861                 return true;
    862             case R.id.action_crop: {
    863                 Intent intent = new Intent(CropActivity.CROP_ACTION);
    864                 intent.setClass(this, CropActivity.class);
    865                 intent.setDataAndType(localData.getContentUri(), localData.getMimeType())
    866                         .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    867                 startActivityForResult(intent, REQ_CODE_DONT_SWITCH_TO_PREVIEW);
    868                 return true;
    869             }
    870             case R.id.action_setas: {
    871                 Intent intent = new Intent(Intent.ACTION_ATTACH_DATA)
    872                         .setDataAndType(localData.getContentUri(),
    873                                 localData.getMimeType())
    874                         .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    875                 intent.putExtra("mimeType", intent.getType());
    876                 startActivityForResult(Intent.createChooser(
    877                         intent, getString(R.string.set_as)), REQ_CODE_DONT_SWITCH_TO_PREVIEW);
    878                 return true;
    879             }
    880             case R.id.action_details:
    881                 (new AsyncTask<Void, Void, MediaDetails>() {
    882                     @Override
    883                     protected MediaDetails doInBackground(Void... params) {
    884                         return localData.getMediaDetails(CameraActivity.this);
    885                     }
    886 
    887                     @Override
    888                     protected void onPostExecute(MediaDetails mediaDetails) {
    889                         if (mediaDetails != null) {
    890                             DetailsDialog.create(CameraActivity.this, mediaDetails).show();
    891                         }
    892                     }
    893                 }).execute();
    894                 return true;
    895             case R.id.action_show_on_map:
    896                 double[] latLong = localData.getLatLong();
    897                 if (latLong != null) {
    898                     CameraUtil.showOnMap(this, latLong);
    899                 }
    900                 return true;
    901             default:
    902                 return super.onOptionsItemSelected(item);
    903         }
    904     }
    905 
    906     private boolean isCaptureIntent() {
    907         if (MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction())
    908                 || MediaStore.ACTION_IMAGE_CAPTURE.equals(getIntent().getAction())
    909                 || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(getIntent().getAction())) {
    910             return true;
    911         } else {
    912             return false;
    913         }
    914     }
    915 
    916     @Override
    917     public void onCreate(Bundle state) {
    918         super.onCreate(state);
    919         GcamHelper.init(getContentResolver());
    920 
    921         getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
    922         setContentView(R.layout.camera_filmstrip);
    923         mActionBar = getActionBar();
    924         mActionBar.addOnMenuVisibilityListener(this);
    925 
    926         if (ApiHelper.HAS_ROTATION_ANIMATION) {
    927             setRotationAnimation();
    928         }
    929 
    930         mMainHandler = new MainHandler(getMainLooper());
    931         // Check if this is in the secure camera mode.
    932         Intent intent = getIntent();
    933         String action = intent.getAction();
    934         if (INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action)
    935                 || ACTION_IMAGE_CAPTURE_SECURE.equals(action)) {
    936             mSecureCamera = true;
    937         } else {
    938             mSecureCamera = intent.getBooleanExtra(SECURE_CAMERA_EXTRA, false);
    939         }
    940 
    941         if (mSecureCamera) {
    942             // Change the window flags so that secure camera can show when locked
    943             Window win = getWindow();
    944             WindowManager.LayoutParams params = win.getAttributes();
    945             params.flags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
    946             win.setAttributes(params);
    947 
    948             // Filter for screen off so that we can finish secure camera activity
    949             // when screen is off.
    950             IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
    951             registerReceiver(mScreenOffReceiver, filter);
    952             // TODO: This static screen off event receiver is a workaround to the
    953             // double onResume() invocation (onResume->onPause->onResume). We should
    954             // find a better solution to this.
    955             if (sScreenOffReceiver == null) {
    956                 sScreenOffReceiver = new ScreenOffReceiver();
    957                 registerReceiver(sScreenOffReceiver, filter);
    958             }
    959         }
    960         mAboveFilmstripControlLayout =
    961                 (FrameLayout) findViewById(R.id.camera_above_filmstrip_layout);
    962         mAboveFilmstripControlLayout.setFitsSystemWindows(true);
    963         // Hide action bar first since we are in full screen mode first, and
    964         // switch the system UI to lights-out mode.
    965         this.setSystemBarsVisibility(false);
    966         mPanoramaManager = AppManagerFactory.getInstance(this)
    967                 .getPanoramaStitchingManager();
    968         mPanoramaManager.addTaskListener(mStitchingListener);
    969         LayoutInflater inflater = getLayoutInflater();
    970         View rootLayout = inflater.inflate(R.layout.camera, null, false);
    971         mCameraModuleRootView = rootLayout.findViewById(R.id.camera_app_root);
    972         mPanoStitchingPanel = findViewById(R.id.pano_stitching_progress_panel);
    973         mBottomProgress = (ProgressBar) findViewById(R.id.pano_stitching_progress_bar);
    974         mCameraPreviewData = new CameraPreviewData(rootLayout,
    975                 FilmStripView.ImageData.SIZE_FULL,
    976                 FilmStripView.ImageData.SIZE_FULL);
    977         // Put a CameraPreviewData at the first position.
    978         mWrappedDataAdapter = new FixedFirstDataAdapter(
    979                 new CameraDataAdapter(new ColorDrawable(
    980                         getResources().getColor(R.color.photo_placeholder))),
    981                 mCameraPreviewData);
    982         mFilmStripView = (FilmStripView) findViewById(R.id.filmstrip_view);
    983         mFilmStripView.setViewGap(
    984                 getResources().getDimensionPixelSize(R.dimen.camera_film_strip_gap));
    985         mPanoramaViewHelper = new PanoramaViewHelper(this);
    986         mPanoramaViewHelper.onCreate();
    987         mFilmStripView.setPanoramaViewHelper(mPanoramaViewHelper);
    988         // Set up the camera preview first so the preview shows up ASAP.
    989         mFilmStripView.setListener(mFilmStripListener);
    990 
    991         int moduleIndex = -1;
    992         if (MediaStore.INTENT_ACTION_VIDEO_CAMERA.equals(getIntent().getAction())
    993                 || MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction())) {
    994             moduleIndex = ModuleSwitcher.VIDEO_MODULE_INDEX;
    995         } else if (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(getIntent().getAction())
    996                 || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(getIntent()
    997                         .getAction())
    998                 || MediaStore.ACTION_IMAGE_CAPTURE.equals(getIntent().getAction())
    999                 || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(getIntent().getAction())) {
   1000             moduleIndex = ModuleSwitcher.PHOTO_MODULE_INDEX;
   1001         } else {
   1002             // If the activity has not been started using an explicit intent,
   1003             // read the module index from the last time the user changed modes
   1004             SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
   1005             moduleIndex = prefs.getInt(CameraSettings.KEY_STARTUP_MODULE_INDEX, -1);
   1006             if ((moduleIndex == ModuleSwitcher.GCAM_MODULE_INDEX &&
   1007                     !GcamHelper.hasGcamCapture()) || moduleIndex < 0) {
   1008                 moduleIndex = ModuleSwitcher.PHOTO_MODULE_INDEX;
   1009             }
   1010         }
   1011 
   1012         mOrientationListener = new MyOrientationEventListener(this);
   1013         setModuleFromIndex(moduleIndex);
   1014         mCurrentModule.init(this, mCameraModuleRootView);
   1015 
   1016         if (!mSecureCamera) {
   1017             mDataAdapter = mWrappedDataAdapter;
   1018             mFilmStripView.setDataAdapter(mDataAdapter);
   1019             if (!isCaptureIntent()) {
   1020                 mDataAdapter.requestLoad(getContentResolver());
   1021             }
   1022         } else {
   1023             // Put a lock placeholder as the last image by setting its date to
   1024             // 0.
   1025             ImageView v = (ImageView) getLayoutInflater().inflate(
   1026                     R.layout.secure_album_placeholder, null);
   1027             v.setOnClickListener(new View.OnClickListener() {
   1028                 @Override
   1029                 public void onClick(View view) {
   1030                     CameraUtil.launchGallery(CameraActivity.this);
   1031                     finish();
   1032                 }
   1033             });
   1034             mDataAdapter = new FixedLastDataAdapter(
   1035                     mWrappedDataAdapter,
   1036                     new SimpleViewData(
   1037                             v,
   1038                             v.getDrawable().getIntrinsicWidth(),
   1039                             v.getDrawable().getIntrinsicHeight(),
   1040                             0, 0));
   1041             // Flush out all the original data.
   1042             mDataAdapter.flush();
   1043             mFilmStripView.setDataAdapter(mDataAdapter);
   1044         }
   1045 
   1046         setupNfcBeamPush();
   1047 
   1048         mLocalImagesObserver = new LocalMediaObserver();
   1049         mLocalVideosObserver = new LocalMediaObserver();
   1050 
   1051         getContentResolver().registerContentObserver(
   1052                 MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true,
   1053                 mLocalImagesObserver);
   1054         getContentResolver().registerContentObserver(
   1055                 MediaStore.Video.Media.EXTERNAL_CONTENT_URI, true,
   1056                 mLocalVideosObserver);
   1057     }
   1058 
   1059     private void setRotationAnimation() {
   1060         int rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
   1061         rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE;
   1062         Window win = getWindow();
   1063         WindowManager.LayoutParams winParams = win.getAttributes();
   1064         winParams.rotationAnimation = rotationAnimation;
   1065         win.setAttributes(winParams);
   1066     }
   1067 
   1068     @Override
   1069     public void onUserInteraction() {
   1070         super.onUserInteraction();
   1071         mCurrentModule.onUserInteraction();
   1072     }
   1073 
   1074     @Override
   1075     public boolean dispatchTouchEvent(MotionEvent ev) {
   1076         boolean result = super.dispatchTouchEvent(ev);
   1077         if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
   1078             // Real deletion is postponed until the next user interaction after
   1079             // the gesture that triggers deletion. Until real deletion is performed,
   1080             // users can click the undo button to bring back the image that they
   1081             // chose to delete.
   1082             if (mPendingDeletion && !mIsUndoingDeletion) {
   1083                  performDeletion();
   1084             }
   1085         }
   1086         return result;
   1087     }
   1088 
   1089     @Override
   1090     public void onPause() {
   1091         // Delete photos that are pending deletion
   1092         performDeletion();
   1093         mOrientationListener.disable();
   1094         mCurrentModule.onPauseBeforeSuper();
   1095         super.onPause();
   1096         mCurrentModule.onPauseAfterSuper();
   1097 
   1098         mLocalImagesObserver.setActivityPaused(true);
   1099         mLocalVideosObserver.setActivityPaused(true);
   1100     }
   1101 
   1102     @Override
   1103     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
   1104         if (requestCode == REQ_CODE_DONT_SWITCH_TO_PREVIEW) {
   1105             mResetToPreviewOnResume = false;
   1106         } else {
   1107             super.onActivityResult(requestCode, resultCode, data);
   1108         }
   1109     }
   1110 
   1111     @Override
   1112     public void onResume() {
   1113         // TODO: Handle this in OrientationManager.
   1114         // Auto-rotate off
   1115         if (Settings.System.getInt(getContentResolver(),
   1116                 Settings.System.ACCELEROMETER_ROTATION, 0) == 0) {
   1117             setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
   1118             mAutoRotateScreen = false;
   1119         } else {
   1120             setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
   1121             mAutoRotateScreen = true;
   1122         }
   1123         mOrientationListener.enable();
   1124         mCurrentModule.onResumeBeforeSuper();
   1125         super.onResume();
   1126         mCurrentModule.onResumeAfterSuper();
   1127 
   1128         setSwipingEnabled(true);
   1129 
   1130         if (mResetToPreviewOnResume) {
   1131             // Go to the preview on resume.
   1132             mFilmStripView.getController().goToFirstItem();
   1133         }
   1134         // Default is showing the preview, unless disabled by explicitly
   1135         // starting an activity we want to return from to the filmstrip rather
   1136         // than the preview.
   1137         mResetToPreviewOnResume = true;
   1138 
   1139         if (mLocalVideosObserver.isMediaDataChangedDuringPause()
   1140                 || mLocalImagesObserver.isMediaDataChangedDuringPause()) {
   1141             if (!mSecureCamera) {
   1142                 // If it's secure camera, requestLoad() should not be called
   1143                 // as it will load all the data.
   1144                 mDataAdapter.requestLoad(getContentResolver());
   1145             }
   1146         }
   1147         mLocalImagesObserver.setActivityPaused(false);
   1148         mLocalVideosObserver.setActivityPaused(false);
   1149     }
   1150 
   1151     @Override
   1152     public void onStart() {
   1153         super.onStart();
   1154         bindMediaSaveService();
   1155         mPanoramaViewHelper.onStart();
   1156     }
   1157 
   1158     @Override
   1159     protected void onStop() {
   1160         super.onStop();
   1161         mPanoramaViewHelper.onStop();
   1162         unbindMediaSaveService();
   1163     }
   1164 
   1165     @Override
   1166     public void onDestroy() {
   1167         if (mSecureCamera) {
   1168             unregisterReceiver(mScreenOffReceiver);
   1169         }
   1170         getContentResolver().unregisterContentObserver(mLocalImagesObserver);
   1171         getContentResolver().unregisterContentObserver(mLocalVideosObserver);
   1172 
   1173         super.onDestroy();
   1174     }
   1175 
   1176     @Override
   1177     public void onConfigurationChanged(Configuration config) {
   1178         super.onConfigurationChanged(config);
   1179         mCurrentModule.onConfigurationChanged(config);
   1180     }
   1181 
   1182     @Override
   1183     public boolean onKeyDown(int keyCode, KeyEvent event) {
   1184         if (mFilmStripView.inCameraFullscreen()) {
   1185             if (mCurrentModule.onKeyDown(keyCode, event)) {
   1186                 return true;
   1187             }
   1188             // Prevent software keyboard or voice search from showing up.
   1189             if (keyCode == KeyEvent.KEYCODE_SEARCH
   1190                     || keyCode == KeyEvent.KEYCODE_MENU) {
   1191                 if (event.isLongPress()) {
   1192                     return true;
   1193                 }
   1194             }
   1195         }
   1196 
   1197         return super.onKeyDown(keyCode, event);
   1198     }
   1199 
   1200     @Override
   1201     public boolean onKeyUp(int keyCode, KeyEvent event) {
   1202         if (mFilmStripView.inCameraFullscreen() && mCurrentModule.onKeyUp(keyCode, event)) {
   1203             return true;
   1204         }
   1205         return super.onKeyUp(keyCode, event);
   1206     }
   1207 
   1208     @Override
   1209     public void onBackPressed() {
   1210         if (!mFilmStripView.inCameraFullscreen()) {
   1211             mFilmStripView.getController().goToFirstItem();
   1212         } else if (!mCurrentModule.onBackPressed()) {
   1213             super.onBackPressed();
   1214         }
   1215     }
   1216 
   1217     public boolean isAutoRotateScreen() {
   1218         return mAutoRotateScreen;
   1219     }
   1220 
   1221     protected void updateStorageSpace() {
   1222         mStorageSpaceBytes = Storage.getAvailableSpace();
   1223     }
   1224 
   1225     protected long getStorageSpaceBytes() {
   1226         return mStorageSpaceBytes;
   1227     }
   1228 
   1229     protected void updateStorageSpaceAndHint() {
   1230         updateStorageSpace();
   1231         updateStorageHint(mStorageSpaceBytes);
   1232     }
   1233 
   1234     protected void updateStorageHint(long storageSpace) {
   1235         String message = null;
   1236         if (storageSpace == Storage.UNAVAILABLE) {
   1237             message = getString(R.string.no_storage);
   1238         } else if (storageSpace == Storage.PREPARING) {
   1239             message = getString(R.string.preparing_sd);
   1240         } else if (storageSpace == Storage.UNKNOWN_SIZE) {
   1241             message = getString(R.string.access_sd_fail);
   1242         } else if (storageSpace <= Storage.LOW_STORAGE_THRESHOLD_BYTES) {
   1243             message = getString(R.string.spaceIsLow_content);
   1244         }
   1245 
   1246         if (message != null) {
   1247             if (mStorageHint == null) {
   1248                 mStorageHint = OnScreenHint.makeText(this, message);
   1249             } else {
   1250                 mStorageHint.setText(message);
   1251             }
   1252             mStorageHint.show();
   1253         } else if (mStorageHint != null) {
   1254             mStorageHint.cancel();
   1255             mStorageHint = null;
   1256         }
   1257     }
   1258 
   1259     protected void setResultEx(int resultCode) {
   1260         mResultCodeForTesting = resultCode;
   1261         setResult(resultCode);
   1262     }
   1263 
   1264     protected void setResultEx(int resultCode, Intent data) {
   1265         mResultCodeForTesting = resultCode;
   1266         mResultDataForTesting = data;
   1267         setResult(resultCode, data);
   1268     }
   1269 
   1270     public int getResultCode() {
   1271         return mResultCodeForTesting;
   1272     }
   1273 
   1274     public Intent getResultData() {
   1275         return mResultDataForTesting;
   1276     }
   1277 
   1278     public boolean isSecureCamera() {
   1279         return mSecureCamera;
   1280     }
   1281 
   1282     @Override
   1283     public void onModuleSelected(int moduleIndex) {
   1284         if (mCurrentModuleIndex == moduleIndex) {
   1285             return;
   1286         }
   1287 
   1288         CameraHolder.instance().keep();
   1289         closeModule(mCurrentModule);
   1290         setModuleFromIndex(moduleIndex);
   1291 
   1292         openModule(mCurrentModule);
   1293         mCurrentModule.onOrientationChanged(mLastRawOrientation);
   1294         if (mMediaSaveService != null) {
   1295             mCurrentModule.onMediaSaveServiceConnected(mMediaSaveService);
   1296         }
   1297 
   1298         // Store the module index so we can use it the next time the Camera
   1299         // starts up.
   1300         SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
   1301         prefs.edit().putInt(CameraSettings.KEY_STARTUP_MODULE_INDEX, moduleIndex).apply();
   1302     }
   1303 
   1304     /**
   1305      * Sets the mCurrentModuleIndex, creates a new module instance for the given
   1306      * index an sets it as mCurrentModule.
   1307      */
   1308     private void setModuleFromIndex(int moduleIndex) {
   1309         mCurrentModuleIndex = moduleIndex;
   1310         switch (moduleIndex) {
   1311             case ModuleSwitcher.VIDEO_MODULE_INDEX:
   1312                 mCurrentModule = new VideoModule();
   1313                 break;
   1314 
   1315             case ModuleSwitcher.PHOTO_MODULE_INDEX:
   1316                 mCurrentModule = new PhotoModule();
   1317                 break;
   1318 
   1319             case ModuleSwitcher.WIDE_ANGLE_PANO_MODULE_INDEX:
   1320                 mCurrentModule = new WideAnglePanoramaModule();
   1321                 break;
   1322 
   1323             case ModuleSwitcher.LIGHTCYCLE_MODULE_INDEX:
   1324                 mCurrentModule = PhotoSphereHelper.createPanoramaModule();
   1325                 break;
   1326             case ModuleSwitcher.GCAM_MODULE_INDEX:
   1327                 // Force immediate release of Camera instance
   1328                 CameraHolder.instance().strongRelease();
   1329                 mCurrentModule = GcamHelper.createGcamModule();
   1330                 break;
   1331             default:
   1332                 // Fall back to photo mode.
   1333                 mCurrentModule = new PhotoModule();
   1334                 mCurrentModuleIndex = ModuleSwitcher.PHOTO_MODULE_INDEX;
   1335                 break;
   1336         }
   1337     }
   1338 
   1339     /**
   1340      * Launches an ACTION_EDIT intent for the given local data item.
   1341      */
   1342     public void launchEditor(LocalData data) {
   1343         Intent intent = new Intent(Intent.ACTION_EDIT)
   1344                 .setDataAndType(data.getContentUri(), data.getMimeType())
   1345                 .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
   1346         startActivityForResult(Intent.createChooser(intent, null),
   1347                 REQ_CODE_DONT_SWITCH_TO_PREVIEW);
   1348     }
   1349 
   1350     /**
   1351      * Launch the tiny planet editor.
   1352      *
   1353      * @param data the data must be a 360 degree stereographically mapped
   1354      *            panoramic image. It will not be modified, instead a new item
   1355      *            with the result will be added to the filmstrip.
   1356      */
   1357     public void launchTinyPlanetEditor(LocalData data) {
   1358         TinyPlanetFragment fragment = new TinyPlanetFragment();
   1359         Bundle bundle = new Bundle();
   1360         bundle.putString(TinyPlanetFragment.ARGUMENT_URI, data.getContentUri().toString());
   1361         bundle.putString(TinyPlanetFragment.ARGUMENT_TITLE, data.getTitle());
   1362         fragment.setArguments(bundle);
   1363         fragment.show(getFragmentManager(), "tiny_planet");
   1364     }
   1365 
   1366     private void openModule(CameraModule module) {
   1367         module.init(this, mCameraModuleRootView);
   1368         module.onResumeBeforeSuper();
   1369         module.onResumeAfterSuper();
   1370     }
   1371 
   1372     private void closeModule(CameraModule module) {
   1373         module.onPauseBeforeSuper();
   1374         module.onPauseAfterSuper();
   1375         ((ViewGroup) mCameraModuleRootView).removeAllViews();
   1376     }
   1377 
   1378     private void performDeletion() {
   1379         if (!mPendingDeletion) {
   1380             return;
   1381         }
   1382         hideUndoDeletionBar(false);
   1383         mDataAdapter.executeDeletion(CameraActivity.this);
   1384         updateActionBarMenu(mFilmStripView.getCurrentId());
   1385     }
   1386 
   1387     public void showUndoDeletionBar() {
   1388         if (mPendingDeletion) {
   1389             performDeletion();
   1390         }
   1391         Log.v(TAG, "showing undo bar");
   1392         mPendingDeletion = true;
   1393         if (mUndoDeletionBar == null) {
   1394             ViewGroup v = (ViewGroup) getLayoutInflater().inflate(
   1395                     R.layout.undo_bar, mAboveFilmstripControlLayout, true);
   1396             mUndoDeletionBar = (ViewGroup) v.findViewById(R.id.camera_undo_deletion_bar);
   1397             View button = mUndoDeletionBar.findViewById(R.id.camera_undo_deletion_button);
   1398             button.setOnClickListener(new View.OnClickListener() {
   1399                 @Override
   1400                 public void onClick(View view) {
   1401                     mDataAdapter.undoDataRemoval();
   1402                     hideUndoDeletionBar(true);
   1403                 }
   1404             });
   1405             // Setting undo bar clickable to avoid touch events going through
   1406             // the bar to the buttons (eg. edit button, etc) underneath the bar.
   1407             mUndoDeletionBar.setClickable(true);
   1408             // When there is user interaction going on with the undo button, we
   1409             // do not want to hide the undo bar.
   1410             button.setOnTouchListener(new View.OnTouchListener() {
   1411                 @Override
   1412                 public boolean onTouch(View v, MotionEvent event) {
   1413                     if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
   1414                         mIsUndoingDeletion = true;
   1415                     } else if (event.getActionMasked() == MotionEvent.ACTION_UP) {
   1416                         mIsUndoingDeletion =false;
   1417                     }
   1418                     return false;
   1419                 }
   1420             });
   1421         }
   1422         mUndoDeletionBar.setAlpha(0f);
   1423         mUndoDeletionBar.setVisibility(View.VISIBLE);
   1424         mUndoDeletionBar.animate().setDuration(200).alpha(1f).setListener(null).start();
   1425     }
   1426 
   1427     private void hideUndoDeletionBar(boolean withAnimation) {
   1428         Log.v(TAG, "Hiding undo deletion bar");
   1429         mPendingDeletion = false;
   1430         if (mUndoDeletionBar != null) {
   1431             if (withAnimation) {
   1432                 mUndoDeletionBar.animate()
   1433                         .setDuration(200)
   1434                         .alpha(0f)
   1435                         .setListener(new Animator.AnimatorListener() {
   1436                             @Override
   1437                             public void onAnimationStart(Animator animation) {
   1438                                 // Do nothing.
   1439                             }
   1440 
   1441                             @Override
   1442                             public void onAnimationEnd(Animator animation) {
   1443                                 mUndoDeletionBar.setVisibility(View.GONE);
   1444                             }
   1445 
   1446                             @Override
   1447                             public void onAnimationCancel(Animator animation) {
   1448                                 // Do nothing.
   1449                             }
   1450 
   1451                             @Override
   1452                             public void onAnimationRepeat(Animator animation) {
   1453                                 // Do nothing.
   1454                             }
   1455                         })
   1456                         .start();
   1457             } else {
   1458                 mUndoDeletionBar.setVisibility(View.GONE);
   1459             }
   1460         }
   1461     }
   1462 
   1463     @Override
   1464     public void onShowSwitcherPopup() {
   1465     }
   1466 
   1467     /**
   1468      * Enable/disable swipe-to-filmstrip. Will always disable swipe if in
   1469      * capture intent.
   1470      *
   1471      * @param enable {@code true} to enable swipe.
   1472      */
   1473     public void setSwipingEnabled(boolean enable) {
   1474         if (isCaptureIntent()) {
   1475             mCameraPreviewData.lockPreview(true);
   1476         } else {
   1477             mCameraPreviewData.lockPreview(!enable);
   1478         }
   1479     }
   1480 
   1481 
   1482     /**
   1483      * Check whether camera controls are visible.
   1484      *
   1485      * @return whether controls are visible.
   1486      */
   1487     private boolean arePreviewControlsVisible() {
   1488         return mCurrentModule.arePreviewControlsVisible();
   1489     }
   1490 
   1491     /**
   1492      * Show or hide the {@link CameraControls} using the current module's
   1493      * implementation of {@link #onPreviewFocusChanged}.
   1494      *
   1495      * @param showControls whether to show camera controls.
   1496      */
   1497     private void setPreviewControlsVisibility(boolean showControls) {
   1498         mCurrentModule.onPreviewFocusChanged(showControls);
   1499     }
   1500 
   1501     // Accessor methods for getting latency times used in performance testing
   1502     public long getAutoFocusTime() {
   1503         return (mCurrentModule instanceof PhotoModule) ?
   1504                 ((PhotoModule) mCurrentModule).mAutoFocusTime : -1;
   1505     }
   1506 
   1507     public long getShutterLag() {
   1508         return (mCurrentModule instanceof PhotoModule) ?
   1509                 ((PhotoModule) mCurrentModule).mShutterLag : -1;
   1510     }
   1511 
   1512     public long getShutterToPictureDisplayedTime() {
   1513         return (mCurrentModule instanceof PhotoModule) ?
   1514                 ((PhotoModule) mCurrentModule).mShutterToPictureDisplayedTime : -1;
   1515     }
   1516 
   1517     public long getPictureDisplayedToJpegCallbackTime() {
   1518         return (mCurrentModule instanceof PhotoModule) ?
   1519                 ((PhotoModule) mCurrentModule).mPictureDisplayedToJpegCallbackTime : -1;
   1520     }
   1521 
   1522     public long getJpegCallbackFinishTime() {
   1523         return (mCurrentModule instanceof PhotoModule) ?
   1524                 ((PhotoModule) mCurrentModule).mJpegCallbackFinishTime : -1;
   1525     }
   1526 
   1527     public long getCaptureStartTime() {
   1528         return (mCurrentModule instanceof PhotoModule) ?
   1529                 ((PhotoModule) mCurrentModule).mCaptureStartTime : -1;
   1530     }
   1531 
   1532     public boolean isRecording() {
   1533         return (mCurrentModule instanceof VideoModule) ?
   1534                 ((VideoModule) mCurrentModule).isRecording() : false;
   1535     }
   1536 
   1537     public CameraOpenErrorCallback getCameraOpenErrorCallback() {
   1538         return mCameraOpenErrorCallback;
   1539     }
   1540 
   1541     // For debugging purposes only.
   1542     public CameraModule getCurrentModule() {
   1543         return mCurrentModule;
   1544     }
   1545 }
   1546