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