CameraActivity.java revision 357b7673f03d9b45d281d585cc3f4f87dc7d727b
1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.camera;
18
19import android.animation.Animator;
20import android.annotation.TargetApi;
21import android.app.ActionBar;
22import android.app.Activity;
23import android.content.ActivityNotFoundException;
24import android.content.BroadcastReceiver;
25import android.content.ContentResolver;
26import android.content.Context;
27import android.content.Intent;
28import android.content.IntentFilter;
29import android.content.SharedPreferences;
30import android.content.pm.ActivityInfo;
31import android.content.res.Configuration;
32import android.graphics.Bitmap;
33import android.graphics.Color;
34import android.graphics.SurfaceTexture;
35import android.graphics.drawable.ColorDrawable;
36import android.net.Uri;
37import android.nfc.NfcAdapter;
38import android.nfc.NfcAdapter.CreateBeamUrisCallback;
39import android.nfc.NfcEvent;
40import android.os.AsyncTask;
41import android.os.Build;
42import android.os.Bundle;
43import android.os.Handler;
44import android.os.Looper;
45import android.os.Message;
46import android.preference.PreferenceManager;
47import android.provider.MediaStore;
48import android.provider.Settings;
49import android.util.Log;
50import android.view.Gravity;
51import android.view.KeyEvent;
52import android.view.LayoutInflater;
53import android.view.Menu;
54import android.view.MenuInflater;
55import android.view.MenuItem;
56import android.view.MotionEvent;
57import android.view.View;
58import android.view.ViewGroup;
59import android.view.Window;
60import android.view.WindowManager;
61import android.widget.FrameLayout;
62import android.widget.FrameLayout.LayoutParams;
63import android.widget.ImageView;
64import android.widget.PopupWindow;
65import android.widget.ProgressBar;
66import android.widget.ShareActionProvider;
67
68import com.android.camera.app.AppController;
69import com.android.camera.app.AppManagerFactory;
70import com.android.camera.app.CameraApp;
71import com.android.camera.app.CameraAppUI;
72import com.android.camera.app.CameraController;
73import com.android.camera.app.CameraManager;
74import com.android.camera.app.CameraManagerFactory;
75import com.android.camera.app.CameraProvider;
76import com.android.camera.app.CameraServices;
77import com.android.camera.app.ImageTaskManager;
78import com.android.camera.app.MediaSaver;
79import com.android.camera.app.ModuleManagerImpl;
80import com.android.camera.app.OrientationManager;
81import com.android.camera.app.OrientationManagerImpl;
82import com.android.camera.app.PanoramaStitchingManager;
83import com.android.camera.app.PlaceholderManager;
84import com.android.camera.crop.CropActivity;
85import com.android.camera.data.CameraDataAdapter;
86import com.android.camera.data.CameraPreviewData;
87import com.android.camera.data.FixedFirstDataAdapter;
88import com.android.camera.data.FixedLastDataAdapter;
89import com.android.camera.data.InProgressDataWrapper;
90import com.android.camera.data.LocalData;
91import com.android.camera.data.LocalDataAdapter;
92import com.android.camera.data.LocalMediaObserver;
93import com.android.camera.data.MediaDetails;
94import com.android.camera.data.SimpleViewData;
95import com.android.camera.filmstrip.FilmstripController;
96import com.android.camera.filmstrip.FilmstripImageData;
97import com.android.camera.filmstrip.FilmstripListener;
98import com.android.camera.module.ModuleController;
99import com.android.camera.module.ModulesInfo;
100import com.android.camera.settings.SettingsManager;
101import com.android.camera.settings.SettingsManager.SettingsCapabilities;
102import com.android.camera.settings.SettingsManager.StartupModuleSetting;
103import com.android.camera.tinyplanet.TinyPlanetFragment;
104import com.android.camera.ui.CameraControls;
105import com.android.camera.ui.DetailsDialog;
106import com.android.camera.ui.FilmstripView;
107import com.android.camera.ui.MainActivityLayout;
108import com.android.camera.ui.ModeListView;
109import com.android.camera.ui.SettingsView;
110import com.android.camera.util.ApiHelper;
111import com.android.camera.util.CameraUtil;
112import com.android.camera.util.GcamHelper;
113import com.android.camera.util.IntentHelper;
114import com.android.camera.util.PhotoSphereHelper.PanoramaViewHelper;
115import com.android.camera.util.UsageStatistics;
116import com.android.camera2.R;
117
118import java.io.File;
119import java.util.List;
120
121public class CameraActivity extends Activity
122        implements AppController, CameraManager.CameraOpenCallback,
123        ActionBar.OnMenuVisibilityListener, ShareActionProvider.OnShareTargetSelectedListener,
124        OrientationManager.OnOrientationChangeListener {
125
126    private static final String TAG = "CAM_Activity";
127
128    private static final String INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE =
129            "android.media.action.STILL_IMAGE_CAMERA_SECURE";
130    public static final String ACTION_IMAGE_CAPTURE_SECURE =
131            "android.media.action.IMAGE_CAPTURE_SECURE";
132    public static final String ACTION_TRIM_VIDEO =
133            "com.android.camera.action.TRIM";
134    public static final String MEDIA_ITEM_PATH = "media-item-path";
135
136    // The intent extra for camera from secure lock screen. True if the gallery
137    // should only show newly captured pictures. sSecureAlbumId does not
138    // increment. This is used when switching between camera, camcorder, and
139    // panorama. If the extra is not set, it is in the normal camera mode.
140    public static final String SECURE_CAMERA_EXTRA = "secure_camera";
141
142    /**
143     * Request code from an activity we started that indicated that we do not want
144     * to reset the view to the preview in onResume.
145     */
146    public static final int REQ_CODE_DONT_SWITCH_TO_PREVIEW = 142;
147
148    public static final int REQ_CODE_GCAM_DEBUG_POSTCAPTURE = 999;
149
150    private static final int HIDE_ACTION_BAR = 1;
151    private static final long SHOW_ACTION_BAR_TIMEOUT_MS = 3000;
152
153    /**
154     * Whether onResume should reset the view to the preview.
155     */
156    private boolean mResetToPreviewOnResume = true;
157
158    // Supported operations at FilmStripView. Different data has different
159    // set of supported operations.
160    private static final int SUPPORT_DELETE = 1 << 0;
161    private static final int SUPPORT_ROTATE = 1 << 1;
162    private static final int SUPPORT_INFO = 1 << 2;
163    private static final int SUPPORT_CROP = 1 << 3;
164    private static final int SUPPORT_SETAS = 1 << 4;
165    private static final int SUPPORT_EDIT = 1 << 5;
166    private static final int SUPPORT_TRIM = 1 << 6;
167    private static final int SUPPORT_SHARE = 1 << 7;
168    private static final int SUPPORT_SHARE_PANORAMA360 = 1 << 8;
169    private static final int SUPPORT_SHOW_ON_MAP = 1 << 9;
170    private static final int SUPPORT_ALL = 0xffffffff;
171
172    /**
173     * This data adapter is used by FilmStripView.
174     */
175    private LocalDataAdapter mDataAdapter;
176    /**
177     * This data adapter represents the real local camera data.
178     */
179    private LocalDataAdapter mWrappedDataAdapter;
180
181    private SettingsManager mSettingsManager;
182    private SettingsController mSettingsController;
183    private PanoramaStitchingManager mPanoramaManager;
184    private PlaceholderManager mPlaceholderManager;
185    private ModeListView mModeListView;
186    private int mCurrentModeIndex;
187    private CameraModule mCurrentModule;
188    private ModuleController mCurrentModule2;
189    private ModuleManagerImpl mModuleManager;
190    private FrameLayout mAboveFilmstripControlLayout;
191    private FrameLayout mCameraModuleRootView;
192    private FilmstripController mFilmstripController;
193    private ProgressBar mBottomProgress;
194    private View mPanoStitchingPanel;
195    private int mResultCodeForTesting;
196    private Intent mResultDataForTesting;
197    private OnScreenHint mStorageHint;
198    private long mStorageSpaceBytes = Storage.LOW_STORAGE_THRESHOLD_BYTES;
199    private boolean mAutoRotateScreen;
200    private boolean mSecureCamera;
201    private int mLastRawOrientation;
202    private OrientationManagerImpl mOrientationManager;
203    private LocationManager mLocationManager;
204    private Handler mMainHandler;
205    private PanoramaViewHelper mPanoramaViewHelper;
206    private CameraPreviewData mCameraPreviewData;
207    private ActionBar mActionBar;
208    private OnActionBarVisibilityListener mOnActionBarVisibilityListener = null;
209    private Menu mActionBarMenu;
210    private ViewGroup mUndoDeletionBar;
211    private boolean mIsUndoingDeletion = false;
212
213    private final Uri[] mNfcPushUris = new Uri[1];
214
215    private ShareActionProvider mStandardShareActionProvider;
216    private Intent mStandardShareIntent;
217    private ShareActionProvider mPanoramaShareActionProvider;
218    private Intent mPanoramaShareIntent;
219    private LocalMediaObserver mLocalImagesObserver;
220    private LocalMediaObserver mLocalVideosObserver;
221
222    private final int DEFAULT_SYSTEM_UI_VISIBILITY = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
223            | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
224    private boolean mPendingDeletion = false;
225
226    private Intent mVideoShareIntent;
227    private Intent mImageShareIntent;
228
229    private CameraController mCameraController;
230    private boolean mPaused;
231    private CameraAppUI mCameraAppUI;
232
233    private MediaSaver mMediaSaver;
234
235
236    // close activity when screen turns off
237    private final BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() {
238        @Override
239        public void onReceive(Context context, Intent intent) {
240            finish();
241        }
242    };
243
244    private static BroadcastReceiver sScreenOffReceiver;
245
246    @Override
247    public void onCameraOpened(CameraManager.CameraProxy camera) {
248        if (!mModuleManager.getModuleAgent(mCurrentModeIndex).requestAppForCamera()) {
249            // We shouldn't be here. Just close the camera and leave.
250            camera.release(false);
251            throw new IllegalStateException("Camera opened but the module shouldn't be " +
252                    "requesting");
253        }
254        if (mCurrentModule2 != null) {
255            SettingsCapabilities capabilities =
256                SettingsController.getSettingsCapabilities(camera);
257            mSettingsManager.changeCamera(camera.getCameraId(), capabilities);
258            mCurrentModule2.onCameraAvailable(camera);
259        }
260    }
261
262    @Override
263    public void onCameraDisabled(int cameraId) {
264        UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA, UsageStatistics.ACTION_OPEN_FAIL,
265                "security");
266
267        CameraUtil.showErrorAndFinish(this, R.string.camera_disabled);
268    }
269
270    @Override
271    public void onDeviceOpenFailure(int cameraId) {
272        UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
273                UsageStatistics.ACTION_OPEN_FAIL, "open");
274
275        CameraUtil.showErrorAndFinish(this, R.string.cannot_connect_camera);
276    }
277
278    @Override
279    public void onReconnectionFailure(CameraManager mgr) {
280        UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
281                UsageStatistics.ACTION_OPEN_FAIL, "reconnect");
282
283        CameraUtil.showErrorAndFinish(this, R.string.cannot_connect_camera);
284    }
285
286    private class MainHandler extends Handler {
287        public MainHandler(Looper looper) {
288            super(looper);
289        }
290
291        @Override
292        public void handleMessage(Message msg) {
293            if (msg.what == HIDE_ACTION_BAR) {
294                removeMessages(HIDE_ACTION_BAR);
295                CameraActivity.this.setSystemBarsVisibility(false);
296            }
297        }
298    }
299
300    public interface OnActionBarVisibilityListener {
301        public void onActionBarVisibilityChanged(boolean isVisible);
302    }
303
304    public void setOnActionBarVisibilityListener(OnActionBarVisibilityListener listener) {
305        mOnActionBarVisibilityListener = listener;
306    }
307
308    private String fileNameFromDataID(int dataID) {
309        final LocalData localData = mDataAdapter.getLocalData(dataID);
310
311        File localFile = new File(localData.getPath());
312        return localFile.getName();
313    }
314
315    private final FilmstripListener mFilmStripListener =
316            new FilmstripListener() {
317                @Override
318                public void onDataPromoted(int dataID) {
319                    UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
320                            UsageStatistics.ACTION_DELETE, "promoted", 0,
321                            UsageStatistics.hashFileName(fileNameFromDataID(dataID)));
322
323                    removeData(dataID);
324                }
325
326                @Override
327                public void onDataDemoted(int dataID) {
328                    UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
329                            UsageStatistics.ACTION_DELETE, "demoted", 0,
330                            UsageStatistics.hashFileName(fileNameFromDataID(dataID)));
331
332                    removeData(dataID);
333                }
334
335                @Override
336                public void onDataFullScreenChange(int dataID, boolean full) {
337                    boolean isCameraID = isCameraPreview(dataID);
338                    if (!isCameraID) {
339                        if (!full) {
340                            // Always show action bar in filmstrip mode
341                            CameraActivity.this.setSystemBarsVisibility(true, false);
342                        } else if (mActionBar.isShowing()) {
343                            // Hide action bar after time out in full screen mode
344                            mMainHandler.sendEmptyMessageDelayed(HIDE_ACTION_BAR,
345                                    SHOW_ACTION_BAR_TIMEOUT_MS);
346                        }
347                    }
348                }
349
350                /**
351                 * Check if the local data corresponding to dataID is the camera
352                 * preview.
353                 *
354                 * @param dataID the ID of the local data
355                 * @return true if the local data is not null and it is the
356                 *         camera preview.
357                 */
358                private boolean isCameraPreview(int dataID) {
359                    LocalData localData = mDataAdapter.getLocalData(dataID);
360                    if (localData == null) {
361                        Log.w(TAG, "Current data ID not found.");
362                        return false;
363                    }
364                    return localData.getLocalDataType() == LocalData.LOCAL_CAMERA_PREVIEW;
365                }
366
367                @Override
368                public void onDataReloaded() {
369                    setPreviewControlsVisibility(true);
370                    CameraActivity.this.setSystemBarsVisibility(false);
371                }
372
373                @Override
374                public void onCurrentDataCentered(int dataID) {
375                    if (dataID != 0 && !mFilmstripController.isCameraPreview()) {
376                        // For now, We ignore all items that are not the camera preview.
377                        return;
378                    }
379
380                    if (!arePreviewControlsVisible()) {
381                        setPreviewControlsVisibility(true);
382                        CameraActivity.this.setSystemBarsVisibility(false);
383                    }
384                }
385
386                @Override
387                public void onCurrentDataOffCentered(int dataID) {
388                    if (dataID != 0 && !mFilmstripController.isCameraPreview()) {
389                        // For now, We ignore all items that are not the camera preview.
390                        return;
391                    }
392
393                    if (arePreviewControlsVisible()) {
394                        setPreviewControlsVisibility(false);
395                    }
396                }
397
398                @Override
399                public void onDataFocusChanged(final int dataID, final boolean focused) {
400                    // Delay hiding action bar if there is any user interaction
401                    if (mMainHandler.hasMessages(HIDE_ACTION_BAR)) {
402                        mMainHandler.removeMessages(HIDE_ACTION_BAR);
403                        mMainHandler.sendEmptyMessageDelayed(HIDE_ACTION_BAR,
404                                SHOW_ACTION_BAR_TIMEOUT_MS);
405                    }
406                    // TODO: This callback is UI event callback, should always
407                    // happen on UI thread. Find the reason for this
408                    // runOnUiThread() and fix it.
409                    runOnUiThread(new Runnable() {
410                        @Override
411                        public void run() {
412                            LocalData currentData = mDataAdapter.getLocalData(dataID);
413                            if (currentData == null) {
414                                Log.w(TAG, "Current data ID not found.");
415                                hidePanoStitchingProgress();
416                                return;
417                            }
418                            boolean isCameraID = currentData.getLocalDataType() ==
419                                    LocalData.LOCAL_CAMERA_PREVIEW;
420                            if (!focused) {
421                                if (isCameraID) {
422                                    mCurrentModule.onPreviewFocusChanged(false);
423                                    CameraActivity.this.setSystemBarsVisibility(true);
424                                }
425                                hidePanoStitchingProgress();
426                            } else {
427                                if (isCameraID) {
428                                    // Don't show the action bar in Camera
429                                    // preview.
430                                    CameraActivity.this.setSystemBarsVisibility(false);
431
432                                    if (mPendingDeletion) {
433                                        performDeletion();
434                                    }
435                                } else {
436                                    updateActionBarMenu(dataID);
437                                }
438
439                                Uri contentUri = currentData.getContentUri();
440                                if (contentUri == null) {
441                                    hidePanoStitchingProgress();
442                                    return;
443                                }
444                                int panoStitchingProgress = mPanoramaManager.getTaskProgress(contentUri);
445                                if (panoStitchingProgress < 0) {
446                                    hidePanoStitchingProgress();
447                                    return;
448                                }
449                                showPanoStitchingProgress();
450                                updateStitchingProgress(panoStitchingProgress);
451                            }
452                        }
453                    });
454                }
455
456                @Override
457                public void onToggleSystemDecorsVisibility(int dataID) {
458                    // If action bar is showing, hide it immediately, otherwise
459                    // show action bar and hide it later
460                    if (mActionBar.isShowing()) {
461                        CameraActivity.this.setSystemBarsVisibility(false);
462                    } else {
463                        // Don't show the action bar if that is the camera preview.
464                        boolean isCameraID = isCameraPreview(dataID);
465                        if (!isCameraID) {
466                            CameraActivity.this.setSystemBarsVisibility(true, true);
467                        }
468                    }
469                }
470
471                @Override
472                public void setSystemDecorsVisibility(boolean visible) {
473                    CameraActivity.this.setSystemBarsVisibility(visible);
474                }
475            };
476
477    public void gotoGallery() {
478        UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA, UsageStatistics.ACTION_FILMSTRIP,
479                "thumbnailTap");
480
481        mFilmstripController.goToNextItem();
482    }
483
484    /**
485     * If {@param visible} is false, this hides the action bar and switches the system UI
486     * to lights-out mode.
487     */
488    // TODO: This should not be called outside of the activity.
489    public void setSystemBarsVisibility(boolean visible) {
490        setSystemBarsVisibility(visible, false);
491    }
492
493    /**
494     * If {@param visible} is false, this hides the action bar and switches the
495     * system UI to lights-out mode. If {@param hideLater} is true, a delayed message
496     * will be sent after a timeout to hide the action bar.
497     */
498    private void setSystemBarsVisibility(boolean visible, boolean hideLater) {
499        mMainHandler.removeMessages(HIDE_ACTION_BAR);
500
501        int currentSystemUIVisibility = mAboveFilmstripControlLayout.getSystemUiVisibility();
502        int newSystemUIVisibility = DEFAULT_SYSTEM_UI_VISIBILITY |
503                (visible ? View.SYSTEM_UI_FLAG_VISIBLE :
504                        View.SYSTEM_UI_FLAG_LOW_PROFILE | View.SYSTEM_UI_FLAG_FULLSCREEN);
505        if (newSystemUIVisibility != currentSystemUIVisibility) {
506            mAboveFilmstripControlLayout.setSystemUiVisibility(newSystemUIVisibility);
507        }
508
509        boolean currentActionBarVisibility = mActionBar.isShowing();
510        if (visible != currentActionBarVisibility) {
511            if (visible) {
512                mActionBar.show();
513            } else {
514                mActionBar.hide();
515            }
516            if (mOnActionBarVisibilityListener != null) {
517                mOnActionBarVisibilityListener.onActionBarVisibilityChanged(visible);
518            }
519        }
520
521        // Now delay hiding the bars
522        if (visible && hideLater) {
523            mMainHandler.sendEmptyMessageDelayed(HIDE_ACTION_BAR, SHOW_ACTION_BAR_TIMEOUT_MS);
524        }
525    }
526
527    private void hidePanoStitchingProgress() {
528        mPanoStitchingPanel.setVisibility(View.GONE);
529    }
530
531    private void showPanoStitchingProgress() {
532        mPanoStitchingPanel.setVisibility(View.VISIBLE);
533    }
534
535    private void updateStitchingProgress(int progress) {
536        mBottomProgress.setProgress(progress);
537    }
538
539    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
540    private void setupNfcBeamPush() {
541        NfcAdapter adapter = NfcAdapter.getDefaultAdapter(CameraActivity.this);
542        if (adapter == null) {
543            return;
544        }
545
546        if (!ApiHelper.HAS_SET_BEAM_PUSH_URIS) {
547            // Disable beaming
548            adapter.setNdefPushMessage(null, CameraActivity.this);
549            return;
550        }
551
552        adapter.setBeamPushUris(null, CameraActivity.this);
553        adapter.setBeamPushUrisCallback(new CreateBeamUrisCallback() {
554            @Override
555            public Uri[] createBeamUris(NfcEvent event) {
556                return mNfcPushUris;
557            }
558        }, CameraActivity.this);
559    }
560
561    private void setNfcBeamPushUri(Uri uri) {
562        mNfcPushUris[0] = uri;
563    }
564
565    private void setStandardShareIntent(Uri contentUri, String mimeType) {
566        mStandardShareIntent = getShareIntentFromType(mimeType);
567        if (mStandardShareIntent != null) {
568            mStandardShareIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
569            mStandardShareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
570            if (mStandardShareActionProvider != null) {
571                mStandardShareActionProvider.setShareIntent(mStandardShareIntent);
572            }
573        }
574    }
575
576    /**
577     * Get the share intent according to the mimeType
578     *
579     * @param mimeType The mimeType of current data.
580     * @return the video/image's ShareIntent or null if mimeType is invalid.
581     */
582    private Intent getShareIntentFromType(String mimeType) {
583        // Lazily create the intent object.
584        if (mimeType.startsWith("video/")) {
585            if (mVideoShareIntent == null) {
586                mVideoShareIntent = new Intent(Intent.ACTION_SEND);
587                mVideoShareIntent.setType("video/*");
588            }
589            return mVideoShareIntent;
590        } else if (mimeType.startsWith("image/")) {
591            if (mImageShareIntent == null) {
592                mImageShareIntent = new Intent(Intent.ACTION_SEND);
593                mImageShareIntent.setType("image/*");
594            }
595            return mImageShareIntent;
596        }
597        Log.w(TAG, "unsupported mimeType " + mimeType);
598        return null;
599    }
600
601    private void setPanoramaShareIntent(Uri contentUri) {
602        if (mPanoramaShareIntent == null) {
603            mPanoramaShareIntent = new Intent(Intent.ACTION_SEND);
604        }
605        mPanoramaShareIntent.setType("application/vnd.google.panorama360+jpg");
606        mPanoramaShareIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
607        if (mPanoramaShareActionProvider != null) {
608            mPanoramaShareActionProvider.setShareIntent(mPanoramaShareIntent);
609        }
610    }
611
612    @Override
613    public void onMenuVisibilityChanged(boolean isVisible) {
614        // If menu is showing, we need to make sure action bar does not go away.
615        mMainHandler.removeMessages(HIDE_ACTION_BAR);
616        if (!isVisible) {
617            mMainHandler.sendEmptyMessageDelayed(HIDE_ACTION_BAR, SHOW_ACTION_BAR_TIMEOUT_MS);
618        }
619    }
620
621    @Override
622    public boolean onShareTargetSelected(ShareActionProvider shareActionProvider, Intent intent) {
623        int currentDataId = mFilmstripController.getCurrentId();
624        if (currentDataId < 0) {
625            return false;
626        }
627        UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA, UsageStatistics.ACTION_SHARE,
628                intent.getComponent().getPackageName(), 0,
629                UsageStatistics.hashFileName(fileNameFromDataID(currentDataId)));
630        return true;
631    }
632
633    /**
634     * According to the data type, make the menu items for supported operations
635     * visible.
636     *
637     * @param dataID the data ID of the current item.
638     */
639    private void updateActionBarMenu(int dataID) {
640        LocalData currentData = mDataAdapter.getLocalData(dataID);
641        if (currentData == null) {
642            return;
643        }
644        int type = currentData.getLocalDataType();
645
646        if (mActionBarMenu == null) {
647            return;
648        }
649
650        int supported = 0;
651
652        switch (type) {
653            case LocalData.LOCAL_IMAGE:
654                supported |= SUPPORT_DELETE | SUPPORT_ROTATE | SUPPORT_INFO
655                        | SUPPORT_CROP | SUPPORT_SETAS | SUPPORT_EDIT
656                        | SUPPORT_SHARE | SUPPORT_SHOW_ON_MAP;
657                break;
658            case LocalData.LOCAL_VIDEO:
659                supported |= SUPPORT_DELETE | SUPPORT_INFO | SUPPORT_TRIM
660                        | SUPPORT_SHARE;
661                break;
662            case LocalData.LOCAL_PHOTO_SPHERE:
663                supported |= SUPPORT_DELETE | SUPPORT_ROTATE | SUPPORT_INFO
664                        | SUPPORT_CROP | SUPPORT_SETAS | SUPPORT_EDIT
665                        | SUPPORT_SHARE | SUPPORT_SHOW_ON_MAP;
666                break;
667            case LocalData.LOCAL_360_PHOTO_SPHERE:
668                supported |= SUPPORT_DELETE | SUPPORT_ROTATE | SUPPORT_INFO
669                        | SUPPORT_CROP | SUPPORT_SETAS | SUPPORT_EDIT
670                        | SUPPORT_SHARE | SUPPORT_SHARE_PANORAMA360
671                        | SUPPORT_SHOW_ON_MAP;
672                break;
673            default:
674                break;
675        }
676
677        // In secure camera mode, we only support delete operation.
678        if (isSecureCamera()) {
679            supported &= SUPPORT_DELETE;
680        }
681
682        setMenuItemVisible(mActionBarMenu, R.id.action_delete,
683                (supported & SUPPORT_DELETE) != 0);
684        setMenuItemVisible(mActionBarMenu, R.id.action_rotate_ccw,
685                (supported & SUPPORT_ROTATE) != 0);
686        setMenuItemVisible(mActionBarMenu, R.id.action_rotate_cw,
687                (supported & SUPPORT_ROTATE) != 0);
688        setMenuItemVisible(mActionBarMenu, R.id.action_details,
689                (supported & SUPPORT_INFO) != 0);
690        setMenuItemVisible(mActionBarMenu, R.id.action_crop,
691                (supported & SUPPORT_CROP) != 0);
692        setMenuItemVisible(mActionBarMenu, R.id.action_setas,
693                (supported & SUPPORT_SETAS) != 0);
694        setMenuItemVisible(mActionBarMenu, R.id.action_edit,
695                (supported & SUPPORT_EDIT) != 0);
696        setMenuItemVisible(mActionBarMenu, R.id.action_trim,
697                (supported & SUPPORT_TRIM) != 0);
698
699        boolean standardShare = (supported & SUPPORT_SHARE) != 0;
700        boolean panoramaShare = (supported & SUPPORT_SHARE_PANORAMA360) != 0;
701        setMenuItemVisible(mActionBarMenu, R.id.action_share, standardShare);
702        setMenuItemVisible(mActionBarMenu, R.id.action_share_panorama, panoramaShare);
703
704        if (panoramaShare) {
705            // For 360 PhotoSphere, relegate standard share to the overflow menu
706            MenuItem item = mActionBarMenu.findItem(R.id.action_share);
707            if (item != null) {
708                item.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
709                item.setTitle(getResources().getString(R.string.share_as_photo));
710            }
711            // And, promote "share as panorama" to action bar
712            item = mActionBarMenu.findItem(R.id.action_share_panorama);
713            if (item != null) {
714                item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
715            }
716            setPanoramaShareIntent(currentData.getContentUri());
717        }
718        if (standardShare) {
719            if (!panoramaShare) {
720                MenuItem item = mActionBarMenu.findItem(R.id.action_share);
721                if (item != null) {
722                    item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
723                    item.setTitle(getResources().getString(R.string.share));
724                }
725            }
726            setStandardShareIntent(currentData.getContentUri(), currentData.getMimeType());
727            setNfcBeamPushUri(currentData.getContentUri());
728        }
729
730        boolean itemHasLocation = currentData.getLatLong() != null;
731        setMenuItemVisible(mActionBarMenu, R.id.action_show_on_map,
732                itemHasLocation && (supported & SUPPORT_SHOW_ON_MAP) != 0);
733    }
734
735    private void setMenuItemVisible(Menu menu, int itemId, boolean visible) {
736        MenuItem item = menu.findItem(itemId);
737        if (item != null) {
738            item.setVisible(visible);
739        }
740    }
741
742    private final ImageTaskManager.TaskListener mPlaceholderListener =
743            new ImageTaskManager.TaskListener() {
744
745                @Override
746                public void onTaskQueued(String filePath, final Uri imageUri) {
747                    mMainHandler.post(new Runnable() {
748                        @Override
749                        public void run() {
750                            notifyNewMedia(imageUri);
751                            int dataID = mDataAdapter.findDataByContentUri(imageUri);
752                            if (dataID != -1) {
753                                LocalData d = mDataAdapter.getLocalData(dataID);
754                                InProgressDataWrapper newData = new InProgressDataWrapper(d, true);
755                                mDataAdapter.updateData(dataID, newData);
756                            }
757                        }
758                    });
759                }
760
761                @Override
762                public void onTaskDone(String filePath, final Uri imageUri) {
763                    mMainHandler.post(new Runnable() {
764                        @Override
765                        public void run() {
766                            mDataAdapter.refresh(getContentResolver(), imageUri);
767                        }
768                    });
769                }
770
771                @Override
772                public void onTaskProgress(String filePath, Uri imageUri, int progress) {
773                    // Do nothing
774                }
775            };
776
777    private final ImageTaskManager.TaskListener mStitchingListener =
778            new ImageTaskManager.TaskListener() {
779                @Override
780                public void onTaskQueued(String filePath, final Uri imageUri) {
781                    mMainHandler.post(new Runnable() {
782                        @Override
783                        public void run() {
784                            notifyNewMedia(imageUri);
785                            int dataID = mDataAdapter.findDataByContentUri(imageUri);
786                            if (dataID != -1) {
787                                // Don't allow special UI actions (swipe to
788                                // delete, for example) on in-progress data.
789                                LocalData d = mDataAdapter.getLocalData(dataID);
790                                InProgressDataWrapper newData = new InProgressDataWrapper(d);
791                                mDataAdapter.updateData(dataID, newData);
792                            }
793                        }
794                    });
795                }
796
797                @Override
798                public void onTaskDone(String filePath, final Uri imageUri) {
799                    Log.v(TAG, "onTaskDone:" + filePath);
800                    mMainHandler.post(new Runnable() {
801                        @Override
802                        public void run() {
803                            int doneID = mDataAdapter.findDataByContentUri(imageUri);
804                            int currentDataId = mFilmstripController.getCurrentId();
805
806                            if (currentDataId == doneID) {
807                                hidePanoStitchingProgress();
808                                updateStitchingProgress(0);
809                            }
810
811                            mDataAdapter.refresh(getContentResolver(), imageUri);
812                        }
813                    });
814                }
815
816                @Override
817                public void onTaskProgress(
818                        String filePath, final Uri imageUri, final int progress) {
819                    mMainHandler.post(new Runnable() {
820                        @Override
821                        public void run() {
822                            int currentDataId = mFilmstripController.getCurrentId();
823                            if (currentDataId == -1) {
824                                return;
825                            }
826                            if (imageUri.equals(
827                                    mDataAdapter.getLocalData(currentDataId).getContentUri())) {
828                                updateStitchingProgress(progress);
829                            }
830                        }
831                    });
832                }
833            };
834
835    @Override
836    public Context getAndroidContext() {
837        return this;
838    }
839
840    @Override
841    public int getCurrentModuleIndex() {
842        return mCurrentModeIndex;
843    }
844
845    @Override
846    public SurfaceTexture getPreviewBuffer() {
847        // TODO: implement this
848        return null;
849    }
850
851    @Override
852    public FrameLayout getModuleLayoutRoot() {
853        return mCameraModuleRootView;
854    }
855
856    @Override
857    public void setShutterEventsListener(ShutterEventsListener listener) {
858        // TODO: implement this
859    }
860
861    @Override
862    public void setShutterEnabled(boolean enabled) {
863        // TODO: implement this
864    }
865
866    @Override
867    public boolean isShutterEnabled() {
868        // TODO: implement this
869        return false;
870    }
871
872    @Override
873    public void startPreCaptureAnimation() {
874        // TODO: implement this
875    }
876
877    @Override
878    public void cancelPreCaptureAnimation() {
879        // TODO: implement this
880    }
881
882    @Override
883    public void startPostCaptureAnimation() {
884        // TODO: implement this
885    }
886
887    @Override
888    public void startPostCaptureAnimation(Bitmap thumbnail) {
889        // TODO: implement this
890    }
891
892    @Override
893    public void cancelPostCaptureAnimation() {
894        // TODO: implement this
895    }
896
897    @Override
898    public OrientationManager getOrientationManager() {
899        return mOrientationManager;
900    }
901
902    @Override
903    public LocationManager getLocationManager() {
904        return mLocationManager;
905    }
906
907    @Override
908    public void lockOrientation() {
909        mOrientationManager.lockOrientation();
910    }
911
912    @Override
913    public void unlockOrientation() {
914        mOrientationManager.unlockOrientation();
915    }
916
917    @Override
918    public void notifyNewMedia(Uri uri) {
919        ContentResolver cr = getContentResolver();
920        String mimeType = cr.getType(uri);
921        if (mimeType.startsWith("video/")) {
922            sendBroadcast(new Intent(CameraUtil.ACTION_NEW_VIDEO, uri));
923            mDataAdapter.addNewVideo(cr, uri);
924        } else if (mimeType.startsWith("image/")) {
925            CameraUtil.broadcastNewPicture(this, uri);
926            mDataAdapter.addNewPhoto(cr, uri);
927        } else if (mimeType.startsWith("application/stitching-preview")) {
928            mDataAdapter.addNewPhoto(cr, uri);
929        } else if (mimeType.startsWith(PlaceholderManager.PLACEHOLDER_MIME_TYPE)) {
930            mDataAdapter.addNewPhoto(cr, uri);
931        } else {
932            android.util.Log.w(TAG, "Unknown new media with MIME type:"
933                    + mimeType + ", uri:" + uri);
934        }
935    }
936
937    @Override
938    public CameraProvider getCameraProvider() {
939        return mCameraController;
940    }
941
942    private void removeData(int dataID) {
943        mDataAdapter.removeData(CameraActivity.this, dataID);
944        if (mDataAdapter.getTotalNumber() > 1) {
945            showUndoDeletionBar();
946        } else {
947            // If camera preview is the only view left in filmstrip,
948            // no need to show undo bar.
949            mPendingDeletion = true;
950            performDeletion();
951        }
952    }
953
954
955    @Override
956    public boolean onCreateOptionsMenu(Menu menu) {
957        // Inflate the menu items for use in the action bar
958        MenuInflater inflater = getMenuInflater();
959        inflater.inflate(R.menu.operations, menu);
960        mActionBarMenu = menu;
961
962        // Configure the standard share action provider
963        MenuItem item = menu.findItem(R.id.action_share);
964        mStandardShareActionProvider = (ShareActionProvider) item.getActionProvider();
965        mStandardShareActionProvider.setShareHistoryFileName("standard_share_history.xml");
966        if (mStandardShareIntent != null) {
967            mStandardShareActionProvider.setShareIntent(mStandardShareIntent);
968        }
969
970        // Configure the panorama share action provider
971        item = menu.findItem(R.id.action_share_panorama);
972        mPanoramaShareActionProvider = (ShareActionProvider) item.getActionProvider();
973        mPanoramaShareActionProvider.setShareHistoryFileName("panorama_share_history.xml");
974        if (mPanoramaShareIntent != null) {
975            mPanoramaShareActionProvider.setShareIntent(mPanoramaShareIntent);
976        }
977
978        mStandardShareActionProvider.setOnShareTargetSelectedListener(this);
979        mPanoramaShareActionProvider.setOnShareTargetSelectedListener(this);
980
981        return super.onCreateOptionsMenu(menu);
982    }
983
984    @Override
985    public boolean onOptionsItemSelected(MenuItem item) {
986        int currentDataId = mFilmstripController.getCurrentId();
987        if (currentDataId < 0) {
988            return false;
989        }
990        final LocalData localData = mDataAdapter.getLocalData(currentDataId);
991
992        // Handle presses on the action bar items
993        switch (item.getItemId()) {
994            case android.R.id.home:
995                // ActionBar's Up/Home button was clicked
996                try {
997                    startActivity(IntentHelper.getGalleryIntent(this));
998                    return true;
999                } catch (ActivityNotFoundException e) {
1000                    Log.w(TAG, "Failed to launch gallery activity, closing");
1001                    finish();
1002                }
1003            case R.id.action_delete:
1004                UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
1005                        UsageStatistics.ACTION_DELETE, null, 0,
1006                        UsageStatistics.hashFileName(fileNameFromDataID(currentDataId)));
1007                removeData(currentDataId);
1008                return true;
1009            case R.id.action_edit:
1010                UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
1011                        UsageStatistics.ACTION_EDIT, null, 0,
1012                        UsageStatistics.hashFileName(fileNameFromDataID(currentDataId)));
1013                launchEditor(localData);
1014                return true;
1015            case R.id.action_trim: {
1016                // This is going to be handled by the Gallery app.
1017                Intent intent = new Intent(ACTION_TRIM_VIDEO);
1018                LocalData currentData = mDataAdapter.getLocalData(mFilmstripController.getCurrentId());
1019                intent.setData(currentData.getContentUri());
1020                // We need the file path to wrap this into a RandomAccessFile.
1021                intent.putExtra(MEDIA_ITEM_PATH, currentData.getPath());
1022                startActivityForResult(intent, REQ_CODE_DONT_SWITCH_TO_PREVIEW);
1023                return true;
1024            }
1025            case R.id.action_rotate_ccw:
1026                localData.rotate90Degrees(this, mDataAdapter, currentDataId, false);
1027                return true;
1028            case R.id.action_rotate_cw:
1029                localData.rotate90Degrees(this, mDataAdapter, currentDataId, true);
1030                return true;
1031            case R.id.action_crop: {
1032                UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
1033                        UsageStatistics.ACTION_CROP, null, 0,
1034                        UsageStatistics.hashFileName(fileNameFromDataID(currentDataId)));
1035                Intent intent = new Intent(CropActivity.CROP_ACTION);
1036                intent.setClass(this, CropActivity.class);
1037                intent.setDataAndType(localData.getContentUri(), localData.getMimeType())
1038                        .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
1039                startActivityForResult(intent, REQ_CODE_DONT_SWITCH_TO_PREVIEW);
1040                return true;
1041            }
1042            case R.id.action_setas: {
1043                Intent intent = new Intent(Intent.ACTION_ATTACH_DATA)
1044                        .setDataAndType(localData.getContentUri(),
1045                                localData.getMimeType())
1046                        .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
1047                intent.putExtra("mimeType", intent.getType());
1048                startActivityForResult(Intent.createChooser(
1049                        intent, getString(R.string.set_as)), REQ_CODE_DONT_SWITCH_TO_PREVIEW);
1050                return true;
1051            }
1052            case R.id.action_details:
1053                (new AsyncTask<Void, Void, MediaDetails>() {
1054                    @Override
1055                    protected MediaDetails doInBackground(Void... params) {
1056                        return localData.getMediaDetails(CameraActivity.this);
1057                    }
1058
1059                    @Override
1060                    protected void onPostExecute(MediaDetails mediaDetails) {
1061                        if (mediaDetails != null) {
1062                            DetailsDialog.create(CameraActivity.this, mediaDetails).show();
1063                        }
1064                    }
1065                }).execute();
1066                return true;
1067            case R.id.action_show_on_map:
1068                double[] latLong = localData.getLatLong();
1069                if (latLong != null) {
1070                    CameraUtil.showOnMap(this, latLong);
1071                }
1072                return true;
1073            default:
1074                return super.onOptionsItemSelected(item);
1075        }
1076    }
1077
1078    private boolean isCaptureIntent() {
1079        if (MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction())
1080                || MediaStore.ACTION_IMAGE_CAPTURE.equals(getIntent().getAction())
1081                || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(getIntent().getAction())) {
1082            return true;
1083        } else {
1084            return false;
1085        }
1086    }
1087
1088    @Override
1089    public void onCreate(Bundle state) {
1090        super.onCreate(state);
1091        GcamHelper.init(getContentResolver());
1092
1093        getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
1094        setContentView(R.layout.activity_main);
1095        mActionBar = getActionBar();
1096        mActionBar.addOnMenuVisibilityListener(this);
1097        mMainHandler = new MainHandler(getMainLooper());
1098        mCameraController =
1099                new CameraController(this, this, mMainHandler,
1100                        CameraManagerFactory.getAndroidCameraManager());
1101        // TODO: Try to move all the resources allocation to happen as soon as
1102        // possible so we can call module.init() at the earliest time.
1103        mModuleManager = new ModuleManagerImpl();
1104        ModulesInfo.setupModules(this, mModuleManager);
1105
1106        mModeListView = (ModeListView) findViewById(R.id.mode_list_layout);
1107        if (ApiHelper.HAS_ROTATION_ANIMATION) {
1108            setRotationAnimation();
1109        }
1110
1111        // Check if this is in the secure camera mode.
1112        Intent intent = getIntent();
1113        String action = intent.getAction();
1114        if (INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action)
1115                || ACTION_IMAGE_CAPTURE_SECURE.equals(action)) {
1116            mSecureCamera = true;
1117        } else {
1118            mSecureCamera = intent.getBooleanExtra(SECURE_CAMERA_EXTRA, false);
1119        }
1120
1121        if (mSecureCamera) {
1122            // Change the window flags so that secure camera can show when locked
1123            Window win = getWindow();
1124            WindowManager.LayoutParams params = win.getAttributes();
1125            params.flags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
1126            win.setAttributes(params);
1127
1128            // Filter for screen off so that we can finish secure camera activity
1129            // when screen is off.
1130            IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
1131            registerReceiver(mScreenOffReceiver, filter);
1132        }
1133        mAboveFilmstripControlLayout =
1134                (FrameLayout) findViewById(R.id.camera_above_filmstrip_layout);
1135        mAboveFilmstripControlLayout.setFitsSystemWindows(true);
1136        // Hide action bar first since we are in full screen mode first, and
1137        // switch the system UI to lights-out mode.
1138        this.setSystemBarsVisibility(false);
1139        mPanoramaManager = AppManagerFactory.getInstance(this)
1140                .getPanoramaStitchingManager();
1141        mPlaceholderManager = AppManagerFactory.getInstance(this)
1142                .getGcamProcessingManager();
1143        mPanoramaManager.addTaskListener(mStitchingListener);
1144        mPlaceholderManager.addTaskListener(mPlaceholderListener);
1145        LayoutInflater inflater = getLayoutInflater();
1146        View rootLayout = inflater.inflate(R.layout.camera, null, false);
1147        mCameraModuleRootView = (FrameLayout) rootLayout.findViewById(R.id.camera_app_root);
1148        mPanoStitchingPanel = findViewById(R.id.pano_stitching_progress_panel);
1149        mBottomProgress = (ProgressBar) findViewById(R.id.pano_stitching_progress_bar);
1150        mCameraPreviewData = new CameraPreviewData(rootLayout,
1151                FilmstripImageData.SIZE_FULL,
1152                FilmstripImageData.SIZE_FULL);
1153        // Put a CameraPreviewData at the first position.
1154        mWrappedDataAdapter = new FixedFirstDataAdapter(
1155                new CameraDataAdapter(new ColorDrawable(
1156                        getResources().getColor(R.color.photo_placeholder))),
1157                mCameraPreviewData);
1158        mFilmstripController = ((FilmstripView) findViewById(R.id.filmstrip_view)).getController();
1159        mFilmstripController.setViewGap(
1160                getResources().getDimensionPixelSize(R.dimen.camera_film_strip_gap));
1161        mPanoramaViewHelper = new PanoramaViewHelper(this);
1162        mPanoramaViewHelper.onCreate();
1163        mFilmstripController.setPanoramaViewHelper(mPanoramaViewHelper);
1164        // Set up the camera preview first so the preview shows up ASAP.
1165        mFilmstripController.setListener(mFilmStripListener);
1166
1167        // TODO: Remove the 3rd parameter once mCameraModuleRoot is moved out of filmstrip
1168        mCameraAppUI = new CameraAppUI(this,
1169                (MainActivityLayout) findViewById(R.id.activity_root_view),
1170                mCameraModuleRootView,
1171                isSecureCamera(), isCaptureIntent());
1172
1173        mSettingsManager = new SettingsManager(this, null, mCameraController.getNumberOfCameras());
1174
1175        mLocationManager = new LocationManager(this,
1176            new LocationManager.Listener() {
1177                @Override
1178                public void showGpsOnScreenIndicator(boolean hasSignal) {
1179                }
1180
1181                @Override
1182                public void hideGpsOnScreenIndicator() {
1183                }
1184            });
1185
1186        mSettingsController = new SettingsController(this, mSettingsManager, mLocationManager);
1187
1188        int modeIndex = -1;
1189        if (MediaStore.INTENT_ACTION_VIDEO_CAMERA.equals(getIntent().getAction())
1190                || MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction())) {
1191            modeIndex = ModeListView.MODE_VIDEO;
1192        } else if (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(getIntent().getAction())
1193                || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(getIntent()
1194                        .getAction())) {
1195            modeIndex = ModeListView.MODE_PHOTO;
1196            if (mSettingsManager.getInt(new StartupModuleSetting())
1197                        == ModeListView.MODE_GCAM && GcamHelper.hasGcamCapture()) {
1198                modeIndex = ModeListView.MODE_GCAM;
1199            }
1200        } else if (MediaStore.ACTION_IMAGE_CAPTURE.equals(getIntent().getAction())
1201                || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(getIntent().getAction())) {
1202            modeIndex = ModeListView.MODE_PHOTO;
1203        } else {
1204            // If the activity has not been started using an explicit intent,
1205            // read the module index from the last time the user changed modes
1206            modeIndex = mSettingsManager.getInt(new StartupModuleSetting());
1207            if ((modeIndex == ModeListView.MODE_GCAM &&
1208                    !GcamHelper.hasGcamCapture()) || modeIndex < 0) {
1209                modeIndex = ModeListView.MODE_PHOTO;
1210            }
1211        }
1212
1213        mOrientationManager = new OrientationManagerImpl(this);
1214        mOrientationManager.addOnOrientationChangeListener(mMainHandler, this);
1215
1216        setModuleFromModeIndex(modeIndex);
1217
1218        // TODO: Remove this when refactor is done.
1219        if (modeIndex == ModulesInfo.MODULE_PHOTO ||
1220                modeIndex == ModulesInfo.MODULE_VIDEO) {
1221            mCameraAppUI.prepareModuleUI();
1222        }
1223        mCurrentModule.init(this, mCameraModuleRootView);
1224
1225        if (!mSecureCamera) {
1226            mDataAdapter = mWrappedDataAdapter;
1227            mFilmstripController.setDataAdapter(mDataAdapter);
1228            if (!isCaptureIntent()) {
1229                mDataAdapter.requestLoad(getContentResolver());
1230            }
1231        } else {
1232            // Put a lock placeholder as the last image by setting its date to
1233            // 0.
1234            ImageView v = (ImageView) getLayoutInflater().inflate(
1235                    R.layout.secure_album_placeholder, null);
1236            v.setOnClickListener(new View.OnClickListener() {
1237                @Override
1238                public void onClick(View view) {
1239                    try {
1240                        UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
1241                                UsageStatistics.ACTION_GALLERY, null);
1242                        startActivity(IntentHelper.getGalleryIntent(CameraActivity.this));
1243                    } catch (ActivityNotFoundException e) {
1244                        Log.w(TAG, "Failed to launch gallery activity, closing");
1245                    }
1246                    finish();
1247                }
1248            });
1249            mDataAdapter = new FixedLastDataAdapter(
1250                    mWrappedDataAdapter,
1251                    new SimpleViewData(
1252                            v,
1253                            v.getDrawable().getIntrinsicWidth(),
1254                            v.getDrawable().getIntrinsicHeight(),
1255                            0, 0));
1256            // Flush out all the original data.
1257            mDataAdapter.flush();
1258            mFilmstripController.setDataAdapter(mDataAdapter);
1259        }
1260
1261        setupNfcBeamPush();
1262
1263        mLocalImagesObserver = new LocalMediaObserver();
1264        mLocalVideosObserver = new LocalMediaObserver();
1265
1266        getContentResolver().registerContentObserver(
1267                MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true,
1268                mLocalImagesObserver);
1269        getContentResolver().registerContentObserver(
1270                MediaStore.Video.Media.EXTERNAL_CONTENT_URI, true,
1271                mLocalVideosObserver);
1272    }
1273
1274    private void setRotationAnimation() {
1275        int rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
1276        rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE;
1277        Window win = getWindow();
1278        WindowManager.LayoutParams winParams = win.getAttributes();
1279        winParams.rotationAnimation = rotationAnimation;
1280        win.setAttributes(winParams);
1281    }
1282
1283    @Override
1284    public void onUserInteraction() {
1285        super.onUserInteraction();
1286        mCurrentModule.onUserInteraction();
1287    }
1288
1289    @Override
1290    public boolean dispatchTouchEvent(MotionEvent ev) {
1291        boolean result = super.dispatchTouchEvent(ev);
1292        if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
1293            // Real deletion is postponed until the next user interaction after
1294            // the gesture that triggers deletion. Until real deletion is performed,
1295            // users can click the undo button to bring back the image that they
1296            // chose to delete.
1297            if (mPendingDeletion && !mIsUndoingDeletion) {
1298                performDeletion();
1299            }
1300        }
1301        return result;
1302    }
1303
1304    @Override
1305    public void onPause() {
1306        mPaused = true;
1307
1308        // Delete photos that are pending deletion
1309        performDeletion();
1310        // TODO: call mCurrentModule.pause() instead after all the modules
1311        // support pause().
1312        mCurrentModule.pause();
1313        mOrientationManager.pause();
1314        // Close the camera and wait for the operation done.
1315        mCameraController.closeCamera();
1316
1317        mLocalImagesObserver.setActivityPaused(true);
1318        mLocalVideosObserver.setActivityPaused(true);
1319        super.onPause();
1320    }
1321
1322    @Override
1323    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
1324        if (requestCode == REQ_CODE_DONT_SWITCH_TO_PREVIEW) {
1325            mResetToPreviewOnResume = false;
1326        } else {
1327            super.onActivityResult(requestCode, resultCode, data);
1328        }
1329    }
1330
1331    @Override
1332    public void onResume() {
1333        mPaused = false;
1334
1335        // TODO: Handle this in OrientationManager.
1336        // Auto-rotate off
1337        if (Settings.System.getInt(getContentResolver(),
1338                Settings.System.ACCELEROMETER_ROTATION, 0) == 0) {
1339            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
1340            mAutoRotateScreen = false;
1341        } else {
1342            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
1343            mAutoRotateScreen = true;
1344        }
1345
1346        UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
1347                UsageStatistics.ACTION_FOREGROUNDED, this.getClass().getSimpleName());
1348
1349        mOrientationManager.resume();
1350        super.onResume();
1351        mCurrentModule.resume();
1352
1353        setSwipingEnabled(true);
1354
1355        if (mResetToPreviewOnResume) {
1356            // Go to the preview on resume.
1357            mFilmstripController.goToFirstItem();
1358        }
1359        // Default is showing the preview, unless disabled by explicitly
1360        // starting an activity we want to return from to the filmstrip rather
1361        // than the preview.
1362        mResetToPreviewOnResume = true;
1363
1364        if (mLocalVideosObserver.isMediaDataChangedDuringPause()
1365                || mLocalImagesObserver.isMediaDataChangedDuringPause()) {
1366            if (!mSecureCamera) {
1367                // If it's secure camera, requestLoad() should not be called
1368                // as it will load all the data.
1369                mDataAdapter.requestLoad(getContentResolver());
1370            }
1371        }
1372        mLocalImagesObserver.setActivityPaused(false);
1373        mLocalVideosObserver.setActivityPaused(false);
1374
1375        mModeListView.startAccordionAnimation();
1376    }
1377
1378    @Override
1379    public void onStart() {
1380        super.onStart();
1381        mPanoramaViewHelper.onStart();
1382    }
1383
1384    @Override
1385    protected void onStop() {
1386        mPanoramaViewHelper.onStop();
1387
1388        CameraManagerFactory.recycle();
1389        super.onStop();
1390    }
1391
1392    @Override
1393    public void onDestroy() {
1394        if (mSecureCamera) {
1395            unregisterReceiver(mScreenOffReceiver);
1396        }
1397        getContentResolver().unregisterContentObserver(mLocalImagesObserver);
1398        getContentResolver().unregisterContentObserver(mLocalVideosObserver);
1399        super.onDestroy();
1400    }
1401
1402    @Override
1403    public void onConfigurationChanged(Configuration config) {
1404        super.onConfigurationChanged(config);
1405        mCurrentModule.onConfigurationChanged(config);
1406    }
1407
1408    @Override
1409    public boolean onKeyDown(int keyCode, KeyEvent event) {
1410        if (mFilmstripController.inCameraFullscreen()) {
1411            if (mCurrentModule.onKeyDown(keyCode, event)) {
1412                return true;
1413            }
1414            // Prevent software keyboard or voice search from showing up.
1415            if (keyCode == KeyEvent.KEYCODE_SEARCH
1416                    || keyCode == KeyEvent.KEYCODE_MENU) {
1417                if (event.isLongPress()) {
1418                    return true;
1419                }
1420            }
1421        }
1422
1423        return super.onKeyDown(keyCode, event);
1424    }
1425
1426    @Override
1427    public boolean onKeyUp(int keyCode, KeyEvent event) {
1428        if (mFilmstripController.inCameraFullscreen() && mCurrentModule.onKeyUp(keyCode, event)) {
1429            return true;
1430        }
1431        return super.onKeyUp(keyCode, event);
1432    }
1433
1434    @Override
1435    public void onBackPressed() {
1436        if (!mFilmstripController.inCameraFullscreen()) {
1437            mFilmstripController.goToFirstItem();
1438        } else if (!mCurrentModule.onBackPressed()) {
1439            super.onBackPressed();
1440        }
1441    }
1442
1443    public boolean isAutoRotateScreen() {
1444        return mAutoRotateScreen;
1445    }
1446
1447    protected void updateStorageSpace() {
1448        mStorageSpaceBytes = Storage.getAvailableSpace();
1449    }
1450
1451    protected long getStorageSpaceBytes() {
1452        return mStorageSpaceBytes;
1453    }
1454
1455    protected void updateStorageSpaceAndHint() {
1456        updateStorageSpace();
1457        updateStorageHint(mStorageSpaceBytes);
1458    }
1459
1460    protected void updateStorageHint(long storageSpace) {
1461        String message = null;
1462        if (storageSpace == Storage.UNAVAILABLE) {
1463            message = getString(R.string.no_storage);
1464        } else if (storageSpace == Storage.PREPARING) {
1465            message = getString(R.string.preparing_sd);
1466        } else if (storageSpace == Storage.UNKNOWN_SIZE) {
1467            message = getString(R.string.access_sd_fail);
1468        } else if (storageSpace <= Storage.LOW_STORAGE_THRESHOLD_BYTES) {
1469            message = getString(R.string.spaceIsLow_content);
1470        }
1471
1472        if (message != null) {
1473            if (mStorageHint == null) {
1474                mStorageHint = OnScreenHint.makeText(this, message);
1475            } else {
1476                mStorageHint.setText(message);
1477            }
1478            mStorageHint.show();
1479        } else if (mStorageHint != null) {
1480            mStorageHint.cancel();
1481            mStorageHint = null;
1482        }
1483    }
1484
1485    protected void setResultEx(int resultCode) {
1486        mResultCodeForTesting = resultCode;
1487        setResult(resultCode);
1488    }
1489
1490    protected void setResultEx(int resultCode, Intent data) {
1491        mResultCodeForTesting = resultCode;
1492        mResultDataForTesting = data;
1493        setResult(resultCode, data);
1494    }
1495
1496    public int getResultCode() {
1497        return mResultCodeForTesting;
1498    }
1499
1500    public Intent getResultData() {
1501        return mResultDataForTesting;
1502    }
1503
1504    public boolean isSecureCamera() {
1505        return mSecureCamera;
1506    }
1507
1508    @Override
1509    public boolean isPaused() {
1510        return mPaused;
1511    }
1512
1513    @Override
1514    public void onModeSelected(int modeIndex) {
1515        if (mCurrentModeIndex == modeIndex) {
1516            return;
1517        }
1518
1519        if (modeIndex == ModeListView.MODE_SETTING) {
1520            onSettingsSelected();
1521            return;
1522        }
1523
1524        CameraHolder.instance().keep();
1525        closeModule(mCurrentModule);
1526        int oldModuleIndex = mCurrentModeIndex;
1527        setModuleFromModeIndex(modeIndex);
1528
1529        // TODO: The following check is temporary for quick switch between video and photo.
1530        // When the refactor is done, similar logic will be applied to all modules.
1531        if (mCurrentModeIndex == ModulesInfo.MODULE_PHOTO
1532                || mCurrentModeIndex == ModulesInfo.MODULE_VIDEO) {
1533            if (oldModuleIndex != ModulesInfo.MODULE_PHOTO
1534                    && oldModuleIndex != ModulesInfo.MODULE_VIDEO) {
1535                mCameraAppUI.prepareModuleUI();
1536            } else {
1537                mCameraAppUI.clearModuleUI();
1538            }
1539        } else {
1540            // This is the old way of removing all views in CameraRootView. Will
1541            // be deprecated soon. It is here to make sure modules that haven't
1542            // been refactored can still function.
1543            mCameraAppUI.clearCameraUI();
1544        }
1545
1546        openModule(mCurrentModule);
1547        mCurrentModule.onOrientationChanged(mLastRawOrientation);
1548        if (mMediaSaver != null) {
1549            mCurrentModule.onMediaSaverAvailable(mMediaSaver);
1550        }
1551        // Store the module index so we can use it the next time the Camera
1552        // starts up.
1553        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
1554        prefs.edit().putInt(CameraSettings.KEY_STARTUP_MODULE_INDEX, modeIndex).apply();
1555    }
1556
1557    public void onSettingsSelected() {
1558        // Temporary until we finalize the touch flow.
1559        LayoutInflater inflater = getLayoutInflater();
1560        SettingsView settingsView = (SettingsView) inflater.inflate(R.layout.settings_list_layout,
1561            null, false);
1562        settingsView.setSettingsListener(mSettingsController);
1563        PopupWindow popup = new PopupWindow(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
1564        popup.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
1565        popup.setOutsideTouchable(true);
1566        popup.setFocusable(true);
1567        popup.setContentView(settingsView);
1568        popup.showAtLocation(mModeListView.getRootView(), Gravity.CENTER, 0, 0);
1569    }
1570
1571    /**
1572     * Sets the mCurrentModuleIndex, creates a new module instance for the given
1573     * index an sets it as mCurrentModule.
1574     */
1575    private void setModuleFromModeIndex(int modeIndex) {
1576        ModuleManagerImpl.ModuleAgent agent = mModuleManager.getModuleAgent(modeIndex);
1577        if (agent == null) {
1578            return;
1579        }
1580        if (!agent.requestAppForCamera()) {
1581            mCameraController.closeCamera();
1582        }
1583        mCurrentModeIndex = agent.getModuleId();
1584        mCurrentModule2 = agent.createModule(this);
1585        mCurrentModule = (CameraModule) mCurrentModule2;
1586    }
1587
1588    @Override
1589    public SettingsManager getSettingsManager() {
1590        return mSettingsManager;
1591    }
1592
1593    @Override
1594    public CameraServices getServices() {
1595        return (CameraServices) getApplication();
1596    }
1597
1598    @Override
1599    public SettingsController getSettingsController() {
1600        return mSettingsController;
1601    }
1602
1603    /**
1604     * Launches an ACTION_EDIT intent for the given local data item.
1605     */
1606    public void launchEditor(LocalData data) {
1607        Intent intent = new Intent(Intent.ACTION_EDIT)
1608                .setDataAndType(data.getContentUri(), data.getMimeType())
1609                .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
1610        try {
1611            startActivityForResult(intent, REQ_CODE_DONT_SWITCH_TO_PREVIEW);
1612        } catch (ActivityNotFoundException e) {
1613            startActivityForResult(Intent.createChooser(intent, null),
1614                    REQ_CODE_DONT_SWITCH_TO_PREVIEW);
1615        }
1616    }
1617
1618    /**
1619     * Launch the tiny planet editor.
1620     *
1621     * @param data The data must be a 360 degree stereographically mapped
1622     *             panoramic image. It will not be modified, instead a new item
1623     *             with the result will be added to the filmstrip.
1624     */
1625    public void launchTinyPlanetEditor(LocalData data) {
1626        TinyPlanetFragment fragment = new TinyPlanetFragment();
1627        Bundle bundle = new Bundle();
1628        bundle.putString(TinyPlanetFragment.ARGUMENT_URI, data.getContentUri().toString());
1629        bundle.putString(TinyPlanetFragment.ARGUMENT_TITLE, data.getTitle());
1630        fragment.setArguments(bundle);
1631        fragment.show(getFragmentManager(), "tiny_planet");
1632    }
1633
1634    private void openModule(CameraModule module) {
1635        module.init(this, mCameraModuleRootView);
1636        module.resume();
1637    }
1638
1639    private void closeModule(CameraModule module) {
1640        module.pause();
1641    }
1642
1643    private void performDeletion() {
1644        if (!mPendingDeletion) {
1645            return;
1646        }
1647        hideUndoDeletionBar(false);
1648        mDataAdapter.executeDeletion(CameraActivity.this);
1649
1650        int currentId = mFilmstripController.getCurrentId();
1651        updateActionBarMenu(currentId);
1652        mFilmStripListener.onCurrentDataCentered(currentId);
1653    }
1654
1655    public void showUndoDeletionBar() {
1656        if (mPendingDeletion) {
1657            performDeletion();
1658        }
1659        Log.v(TAG, "showing undo bar");
1660        mPendingDeletion = true;
1661        if (mUndoDeletionBar == null) {
1662            ViewGroup v = (ViewGroup) getLayoutInflater().inflate(R.layout.undo_bar,
1663                    mAboveFilmstripControlLayout, true);
1664            mUndoDeletionBar = (ViewGroup) v.findViewById(R.id.camera_undo_deletion_bar);
1665            View button = mUndoDeletionBar.findViewById(R.id.camera_undo_deletion_button);
1666            button.setOnClickListener(new View.OnClickListener() {
1667                @Override
1668                public void onClick(View view) {
1669                    mDataAdapter.undoDataRemoval();
1670                    hideUndoDeletionBar(true);
1671                }
1672            });
1673            // Setting undo bar clickable to avoid touch events going through
1674            // the bar to the buttons (eg. edit button, etc) underneath the bar.
1675            mUndoDeletionBar.setClickable(true);
1676            // When there is user interaction going on with the undo button, we
1677            // do not want to hide the undo bar.
1678            button.setOnTouchListener(new View.OnTouchListener() {
1679                @Override
1680                public boolean onTouch(View v, MotionEvent event) {
1681                    if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
1682                        mIsUndoingDeletion = true;
1683                    } else if (event.getActionMasked() == MotionEvent.ACTION_UP) {
1684                        mIsUndoingDeletion = false;
1685                    }
1686                    return false;
1687                }
1688            });
1689        }
1690        mUndoDeletionBar.setAlpha(0f);
1691        mUndoDeletionBar.setVisibility(View.VISIBLE);
1692        mUndoDeletionBar.animate().setDuration(200).alpha(1f).setListener(null).start();
1693    }
1694
1695    private void hideUndoDeletionBar(boolean withAnimation) {
1696        Log.v(TAG, "Hiding undo deletion bar");
1697        mPendingDeletion = false;
1698        if (mUndoDeletionBar != null) {
1699            if (withAnimation) {
1700                mUndoDeletionBar.animate().setDuration(200).alpha(0f)
1701                        .setListener(new Animator.AnimatorListener() {
1702                            @Override
1703                            public void onAnimationStart(Animator animation) {
1704                                // Do nothing.
1705                            }
1706
1707                            @Override
1708                            public void onAnimationEnd(Animator animation) {
1709                                mUndoDeletionBar.setVisibility(View.GONE);
1710                            }
1711
1712                            @Override
1713                            public void onAnimationCancel(Animator animation) {
1714                                // Do nothing.
1715                            }
1716
1717                            @Override
1718                            public void onAnimationRepeat(Animator animation) {
1719                                // Do nothing.
1720                            }
1721                        }).start();
1722            } else {
1723                mUndoDeletionBar.setVisibility(View.GONE);
1724            }
1725        }
1726    }
1727
1728    @Override
1729    public void onOrientationChanged(int orientation) {
1730        // We keep the last known orientation. So if the user first orient
1731        // the camera then point the camera to floor or sky, we still have
1732        // the correct orientation.
1733        if (orientation == OrientationManager.ORIENTATION_UNKNOWN) {
1734            return;
1735        }
1736        mLastRawOrientation = orientation;
1737        if (mCurrentModule != null) {
1738            mCurrentModule.onOrientationChanged(orientation);
1739        }
1740    }
1741
1742    /**
1743     * Enable/disable swipe-to-filmstrip. Will always disable swipe if in
1744     * capture intent.
1745     *
1746     * @param enable {@code true} to enable swipe.
1747     */
1748    public void setSwipingEnabled(boolean enable) {
1749        if (isCaptureIntent()) {
1750            mCameraPreviewData.lockPreview(true);
1751        } else {
1752            mCameraPreviewData.lockPreview(!enable);
1753        }
1754    }
1755
1756
1757    /**
1758     * Check whether camera controls are visible.
1759     *
1760     * @return whether controls are visible.
1761     */
1762    private boolean arePreviewControlsVisible() {
1763        return mCurrentModule.arePreviewControlsVisible();
1764    }
1765
1766    /**
1767     * Show or hide the {@link CameraControls} using the current module's
1768     * implementation of {@link #onPreviewFocusChanged}.
1769     *
1770     * @param showControls whether to show camera controls.
1771     */
1772    private void setPreviewControlsVisibility(boolean showControls) {
1773        mCurrentModule.onPreviewFocusChanged(showControls);
1774    }
1775
1776    // Accessor methods for getting latency times used in performance testing
1777    public long getAutoFocusTime() {
1778        return (mCurrentModule instanceof PhotoModule) ?
1779                ((PhotoModule) mCurrentModule).mAutoFocusTime : -1;
1780    }
1781
1782    public long getShutterLag() {
1783        return (mCurrentModule instanceof PhotoModule) ?
1784                ((PhotoModule) mCurrentModule).mShutterLag : -1;
1785    }
1786
1787    public long getShutterToPictureDisplayedTime() {
1788        return (mCurrentModule instanceof PhotoModule) ?
1789                ((PhotoModule) mCurrentModule).mShutterToPictureDisplayedTime : -1;
1790    }
1791
1792    public long getPictureDisplayedToJpegCallbackTime() {
1793        return (mCurrentModule instanceof PhotoModule) ?
1794                ((PhotoModule) mCurrentModule).mPictureDisplayedToJpegCallbackTime : -1;
1795    }
1796
1797    public long getJpegCallbackFinishTime() {
1798        return (mCurrentModule instanceof PhotoModule) ?
1799                ((PhotoModule) mCurrentModule).mJpegCallbackFinishTime : -1;
1800    }
1801
1802    public long getCaptureStartTime() {
1803        return (mCurrentModule instanceof PhotoModule) ?
1804                ((PhotoModule) mCurrentModule).mCaptureStartTime : -1;
1805    }
1806
1807    public boolean isRecording() {
1808        return (mCurrentModule instanceof VideoModule) ?
1809                ((VideoModule) mCurrentModule).isRecording() : false;
1810    }
1811
1812    public CameraManager.CameraOpenCallback getCameraOpenErrorCallback() {
1813        return mCameraController;
1814    }
1815
1816    // For debugging purposes only.
1817    public CameraModule getCurrentModule() {
1818        return mCurrentModule;
1819    }
1820}
1821