CameraActivity.java revision 17c12ec9107d5e5cb9f2adc92e8f3bdf094dddc3
1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.camera;
18
19import android.animation.Animator;
20import android.annotation.TargetApi;
21import android.app.ActionBar;
22import android.app.Activity;
23import android.app.AlertDialog;
24import android.app.Dialog;
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.pm.PackageManager;
34import android.content.res.Configuration;
35import android.graphics.Bitmap;
36import android.graphics.Matrix;
37import android.graphics.Point;
38import android.graphics.SurfaceTexture;
39import android.graphics.drawable.ColorDrawable;
40import android.graphics.drawable.Drawable;
41import android.net.Uri;
42import android.nfc.NfcAdapter;
43import android.nfc.NfcAdapter.CreateBeamUrisCallback;
44import android.nfc.NfcEvent;
45import android.os.Build;
46import android.os.Bundle;
47import android.os.Handler;
48import android.os.HandlerThread;
49import android.os.Looper;
50import android.os.Message;
51import android.preference.PreferenceManager;
52import android.provider.MediaStore;
53import android.provider.Settings;
54import android.util.CameraPerformanceTracker;
55import android.util.Log;
56import android.view.ContextMenu;
57import android.view.ContextMenu.ContextMenuInfo;
58import android.view.KeyEvent;
59import android.view.Menu;
60import android.view.MenuInflater;
61import android.view.MenuItem;
62import android.view.MotionEvent;
63import android.view.View;
64import android.view.ViewGroup;
65import android.view.Window;
66import android.view.WindowManager;
67import android.widget.FrameLayout;
68import android.widget.ImageView;
69import android.widget.ShareActionProvider;
70
71import com.android.camera.app.AppController;
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.LocationManager;
79import com.android.camera.app.ModuleManagerImpl;
80import com.android.camera.app.OrientationManager;
81import com.android.camera.app.OrientationManagerImpl;
82import com.android.camera.data.CameraDataAdapter;
83import com.android.camera.data.FixedLastDataAdapter;
84import com.android.camera.data.LocalData;
85import com.android.camera.data.LocalDataAdapter;
86import com.android.camera.data.LocalDataUtil;
87import com.android.camera.data.LocalMediaObserver;
88import com.android.camera.data.MediaDetails;
89import com.android.camera.data.PanoramaMetadataLoader;
90import com.android.camera.data.RgbzMetadataLoader;
91import com.android.camera.data.SimpleViewData;
92import com.android.camera.filmstrip.FilmstripContentPanel;
93import com.android.camera.filmstrip.FilmstripController;
94import com.android.camera.hardware.HardwareSpec;
95import com.android.camera.hardware.HardwareSpecImpl;
96import com.android.camera.module.ModuleController;
97import com.android.camera.module.ModulesInfo;
98import com.android.camera.session.CaptureSessionManager;
99import com.android.camera.session.CaptureSessionManager.SessionListener;
100import com.android.camera.settings.CameraSettingsActivity;
101import com.android.camera.settings.SettingsManager;
102import com.android.camera.settings.SettingsManager.SettingsCapabilities;
103import com.android.camera.settings.SettingsUtil;
104import com.android.camera.tinyplanet.TinyPlanetFragment;
105import com.android.camera.ui.AbstractTutorialOverlay;
106import com.android.camera.ui.DetailsDialog;
107import com.android.camera.ui.MainActivityLayout;
108import com.android.camera.ui.ModeListView;
109import com.android.camera.ui.ModeListView.ModeListVisibilityChangedListener;
110import com.android.camera.ui.PreviewStatusListener;
111import com.android.camera.util.ApiHelper;
112import com.android.camera.util.Callback;
113import com.android.camera.util.CameraUtil;
114import com.android.camera.util.FeedbackHelper;
115import com.android.camera.util.GalleryHelper;
116import com.android.camera.util.GcamHelper;
117import com.android.camera.util.IntentHelper;
118import com.android.camera.util.PhotoSphereHelper.PanoramaViewHelper;
119import com.android.camera.util.ReleaseDialogHelper;
120import com.android.camera.util.UsageStatistics;
121import com.android.camera.widget.FilmstripView;
122import com.android.camera2.R;
123import com.google.common.logging.eventprotos;
124import com.google.common.logging.eventprotos.CameraEvent.InteractionCause;
125import com.google.common.logging.eventprotos.NavigationChange;
126
127import java.io.File;
128import java.io.FileInputStream;
129import java.io.FileNotFoundException;
130import java.lang.ref.WeakReference;
131import java.util.ArrayList;
132import java.util.List;
133
134public class CameraActivity extends Activity
135        implements AppController, CameraManager.CameraOpenCallback,
136        ActionBar.OnMenuVisibilityListener, ShareActionProvider.OnShareTargetSelectedListener,
137        OrientationManager.OnOrientationChangeListener {
138
139    private static final String TAG = "CameraActivity";
140
141    private static final String INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE =
142            "android.media.action.STILL_IMAGE_CAMERA_SECURE";
143    public static final String ACTION_IMAGE_CAPTURE_SECURE =
144            "android.media.action.IMAGE_CAPTURE_SECURE";
145
146    // The intent extra for camera from secure lock screen. True if the gallery
147    // should only show newly captured pictures. sSecureAlbumId does not
148    // increment. This is used when switching between camera, camcorder, and
149    // panorama. If the extra is not set, it is in the normal camera mode.
150    public static final String SECURE_CAMERA_EXTRA = "secure_camera";
151
152    /**
153     * Request code from an activity we started that indicated that we do not
154     * want to reset the view to the preview in onResume.
155     */
156    public static final int REQ_CODE_DONT_SWITCH_TO_PREVIEW = 142;
157
158    public static final int REQ_CODE_GCAM_DEBUG_POSTCAPTURE = 999;
159
160    private static final int MSG_CLEAR_SCREEN_ON_FLAG = 2;
161    private static final long SCREEN_DELAY_MS = 2 * 60 * 1000; // 2 mins.
162    private static final int MAX_PEEK_BITMAP_PIXELS = 1600000; // 1.6 * 4 MBs.
163
164    /** Should be used wherever a context is needed. */
165    private Context mAppContext;
166
167    /**
168     * Whether onResume should reset the view to the preview.
169     */
170    private boolean mResetToPreviewOnResume = true;
171
172    /**
173     * This data adapter is used by FilmStripView.
174     */
175    private LocalDataAdapter mDataAdapter;
176
177    /**
178     * TODO: This should be moved to the app level.
179     */
180    private SettingsManager mSettingsManager;
181
182    private ModeListView mModeListView;
183    private int mCurrentModeIndex;
184    private CameraModule mCurrentModule;
185    private ModuleManagerImpl mModuleManager;
186    private FrameLayout mAboveFilmstripControlLayout;
187    private FilmstripController mFilmstripController;
188    private boolean mFilmstripVisible;
189    private int mResultCodeForTesting;
190    private Intent mResultDataForTesting;
191    private OnScreenHint mStorageHint;
192    private long mStorageSpaceBytes = Storage.LOW_STORAGE_THRESHOLD_BYTES;
193    private boolean mAutoRotateScreen;
194    private boolean mSecureCamera;
195    private int mLastRawOrientation;
196    private OrientationManagerImpl mOrientationManager;
197    private LocationManager mLocationManager;
198    private ButtonManager mButtonManager;
199    private Handler mMainHandler;
200    private PanoramaViewHelper mPanoramaViewHelper;
201    private ActionBar mActionBar;
202    private ViewGroup mUndoDeletionBar;
203    private boolean mIsUndoingDeletion = false;
204
205    private final Uri[] mNfcPushUris = new Uri[1];
206
207    private LocalMediaObserver mLocalImagesObserver;
208    private LocalMediaObserver mLocalVideosObserver;
209
210    private boolean mPendingDeletion = false;
211
212    private CameraController mCameraController;
213    private boolean mPaused;
214    private CameraAppUI mCameraAppUI;
215
216    private PeekAnimationHandler mPeekAnimationHandler;
217    private HandlerThread mPeekAnimationThread;
218
219    private FeedbackHelper mFeedbackHelper;
220
221    private Intent mGalleryIntent;
222    private long mOnCreateTime;
223
224    private Menu mActionBarMenu;
225
226    @Override
227    public CameraAppUI getCameraAppUI() {
228        return mCameraAppUI;
229    }
230
231    // close activity when screen turns off
232    private final BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() {
233        @Override
234        public void onReceive(Context context, Intent intent) {
235            finish();
236        }
237    };
238
239    /**
240     * Whether the screen is kept turned on.
241     */
242    private boolean mKeepScreenOn;
243    private int mLastLayoutOrientation;
244    private final CameraAppUI.BottomPanel.Listener mMyFilmstripBottomControlListener =
245            new CameraAppUI.BottomPanel.Listener() {
246
247                /**
248                 * If the current photo is a photo sphere, this will launch the
249                 * Photo Sphere panorama viewer.
250                 */
251                @Override
252                public void onExternalViewer() {
253                    if (mPanoramaViewHelper == null) {
254                        return;
255                    }
256                    final LocalData data = getCurrentLocalData();
257                    if (data == null) {
258                        return;
259                    }
260                    final Uri contentUri = data.getContentUri();
261                    if (contentUri == Uri.EMPTY) {
262                        return;
263                    }
264
265                    if (PanoramaMetadataLoader.isPanoramaAndUseViewer(data)) {
266                        mPanoramaViewHelper.showPanorama(CameraActivity.this, contentUri);
267                    } else if (RgbzMetadataLoader.hasRGBZData(data)) {
268                        mPanoramaViewHelper.showRgbz(contentUri);
269                    }
270                }
271
272                @Override
273                public void onEdit() {
274                    LocalData data = getCurrentLocalData();
275                    if (data == null) {
276                        return;
277                    }
278                    launchEditor(data);
279                }
280
281                @Override
282                public void onTinyPlanet() {
283                    LocalData data = getCurrentLocalData();
284                    if (data == null) {
285                        return;
286                    }
287                    launchTinyPlanetEditor(data);
288                }
289
290                @Override
291                public void onDelete() {
292                    final int currentDataId = getCurrentDataId();
293                    UsageStatistics.photoInteraction(
294                            UsageStatistics.hashFileName(fileNameFromDataID(currentDataId)),
295                            eventprotos.CameraEvent.InteractionType.DELETE,
296                            InteractionCause.BUTTON);
297                    removeData(currentDataId);
298                }
299
300                @Override
301                public void onShare() {
302                    final LocalData data = getCurrentLocalData();
303
304                    // If applicable, show release information before this item
305                    // is shared.
306                    if (PanoramaMetadataLoader.isPanorama(data)
307                            || RgbzMetadataLoader.hasRGBZData(data)) {
308                        ReleaseDialogHelper.showReleaseInfoDialog(CameraActivity.this,
309                                new Callback<Void>() {
310                                    @Override
311                                    public void onCallback(Void result) {
312                                        share(data);
313                                    }
314                                });
315                    } else {
316                        share(data);
317                    }
318                }
319
320                private void share(LocalData data) {
321                    Intent shareIntent = getShareIntentByData(data);
322                    if (shareIntent != null) {
323                        try {
324                            launchActivityByIntent(shareIntent);
325                            mCameraAppUI.getFilmstripBottomControls().setShareEnabled(false);
326                        } catch (ActivityNotFoundException ex) {
327                            // Nothing.
328                        }
329                    }
330                }
331
332                private int getCurrentDataId() {
333                    return mFilmstripController.getCurrentId();
334                }
335
336                private LocalData getCurrentLocalData() {
337                    return mDataAdapter.getLocalData(getCurrentDataId());
338                }
339
340                /**
341                 * Sets up the share intent and NFC properly according to the
342                 * data.
343                 *
344                 * @param data The data to be shared.
345                 */
346                private Intent getShareIntentByData(final LocalData data) {
347                    Intent intent = null;
348                    final Uri contentUri = data.getContentUri();
349                    if (PanoramaMetadataLoader.isPanorama360(data) &&
350                            data.getContentUri() != Uri.EMPTY) {
351                        intent = new Intent(Intent.ACTION_SEND);
352                        intent.setType("application/vnd.google.panorama360+jpg");
353                        intent.putExtra(Intent.EXTRA_STREAM, contentUri);
354                    } else if (data.isDataActionSupported(LocalData.DATA_ACTION_SHARE)) {
355                        final String mimeType = data.getMimeType();
356                        intent = getShareIntentFromType(mimeType);
357                        if (intent != null) {
358                            intent.putExtra(Intent.EXTRA_STREAM, contentUri);
359                            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
360                        }
361                        intent = Intent.createChooser(intent, null);
362                    }
363                    return intent;
364                }
365
366                /**
367                 * Get the share intent according to the mimeType
368                 *
369                 * @param mimeType The mimeType of current data.
370                 * @return the video/image's ShareIntent or null if mimeType is
371                 *         invalid.
372                 */
373                private Intent getShareIntentFromType(String mimeType) {
374                    // Lazily create the intent object.
375                    Intent intent = new Intent(Intent.ACTION_SEND);
376                    if (mimeType.startsWith("video/")) {
377                        intent.setType("video/*");
378                    } else {
379                        if (mimeType.startsWith("image/")) {
380                            intent.setType("image/*");
381                        } else {
382                            Log.w(TAG, "unsupported mimeType " + mimeType);
383                        }
384                    }
385                    return intent;
386                }
387
388                @Override
389                public void onProgressErrorClicked() {
390                    LocalData data = getCurrentLocalData();
391                    getServices().getCaptureSessionManager().removeErrorMessage(
392                            data.getContentUri());
393                    updateBottomControlsByData(data);
394                }
395            };
396
397    private ComboPreferences mPreferences;
398
399    @Override
400    public void onCameraOpened(CameraManager.CameraProxy camera) {
401        /**
402         * The current UI requires that the flash option visibility in front-facing
403         * camera be
404         *   * disabled if back facing camera supports flash
405         *   * hidden if back facing camera does not support flash
406         * We save whether back facing camera supports flash because we cannot get
407         * this in front facing camera without a camera switch.
408         *
409         * If this preference is cleared, we also need to clear the camera facing
410         * setting so we default to opening the camera in back facing camera, and
411         * can save this flash support value again.
412         */
413        if (!mSettingsManager.isSet(SettingsManager.SETTING_FLASH_SUPPORTED_BACK_CAMERA)) {
414            HardwareSpec hardware = new HardwareSpecImpl(camera.getParameters());
415            mSettingsManager.setBoolean(SettingsManager.SETTING_FLASH_SUPPORTED_BACK_CAMERA,
416                hardware.isFlashSupported());
417        }
418
419        if (!mModuleManager.getModuleAgent(mCurrentModeIndex).requestAppForCamera()) {
420            // We shouldn't be here. Just close the camera and leave.
421            camera.release(false);
422            throw new IllegalStateException("Camera opened but the module shouldn't be " +
423                    "requesting");
424        }
425        if (mCurrentModule != null) {
426            SettingsCapabilities capabilities =
427                    SettingsUtil.getSettingsCapabilities(camera);
428            mSettingsManager.changeCamera(camera.getCameraId(), capabilities);
429            mCurrentModule.onCameraAvailable(camera);
430        }
431        mCameraAppUI.onChangeCamera();
432    }
433
434    @Override
435    public void onCameraDisabled(int cameraId) {
436        UsageStatistics.cameraFailure(eventprotos.CameraFailure.FailureReason.SECURITY);
437
438        CameraUtil.showErrorAndFinish(this, R.string.camera_disabled);
439    }
440
441    @Override
442    public void onDeviceOpenFailure(int cameraId) {
443        UsageStatistics.cameraFailure(eventprotos.CameraFailure.FailureReason.OPEN_FAILURE);
444
445        CameraUtil.showErrorAndFinish(this, R.string.cannot_connect_camera);
446    }
447
448    @Override
449    public void onDeviceOpenedAlready(int cameraId) {
450        CameraUtil.showErrorAndFinish(this, R.string.cannot_connect_camera);
451    }
452
453    @Override
454    public void onReconnectionFailure(CameraManager mgr) {
455        UsageStatistics.cameraFailure(eventprotos.CameraFailure.FailureReason.RECONNECT_FAILURE);
456
457        CameraUtil.showErrorAndFinish(this, R.string.cannot_connect_camera);
458    }
459
460    private static class MainHandler extends Handler {
461        final WeakReference<CameraActivity> mActivity;
462
463        public MainHandler(CameraActivity activity, Looper looper) {
464            super(looper);
465            mActivity = new WeakReference<CameraActivity>(activity);
466        }
467
468        @Override
469        public void handleMessage(Message msg) {
470            CameraActivity activity = mActivity.get();
471            if (activity == null) {
472                return;
473            }
474            switch (msg.what) {
475
476                case MSG_CLEAR_SCREEN_ON_FLAG: {
477                    if (!activity.mPaused) {
478                        activity.getWindow().clearFlags(
479                                WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
480                    }
481                    break;
482                }
483            }
484        }
485    }
486
487    private String fileNameFromDataID(int dataID) {
488        final LocalData localData = mDataAdapter.getLocalData(dataID);
489
490        File localFile = new File(localData.getPath());
491        return localFile.getName();
492    }
493
494    private final FilmstripContentPanel.Listener mFilmstripListener =
495            new FilmstripContentPanel.Listener() {
496
497                @Override
498                public void onSwipeOut() {
499                    UsageStatistics.changeScreen(eventprotos.NavigationChange.Mode.PHOTO_CAPTURE,
500                            eventprotos.CameraEvent.InteractionCause.SWIPE_RIGHT);
501                }
502
503                @Override
504                public void onSwipeOutBegin() {
505                    mActionBar.hide();
506                    if (mCurrentModule != null) {
507                        mCurrentModule
508                                .onPreviewVisibilityChanged(ModuleController.VISIBILITY_VISIBLE);
509                    }
510                }
511
512                @Override
513                public void onFilmstripHidden() {
514                    mFilmstripVisible = false;
515                    CameraActivity.this.setFilmstripUiVisibility(false);
516                    // When the user hide the filmstrip (either swipe out or
517                    // tap on back key) we move to the first item so next time
518                    // when the user swipe in the filmstrip, the most recent
519                    // one is shown.
520                    mFilmstripController.goToFirstItem();
521                }
522
523                @Override
524                public void onFilmstripShown() {
525                    mFilmstripVisible = true;
526                    decrementPeekAnimPlayTimes();
527                    updateUiByData(mFilmstripController.getCurrentId());
528                    if (mCurrentModule != null) {
529                        mCurrentModule
530                                .onPreviewVisibilityChanged(ModuleController.VISIBILITY_HIDDEN);
531                    }
532                }
533
534                @Override
535                public void onFocusedDataLongPressed(int dataId) {
536                    // Do nothing.
537                }
538
539                @Override
540                public void onFocusedDataPromoted(int dataID) {
541                    UsageStatistics.photoInteraction(
542                            UsageStatistics.hashFileName(fileNameFromDataID(dataID)),
543                            eventprotos.CameraEvent.InteractionType.DELETE,
544                            InteractionCause.SWIPE_UP);
545
546                    removeData(dataID);
547                }
548
549                @Override
550                public void onFocusedDataDemoted(int dataID) {
551                    UsageStatistics.photoInteraction(
552                            UsageStatistics.hashFileName(fileNameFromDataID(dataID)),
553                            eventprotos.CameraEvent.InteractionType.DELETE,
554                            InteractionCause.SWIPE_DOWN);
555
556                    removeData(dataID);
557                }
558
559                @Override
560                public void onEnterFullScreenUiShown(int dataId) {
561                    if (mFilmstripVisible) {
562                        CameraActivity.this.setFilmstripUiVisibility(true);
563                    }
564                }
565
566                @Override
567                public void onLeaveFullScreenUiShown(int dataId) {
568                    // Do nothing.
569                }
570
571                @Override
572                public void onEnterFullScreenUiHidden(int dataId) {
573                    if (mFilmstripVisible) {
574                        CameraActivity.this.setFilmstripUiVisibility(false);
575                    }
576                }
577
578                @Override
579                public void onLeaveFullScreenUiHidden(int dataId) {
580                    // Do nothing.
581                }
582
583                @Override
584                public void onEnterFilmstrip(int dataId) {
585                    if (mFilmstripVisible) {
586                        CameraActivity.this.setFilmstripUiVisibility(true);
587                    }
588                }
589
590                @Override
591                public void onLeaveFilmstrip(int dataId) {
592                    // Do nothing.
593                }
594
595                @Override
596                public void onDataReloaded() {
597                    if (!mFilmstripVisible) {
598                        return;
599                    }
600                    updateUiByData(mFilmstripController.getCurrentId());
601                }
602
603                @Override
604                public void onDataUpdated(int dataId) {
605                    if (!mFilmstripVisible) {
606                        return;
607                    }
608                    updateUiByData(mFilmstripController.getCurrentId());
609                }
610
611                @Override
612                public void onEnterZoomView(int dataID) {
613                    if (mFilmstripVisible) {
614                        CameraActivity.this.setFilmstripUiVisibility(false);
615                    }
616                }
617
618                @Override
619                public void onDataFocusChanged(final int prevDataId, final int newDataId) {
620                    if (!mFilmstripVisible) {
621                        return;
622                    }
623                    // TODO: This callback is UI event callback, should always
624                    // happen on UI thread. Find the reason for this
625                    // runOnUiThread() and fix it.
626                    runOnUiThread(new Runnable() {
627                        @Override
628                        public void run() {
629                            updateUiByData(newDataId);
630                        }
631                    });
632                }
633            };
634
635    private final LocalDataAdapter.LocalDataListener mLocalDataListener =
636            new LocalDataAdapter.LocalDataListener() {
637                @Override
638                public void onMetadataUpdated(List<Integer> updatedData) {
639                    int currentDataId = mFilmstripController.getCurrentId();
640                    for (Integer dataId : updatedData) {
641                        if (dataId == currentDataId) {
642                            updateBottomControlsByData(mDataAdapter.getLocalData(dataId));
643                        }
644                    }
645                }
646
647                @Override
648                public void onNewDataAdded(LocalData data) {
649                    startPeekAnimation(data);
650                }
651            };
652
653    public void gotoGallery() {
654        UsageStatistics.changeScreen(NavigationChange.Mode.FILMSTRIP,
655                InteractionCause.BUTTON);
656
657        mFilmstripController.goToNextItem();
658    }
659
660    /**
661     * If 'visible' is false, this hides the action bar and switches the
662     * filmstrip UI to lights-out mode.
663     *
664     * @param visible is false, this hides the action bar and switches the
665     *            filmstrip UI to lights-out mode.
666     */
667    private void setFilmstripUiVisibility(boolean visible) {
668        int currentSystemUIVisibility = mAboveFilmstripControlLayout.getSystemUiVisibility();
669        int newSystemUIVisibility = (visible ? View.SYSTEM_UI_FLAG_VISIBLE
670                : View.SYSTEM_UI_FLAG_FULLSCREEN);
671        if (newSystemUIVisibility != currentSystemUIVisibility) {
672            mAboveFilmstripControlLayout.setSystemUiVisibility(newSystemUIVisibility);
673        }
674
675        mCameraAppUI.getFilmstripBottomControls().setVisible(visible);
676        if (visible != mActionBar.isShowing()) {
677            if (visible) {
678                mActionBar.show();
679            } else {
680                mActionBar.hide();
681            }
682        }
683    }
684
685    private void hideSessionProgress() {
686        mCameraAppUI.getFilmstripBottomControls().hideProgress();
687    }
688
689    private void showSessionProgress(CharSequence message) {
690        CameraAppUI.BottomPanel controls =  mCameraAppUI.getFilmstripBottomControls();
691        controls.setProgressText(message);
692        controls.hideControls();
693        controls.hideProgressError();
694        controls.showProgress();
695    }
696
697    private void showProcessError(CharSequence message) {
698        mCameraAppUI.getFilmstripBottomControls().showProgressError(message);
699    }
700
701    private void updateSessionProgress(int progress) {
702        mCameraAppUI.getFilmstripBottomControls().setProgress(progress);
703    }
704
705    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
706    private void setupNfcBeamPush() {
707        NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mAppContext);
708        if (adapter == null) {
709            return;
710        }
711
712        if (!ApiHelper.HAS_SET_BEAM_PUSH_URIS) {
713            // Disable beaming
714            adapter.setNdefPushMessage(null, CameraActivity.this);
715            return;
716        }
717
718        adapter.setBeamPushUris(null, CameraActivity.this);
719        adapter.setBeamPushUrisCallback(new CreateBeamUrisCallback() {
720            @Override
721            public Uri[] createBeamUris(NfcEvent event) {
722                return mNfcPushUris;
723            }
724        }, CameraActivity.this);
725    }
726
727    @Override
728    public void onMenuVisibilityChanged(boolean isVisible) {
729        // TODO: Remove this or bring back the original implementation: cancel
730        // auto-hide actionbar.
731    }
732
733    @Override
734    public boolean onShareTargetSelected(ShareActionProvider shareActionProvider, Intent intent) {
735        int currentDataId = mFilmstripController.getCurrentId();
736        if (currentDataId < 0) {
737            return false;
738        }
739        UsageStatistics.photoInteraction(
740                UsageStatistics.hashFileName(fileNameFromDataID(currentDataId)),
741                eventprotos.CameraEvent.InteractionType.SHARE,
742                InteractionCause.BUTTON);
743        // TODO add intent.getComponent().getPackageName()
744        return true;
745    }
746
747    // Note: All callbacks come back on the main thread.
748    private final SessionListener mSessionListener =
749            new SessionListener() {
750                @Override
751                public void onSessionQueued(final Uri uri) {
752                    notifyNewMedia(uri);
753                }
754
755                @Override
756                public void onSessionDone(final Uri uri) {
757                    Log.v(TAG, "onSessionDone:" + uri);
758                    mDataAdapter.finishSession(uri);
759                }
760
761                @Override
762                public void onSessionProgress(final Uri uri, final int progress) {
763                    if (progress < 0) {
764                        // Do nothing, there is no task for this URI.
765                        return;
766                    }
767                    int currentDataId = mFilmstripController.getCurrentId();
768                    if (currentDataId == -1) {
769                        return;
770                    }
771                    if (uri.equals(
772                            mDataAdapter.getLocalData(currentDataId).getContentUri())) {
773                        updateSessionProgress(progress);
774                    }
775                }
776
777                @Override
778                public void onSessionUpdated(Uri uri) {
779                    mDataAdapter.refresh(uri);
780                }
781
782                @Override
783                public void onSessionFailed(Uri uri, CharSequence reason) {
784                    Log.v(TAG, "onSessionFailed:" + uri);
785
786                    int failedDataId = mDataAdapter.findDataByContentUri(uri);
787                    int currentDataId = mFilmstripController.getCurrentId();
788
789                    if (currentDataId == failedDataId) {
790                        updateSessionProgress(0);
791                        showProcessError(reason);
792                    }
793                    // HERE
794                    mDataAdapter.refresh(uri);
795                }
796            };
797
798    @Override
799    public Context getAndroidContext() {
800        return mAppContext;
801    }
802
803    @Override
804    public void launchActivityByIntent(Intent intent) {
805        startActivityForResult(intent, REQ_CODE_DONT_SWITCH_TO_PREVIEW);
806    }
807
808    @Override
809    public int getCurrentModuleIndex() {
810        return mCurrentModeIndex;
811    }
812
813    @Override
814    public ModuleController getCurrentModuleController() {
815        return mCurrentModule;
816    }
817
818    @Override
819    public int getQuickSwitchToModuleId(int currentModuleIndex) {
820        return mModuleManager.getQuickSwitchToModuleId(currentModuleIndex, mSettingsManager,
821                mAppContext);
822    }
823
824    @Override
825    public SurfaceTexture getPreviewBuffer() {
826        // TODO: implement this
827        return null;
828    }
829
830    @Override
831    public void onPreviewReadyToStart() {
832        mCameraAppUI.onPreviewReadyToStart();
833    }
834
835    @Override
836    public void onPreviewStarted() {
837        mCameraAppUI.onPreviewStarted();
838    }
839
840    @Override
841    public void addPreviewAreaSizeChangedListener(
842            PreviewStatusListener.PreviewAreaChangedListener listener) {
843        mCameraAppUI.addPreviewAreaChangedListener(listener);
844    }
845
846    @Override
847    public void removePreviewAreaSizeChangedListener(
848            PreviewStatusListener.PreviewAreaChangedListener listener) {
849        mCameraAppUI.removePreviewAreaChangedListener(listener);
850    }
851
852    @Override
853    public void setupOneShotPreviewListener() {
854        mCameraController.setOneShotPreviewCallback(mMainHandler,
855                new CameraManager.CameraPreviewDataCallback() {
856                    @Override
857                    public void onPreviewFrame(byte[] data, CameraManager.CameraProxy camera) {
858                        mCameraAppUI.onNewPreviewFrame();
859                    }
860                });
861    }
862
863    @Override
864    public void updatePreviewAspectRatio(float aspectRatio) {
865        mCameraAppUI.updatePreviewAspectRatio(aspectRatio);
866    }
867
868    @Override
869    public boolean shouldShowShimmy() {
870        int remainingTimes = mSettingsManager.getInt(
871                SettingsManager.SETTING_SHIMMY_REMAINING_PLAY_TIMES_INDEX);
872        return remainingTimes > 0;
873    }
874
875    @Override
876    public void decrementShimmyPlayTimes() {
877        int remainingTimes = mSettingsManager.getInt(
878                SettingsManager.SETTING_SHIMMY_REMAINING_PLAY_TIMES_INDEX) - 1;
879        if (remainingTimes >= 0) {
880            mSettingsManager.setInt(SettingsManager.SETTING_SHIMMY_REMAINING_PLAY_TIMES_INDEX,
881                    remainingTimes);
882        }
883    }
884
885    @Override
886    public void updatePreviewTransform(Matrix matrix) {
887        mCameraAppUI.updatePreviewTransform(matrix);
888    }
889
890    @Override
891    public void setPreviewStatusListener(PreviewStatusListener previewStatusListener) {
892        mCameraAppUI.setPreviewStatusListener(previewStatusListener);
893    }
894
895    @Override
896    public FrameLayout getModuleLayoutRoot() {
897        return mCameraAppUI.getModuleRootView();
898    }
899
900    @Override
901    public void setShutterEventsListener(ShutterEventsListener listener) {
902        // TODO: implement this
903    }
904
905    @Override
906    public void setShutterEnabled(boolean enabled) {
907        // TODO: implement this
908    }
909
910    @Override
911    public boolean isShutterEnabled() {
912        // TODO: implement this
913        return false;
914    }
915
916    @Override
917    public void startPreCaptureAnimation() {
918        mCameraAppUI.startPreCaptureAnimation();
919    }
920
921    @Override
922    public void cancelPreCaptureAnimation() {
923        // TODO: implement this
924    }
925
926    @Override
927    public void startPostCaptureAnimation() {
928        // TODO: implement this
929    }
930
931    @Override
932    public void startPostCaptureAnimation(Bitmap thumbnail) {
933        // TODO: implement this
934    }
935
936    @Override
937    public void cancelPostCaptureAnimation() {
938        // TODO: implement this
939    }
940
941    @Override
942    public OrientationManager getOrientationManager() {
943        return mOrientationManager;
944    }
945
946    @Override
947    public LocationManager getLocationManager() {
948        return mLocationManager;
949    }
950
951    @Override
952    public void lockOrientation() {
953        if (mOrientationManager != null) {
954            mOrientationManager.lockOrientation();
955        }
956    }
957
958    @Override
959    public void unlockOrientation() {
960        if (mOrientationManager != null) {
961            mOrientationManager.unlockOrientation();
962        }
963    }
964
965    /**
966     * Decrement the remaining play times for peek animation.
967     */
968    private void decrementPeekAnimPlayTimes() {
969        int remainingTimes = mSettingsManager.getInt(
970                SettingsManager.SETTING_FILMSTRIP_PEEK_ANIM_REMAINING_PLAY_TIMES_INDEX) - 1;
971        if (remainingTimes < 0) {
972            return;
973        }
974        mSettingsManager
975                .setInt(SettingsManager.SETTING_FILMSTRIP_PEEK_ANIM_REMAINING_PLAY_TIMES_INDEX,
976                        remainingTimes);
977    }
978
979    /**
980     * Starts the filmstrip peek animation if the filmstrip is not visible.
981     * Only {@link LocalData#LOCAL_IMAGE}, {@link
982     * LocalData#LOCAL_IN_PROGRESS_DATA} and {@link
983     * LocalData#LOCAL_VIDEO} are supported.
984     *
985     * @param data The data to peek.
986     */
987    private void startPeekAnimation(final LocalData data) {
988        if (mFilmstripVisible || mPeekAnimationHandler == null) {
989            return;
990        }
991
992        int dataType = data.getLocalDataType();
993        if (dataType != LocalData.LOCAL_IMAGE && dataType != LocalData.LOCAL_IN_PROGRESS_DATA &&
994                dataType != LocalData.LOCAL_VIDEO) {
995            return;
996        }
997
998        int remainingTimes = mSettingsManager.getInt(
999                SettingsManager.SETTING_FILMSTRIP_PEEK_ANIM_REMAINING_PLAY_TIMES_INDEX);
1000        if (remainingTimes <= 0) {
1001            return;
1002        }
1003        mPeekAnimationHandler.startDecodingJob(data, new Callback<Bitmap>() {
1004            @Override
1005            public void onCallback(Bitmap result) {
1006                mCameraAppUI.startPeekAnimation(result, true);
1007            }
1008        });
1009    }
1010
1011    @Override
1012    public void notifyNewMedia(Uri uri) {
1013        if (Storage.isSessionUri(uri)) {
1014            mDataAdapter.addNewSession(uri);
1015        } else {
1016
1017            ContentResolver cr = getContentResolver();
1018            String mimeType = cr.getType(uri);
1019            if (LocalDataUtil.isMimeTypeVideo(mimeType)) {
1020                sendBroadcast(new Intent(CameraUtil.ACTION_NEW_VIDEO, uri));
1021                mDataAdapter.addNewVideo(uri);
1022            } else if (LocalDataUtil.isMimeTypeImage(mimeType)) {
1023                CameraUtil.broadcastNewPicture(mAppContext, uri);
1024                mDataAdapter.addNewPhoto(uri);
1025            } else {
1026                android.util.Log.w(TAG, "Unknown new media with MIME type:"
1027                        + mimeType + ", uri:" + uri);
1028            }
1029        }
1030    }
1031
1032    @Override
1033    public void enableKeepScreenOn(boolean enabled) {
1034        if (mPaused) {
1035            return;
1036        }
1037
1038        mKeepScreenOn = enabled;
1039        if (mKeepScreenOn) {
1040            mMainHandler.removeMessages(MSG_CLEAR_SCREEN_ON_FLAG);
1041            getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1042        } else {
1043            keepScreenOnForAWhile();
1044        }
1045    }
1046
1047    @Override
1048    public CameraProvider getCameraProvider() {
1049        return mCameraController;
1050    }
1051
1052    private void removeData(int dataID) {
1053        mDataAdapter.removeData(dataID);
1054        if (mDataAdapter.getTotalNumber() > 1) {
1055            showUndoDeletionBar();
1056        } else {
1057            // If camera preview is the only view left in filmstrip,
1058            // no need to show undo bar.
1059            mPendingDeletion = true;
1060            performDeletion();
1061            if (mFilmstripVisible) {
1062                mCameraAppUI.getFilmstripContentPanel().animateHide();
1063            }
1064        }
1065    }
1066
1067    @Override
1068    public boolean onOptionsItemSelected(MenuItem item) {
1069        // Handle presses on the action bar items
1070        switch (item.getItemId()) {
1071            case android.R.id.home:
1072                if (mFilmstripVisible && startGallery()) {
1073                    return true;
1074                }
1075                onBackPressed();
1076                return true;
1077            case R.id.action_details:
1078                showDetailsDialog(mFilmstripController.getCurrentId());
1079                return true;
1080            default:
1081                return super.onOptionsItemSelected(item);
1082        }
1083    }
1084
1085    private boolean isCaptureIntent() {
1086        if (MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction())
1087                || MediaStore.ACTION_IMAGE_CAPTURE.equals(getIntent().getAction())
1088                || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(getIntent().getAction())) {
1089            return true;
1090        } else {
1091            return false;
1092        }
1093    }
1094
1095    private final SettingsManager.StrictUpgradeCallback mStrictUpgradeCallback
1096        = new SettingsManager.StrictUpgradeCallback() {
1097                @Override
1098                public void upgrade(SettingsManager settingsManager, int version) {
1099                    // Show the location dialog on upgrade if
1100                    //  (a) the user has never set this option (status quo).
1101                    //  (b) the user opt'ed out previously.
1102                    if (settingsManager.isSet(SettingsManager.SETTING_RECORD_LOCATION) &&
1103                            !settingsManager.getBoolean(SettingsManager.SETTING_RECORD_LOCATION)) {
1104                        settingsManager.remove(SettingsManager.SETTING_RECORD_LOCATION);
1105                    }
1106                }
1107            };
1108
1109    private final CameraManager.CameraExceptionCallback mCameraDefaultExceptionCallback
1110        = new CameraManager.CameraExceptionCallback() {
1111                @Override
1112                public void onCameraException(RuntimeException e) {
1113                    Log.d(TAG, "Camera Exception", e);
1114                    CameraUtil.showErrorAndFinish(CameraActivity.this,
1115                            R.string.cannot_connect_camera);
1116                }
1117            };
1118
1119    @Override
1120    public void onCreate(Bundle state) {
1121        super.onCreate(state);
1122        CameraPerformanceTracker.onEvent(CameraPerformanceTracker.ACTIVITY_START);
1123        mOnCreateTime = System.currentTimeMillis();
1124        mAppContext = getApplicationContext();
1125        GcamHelper.init(getContentResolver());
1126
1127        getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
1128        setContentView(R.layout.activity_main);
1129        mActionBar = getActionBar();
1130        mActionBar.addOnMenuVisibilityListener(this);
1131        mMainHandler = new MainHandler(this, getMainLooper());
1132        mCameraController =
1133                new CameraController(mAppContext, this, mMainHandler,
1134                        CameraManagerFactory.getAndroidCameraManager());
1135        mCameraController.setCameraDefaultExceptionCallback(mCameraDefaultExceptionCallback,
1136                mMainHandler);
1137
1138        mPreferences = new ComboPreferences(mAppContext);
1139
1140        mSettingsManager = new SettingsManager(mAppContext, this,
1141                mCameraController.getNumberOfCameras(), mStrictUpgradeCallback);
1142
1143        // Remove this after we get rid of ComboPreferences.
1144        int cameraId = Integer.parseInt(mSettingsManager.get(SettingsManager.SETTING_CAMERA_ID));
1145        mPreferences.setLocalId(mAppContext, cameraId);
1146        CameraSettings.upgradeGlobalPreferences(mPreferences,
1147                mCameraController.getNumberOfCameras());
1148        // TODO: Try to move all the resources allocation to happen as soon as
1149        // possible so we can call module.init() at the earliest time.
1150        mModuleManager = new ModuleManagerImpl();
1151        ModulesInfo.setupModules(mAppContext, mModuleManager);
1152
1153        mModeListView = (ModeListView) findViewById(R.id.mode_list_layout);
1154        mModeListView.init(mModuleManager.getSupportedModeIndexList());
1155        if (ApiHelper.HAS_ROTATION_ANIMATION) {
1156            setRotationAnimation();
1157        }
1158        mModeListView.setVisibilityChangedListener(new ModeListVisibilityChangedListener() {
1159            @Override
1160            public void onVisibilityChanged(boolean visible) {
1161                if (mCurrentModule != null) {
1162                    int visibility = visible ? ModuleController.VISIBILITY_COVERED
1163                            : ModuleController.VISIBILITY_VISIBLE;
1164                    mCurrentModule.onPreviewVisibilityChanged(visibility);
1165                }
1166            }
1167        });
1168
1169        // Check if this is in the secure camera mode.
1170        Intent intent = getIntent();
1171        String action = intent.getAction();
1172        if (INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action)
1173                || ACTION_IMAGE_CAPTURE_SECURE.equals(action)) {
1174            mSecureCamera = true;
1175        } else {
1176            mSecureCamera = intent.getBooleanExtra(SECURE_CAMERA_EXTRA, false);
1177        }
1178
1179        if (mSecureCamera) {
1180            // Foreground event caused by lock screen startup.
1181            // It is necessary to log this in onCreate, to avoid the
1182            // onResume->onPause->onResume sequence.
1183            UsageStatistics.foregrounded(
1184                    eventprotos.ForegroundEvent.ForegroundSource.LOCK_SCREEN);
1185
1186            // Change the window flags so that secure camera can show when
1187            // locked
1188            Window win = getWindow();
1189            WindowManager.LayoutParams params = win.getAttributes();
1190            params.flags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
1191            win.setAttributes(params);
1192
1193            // Filter for screen off so that we can finish secure camera
1194            // activity
1195            // when screen is off.
1196            IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
1197            registerReceiver(mScreenOffReceiver, filter);
1198        }
1199        mCameraAppUI = new CameraAppUI(this,
1200                (MainActivityLayout) findViewById(R.id.activity_root_view), isCaptureIntent());
1201
1202        mCameraAppUI.setFilmstripBottomControlsListener(mMyFilmstripBottomControlListener);
1203
1204        mAboveFilmstripControlLayout =
1205                (FrameLayout) findViewById(R.id.camera_filmstrip_content_layout);
1206
1207        // Add the session listener so we can track the session progress
1208        // updates.
1209        getServices().getCaptureSessionManager().addSessionListener(mSessionListener);
1210        mFilmstripController = ((FilmstripView) findViewById(R.id.filmstrip_view)).getController();
1211        mFilmstripController.setImageGap(
1212                getResources().getDimensionPixelSize(R.dimen.camera_film_strip_gap));
1213        mPanoramaViewHelper = new PanoramaViewHelper(this);
1214        mPanoramaViewHelper.onCreate();
1215        // Set up the camera preview first so the preview shows up ASAP.
1216        mDataAdapter = new CameraDataAdapter(mAppContext,
1217                new ColorDrawable(getResources().getColor(R.color.photo_placeholder)));
1218        mDataAdapter.setLocalDataListener(mLocalDataListener);
1219
1220        mCameraAppUI.getFilmstripContentPanel().setFilmstripListener(mFilmstripListener);
1221
1222        mLocationManager = new LocationManager(mAppContext);
1223
1224        int modeIndex = -1;
1225        int photoIndex = getResources().getInteger(R.integer.camera_mode_photo);
1226        int videoIndex = getResources().getInteger(R.integer.camera_mode_video);
1227        int gcamIndex = getResources().getInteger(R.integer.camera_mode_gcam);
1228        if (MediaStore.INTENT_ACTION_VIDEO_CAMERA.equals(getIntent().getAction())
1229                || MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction())) {
1230            modeIndex = videoIndex;
1231        } else if (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(getIntent().getAction())
1232                || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(getIntent()
1233                        .getAction())) {
1234            modeIndex = photoIndex;
1235        } else if (MediaStore.ACTION_IMAGE_CAPTURE.equals(getIntent().getAction())
1236                || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(getIntent().getAction())) {
1237            modeIndex = photoIndex;
1238        } else {
1239            // If the activity has not been started using an explicit intent,
1240            // read the module index from the last time the user changed modes
1241            modeIndex = mSettingsManager.getInt(SettingsManager.SETTING_STARTUP_MODULE_INDEX);
1242            if ((modeIndex == gcamIndex &&
1243                    !GcamHelper.hasGcamCapture()) || modeIndex < 0) {
1244                modeIndex = photoIndex;
1245            }
1246        }
1247
1248        mOrientationManager = new OrientationManagerImpl(this);
1249        mOrientationManager.addOnOrientationChangeListener(mMainHandler, this);
1250
1251        setModuleFromModeIndex(modeIndex);
1252        mCameraAppUI.prepareModuleUI();
1253        mCurrentModule.init(this, isSecureCamera(), isCaptureIntent());
1254
1255        if (!mSecureCamera) {
1256            mFilmstripController.setDataAdapter(mDataAdapter);
1257            if (!isCaptureIntent()) {
1258                mDataAdapter.requestLoad();
1259            }
1260        } else {
1261            // Put a lock placeholder as the last image by setting its date to
1262            // 0.
1263            ImageView v = (ImageView) getLayoutInflater().inflate(
1264                    R.layout.secure_album_placeholder, null);
1265            v.setOnClickListener(new View.OnClickListener() {
1266                @Override
1267                public void onClick(View view) {
1268                    UsageStatistics.changeScreen(NavigationChange.Mode.GALLERY,
1269                            InteractionCause.BUTTON);
1270                    startGallery();
1271                    finish();
1272                }
1273            });
1274            mDataAdapter = new FixedLastDataAdapter(
1275                    mAppContext,
1276                    mDataAdapter,
1277                    new SimpleViewData(
1278                            v,
1279                            v.getDrawable().getIntrinsicWidth(),
1280                            v.getDrawable().getIntrinsicHeight(),
1281                            0, 0));
1282            // Flush out all the original data.
1283            mDataAdapter.flush();
1284            mFilmstripController.setDataAdapter(mDataAdapter);
1285        }
1286
1287        setupNfcBeamPush();
1288
1289        mLocalImagesObserver = new LocalMediaObserver();
1290        mLocalVideosObserver = new LocalMediaObserver();
1291
1292        getContentResolver().registerContentObserver(
1293                MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true,
1294                mLocalImagesObserver);
1295        getContentResolver().registerContentObserver(
1296                MediaStore.Video.Media.EXTERNAL_CONTENT_URI, true,
1297                mLocalVideosObserver);
1298        if (FeedbackHelper.feedbackAvailable()) {
1299            mFeedbackHelper = new FeedbackHelper(mAppContext);
1300        }
1301    }
1302
1303    private void setRotationAnimation() {
1304        int rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
1305        rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE;
1306        Window win = getWindow();
1307        WindowManager.LayoutParams winParams = win.getAttributes();
1308        winParams.rotationAnimation = rotationAnimation;
1309        win.setAttributes(winParams);
1310    }
1311
1312    @Override
1313    public void onUserInteraction() {
1314        super.onUserInteraction();
1315        if (!isFinishing()) {
1316            keepScreenOnForAWhile();
1317        }
1318    }
1319
1320    @Override
1321    public boolean dispatchTouchEvent(MotionEvent ev) {
1322        boolean result = super.dispatchTouchEvent(ev);
1323        if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
1324            // Real deletion is postponed until the next user interaction after
1325            // the gesture that triggers deletion. Until real deletion is
1326            // performed, users can click the undo button to bring back the
1327            // image that they chose to delete.
1328            if (mPendingDeletion && !mIsUndoingDeletion) {
1329                performDeletion();
1330            }
1331        }
1332        return result;
1333    }
1334
1335    @Override
1336    public void onPause() {
1337        mPaused = true;
1338        mPeekAnimationHandler = null;
1339        mPeekAnimationThread.quitSafely();
1340        mPeekAnimationThread = null;
1341        CameraPerformanceTracker.onEvent(CameraPerformanceTracker.ACTIVITY_PAUSE);
1342
1343        // Delete photos that are pending deletion
1344        performDeletion();
1345        mCurrentModule.pause();
1346        mOrientationManager.pause();
1347        // Close the camera and wait for the operation done.
1348        mCameraController.closeCamera();
1349        mPanoramaViewHelper.onPause();
1350
1351        mLocalImagesObserver.setActivityPaused(true);
1352        mLocalVideosObserver.setActivityPaused(true);
1353        resetScreenOn();
1354        super.onPause();
1355    }
1356
1357    @Override
1358    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
1359        if (requestCode == REQ_CODE_DONT_SWITCH_TO_PREVIEW) {
1360            mResetToPreviewOnResume = false;
1361        } else {
1362            super.onActivityResult(requestCode, resultCode, data);
1363        }
1364    }
1365
1366    @Override
1367    public void onResume() {
1368        mPaused = false;
1369        CameraPerformanceTracker.onEvent(CameraPerformanceTracker.ACTIVITY_RESUME);
1370
1371        mLastLayoutOrientation = getResources().getConfiguration().orientation;
1372
1373        // TODO: Handle this in OrientationManager.
1374        // Auto-rotate off
1375        if (Settings.System.getInt(getContentResolver(),
1376                Settings.System.ACCELEROMETER_ROTATION, 0) == 0) {
1377            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
1378            mAutoRotateScreen = false;
1379        } else {
1380            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
1381            mAutoRotateScreen = true;
1382        }
1383
1384        if (isCaptureIntent()) {
1385            // Foreground event caused by photo or video capure intent.
1386            UsageStatistics.foregrounded(
1387                    eventprotos.ForegroundEvent.ForegroundSource.INTENT_PICKER);
1388        } else if (!mSecureCamera) {
1389            // Foreground event that is not caused by an intent.
1390            UsageStatistics.foregrounded(
1391                    eventprotos.ForegroundEvent.ForegroundSource.ICON_LAUNCHER);
1392        }
1393
1394        Drawable galleryLogo;
1395        if (mSecureCamera) {
1396            mGalleryIntent = null;
1397            galleryLogo = null;
1398        } else {
1399            mGalleryIntent = IntentHelper.getDefaultGalleryIntent(mAppContext);
1400            galleryLogo = IntentHelper.getGalleryIcon(mAppContext, mGalleryIntent);
1401        }
1402        if (galleryLogo == null) {
1403            try {
1404                galleryLogo = getPackageManager().getActivityLogo(getComponentName());
1405            } catch (PackageManager.NameNotFoundException e) {
1406                Log.e(TAG, "Can't get the activity logo");
1407            }
1408        }
1409        if (mGalleryIntent != null) {
1410            mActionBar.setDisplayUseLogoEnabled(true);
1411        }
1412        mActionBar.setLogo(galleryLogo);
1413        mOrientationManager.resume();
1414        super.onResume();
1415        mPeekAnimationThread = new HandlerThread("Peek animation");
1416        mPeekAnimationThread.start();
1417        mPeekAnimationHandler = new PeekAnimationHandler(mPeekAnimationThread.getLooper());
1418        mCurrentModule.resume();
1419        setSwipingEnabled(true);
1420
1421        if (mResetToPreviewOnResume) {
1422            mCameraAppUI.resume();
1423        } else {
1424            LocalData data = mDataAdapter.getLocalData(mFilmstripController.getCurrentId());
1425            if (data != null) {
1426                mDataAdapter.refresh(data.getContentUri());
1427            }
1428        }
1429        // The share button might be disabled to avoid double tapping.
1430        mCameraAppUI.getFilmstripBottomControls().setShareEnabled(true);
1431        // Default is showing the preview, unless disabled by explicitly
1432        // starting an activity we want to return from to the filmstrip rather
1433        // than the preview.
1434        mResetToPreviewOnResume = true;
1435
1436        if (mLocalVideosObserver.isMediaDataChangedDuringPause()
1437                || mLocalImagesObserver.isMediaDataChangedDuringPause()) {
1438            if (!mSecureCamera) {
1439                // If it's secure camera, requestLoad() should not be called
1440                // as it will load all the data.
1441                if (!mFilmstripVisible) {
1442                    mDataAdapter.requestLoad();
1443                }
1444            }
1445        }
1446        mLocalImagesObserver.setActivityPaused(false);
1447        mLocalVideosObserver.setActivityPaused(false);
1448
1449        keepScreenOnForAWhile();
1450
1451        // Lights-out mode at all times.
1452        findViewById(R.id.activity_root_view)
1453                .setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE);
1454        mPanoramaViewHelper.onResume();
1455        ReleaseDialogHelper.showReleaseInfoDialogOnStart(this, mSettingsManager);
1456        syncLocationManagerSetting();
1457    }
1458
1459    @Override
1460    public void onStart() {
1461        super.onStart();
1462        mPanoramaViewHelper.onStart();
1463        if (mResetToPreviewOnResume) {
1464            mCameraAppUI.resume();
1465            mResetToPreviewOnResume = false;
1466        }
1467    }
1468
1469    @Override
1470    protected void onStop() {
1471        mPanoramaViewHelper.onStop();
1472        if (mFeedbackHelper != null) {
1473            mFeedbackHelper.stopFeedback();
1474        }
1475
1476        mLocationManager.disconnect();
1477        super.onStop();
1478    }
1479
1480    @Override
1481    public void onDestroy() {
1482        if (mSecureCamera) {
1483            unregisterReceiver(mScreenOffReceiver);
1484        }
1485        mActionBar.removeOnMenuVisibilityListener(this);
1486        mSettingsManager.removeAllListeners();
1487        mCameraController.removeCallbackReceiver();
1488        getContentResolver().unregisterContentObserver(mLocalImagesObserver);
1489        getContentResolver().unregisterContentObserver(mLocalVideosObserver);
1490        getServices().getCaptureSessionManager().removeSessionListener(mSessionListener);
1491        mCameraAppUI.onDestroy();
1492        mCameraController = null;
1493        mSettingsManager = null;
1494        mCameraAppUI = null;
1495        mOrientationManager = null;
1496        mButtonManager = null;
1497        CameraManagerFactory.recycle();
1498        super.onDestroy();
1499    }
1500
1501    @Override
1502    public void onConfigurationChanged(Configuration config) {
1503        super.onConfigurationChanged(config);
1504        Log.v(TAG, "onConfigurationChanged");
1505        if (config.orientation == Configuration.ORIENTATION_UNDEFINED) {
1506            return;
1507        }
1508
1509        if (mLastLayoutOrientation != config.orientation) {
1510            mLastLayoutOrientation = config.orientation;
1511            mCurrentModule.onLayoutOrientationChanged(
1512                    mLastLayoutOrientation == Configuration.ORIENTATION_LANDSCAPE);
1513        }
1514    }
1515
1516    @Override
1517    public boolean onKeyDown(int keyCode, KeyEvent event) {
1518        if (!mFilmstripVisible) {
1519            if (mCurrentModule.onKeyDown(keyCode, event)) {
1520                return true;
1521            }
1522            // Prevent software keyboard or voice search from showing up.
1523            if (keyCode == KeyEvent.KEYCODE_SEARCH
1524                    || keyCode == KeyEvent.KEYCODE_MENU) {
1525                if (event.isLongPress()) {
1526                    return true;
1527                }
1528            }
1529        }
1530
1531        return super.onKeyDown(keyCode, event);
1532    }
1533
1534    @Override
1535    public boolean onKeyUp(int keyCode, KeyEvent event) {
1536        if (!mFilmstripVisible && mCurrentModule.onKeyUp(keyCode, event)) {
1537            return true;
1538        }
1539        return super.onKeyUp(keyCode, event);
1540    }
1541
1542    @Override
1543    public void onBackPressed() {
1544        if (!mCameraAppUI.onBackPressed()) {
1545            if (!mCurrentModule.onBackPressed()) {
1546                super.onBackPressed();
1547            }
1548        }
1549    }
1550
1551    @Override
1552    public boolean isAutoRotateScreen() {
1553        // TODO: Move to OrientationManager.
1554        return mAutoRotateScreen;
1555    }
1556
1557    @Override
1558    public boolean onCreateOptionsMenu(Menu menu) {
1559        MenuInflater inflater = getMenuInflater();
1560        inflater.inflate(R.menu.filmstrip_menu, menu);
1561        mActionBarMenu = menu;
1562        return super.onCreateOptionsMenu(menu);
1563    }
1564
1565    protected void updateStorageSpace() {
1566        mStorageSpaceBytes = Storage.getAvailableSpace();
1567    }
1568
1569    protected long getStorageSpaceBytes() {
1570        return mStorageSpaceBytes;
1571    }
1572
1573    protected void updateStorageSpaceAndHint() {
1574        updateStorageSpace();
1575        updateStorageHint(mStorageSpaceBytes);
1576    }
1577
1578    protected void updateStorageHint(long storageSpace) {
1579        String message = null;
1580        if (storageSpace == Storage.UNAVAILABLE) {
1581            message = getString(R.string.no_storage);
1582        } else if (storageSpace == Storage.PREPARING) {
1583            message = getString(R.string.preparing_sd);
1584        } else if (storageSpace == Storage.UNKNOWN_SIZE) {
1585            message = getString(R.string.access_sd_fail);
1586        } else if (storageSpace <= Storage.LOW_STORAGE_THRESHOLD_BYTES) {
1587            message = getString(R.string.spaceIsLow_content);
1588        }
1589
1590        if (message != null) {
1591            if (mStorageHint == null) {
1592                mStorageHint = OnScreenHint.makeText(mAppContext, message);
1593            } else {
1594                mStorageHint.setText(message);
1595            }
1596            mStorageHint.show();
1597        } else if (mStorageHint != null) {
1598            mStorageHint.cancel();
1599            mStorageHint = null;
1600        }
1601    }
1602
1603    protected void setResultEx(int resultCode) {
1604        mResultCodeForTesting = resultCode;
1605        setResult(resultCode);
1606    }
1607
1608    protected void setResultEx(int resultCode, Intent data) {
1609        mResultCodeForTesting = resultCode;
1610        mResultDataForTesting = data;
1611        setResult(resultCode, data);
1612    }
1613
1614    public int getResultCode() {
1615        return mResultCodeForTesting;
1616    }
1617
1618    public Intent getResultData() {
1619        return mResultDataForTesting;
1620    }
1621
1622    public boolean isSecureCamera() {
1623        return mSecureCamera;
1624    }
1625
1626    @Override
1627    public boolean isPaused() {
1628        return mPaused;
1629    }
1630
1631    @Override
1632    public int getPreferredChildModeIndex(int modeIndex) {
1633        if (modeIndex == getResources().getInteger(R.integer.camera_mode_photo)) {
1634            boolean hdrPlusOn = mSettingsManager.isHdrPlusOn();
1635            if (hdrPlusOn && GcamHelper.hasGcamCapture()) {
1636                modeIndex = getResources().getInteger(R.integer.camera_mode_gcam);
1637            }
1638        }
1639        return modeIndex;
1640    }
1641
1642    @Override
1643    public void onModeSelected(int modeIndex) {
1644        if (mCurrentModeIndex == modeIndex) {
1645            return;
1646        }
1647
1648        CameraPerformanceTracker.onEvent(CameraPerformanceTracker.MODE_SWITCH_START);
1649        // Record last used camera mode for quick switching
1650        if (modeIndex == getResources().getInteger(R.integer.camera_mode_photo)
1651                || modeIndex == getResources().getInteger(R.integer.camera_mode_gcam)) {
1652            mSettingsManager.setInt(SettingsManager.SETTING_KEY_CAMERA_MODULE_LAST_USED_INDEX,
1653                    modeIndex);
1654        }
1655
1656        closeModule(mCurrentModule);
1657        int oldModuleIndex = mCurrentModeIndex;
1658
1659        // Select the correct module index from the mode switcher index.
1660        modeIndex = getPreferredChildModeIndex(modeIndex);
1661        setModuleFromModeIndex(modeIndex);
1662
1663        mCameraAppUI.resetBottomControls(mCurrentModule, modeIndex);
1664        mCameraAppUI.addShutterListener(mCurrentModule);
1665        openModule(mCurrentModule);
1666        mCurrentModule.onOrientationChanged(mLastRawOrientation);
1667        // Store the module index so we can use it the next time the Camera
1668        // starts up.
1669        SharedPreferences prefs = PreferenceManager
1670                .getDefaultSharedPreferences(mAppContext);
1671        prefs.edit().putInt(CameraSettings.KEY_STARTUP_MODULE_INDEX, modeIndex).apply();
1672    }
1673
1674    /**
1675     * Shows the settings dialog.
1676     */
1677    @Override
1678    public void onSettingsSelected() {
1679        Intent intent = new Intent(this, CameraSettingsActivity.class);
1680        startActivity(intent);
1681    }
1682
1683    /**
1684     * Sets the mCurrentModuleIndex, creates a new module instance for the given
1685     * index an sets it as mCurrentModule.
1686     */
1687    private void setModuleFromModeIndex(int modeIndex) {
1688        ModuleManagerImpl.ModuleAgent agent = mModuleManager.getModuleAgent(modeIndex);
1689        if (agent == null) {
1690            return;
1691        }
1692        if (!agent.requestAppForCamera()) {
1693            mCameraController.closeCamera();
1694        }
1695        mCurrentModeIndex = agent.getModuleId();
1696        mCurrentModule = (CameraModule) agent.createModule(this);
1697    }
1698
1699    @Override
1700    public SettingsManager getSettingsManager() {
1701        return mSettingsManager;
1702    }
1703
1704    @Override
1705    public CameraServices getServices() {
1706        return (CameraServices) getApplication();
1707    }
1708
1709    public List<String> getSupportedModeNames() {
1710        List<Integer> indices = mModuleManager.getSupportedModeIndexList();
1711        List<String> supported = new ArrayList<String>();
1712
1713        for (Integer modeIndex : indices) {
1714            String name = CameraUtil.getCameraModeText(modeIndex, mAppContext);
1715            if (name != null && !name.equals("")) {
1716                supported.add(name);
1717            }
1718        }
1719        return supported;
1720    }
1721
1722    @Override
1723    public ButtonManager getButtonManager() {
1724        if (mButtonManager == null) {
1725            mButtonManager = new ButtonManager(this);
1726        }
1727        return mButtonManager;
1728    }
1729
1730    /**
1731     * Creates an AlertDialog appropriate for choosing whether to enable
1732     * location on the first run of the app.
1733     */
1734    public AlertDialog getFirstTimeLocationAlert() {
1735        AlertDialog.Builder builder = new AlertDialog.Builder(this);
1736        builder = SettingsUtil.getFirstTimeLocationAlertBuilder(builder, new Callback<Boolean>() {
1737            @Override
1738            public void onCallback(Boolean locationOn) {
1739                mSettingsManager.setLocation(locationOn, mLocationManager);
1740            }
1741        });
1742        if (builder != null) {
1743            return builder.create();
1744        } else {
1745            return null;
1746        }
1747    }
1748
1749    /**
1750     * Launches an ACTION_EDIT intent for the given local data item. If
1751     * 'withTinyPlanet' is set, this will show a disambig dialog first to let
1752     * the user start either the tiny planet editor or another photo edior.
1753     *
1754     * @param data The data item to edit.
1755     */
1756    public void launchEditor(LocalData data) {
1757        Intent intent = new Intent(Intent.ACTION_EDIT)
1758                .setDataAndType(data.getContentUri(), data.getMimeType())
1759                .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
1760        try {
1761            launchActivityByIntent(intent);
1762        } catch (ActivityNotFoundException e) {
1763            launchActivityByIntent(Intent.createChooser(intent, null));
1764        }
1765    }
1766
1767    @Override
1768    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
1769        super.onCreateContextMenu(menu, v, menuInfo);
1770
1771        MenuInflater inflater = getMenuInflater();
1772        inflater.inflate(R.menu.filmstrip_context_menu, menu);
1773    }
1774
1775    @Override
1776    public boolean onContextItemSelected(MenuItem item) {
1777        switch (item.getItemId()) {
1778            case R.id.tiny_planet_editor:
1779                mMyFilmstripBottomControlListener.onTinyPlanet();
1780                return true;
1781            case R.id.photo_editor:
1782                mMyFilmstripBottomControlListener.onEdit();
1783                return true;
1784        }
1785        return false;
1786    }
1787
1788    /**
1789     * Launch the tiny planet editor.
1790     *
1791     * @param data The data must be a 360 degree stereographically mapped
1792     *            panoramic image. It will not be modified, instead a new item
1793     *            with the result will be added to the filmstrip.
1794     */
1795    public void launchTinyPlanetEditor(LocalData data) {
1796        TinyPlanetFragment fragment = new TinyPlanetFragment();
1797        Bundle bundle = new Bundle();
1798        bundle.putString(TinyPlanetFragment.ARGUMENT_URI, data.getContentUri().toString());
1799        bundle.putString(TinyPlanetFragment.ARGUMENT_TITLE, data.getTitle());
1800        fragment.setArguments(bundle);
1801        fragment.show(getFragmentManager(), "tiny_planet");
1802    }
1803
1804    private void openModule(CameraModule module) {
1805        module.init(this, isSecureCamera(), isCaptureIntent());
1806        module.resume();
1807        int visibility = mFilmstripVisible ? ModuleController.VISIBILITY_HIDDEN
1808                : ModuleController.VISIBILITY_VISIBLE;
1809        module.onPreviewVisibilityChanged(visibility);
1810    }
1811
1812    private void closeModule(CameraModule module) {
1813        module.pause();
1814        mCameraAppUI.clearModuleUI();
1815    }
1816
1817    private void performDeletion() {
1818        if (!mPendingDeletion) {
1819            return;
1820        }
1821        hideUndoDeletionBar(false);
1822        mDataAdapter.executeDeletion();
1823    }
1824
1825    public void showUndoDeletionBar() {
1826        if (mPendingDeletion) {
1827            performDeletion();
1828        }
1829        Log.v(TAG, "showing undo bar");
1830        mPendingDeletion = true;
1831        if (mUndoDeletionBar == null) {
1832            ViewGroup v = (ViewGroup) getLayoutInflater().inflate(R.layout.undo_bar,
1833                    mAboveFilmstripControlLayout, true);
1834            mUndoDeletionBar = (ViewGroup) v.findViewById(R.id.camera_undo_deletion_bar);
1835            View button = mUndoDeletionBar.findViewById(R.id.camera_undo_deletion_button);
1836            button.setOnClickListener(new View.OnClickListener() {
1837                @Override
1838                public void onClick(View view) {
1839                    mDataAdapter.undoDataRemoval();
1840                    hideUndoDeletionBar(true);
1841                }
1842            });
1843            // Setting undo bar clickable to avoid touch events going through
1844            // the bar to the buttons (eg. edit button, etc) underneath the bar.
1845            mUndoDeletionBar.setClickable(true);
1846            // When there is user interaction going on with the undo button, we
1847            // do not want to hide the undo bar.
1848            button.setOnTouchListener(new View.OnTouchListener() {
1849                @Override
1850                public boolean onTouch(View v, MotionEvent event) {
1851                    if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
1852                        mIsUndoingDeletion = true;
1853                    } else if (event.getActionMasked() == MotionEvent.ACTION_UP) {
1854                        mIsUndoingDeletion = false;
1855                    }
1856                    return false;
1857                }
1858            });
1859        }
1860        mUndoDeletionBar.setAlpha(0f);
1861        mUndoDeletionBar.setVisibility(View.VISIBLE);
1862        mUndoDeletionBar.animate().setDuration(200).alpha(1f).setListener(null).start();
1863    }
1864
1865    private void hideUndoDeletionBar(boolean withAnimation) {
1866        Log.v(TAG, "Hiding undo deletion bar");
1867        mPendingDeletion = false;
1868        if (mUndoDeletionBar != null) {
1869            if (withAnimation) {
1870                mUndoDeletionBar.animate().setDuration(200).alpha(0f)
1871                        .setListener(new Animator.AnimatorListener() {
1872                            @Override
1873                            public void onAnimationStart(Animator animation) {
1874                                // Do nothing.
1875                            }
1876
1877                            @Override
1878                            public void onAnimationEnd(Animator animation) {
1879                                mUndoDeletionBar.setVisibility(View.GONE);
1880                            }
1881
1882                            @Override
1883                            public void onAnimationCancel(Animator animation) {
1884                                // Do nothing.
1885                            }
1886
1887                            @Override
1888                            public void onAnimationRepeat(Animator animation) {
1889                                // Do nothing.
1890                            }
1891                        }).start();
1892            } else {
1893                mUndoDeletionBar.setVisibility(View.GONE);
1894            }
1895        }
1896    }
1897
1898    @Override
1899    public void onOrientationChanged(int orientation) {
1900        // We keep the last known orientation. So if the user first orient
1901        // the camera then point the camera to floor or sky, we still have
1902        // the correct orientation.
1903        if (orientation == OrientationManager.ORIENTATION_UNKNOWN) {
1904            return;
1905        }
1906        mLastRawOrientation = orientation;
1907        if (mCurrentModule != null) {
1908            mCurrentModule.onOrientationChanged(orientation);
1909        }
1910    }
1911
1912    /**
1913     * Enable/disable swipe-to-filmstrip. Will always disable swipe if in
1914     * capture intent.
1915     *
1916     * @param enable {@code true} to enable swipe.
1917     */
1918    public void setSwipingEnabled(boolean enable) {
1919        // TODO: Bring back the functionality.
1920        if (isCaptureIntent()) {
1921            // lockPreview(true);
1922        } else {
1923            // lockPreview(!enable);
1924        }
1925    }
1926
1927    // Accessor methods for getting latency times used in performance testing
1928    public long getFirstPreviewTime() {
1929        if (mCurrentModule instanceof PhotoModule) {
1930            long coverHiddenTime = getCameraAppUI().getCoverHiddenTime();
1931            if (coverHiddenTime != -1) {
1932                return coverHiddenTime - mOnCreateTime;
1933            }
1934        }
1935        return -1;
1936    }
1937
1938    public long getAutoFocusTime() {
1939        return (mCurrentModule instanceof PhotoModule) ?
1940                ((PhotoModule) mCurrentModule).mAutoFocusTime : -1;
1941    }
1942
1943    public long getShutterLag() {
1944        return (mCurrentModule instanceof PhotoModule) ?
1945                ((PhotoModule) mCurrentModule).mShutterLag : -1;
1946    }
1947
1948    public long getShutterToPictureDisplayedTime() {
1949        return (mCurrentModule instanceof PhotoModule) ?
1950                ((PhotoModule) mCurrentModule).mShutterToPictureDisplayedTime : -1;
1951    }
1952
1953    public long getPictureDisplayedToJpegCallbackTime() {
1954        return (mCurrentModule instanceof PhotoModule) ?
1955                ((PhotoModule) mCurrentModule).mPictureDisplayedToJpegCallbackTime : -1;
1956    }
1957
1958    public long getJpegCallbackFinishTime() {
1959        return (mCurrentModule instanceof PhotoModule) ?
1960                ((PhotoModule) mCurrentModule).mJpegCallbackFinishTime : -1;
1961    }
1962
1963    public long getCaptureStartTime() {
1964        return (mCurrentModule instanceof PhotoModule) ?
1965                ((PhotoModule) mCurrentModule).mCaptureStartTime : -1;
1966    }
1967
1968    public boolean isRecording() {
1969        return (mCurrentModule instanceof VideoModule) ?
1970                ((VideoModule) mCurrentModule).isRecording() : false;
1971    }
1972
1973    public CameraManager.CameraOpenCallback getCameraOpenErrorCallback() {
1974        return mCameraController;
1975    }
1976
1977    // For debugging purposes only.
1978    public CameraModule getCurrentModule() {
1979        return mCurrentModule;
1980    }
1981
1982    @Override
1983    public void showTutorial(AbstractTutorialOverlay tutorial) {
1984        mCameraAppUI.showTutorial(tutorial, getLayoutInflater());
1985    }
1986
1987    /**
1988     * Reads the current location recording settings and passes it on to the
1989     * location manager.
1990     */
1991    public void syncLocationManagerSetting() {
1992        mSettingsManager.syncLocationManager(mLocationManager);
1993    }
1994
1995    private void keepScreenOnForAWhile() {
1996        if (mKeepScreenOn) {
1997            return;
1998        }
1999        mMainHandler.removeMessages(MSG_CLEAR_SCREEN_ON_FLAG);
2000        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
2001        mMainHandler.sendEmptyMessageDelayed(MSG_CLEAR_SCREEN_ON_FLAG, SCREEN_DELAY_MS);
2002    }
2003
2004    private void resetScreenOn() {
2005        mKeepScreenOn = false;
2006        mMainHandler.removeMessages(MSG_CLEAR_SCREEN_ON_FLAG);
2007        getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
2008    }
2009
2010    /**
2011     * @return {@code true} if the Gallery is launched successfully.
2012     */
2013    private boolean startGallery() {
2014        if (mGalleryIntent == null) {
2015            return false;
2016        }
2017        try {
2018            UsageStatistics.changeScreen(NavigationChange.Mode.GALLERY, InteractionCause.BUTTON);
2019            Intent startGalleryIntent = new Intent(mGalleryIntent);
2020            int currentDataId = mFilmstripController.getCurrentId();
2021            LocalData currentLocalData = mDataAdapter.getLocalData(currentDataId);
2022            if (currentLocalData != null) {
2023                GalleryHelper.setContentUri(startGalleryIntent, currentLocalData.getContentUri());
2024            }
2025            launchActivityByIntent(startGalleryIntent);
2026        } catch (ActivityNotFoundException e) {
2027            Log.w(TAG, "Failed to launch gallery activity, closing");
2028        }
2029        return false;
2030    }
2031
2032    private void setNfcBeamPushUriFromData(LocalData data) {
2033        final Uri uri = data.getContentUri();
2034        if (uri != Uri.EMPTY) {
2035            mNfcPushUris[0] = uri;
2036        } else {
2037            mNfcPushUris[0] = null;
2038        }
2039    }
2040
2041    /**
2042     * Updates the visibility of the filmstrip bottom controls and action bar.
2043     */
2044    private void updateUiByData(final int dataId) {
2045        final LocalData currentData = mDataAdapter.getLocalData(dataId);
2046        if (currentData == null) {
2047            Log.w(TAG, "Current data ID not found.");
2048            hideSessionProgress();
2049            return;
2050        }
2051        updateActionBarMenu(currentData);
2052
2053        /* Bottom controls. */
2054        updateBottomControlsByData(currentData);
2055
2056        if (isSecureCamera()) {
2057            // We cannot show buttons in secure camera since go to other
2058            // activities might create a security hole.
2059            mCameraAppUI.getFilmstripBottomControls().hideControls();
2060            return;
2061        }
2062
2063
2064        setNfcBeamPushUriFromData(currentData);
2065
2066        if (!mDataAdapter.isMetadataUpdated(dataId)) {
2067            mDataAdapter.updateMetadata(dataId);
2068        }
2069    }
2070
2071    /**
2072     * Updates the bottom controls based on the data.
2073     */
2074    private void updateBottomControlsByData(final LocalData currentData) {
2075
2076        final CameraAppUI.BottomPanel filmstripBottomPanel =
2077                mCameraAppUI.getFilmstripBottomControls();
2078        filmstripBottomPanel.showControls();
2079        filmstripBottomPanel.setEditButtonVisibility(
2080                currentData.isDataActionSupported(LocalData.DATA_ACTION_EDIT));
2081        filmstripBottomPanel.setShareButtonVisibility(
2082                currentData.isDataActionSupported(LocalData.DATA_ACTION_SHARE));
2083        filmstripBottomPanel.setDeleteButtonVisibility(
2084                currentData.isDataActionSupported(LocalData.DATA_ACTION_DELETE));
2085
2086        /* Progress bar */
2087
2088        Uri contentUri = currentData.getContentUri();
2089        CaptureSessionManager sessionManager = getServices()
2090                .getCaptureSessionManager();
2091
2092        if (sessionManager.hasErrorMessage(contentUri)) {
2093            showProcessError(sessionManager.getErrorMesage(contentUri));
2094        } else {
2095            filmstripBottomPanel.hideProgressError();
2096            int sessionProgress = sessionManager.getSessionProgress(contentUri);
2097
2098            if (sessionProgress < 0) {
2099                hideSessionProgress();
2100            } else {
2101                CharSequence progressMessage = sessionManager
2102                        .getSessionProgressMessage(contentUri);
2103                showSessionProgress(progressMessage);
2104                updateSessionProgress(sessionProgress);
2105            }
2106        }
2107
2108        /* View button */
2109
2110        // We need to add this to a separate DB.
2111        final int viewButtonVisibility;
2112        if (PanoramaMetadataLoader.isPanoramaAndUseViewer(currentData)) {
2113            viewButtonVisibility = CameraAppUI.BottomPanel.VIEWER_PHOTO_SPHERE;
2114        } else if (RgbzMetadataLoader.hasRGBZData(currentData)) {
2115            viewButtonVisibility = CameraAppUI.BottomPanel.VIEWER_REFOCUS;
2116        } else {
2117            viewButtonVisibility = CameraAppUI.BottomPanel.VIEWER_NONE;
2118        }
2119
2120        filmstripBottomPanel.setTinyPlanetEnabled(
2121                PanoramaMetadataLoader.isPanorama360(currentData));
2122        filmstripBottomPanel.setViewerButtonVisibility(viewButtonVisibility);
2123    }
2124
2125    private class PeekAnimationHandler extends Handler {
2126        private class DataAndCallback {
2127            LocalData mData;
2128            com.android.camera.util.Callback<Bitmap> mCallback;
2129
2130            public DataAndCallback(LocalData data, com.android.camera.util.Callback<Bitmap>
2131                    callback) {
2132                mData = data;
2133                mCallback = callback;
2134            }
2135        }
2136
2137        public PeekAnimationHandler(Looper looper) {
2138            super(looper);
2139        }
2140
2141        /**
2142         * Starts the animation decoding job and posts a {@code Runnable} back
2143         * when when the decoding is done.
2144         *
2145         * @param data The data item to decode the thumbnail for.
2146         * @param callback {@link com.android.camera.util.Callback} after the
2147         *                 decoding is done.
2148         */
2149        public void startDecodingJob(final LocalData data,
2150                final com.android.camera.util.Callback<Bitmap> callback) {
2151            PeekAnimationHandler.this.obtainMessage(0 /** dummy integer **/,
2152                    new DataAndCallback(data, callback)).sendToTarget();
2153        }
2154
2155        @Override
2156        public void handleMessage(Message msg) {
2157            final LocalData data = ((DataAndCallback) msg.obj).mData;
2158            final com.android.camera.util.Callback<Bitmap> callback =
2159                    ((DataAndCallback) msg.obj).mCallback;
2160            if (data == null || callback == null) {
2161                return;
2162            }
2163
2164            final Bitmap bitmap;
2165            switch (data.getLocalDataType()) {
2166                case LocalData.LOCAL_IMAGE:
2167                case LocalData.LOCAL_IN_PROGRESS_DATA:
2168                    FileInputStream stream;
2169                    try {
2170                        stream = new FileInputStream(data.getPath());
2171                    } catch (FileNotFoundException e) {
2172                        Log.e(TAG, "File not found:" + data.getPath());
2173                        return;
2174                    }
2175                    Point dim = CameraUtil.resizeToFill(data.getWidth(), data.getHeight(),
2176                            data.getRotation(), mAboveFilmstripControlLayout.getWidth(),
2177                            mAboveFilmstripControlLayout.getMeasuredHeight());
2178                    if (data.getRotation() % 180 != 0) {
2179                        int dummy = dim.x;
2180                        dim.x = dim.y;
2181                        dim.y = dummy;
2182                    }
2183                    bitmap = LocalDataUtil
2184                            .loadImageThumbnailFromStream(stream, data.getWidth(), data.getHeight(),
2185                                    (int) (dim.x * 0.7f), (int) (dim.y * 0.7),
2186                                    data.getRotation(), MAX_PEEK_BITMAP_PIXELS);
2187                    break;
2188
2189                case LocalData.LOCAL_VIDEO:
2190                    bitmap = LocalDataUtil.loadVideoThumbnail(data.getPath());
2191                    break;
2192
2193                default:
2194                    bitmap = null;
2195                    break;
2196            }
2197
2198            if (bitmap == null) {
2199                return;
2200            }
2201
2202            mMainHandler.post(new Runnable() {
2203                @Override
2204                public void run() {
2205                    callback.onCallback(bitmap);
2206                    mCameraAppUI.startPeekAnimation(bitmap, true);
2207                }
2208            });
2209        }
2210    }
2211
2212    private void showDetailsDialog(int dataId) {
2213        final LocalData data = mDataAdapter.getLocalData(dataId);
2214        if (data == null) {
2215            return;
2216        }
2217        MediaDetails details = data.getMediaDetails(getAndroidContext());
2218        if (details == null) {
2219            return;
2220        }
2221        Dialog detailDialog = DetailsDialog.create(CameraActivity.this, details);
2222        detailDialog.show();
2223
2224        UsageStatistics.photoInteraction(
2225                UsageStatistics.hashFileName(fileNameFromDataID(dataId)),
2226                eventprotos.CameraEvent.InteractionType.DETAILS,
2227                InteractionCause.BUTTON);
2228    }
2229
2230    /**
2231     * Show or hide action bar items depending on current data type.
2232     */
2233    private void updateActionBarMenu(LocalData data) {
2234        if (mActionBarMenu == null) {
2235            return;
2236        }
2237
2238        MenuItem detailsMenuItem = mActionBarMenu.findItem(R.id.action_details);
2239        if (detailsMenuItem == null) {
2240            return;
2241        }
2242
2243        int type = data.getLocalDataType();
2244        boolean showDetails = (type == LocalData.LOCAL_IMAGE) || (type == LocalData.LOCAL_VIDEO);
2245        detailsMenuItem.setVisible(showDetails);
2246    }
2247}
2248