CameraActivity.java revision ea7db650e3468f913bbea5bad52875c93bde9ba9
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 if (action == null) { 1665 source = ForegroundSource.UNKNOWN_SOURCE; 1666 } else { 1667 switch (action) { 1668 case MediaStore.ACTION_IMAGE_CAPTURE: 1669 source = ForegroundSource.ACTION_IMAGE_CAPTURE; 1670 break; 1671 case MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA: 1672 // was UNKNOWN_SOURCE in Fishlake. 1673 source = ForegroundSource.ACTION_STILL_IMAGE_CAMERA; 1674 break; 1675 case MediaStore.INTENT_ACTION_VIDEO_CAMERA: 1676 // was UNKNOWN_SOURCE in Fishlake. 1677 source = ForegroundSource.ACTION_VIDEO_CAMERA; 1678 break; 1679 case MediaStore.ACTION_VIDEO_CAPTURE: 1680 source = ForegroundSource.ACTION_VIDEO_CAPTURE; 1681 break; 1682 case MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE: 1683 // was ACTION_IMAGE_CAPTURE_SECURE in Fishlake. 1684 source = ForegroundSource.ACTION_STILL_IMAGE_CAMERA_SECURE; 1685 break; 1686 case MediaStore.ACTION_IMAGE_CAPTURE_SECURE: 1687 source = ForegroundSource.ACTION_IMAGE_CAPTURE_SECURE; 1688 break; 1689 case Intent.ACTION_MAIN: 1690 source = ForegroundSource.ACTION_MAIN; 1691 break; 1692 default: 1693 source = ForegroundSource.UNKNOWN_SOURCE; 1694 break; 1695 } 1696 } 1697 UsageStatistics.instance().foregrounded(source, currentUserInterfaceMode()); 1698 1699 mGalleryIntent = IntentHelper.getPhotosGalleryIntent(mAppContext); 1700 Drawable galleryLogo = IntentHelper.getGalleryIcon(mAppContext, mGalleryIntent); 1701 if (galleryLogo == null) { 1702 try { 1703 galleryLogo = getPackageManager().getActivityLogo(getComponentName()); 1704 } catch (PackageManager.NameNotFoundException e) { 1705 Log.e(TAG, "Can't get the activity logo"); 1706 } 1707 } 1708 if (mGalleryIntent != null) { 1709 mActionBar.setDisplayUseLogoEnabled(true); 1710 } 1711 mActionBar.setLogo(galleryLogo); 1712 mOrientationManager.resume(); 1713 super.onResume(); 1714 mPeekAnimationThread = new HandlerThread("Peek animation"); 1715 mPeekAnimationThread.start(); 1716 mPeekAnimationHandler = new PeekAnimationHandler(mPeekAnimationThread.getLooper()); 1717 1718 mCurrentModule.hardResetSettings(mSettingsManager); 1719 mCurrentModule.resume(); 1720 UsageStatistics.instance().changeScreen(currentUserInterfaceMode(), 1721 NavigationChange.InteractionCause.BUTTON); 1722 setSwipingEnabled(true); 1723 1724 if (!mResetToPreviewOnResume) { 1725 LocalData data = mDataAdapter.getLocalData(mFilmstripController.getCurrentId()); 1726 if (data != null) { 1727 mDataAdapter.refresh(data.getUri()); 1728 } 1729 } 1730 // The share button might be disabled to avoid double tapping. 1731 mCameraAppUI.getFilmstripBottomControls().setShareEnabled(true); 1732 // Default is showing the preview, unless disabled by explicitly 1733 // starting an activity we want to return from to the filmstrip rather 1734 // than the preview. 1735 mResetToPreviewOnResume = true; 1736 1737 if (mLocalVideosObserver.isMediaDataChangedDuringPause() 1738 || mLocalImagesObserver.isMediaDataChangedDuringPause()) { 1739 if (!mSecureCamera) { 1740 // If it's secure camera, requestLoad() should not be called 1741 // as it will load all the data. 1742 if (!mFilmstripVisible) { 1743 mDataAdapter.requestLoad(new Callback<Void>() { 1744 @Override 1745 public void onCallback(Void result) { 1746 fillTemporarySessions(); 1747 } 1748 }); 1749 } else { 1750 mDataAdapter.requestLoadNewPhotos(); 1751 } 1752 } 1753 } 1754 mLocalImagesObserver.setActivityPaused(false); 1755 mLocalVideosObserver.setActivityPaused(false); 1756 if (!mSecureCamera) { 1757 mLocalImagesObserver.setForegroundChangeListener( 1758 new LocalMediaObserver.ChangeListener() { 1759 @Override 1760 public void onChange() { 1761 mDataAdapter.requestLoadNewPhotos(); 1762 } 1763 }); 1764 } 1765 1766 keepScreenOnForAWhile(); 1767 1768 // Lights-out mode at all times. 1769 final View rootView = findViewById(R.id.activity_root_view); 1770 mLightsOutRunnable.run(); 1771 getWindow().getDecorView().setOnSystemUiVisibilityChangeListener( 1772 new OnSystemUiVisibilityChangeListener() { 1773 @Override 1774 public void onSystemUiVisibilityChange(int visibility) { 1775 mMainHandler.removeCallbacks(mLightsOutRunnable); 1776 mMainHandler.postDelayed(mLightsOutRunnable, LIGHTS_OUT_DELAY_MS); 1777 } 1778 }); 1779 1780 mPanoramaViewHelper.onResume(); 1781 ReleaseDialogHelper.showReleaseInfoDialogOnStart(this, mSettingsManager); 1782 syncLocationManagerSetting(); 1783 1784 final int previewVisibility = getPreviewVisibility(); 1785 updatePreviewRendering(previewVisibility); 1786 1787 mMotionManager.start(); 1788 } 1789 1790 private void fillTemporarySessions() { 1791 if (mSecureCamera) { 1792 return; 1793 } 1794 // There might be sessions still in flight (processed by our service). 1795 // Make sure they're added to the filmstrip. 1796 getServices().getCaptureSessionManager().fillTemporarySession(mSessionListener); 1797 } 1798 1799 @Override 1800 public void onStart() { 1801 super.onStart(); 1802 mIsActivityRunning = true; 1803 mPanoramaViewHelper.onStart(); 1804 1805 /* 1806 * If we're starting after launching a different Activity (lockscreen), 1807 * we need to use the last mode used in the other Activity, and 1808 * not the old one from this Activity. 1809 * 1810 * This needs to happen before CameraAppUI.resume() in order to set the 1811 * mode cover icon to the actual last mode used. 1812 * 1813 * Right now we exclude capture intents from this logic. 1814 */ 1815 int modeIndex = getModeIndex(); 1816 if (!isCaptureIntent() && mCurrentModeIndex != modeIndex) { 1817 onModeSelected(modeIndex); 1818 } 1819 1820 if (mResetToPreviewOnResume) { 1821 mCameraAppUI.resume(); 1822 mResetToPreviewOnResume = false; 1823 } 1824 } 1825 1826 @Override 1827 protected void onStop() { 1828 mIsActivityRunning = false; 1829 mPanoramaViewHelper.onStop(); 1830 if (mFeedbackHelper != null) { 1831 mFeedbackHelper.stopFeedback(); 1832 } 1833 1834 mLocationManager.disconnect(); 1835 super.onStop(); 1836 } 1837 1838 @Override 1839 public void onDestroy() { 1840 if (mSecureCamera) { 1841 unregisterReceiver(mScreenOffReceiver); 1842 } 1843 mActionBar.removeOnMenuVisibilityListener(this); 1844 mSettingsManager.removeAllListeners(); 1845 mCameraController.removeCallbackReceiver(); 1846 getContentResolver().unregisterContentObserver(mLocalImagesObserver); 1847 getContentResolver().unregisterContentObserver(mLocalVideosObserver); 1848 getServices().getCaptureSessionManager().removeSessionListener(mSessionListener); 1849 mCameraAppUI.onDestroy(); 1850 mModeListView.setVisibilityChangedListener(null); 1851 mCameraController = null; 1852 mSettingsManager = null; 1853 mCameraAppUI = null; 1854 mOrientationManager = null; 1855 mButtonManager = null; 1856 CameraAgentFactory.recycle(); 1857 super.onDestroy(); 1858 } 1859 1860 @Override 1861 public void onConfigurationChanged(Configuration config) { 1862 super.onConfigurationChanged(config); 1863 Log.v(TAG, "onConfigurationChanged"); 1864 if (config.orientation == Configuration.ORIENTATION_UNDEFINED) { 1865 return; 1866 } 1867 1868 if (mLastLayoutOrientation != config.orientation) { 1869 mLastLayoutOrientation = config.orientation; 1870 mCurrentModule.onLayoutOrientationChanged( 1871 mLastLayoutOrientation == Configuration.ORIENTATION_LANDSCAPE); 1872 } 1873 } 1874 1875 @Override 1876 public boolean onKeyDown(int keyCode, KeyEvent event) { 1877 if (!mFilmstripVisible) { 1878 if (mCurrentModule.onKeyDown(keyCode, event)) { 1879 return true; 1880 } 1881 // Prevent software keyboard or voice search from showing up. 1882 if (keyCode == KeyEvent.KEYCODE_SEARCH 1883 || keyCode == KeyEvent.KEYCODE_MENU) { 1884 if (event.isLongPress()) { 1885 return true; 1886 } 1887 } 1888 } 1889 1890 return super.onKeyDown(keyCode, event); 1891 } 1892 1893 @Override 1894 public boolean onKeyUp(int keyCode, KeyEvent event) { 1895 if (!mFilmstripVisible) { 1896 // If a module is in the middle of capture, it should 1897 // consume the key event. 1898 if (mCurrentModule.onKeyUp(keyCode, event)) { 1899 return true; 1900 } else if (keyCode == KeyEvent.KEYCODE_MENU 1901 || keyCode == KeyEvent.KEYCODE_DPAD_LEFT) { 1902 // Let the mode list view consume the event. 1903 mCameraAppUI.openModeList(); 1904 return true; 1905 } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) { 1906 mCameraAppUI.showFilmstrip(); 1907 return true; 1908 } 1909 } else { 1910 if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) { 1911 mFilmstripController.goToNextItem(); 1912 return true; 1913 } else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) { 1914 boolean wentToPrevious = mFilmstripController.goToPreviousItem(); 1915 if (!wentToPrevious) { 1916 // at beginning of filmstrip, hide and go back to preview 1917 mCameraAppUI.hideFilmstrip(); 1918 } 1919 return true; 1920 } 1921 } 1922 return super.onKeyUp(keyCode, event); 1923 } 1924 1925 @Override 1926 public void onBackPressed() { 1927 if (!mCameraAppUI.onBackPressed()) { 1928 if (!mCurrentModule.onBackPressed()) { 1929 super.onBackPressed(); 1930 } 1931 } 1932 } 1933 1934 @Override 1935 public boolean isAutoRotateScreen() { 1936 // TODO: Move to OrientationManager. 1937 return mAutoRotateScreen; 1938 } 1939 1940 @Override 1941 public boolean onCreateOptionsMenu(Menu menu) { 1942 MenuInflater inflater = getMenuInflater(); 1943 inflater.inflate(R.menu.filmstrip_menu, menu); 1944 mActionBarMenu = menu; 1945 return super.onCreateOptionsMenu(menu); 1946 } 1947 1948 protected long getStorageSpaceBytes() { 1949 synchronized (mStorageSpaceLock) { 1950 return mStorageSpaceBytes; 1951 } 1952 } 1953 1954 protected interface OnStorageUpdateDoneListener { 1955 public void onStorageUpdateDone(long bytes); 1956 } 1957 1958 protected void updateStorageSpaceAndHint(final OnStorageUpdateDoneListener callback) { 1959 /* 1960 * We execute disk operations on a background thread in order to 1961 * free up the UI thread. Synchronizing on the lock below ensures 1962 * that when getStorageSpaceBytes is called, the main thread waits 1963 * until this method has completed. 1964 * 1965 * However, .execute() does not ensure this execution block will be 1966 * run right away (.execute() schedules this AsyncTask for sometime 1967 * in the future. executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR) 1968 * tries to execute the task in parellel with other AsyncTasks, but 1969 * there's still no guarantee). 1970 * e.g. don't call this then immediately call getStorageSpaceBytes(). 1971 * Instead, pass in an OnStorageUpdateDoneListener. 1972 */ 1973 (new AsyncTask<Void, Void, Long>() { 1974 @Override 1975 protected Long doInBackground(Void ... arg) { 1976 synchronized (mStorageSpaceLock) { 1977 mStorageSpaceBytes = Storage.getAvailableSpace(); 1978 return mStorageSpaceBytes; 1979 } 1980 } 1981 1982 @Override 1983 protected void onPostExecute(Long bytes) { 1984 updateStorageHint(bytes); 1985 if (callback != null) { 1986 callback.onStorageUpdateDone(bytes); 1987 } 1988 } 1989 }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 1990 } 1991 1992 protected void updateStorageHint(long storageSpace) { 1993 if (!mIsActivityRunning) { 1994 return; 1995 } 1996 1997 String message = null; 1998 if (storageSpace == Storage.UNAVAILABLE) { 1999 message = getString(R.string.no_storage); 2000 } else if (storageSpace == Storage.PREPARING) { 2001 message = getString(R.string.preparing_sd); 2002 } else if (storageSpace == Storage.UNKNOWN_SIZE) { 2003 message = getString(R.string.access_sd_fail); 2004 } else if (storageSpace <= Storage.LOW_STORAGE_THRESHOLD_BYTES) { 2005 message = getString(R.string.spaceIsLow_content); 2006 } 2007 2008 if (message != null) { 2009 Log.w(TAG, "Storage warning: " + message); 2010 if (mStorageHint == null) { 2011 mStorageHint = OnScreenHint.makeText(CameraActivity.this, message); 2012 } else { 2013 mStorageHint.setText(message); 2014 } 2015 mStorageHint.show(); 2016 UsageStatistics.instance().storageWarning(storageSpace); 2017 } else if (mStorageHint != null) { 2018 mStorageHint.cancel(); 2019 mStorageHint = null; 2020 } 2021 } 2022 2023 protected void setResultEx(int resultCode) { 2024 mResultCodeForTesting = resultCode; 2025 setResult(resultCode); 2026 } 2027 2028 protected void setResultEx(int resultCode, Intent data) { 2029 mResultCodeForTesting = resultCode; 2030 mResultDataForTesting = data; 2031 setResult(resultCode, data); 2032 } 2033 2034 public int getResultCode() { 2035 return mResultCodeForTesting; 2036 } 2037 2038 public Intent getResultData() { 2039 return mResultDataForTesting; 2040 } 2041 2042 public boolean isSecureCamera() { 2043 return mSecureCamera; 2044 } 2045 2046 @Override 2047 public boolean isPaused() { 2048 return mPaused; 2049 } 2050 2051 @Override 2052 public int getPreferredChildModeIndex(int modeIndex) { 2053 if (modeIndex == getResources().getInteger(R.integer.camera_mode_photo)) { 2054 boolean hdrPlusOn = Keys.isHdrPlusOn(mSettingsManager); 2055 if (hdrPlusOn && GcamHelper.hasGcamCapture()) { 2056 modeIndex = getResources().getInteger(R.integer.camera_mode_gcam); 2057 } 2058 } 2059 return modeIndex; 2060 } 2061 2062 @Override 2063 public void onModeSelected(int modeIndex) { 2064 if (mCurrentModeIndex == modeIndex) { 2065 return; 2066 } 2067 2068 CameraPerformanceTracker.onEvent(CameraPerformanceTracker.MODE_SWITCH_START); 2069 // Record last used camera mode for quick switching 2070 if (modeIndex == getResources().getInteger(R.integer.camera_mode_photo) 2071 || modeIndex == getResources().getInteger(R.integer.camera_mode_gcam)) { 2072 mSettingsManager.set(SettingsManager.SCOPE_GLOBAL, 2073 Keys.KEY_CAMERA_MODULE_LAST_USED, 2074 modeIndex); 2075 } 2076 2077 closeModule(mCurrentModule); 2078 2079 // Select the correct module index from the mode switcher index. 2080 modeIndex = getPreferredChildModeIndex(modeIndex); 2081 setModuleFromModeIndex(modeIndex); 2082 2083 mCameraAppUI.resetBottomControls(mCurrentModule, modeIndex); 2084 mCameraAppUI.addShutterListener(mCurrentModule); 2085 mCameraAppUI.hideLetterboxing(); 2086 openModule(mCurrentModule); 2087 mCurrentModule.onOrientationChanged(mLastRawOrientation); 2088 // Store the module index so we can use it the next time the Camera 2089 // starts up. 2090 mSettingsManager.set(SettingsManager.SCOPE_GLOBAL, 2091 Keys.KEY_STARTUP_MODULE_INDEX, modeIndex); 2092 } 2093 2094 /** 2095 * Shows the settings dialog. 2096 */ 2097 @Override 2098 public void onSettingsSelected() { 2099 UsageStatistics.instance().controlUsed( 2100 eventprotos.ControlEvent.ControlType.OVERALL_SETTINGS); 2101 Intent intent = new Intent(this, CameraSettingsActivity.class); 2102 startActivity(intent); 2103 } 2104 2105 @Override 2106 public void freezeScreenUntilPreviewReady() { 2107 mCameraAppUI.freezeScreenUntilPreviewReady(); 2108 } 2109 2110 /** 2111 * Sets the mCurrentModuleIndex, creates a new module instance for the given 2112 * index an sets it as mCurrentModule. 2113 */ 2114 private void setModuleFromModeIndex(int modeIndex) { 2115 ModuleManagerImpl.ModuleAgent agent = mModuleManager.getModuleAgent(modeIndex); 2116 if (agent == null) { 2117 return; 2118 } 2119 if (!agent.requestAppForCamera()) { 2120 mCameraController.closeCamera(true); 2121 } 2122 mCurrentModeIndex = agent.getModuleId(); 2123 mCurrentModule = (CameraModule) agent.createModule(this); 2124 } 2125 2126 @Override 2127 public SettingsManager getSettingsManager() { 2128 return mSettingsManager; 2129 } 2130 2131 @Override 2132 public CameraServices getServices() { 2133 return (CameraServices) getApplication(); 2134 } 2135 2136 public List<String> getSupportedModeNames() { 2137 List<Integer> indices = mModuleManager.getSupportedModeIndexList(); 2138 List<String> supported = new ArrayList<String>(); 2139 2140 for (Integer modeIndex : indices) { 2141 String name = CameraUtil.getCameraModeText(modeIndex, mAppContext); 2142 if (name != null && !name.equals("")) { 2143 supported.add(name); 2144 } 2145 } 2146 return supported; 2147 } 2148 2149 @Override 2150 public ButtonManager getButtonManager() { 2151 if (mButtonManager == null) { 2152 mButtonManager = new ButtonManager(this); 2153 } 2154 return mButtonManager; 2155 } 2156 2157 /** 2158 * Creates an AlertDialog appropriate for choosing whether to enable 2159 * location on the first run of the app. 2160 */ 2161 public AlertDialog getFirstTimeLocationAlert() { 2162 AlertDialog.Builder builder = new AlertDialog.Builder(this); 2163 builder = SettingsUtil.getFirstTimeLocationAlertBuilder(builder, new Callback<Boolean>() { 2164 @Override 2165 public void onCallback(Boolean locationOn) { 2166 Keys.setLocation(mSettingsManager, locationOn, mLocationManager); 2167 } 2168 }); 2169 if (builder != null) { 2170 return builder.create(); 2171 } else { 2172 return null; 2173 } 2174 } 2175 2176 /** 2177 * Launches an ACTION_EDIT intent for the given local data item. If 2178 * 'withTinyPlanet' is set, this will show a disambig dialog first to let 2179 * the user start either the tiny planet editor or another photo edior. 2180 * 2181 * @param data The data item to edit. 2182 */ 2183 public void launchEditor(LocalData data) { 2184 Intent intent = new Intent(Intent.ACTION_EDIT) 2185 .setDataAndType(data.getUri(), data.getMimeType()) 2186 .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 2187 try { 2188 launchActivityByIntent(intent); 2189 } catch (ActivityNotFoundException e) { 2190 final String msgEditWith = getResources().getString(R.string.edit_with); 2191 launchActivityByIntent(Intent.createChooser(intent, msgEditWith)); 2192 } 2193 } 2194 2195 @Override 2196 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { 2197 super.onCreateContextMenu(menu, v, menuInfo); 2198 2199 MenuInflater inflater = getMenuInflater(); 2200 inflater.inflate(R.menu.filmstrip_context_menu, menu); 2201 } 2202 2203 @Override 2204 public boolean onContextItemSelected(MenuItem item) { 2205 switch (item.getItemId()) { 2206 case R.id.tiny_planet_editor: 2207 mMyFilmstripBottomControlListener.onTinyPlanet(); 2208 return true; 2209 case R.id.photo_editor: 2210 mMyFilmstripBottomControlListener.onEdit(); 2211 return true; 2212 } 2213 return false; 2214 } 2215 2216 /** 2217 * Launch the tiny planet editor. 2218 * 2219 * @param data The data must be a 360 degree stereographically mapped 2220 * panoramic image. It will not be modified, instead a new item 2221 * with the result will be added to the filmstrip. 2222 */ 2223 public void launchTinyPlanetEditor(LocalData data) { 2224 TinyPlanetFragment fragment = new TinyPlanetFragment(); 2225 Bundle bundle = new Bundle(); 2226 bundle.putString(TinyPlanetFragment.ARGUMENT_URI, data.getUri().toString()); 2227 bundle.putString(TinyPlanetFragment.ARGUMENT_TITLE, data.getTitle()); 2228 fragment.setArguments(bundle); 2229 fragment.show(getFragmentManager(), "tiny_planet"); 2230 } 2231 2232 /** 2233 * Returns what UI mode (capture mode or filmstrip) we are in. 2234 * Returned number one of {@link com.google.common.logging.eventprotos.NavigationChange.Mode} 2235 */ 2236 private int currentUserInterfaceMode() { 2237 int mode = NavigationChange.Mode.UNKNOWN_MODE; 2238 if (mCurrentModeIndex == getResources().getInteger(R.integer.camera_mode_photo)) { 2239 mode = NavigationChange.Mode.PHOTO_CAPTURE; 2240 } 2241 if (mCurrentModeIndex == getResources().getInteger(R.integer.camera_mode_video)) { 2242 mode = NavigationChange.Mode.VIDEO_CAPTURE; 2243 } 2244 if (mCurrentModeIndex == getResources().getInteger(R.integer.camera_mode_refocus)) { 2245 mode = NavigationChange.Mode.LENS_BLUR; 2246 } 2247 if (mCurrentModeIndex == getResources().getInteger(R.integer.camera_mode_gcam)) { 2248 mode = NavigationChange.Mode.HDR_PLUS; 2249 } 2250 if (mCurrentModeIndex == getResources().getInteger(R.integer.camera_mode_photosphere)) { 2251 mode = NavigationChange.Mode.PHOTO_SPHERE; 2252 } 2253 if (mCurrentModeIndex == getResources().getInteger(R.integer.camera_mode_panorama)) { 2254 mode = NavigationChange.Mode.PANORAMA; 2255 } 2256 if (mFilmstripVisible) { 2257 mode = NavigationChange.Mode.FILMSTRIP; 2258 } 2259 return mode; 2260 } 2261 2262 private void openModule(CameraModule module) { 2263 module.init(this, isSecureCamera(), isCaptureIntent()); 2264 module.hardResetSettings(mSettingsManager); 2265 module.resume(); 2266 UsageStatistics.instance().changeScreen(currentUserInterfaceMode(), 2267 NavigationChange.InteractionCause.BUTTON); 2268 updatePreviewVisibility(); 2269 } 2270 2271 private void closeModule(CameraModule module) { 2272 module.pause(); 2273 mCameraAppUI.clearModuleUI(); 2274 } 2275 2276 private void performDeletion() { 2277 if (!mPendingDeletion) { 2278 return; 2279 } 2280 hideUndoDeletionBar(false); 2281 mDataAdapter.executeDeletion(); 2282 } 2283 2284 public void showUndoDeletionBar() { 2285 if (mPendingDeletion) { 2286 performDeletion(); 2287 } 2288 Log.v(TAG, "showing undo bar"); 2289 mPendingDeletion = true; 2290 if (mUndoDeletionBar == null) { 2291 ViewGroup v = (ViewGroup) getLayoutInflater().inflate(R.layout.undo_bar, 2292 mAboveFilmstripControlLayout, true); 2293 mUndoDeletionBar = (ViewGroup) v.findViewById(R.id.camera_undo_deletion_bar); 2294 View button = mUndoDeletionBar.findViewById(R.id.camera_undo_deletion_button); 2295 button.setOnClickListener(new View.OnClickListener() { 2296 @Override 2297 public void onClick(View view) { 2298 mDataAdapter.undoDataRemoval(); 2299 hideUndoDeletionBar(true); 2300 } 2301 }); 2302 // Setting undo bar clickable to avoid touch events going through 2303 // the bar to the buttons (eg. edit button, etc) underneath the bar. 2304 mUndoDeletionBar.setClickable(true); 2305 // When there is user interaction going on with the undo button, we 2306 // do not want to hide the undo bar. 2307 button.setOnTouchListener(new View.OnTouchListener() { 2308 @Override 2309 public boolean onTouch(View v, MotionEvent event) { 2310 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { 2311 mIsUndoingDeletion = true; 2312 } else if (event.getActionMasked() == MotionEvent.ACTION_UP) { 2313 mIsUndoingDeletion = false; 2314 } 2315 return false; 2316 } 2317 }); 2318 } 2319 mUndoDeletionBar.setAlpha(0f); 2320 mUndoDeletionBar.setVisibility(View.VISIBLE); 2321 mUndoDeletionBar.animate().setDuration(200).alpha(1f).setListener(null).start(); 2322 } 2323 2324 private void hideUndoDeletionBar(boolean withAnimation) { 2325 Log.v(TAG, "Hiding undo deletion bar"); 2326 mPendingDeletion = false; 2327 if (mUndoDeletionBar != null) { 2328 if (withAnimation) { 2329 mUndoDeletionBar.animate().setDuration(200).alpha(0f) 2330 .setListener(new Animator.AnimatorListener() { 2331 @Override 2332 public void onAnimationStart(Animator animation) { 2333 // Do nothing. 2334 } 2335 2336 @Override 2337 public void onAnimationEnd(Animator animation) { 2338 mUndoDeletionBar.setVisibility(View.GONE); 2339 } 2340 2341 @Override 2342 public void onAnimationCancel(Animator animation) { 2343 // Do nothing. 2344 } 2345 2346 @Override 2347 public void onAnimationRepeat(Animator animation) { 2348 // Do nothing. 2349 } 2350 }).start(); 2351 } else { 2352 mUndoDeletionBar.setVisibility(View.GONE); 2353 } 2354 } 2355 } 2356 2357 @Override 2358 public void onOrientationChanged(int orientation) { 2359 // We keep the last known orientation. So if the user first orient 2360 // the camera then point the camera to floor or sky, we still have 2361 // the correct orientation. 2362 if (orientation == OrientationManager.ORIENTATION_UNKNOWN) { 2363 return; 2364 } 2365 mLastRawOrientation = orientation; 2366 if (mCurrentModule != null) { 2367 mCurrentModule.onOrientationChanged(orientation); 2368 } 2369 } 2370 2371 /** 2372 * Enable/disable swipe-to-filmstrip. Will always disable swipe if in 2373 * capture intent. 2374 * 2375 * @param enable {@code true} to enable swipe. 2376 */ 2377 public void setSwipingEnabled(boolean enable) { 2378 // TODO: Bring back the functionality. 2379 if (isCaptureIntent()) { 2380 // lockPreview(true); 2381 } else { 2382 // lockPreview(!enable); 2383 } 2384 } 2385 2386 // Accessor methods for getting latency times used in performance testing 2387 public long getFirstPreviewTime() { 2388 if (mCurrentModule instanceof PhotoModule) { 2389 long coverHiddenTime = getCameraAppUI().getCoverHiddenTime(); 2390 if (coverHiddenTime != -1) { 2391 return coverHiddenTime - mOnCreateTime; 2392 } 2393 } 2394 return -1; 2395 } 2396 2397 public long getAutoFocusTime() { 2398 return (mCurrentModule instanceof PhotoModule) ? 2399 ((PhotoModule) mCurrentModule).mAutoFocusTime : -1; 2400 } 2401 2402 public long getShutterLag() { 2403 return (mCurrentModule instanceof PhotoModule) ? 2404 ((PhotoModule) mCurrentModule).mShutterLag : -1; 2405 } 2406 2407 public long getShutterToPictureDisplayedTime() { 2408 return (mCurrentModule instanceof PhotoModule) ? 2409 ((PhotoModule) mCurrentModule).mShutterToPictureDisplayedTime : -1; 2410 } 2411 2412 public long getPictureDisplayedToJpegCallbackTime() { 2413 return (mCurrentModule instanceof PhotoModule) ? 2414 ((PhotoModule) mCurrentModule).mPictureDisplayedToJpegCallbackTime : -1; 2415 } 2416 2417 public long getJpegCallbackFinishTime() { 2418 return (mCurrentModule instanceof PhotoModule) ? 2419 ((PhotoModule) mCurrentModule).mJpegCallbackFinishTime : -1; 2420 } 2421 2422 public long getCaptureStartTime() { 2423 return (mCurrentModule instanceof PhotoModule) ? 2424 ((PhotoModule) mCurrentModule).mCaptureStartTime : -1; 2425 } 2426 2427 public boolean isRecording() { 2428 return (mCurrentModule instanceof VideoModule) ? 2429 ((VideoModule) mCurrentModule).isRecording() : false; 2430 } 2431 2432 public CameraAgent.CameraOpenCallback getCameraOpenErrorCallback() { 2433 return mCameraController; 2434 } 2435 2436 // For debugging purposes only. 2437 public CameraModule getCurrentModule() { 2438 return mCurrentModule; 2439 } 2440 2441 @Override 2442 public void showTutorial(AbstractTutorialOverlay tutorial) { 2443 mCameraAppUI.showTutorial(tutorial, getLayoutInflater()); 2444 } 2445 2446 /** 2447 * Reads the current location recording settings and passes it on to the 2448 * location manager. 2449 */ 2450 public void syncLocationManagerSetting() { 2451 Keys.syncLocationManager(mSettingsManager, mLocationManager); 2452 } 2453 2454 private void keepScreenOnForAWhile() { 2455 if (mKeepScreenOn) { 2456 return; 2457 } 2458 mMainHandler.removeMessages(MSG_CLEAR_SCREEN_ON_FLAG); 2459 getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 2460 mMainHandler.sendEmptyMessageDelayed(MSG_CLEAR_SCREEN_ON_FLAG, SCREEN_DELAY_MS); 2461 } 2462 2463 private void resetScreenOn() { 2464 mKeepScreenOn = false; 2465 mMainHandler.removeMessages(MSG_CLEAR_SCREEN_ON_FLAG); 2466 getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 2467 } 2468 2469 /** 2470 * @return {@code true} if the Gallery is launched successfully. 2471 */ 2472 private boolean startGallery() { 2473 if (mGalleryIntent == null) { 2474 return false; 2475 } 2476 try { 2477 UsageStatistics.instance().changeScreen(NavigationChange.Mode.GALLERY, 2478 NavigationChange.InteractionCause.BUTTON); 2479 Intent startGalleryIntent = new Intent(mGalleryIntent); 2480 int currentDataId = mFilmstripController.getCurrentId(); 2481 LocalData currentLocalData = mDataAdapter.getLocalData(currentDataId); 2482 if (currentLocalData != null) { 2483 GalleryHelper.setContentUri(startGalleryIntent, currentLocalData.getUri()); 2484 } 2485 launchActivityByIntent(startGalleryIntent); 2486 } catch (ActivityNotFoundException e) { 2487 Log.w(TAG, "Failed to launch gallery activity, closing"); 2488 } 2489 return false; 2490 } 2491 2492 private void setNfcBeamPushUriFromData(LocalData data) { 2493 final Uri uri = data.getUri(); 2494 if (uri != Uri.EMPTY) { 2495 mNfcPushUris[0] = uri; 2496 } else { 2497 mNfcPushUris[0] = null; 2498 } 2499 } 2500 2501 /** 2502 * Updates the visibility of the filmstrip bottom controls and action bar. 2503 */ 2504 private void updateUiByData(final int dataId) { 2505 final LocalData currentData = mDataAdapter.getLocalData(dataId); 2506 if (currentData == null) { 2507 Log.w(TAG, "Current data ID not found."); 2508 hideSessionProgress(); 2509 return; 2510 } 2511 updateActionBarMenu(currentData); 2512 2513 /* Bottom controls. */ 2514 updateBottomControlsByData(currentData); 2515 2516 if (isSecureCamera()) { 2517 // We cannot show buttons in secure camera since go to other 2518 // activities might create a security hole. 2519 mCameraAppUI.getFilmstripBottomControls().hideControls(); 2520 return; 2521 } 2522 2523 2524 setNfcBeamPushUriFromData(currentData); 2525 2526 if (!mDataAdapter.isMetadataUpdated(dataId)) { 2527 mDataAdapter.updateMetadata(dataId); 2528 } 2529 } 2530 2531 /** 2532 * Updates the bottom controls based on the data. 2533 */ 2534 private void updateBottomControlsByData(final LocalData currentData) { 2535 2536 final CameraAppUI.BottomPanel filmstripBottomPanel = 2537 mCameraAppUI.getFilmstripBottomControls(); 2538 filmstripBottomPanel.showControls(); 2539 filmstripBottomPanel.setEditButtonVisibility( 2540 currentData.isDataActionSupported(LocalData.DATA_ACTION_EDIT)); 2541 filmstripBottomPanel.setShareButtonVisibility( 2542 currentData.isDataActionSupported(LocalData.DATA_ACTION_SHARE)); 2543 filmstripBottomPanel.setDeleteButtonVisibility( 2544 currentData.isDataActionSupported(LocalData.DATA_ACTION_DELETE)); 2545 2546 /* Progress bar */ 2547 2548 Uri contentUri = currentData.getUri(); 2549 CaptureSessionManager sessionManager = getServices() 2550 .getCaptureSessionManager(); 2551 2552 if (sessionManager.hasErrorMessage(contentUri)) { 2553 showProcessError(sessionManager.getErrorMesage(contentUri)); 2554 } else { 2555 filmstripBottomPanel.hideProgressError(); 2556 CaptureSession session = sessionManager.getSession(contentUri); 2557 2558 if (session != null) { 2559 int sessionProgress = session.getProgress(); 2560 2561 if (sessionProgress < 0) { 2562 hideSessionProgress(); 2563 } else { 2564 CharSequence progressMessage = session.getProgressMessage(); 2565 showSessionProgress(progressMessage); 2566 updateSessionProgress(sessionProgress); 2567 } 2568 } else { 2569 hideSessionProgress(); 2570 } 2571 } 2572 2573 /* View button */ 2574 2575 // We need to add this to a separate DB. 2576 final int viewButtonVisibility; 2577 if (PanoramaMetadataLoader.isPanoramaAndUseViewer(currentData)) { 2578 viewButtonVisibility = CameraAppUI.BottomPanel.VIEWER_PHOTO_SPHERE; 2579 } else if (RgbzMetadataLoader.hasRGBZData(currentData)) { 2580 viewButtonVisibility = CameraAppUI.BottomPanel.VIEWER_REFOCUS; 2581 } else { 2582 viewButtonVisibility = CameraAppUI.BottomPanel.VIEWER_NONE; 2583 } 2584 2585 filmstripBottomPanel.setTinyPlanetEnabled( 2586 PanoramaMetadataLoader.isPanorama360(currentData)); 2587 filmstripBottomPanel.setViewerButtonVisibility(viewButtonVisibility); 2588 } 2589 2590 private class PeekAnimationHandler extends Handler { 2591 private class DataAndCallback { 2592 LocalData mData; 2593 com.android.camera.util.Callback<Bitmap> mCallback; 2594 2595 public DataAndCallback(LocalData data, com.android.camera.util.Callback<Bitmap> 2596 callback) { 2597 mData = data; 2598 mCallback = callback; 2599 } 2600 } 2601 2602 public PeekAnimationHandler(Looper looper) { 2603 super(looper); 2604 } 2605 2606 /** 2607 * Starts the animation decoding job and posts a {@code Runnable} back 2608 * when when the decoding is done. 2609 * 2610 * @param data The data item to decode the thumbnail for. 2611 * @param callback {@link com.android.camera.util.Callback} after the 2612 * decoding is done. 2613 */ 2614 public void startDecodingJob(final LocalData data, 2615 final com.android.camera.util.Callback<Bitmap> callback) { 2616 PeekAnimationHandler.this.obtainMessage(0 /** dummy integer **/, 2617 new DataAndCallback(data, callback)).sendToTarget(); 2618 } 2619 2620 @Override 2621 public void handleMessage(Message msg) { 2622 final LocalData data = ((DataAndCallback) msg.obj).mData; 2623 final com.android.camera.util.Callback<Bitmap> callback = 2624 ((DataAndCallback) msg.obj).mCallback; 2625 if (data == null || callback == null) { 2626 return; 2627 } 2628 2629 final Bitmap bitmap; 2630 switch (data.getLocalDataType()) { 2631 case LocalData.LOCAL_IN_PROGRESS_DATA: 2632 byte[] jpegData = Storage.getJpegForSession(data.getUri()); 2633 if (jpegData != null) { 2634 bitmap = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length); 2635 } else { 2636 bitmap = null; 2637 } 2638 break; 2639 2640 case LocalData.LOCAL_IMAGE: 2641 FileInputStream stream; 2642 try { 2643 stream = new FileInputStream(data.getPath()); 2644 } catch (FileNotFoundException e) { 2645 Log.e(TAG, "File not found:" + data.getPath()); 2646 return; 2647 } 2648 Point dim = CameraUtil.resizeToFill(data.getWidth(), data.getHeight(), 2649 data.getRotation(), mAboveFilmstripControlLayout.getWidth(), 2650 mAboveFilmstripControlLayout.getMeasuredHeight()); 2651 if (data.getRotation() % 180 != 0) { 2652 int dummy = dim.x; 2653 dim.x = dim.y; 2654 dim.y = dummy; 2655 } 2656 bitmap = LocalDataUtil 2657 .loadImageThumbnailFromStream(stream, data.getWidth(), data.getHeight(), 2658 (int) (dim.x * 0.7f), (int) (dim.y * 0.7), 2659 data.getRotation(), MAX_PEEK_BITMAP_PIXELS); 2660 break; 2661 2662 case LocalData.LOCAL_VIDEO: 2663 bitmap = LocalDataUtil.loadVideoThumbnail(data.getPath()); 2664 break; 2665 2666 default: 2667 bitmap = null; 2668 break; 2669 } 2670 2671 if (bitmap == null) { 2672 return; 2673 } 2674 2675 mMainHandler.post(new Runnable() { 2676 @Override 2677 public void run() { 2678 callback.onCallback(bitmap); 2679 } 2680 }); 2681 } 2682 } 2683 2684 private void showDetailsDialog(int dataId) { 2685 final LocalData data = mDataAdapter.getLocalData(dataId); 2686 if (data == null) { 2687 return; 2688 } 2689 MediaDetails details = data.getMediaDetails(getAndroidContext()); 2690 if (details == null) { 2691 return; 2692 } 2693 Dialog detailDialog = DetailsDialog.create(CameraActivity.this, details); 2694 detailDialog.show(); 2695 UsageStatistics.instance().mediaInteraction( 2696 fileNameFromDataID(dataId), MediaInteraction.InteractionType.DETAILS, 2697 NavigationChange.InteractionCause.BUTTON, fileAgeFromDataID(dataId)); 2698 } 2699 2700 /** 2701 * Show or hide action bar items depending on current data type. 2702 */ 2703 private void updateActionBarMenu(LocalData data) { 2704 if (mActionBarMenu == null) { 2705 return; 2706 } 2707 2708 MenuItem detailsMenuItem = mActionBarMenu.findItem(R.id.action_details); 2709 if (detailsMenuItem == null) { 2710 return; 2711 } 2712 2713 int type = data.getLocalDataType(); 2714 boolean showDetails = (type == LocalData.LOCAL_IMAGE) || (type == LocalData.LOCAL_VIDEO); 2715 detailsMenuItem.setVisible(showDetails); 2716 } 2717} 2718