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