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