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