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