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