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