CameraActivity.java revision 44442f6f673a37e5d0fa9c7676f5147fffd97218
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 } 499 500 @Override 501 public void onFilmstripHidden() { 502 mFilmstripVisible = false; 503 CameraActivity.this.setFilmstripUiVisibility(false); 504 // When the user hide the filmstrip (either swipe out or 505 // tap on back key) we move to the first item so next time 506 // when the user swipe in the filmstrip, the most recent 507 // one is shown. 508 mFilmstripController.goToFirstItem(); 509 if (mCurrentModule != null) { 510 mCurrentModule 511 .onPreviewVisibilityChanged(ModuleController.VISIBILITY_VISIBLE); 512 } 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.PreviewAreaSizeChangedListener listener) { 830 mCameraAppUI.addPreviewAreaSizeChangedListener(listener); 831 } 832 833 @Override 834 public void removePreviewAreaSizeChangedListener( 835 PreviewStatusListener.PreviewAreaSizeChangedListener listener) { 836 mCameraAppUI.removePreviewAreaSizeChangedListener(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 if (mSettingsManager.getInt(SettingsManager.SETTING_STARTUP_MODULE_INDEX) 1209 == gcamIndex && GcamHelper.hasGcamCapture()) { 1210 modeIndex = gcamIndex; 1211 } 1212 } else if (MediaStore.ACTION_IMAGE_CAPTURE.equals(getIntent().getAction()) 1213 || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(getIntent().getAction())) { 1214 modeIndex = photoIndex; 1215 } else { 1216 // If the activity has not been started using an explicit intent, 1217 // read the module index from the last time the user changed modes 1218 modeIndex = mSettingsManager.getInt(SettingsManager.SETTING_STARTUP_MODULE_INDEX); 1219 if ((modeIndex == gcamIndex && 1220 !GcamHelper.hasGcamCapture()) || modeIndex < 0) { 1221 modeIndex = photoIndex; 1222 } 1223 } 1224 1225 mOrientationManager = new OrientationManagerImpl(this); 1226 mOrientationManager.addOnOrientationChangeListener(mMainHandler, this); 1227 1228 setModuleFromModeIndex(modeIndex); 1229 mCameraAppUI.prepareModuleUI(); 1230 mCurrentModule.init(this, isSecureCamera(), isCaptureIntent()); 1231 1232 if (!mSecureCamera) { 1233 mFilmstripController.setDataAdapter(mDataAdapter); 1234 if (!isCaptureIntent()) { 1235 mDataAdapter.requestLoad(); 1236 } 1237 } else { 1238 // Put a lock placeholder as the last image by setting its date to 1239 // 0. 1240 ImageView v = (ImageView) getLayoutInflater().inflate( 1241 R.layout.secure_album_placeholder, null); 1242 v.setOnClickListener(new View.OnClickListener() { 1243 @Override 1244 public void onClick(View view) { 1245 UsageStatistics.changeScreen(NavigationChange.Mode.GALLERY, 1246 InteractionCause.BUTTON); 1247 startGallery(); 1248 finish(); 1249 } 1250 }); 1251 mDataAdapter = new FixedLastDataAdapter( 1252 mAppContext, 1253 mDataAdapter, 1254 new SimpleViewData( 1255 v, 1256 v.getDrawable().getIntrinsicWidth(), 1257 v.getDrawable().getIntrinsicHeight(), 1258 0, 0)); 1259 // Flush out all the original data. 1260 mDataAdapter.flush(); 1261 mFilmstripController.setDataAdapter(mDataAdapter); 1262 } 1263 1264 setupNfcBeamPush(); 1265 1266 mLocalImagesObserver = new LocalMediaObserver(); 1267 mLocalVideosObserver = new LocalMediaObserver(); 1268 1269 getContentResolver().registerContentObserver( 1270 MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true, 1271 mLocalImagesObserver); 1272 getContentResolver().registerContentObserver( 1273 MediaStore.Video.Media.EXTERNAL_CONTENT_URI, true, 1274 mLocalVideosObserver); 1275 if (FeedbackHelper.feedbackAvailable()) { 1276 mFeedbackHelper = new FeedbackHelper(mAppContext); 1277 } 1278 } 1279 1280 private void setRotationAnimation() { 1281 int rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE; 1282 rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE; 1283 Window win = getWindow(); 1284 WindowManager.LayoutParams winParams = win.getAttributes(); 1285 winParams.rotationAnimation = rotationAnimation; 1286 win.setAttributes(winParams); 1287 } 1288 1289 @Override 1290 public void onUserInteraction() { 1291 super.onUserInteraction(); 1292 if (!isFinishing()) { 1293 keepScreenOnForAWhile(); 1294 } 1295 } 1296 1297 @Override 1298 public boolean dispatchTouchEvent(MotionEvent ev) { 1299 boolean result = super.dispatchTouchEvent(ev); 1300 if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) { 1301 // Real deletion is postponed until the next user interaction after 1302 // the gesture that triggers deletion. Until real deletion is 1303 // performed, users can click the undo button to bring back the 1304 // image that they chose to delete. 1305 if (mPendingDeletion && !mIsUndoingDeletion) { 1306 performDeletion(); 1307 } 1308 } 1309 return result; 1310 } 1311 1312 @Override 1313 public void onPause() { 1314 mPaused = true; 1315 mPeekAnimationHandler = null; 1316 mPeekAnimationThread.quitSafely(); 1317 mPeekAnimationThread = null; 1318 CameraPerformanceTracker.onEvent(CameraPerformanceTracker.ACTIVITY_PAUSE); 1319 1320 // Delete photos that are pending deletion 1321 performDeletion(); 1322 mCurrentModule.pause(); 1323 mOrientationManager.pause(); 1324 // Close the camera and wait for the operation done. 1325 mCameraController.closeCamera(); 1326 mPanoramaViewHelper.onPause(); 1327 1328 mLocalImagesObserver.setActivityPaused(true); 1329 mLocalVideosObserver.setActivityPaused(true); 1330 resetScreenOn(); 1331 super.onPause(); 1332 } 1333 1334 @Override 1335 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 1336 if (requestCode == REQ_CODE_DONT_SWITCH_TO_PREVIEW) { 1337 mResetToPreviewOnResume = false; 1338 } else { 1339 super.onActivityResult(requestCode, resultCode, data); 1340 } 1341 } 1342 1343 @Override 1344 public void onResume() { 1345 mPaused = false; 1346 CameraPerformanceTracker.onEvent(CameraPerformanceTracker.ACTIVITY_RESUME); 1347 1348 mLastLayoutOrientation = getResources().getConfiguration().orientation; 1349 1350 // TODO: Handle this in OrientationManager. 1351 // Auto-rotate off 1352 if (Settings.System.getInt(getContentResolver(), 1353 Settings.System.ACCELEROMETER_ROTATION, 0) == 0) { 1354 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); 1355 mAutoRotateScreen = false; 1356 } else { 1357 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR); 1358 mAutoRotateScreen = true; 1359 } 1360 1361 if (isCaptureIntent()) { 1362 // Foreground event caused by photo or video capure intent. 1363 UsageStatistics.foregrounded( 1364 eventprotos.ForegroundEvent.ForegroundSource.INTENT_PICKER); 1365 } else if (!mSecureCamera) { 1366 // Foreground event that is not caused by an intent. 1367 UsageStatistics.foregrounded( 1368 eventprotos.ForegroundEvent.ForegroundSource.ICON_LAUNCHER); 1369 } 1370 1371 Drawable galleryLogo; 1372 if (mSecureCamera) { 1373 mGalleryIntent = null; 1374 galleryLogo = null; 1375 } else { 1376 mGalleryIntent = IntentHelper.getDefaultGalleryIntent(mAppContext); 1377 galleryLogo = IntentHelper.getGalleryIcon(mAppContext, mGalleryIntent); 1378 } 1379 if (galleryLogo == null) { 1380 try { 1381 galleryLogo = getPackageManager().getActivityLogo(getComponentName()); 1382 } catch (PackageManager.NameNotFoundException e) { 1383 Log.e(TAG, "Can't get the activity logo"); 1384 } 1385 } 1386 if (mGalleryIntent != null) { 1387 mActionBar.setDisplayUseLogoEnabled(true); 1388 } 1389 mActionBar.setLogo(galleryLogo); 1390 mOrientationManager.resume(); 1391 super.onResume(); 1392 mPeekAnimationThread = new HandlerThread("Peek animation"); 1393 mPeekAnimationThread.start(); 1394 mPeekAnimationHandler = new PeekAnimationHandler(mPeekAnimationThread.getLooper()); 1395 mCurrentModule.resume(); 1396 setSwipingEnabled(true); 1397 1398 if (mResetToPreviewOnResume) { 1399 mCameraAppUI.resume(); 1400 } else { 1401 LocalData data = mDataAdapter.getLocalData(mFilmstripController.getCurrentId()); 1402 if (data != null) { 1403 mDataAdapter.refresh(data.getContentUri(), false); 1404 } 1405 } 1406 // The share button might be disabled to avoid double tapping. 1407 mCameraAppUI.getFilmstripBottomControls().setShareEnabled(true); 1408 // Default is showing the preview, unless disabled by explicitly 1409 // starting an activity we want to return from to the filmstrip rather 1410 // than the preview. 1411 mResetToPreviewOnResume = true; 1412 1413 if (mLocalVideosObserver.isMediaDataChangedDuringPause() 1414 || mLocalImagesObserver.isMediaDataChangedDuringPause()) { 1415 if (!mSecureCamera) { 1416 // If it's secure camera, requestLoad() should not be called 1417 // as it will load all the data. 1418 if (!mFilmstripVisible) { 1419 mDataAdapter.requestLoad(); 1420 } 1421 } 1422 } 1423 mLocalImagesObserver.setActivityPaused(false); 1424 mLocalVideosObserver.setActivityPaused(false); 1425 1426 keepScreenOnForAWhile(); 1427 1428 // Lights-out mode at all times. 1429 findViewById(R.id.activity_root_view) 1430 .setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE); 1431 mPanoramaViewHelper.onResume(); 1432 ReleaseDialogHelper.showReleaseInfoDialogOnStart(this, mSettingsManager); 1433 syncLocationManagerSetting(); 1434 } 1435 1436 @Override 1437 public void onStart() { 1438 super.onStart(); 1439 mPanoramaViewHelper.onStart(); 1440 } 1441 1442 @Override 1443 protected void onStop() { 1444 mPanoramaViewHelper.onStop(); 1445 if (mFeedbackHelper != null) { 1446 mFeedbackHelper.stopFeedback(); 1447 } 1448 1449 mLocationManager.disconnect(); 1450 super.onStop(); 1451 } 1452 1453 @Override 1454 public void onDestroy() { 1455 if (mSecureCamera) { 1456 unregisterReceiver(mScreenOffReceiver); 1457 } 1458 mActionBar.removeOnMenuVisibilityListener(this); 1459 mSettingsManager.removeAllListeners(); 1460 mCameraController.removeCallbackReceiver(); 1461 getContentResolver().unregisterContentObserver(mLocalImagesObserver); 1462 getContentResolver().unregisterContentObserver(mLocalVideosObserver); 1463 getServices().getCaptureSessionManager().removeSessionListener(mSessionListener); 1464 mCameraAppUI.onDestroy(); 1465 mCameraController = null; 1466 mSettingsManager = null; 1467 mCameraAppUI = null; 1468 mOrientationManager = null; 1469 mButtonManager = null; 1470 CameraManagerFactory.recycle(); 1471 super.onDestroy(); 1472 } 1473 1474 @Override 1475 public void onConfigurationChanged(Configuration config) { 1476 super.onConfigurationChanged(config); 1477 Log.v(TAG, "onConfigurationChanged"); 1478 if (config.orientation == Configuration.ORIENTATION_UNDEFINED) { 1479 return; 1480 } 1481 1482 if (mLastLayoutOrientation != config.orientation) { 1483 mLastLayoutOrientation = config.orientation; 1484 mCurrentModule.onLayoutOrientationChanged( 1485 mLastLayoutOrientation == Configuration.ORIENTATION_LANDSCAPE); 1486 } 1487 } 1488 1489 @Override 1490 public boolean onKeyDown(int keyCode, KeyEvent event) { 1491 if (!mFilmstripVisible) { 1492 if (mCurrentModule.onKeyDown(keyCode, event)) { 1493 return true; 1494 } 1495 // Prevent software keyboard or voice search from showing up. 1496 if (keyCode == KeyEvent.KEYCODE_SEARCH 1497 || keyCode == KeyEvent.KEYCODE_MENU) { 1498 if (event.isLongPress()) { 1499 return true; 1500 } 1501 } 1502 } 1503 1504 return super.onKeyDown(keyCode, event); 1505 } 1506 1507 @Override 1508 public boolean onKeyUp(int keyCode, KeyEvent event) { 1509 if (!mFilmstripVisible && mCurrentModule.onKeyUp(keyCode, event)) { 1510 return true; 1511 } 1512 return super.onKeyUp(keyCode, event); 1513 } 1514 1515 @Override 1516 public void onBackPressed() { 1517 if (!mCameraAppUI.onBackPressed()) { 1518 if (!mCurrentModule.onBackPressed()) { 1519 super.onBackPressed(); 1520 } 1521 } 1522 } 1523 1524 @Override 1525 public boolean isAutoRotateScreen() { 1526 // TODO: Move to OrientationManager. 1527 return mAutoRotateScreen; 1528 } 1529 1530 @Override 1531 public boolean onCreateOptionsMenu(Menu menu) { 1532 MenuInflater inflater = getMenuInflater(); 1533 inflater.inflate(R.menu.filmstrip_menu, menu); 1534 mActionBarMenu = menu; 1535 return super.onCreateOptionsMenu(menu); 1536 } 1537 1538 protected void updateStorageSpace() { 1539 mStorageSpaceBytes = Storage.getAvailableSpace(); 1540 } 1541 1542 protected long getStorageSpaceBytes() { 1543 return mStorageSpaceBytes; 1544 } 1545 1546 protected void updateStorageSpaceAndHint() { 1547 updateStorageSpace(); 1548 updateStorageHint(mStorageSpaceBytes); 1549 } 1550 1551 protected void updateStorageHint(long storageSpace) { 1552 String message = null; 1553 if (storageSpace == Storage.UNAVAILABLE) { 1554 message = getString(R.string.no_storage); 1555 } else if (storageSpace == Storage.PREPARING) { 1556 message = getString(R.string.preparing_sd); 1557 } else if (storageSpace == Storage.UNKNOWN_SIZE) { 1558 message = getString(R.string.access_sd_fail); 1559 } else if (storageSpace <= Storage.LOW_STORAGE_THRESHOLD_BYTES) { 1560 message = getString(R.string.spaceIsLow_content); 1561 } 1562 1563 if (message != null) { 1564 if (mStorageHint == null) { 1565 mStorageHint = OnScreenHint.makeText(mAppContext, message); 1566 } else { 1567 mStorageHint.setText(message); 1568 } 1569 mStorageHint.show(); 1570 } else if (mStorageHint != null) { 1571 mStorageHint.cancel(); 1572 mStorageHint = null; 1573 } 1574 } 1575 1576 protected void setResultEx(int resultCode) { 1577 mResultCodeForTesting = resultCode; 1578 setResult(resultCode); 1579 } 1580 1581 protected void setResultEx(int resultCode, Intent data) { 1582 mResultCodeForTesting = resultCode; 1583 mResultDataForTesting = data; 1584 setResult(resultCode, data); 1585 } 1586 1587 public int getResultCode() { 1588 return mResultCodeForTesting; 1589 } 1590 1591 public Intent getResultData() { 1592 return mResultDataForTesting; 1593 } 1594 1595 public boolean isSecureCamera() { 1596 return mSecureCamera; 1597 } 1598 1599 @Override 1600 public boolean isPaused() { 1601 return mPaused; 1602 } 1603 1604 @Override 1605 public void onModeSelected(int modeIndex) { 1606 if (mCurrentModeIndex == modeIndex) { 1607 return; 1608 } 1609 1610 CameraPerformanceTracker.onEvent(CameraPerformanceTracker.MODE_SWITCH_START); 1611 // Record last used camera mode for quick switching 1612 if (modeIndex == getResources().getInteger(R.integer.camera_mode_photo) 1613 || modeIndex == getResources().getInteger(R.integer.camera_mode_gcam)) { 1614 mSettingsManager.setInt(SettingsManager.SETTING_KEY_CAMERA_MODULE_LAST_USED_INDEX, 1615 modeIndex); 1616 } 1617 1618 closeModule(mCurrentModule); 1619 int oldModuleIndex = mCurrentModeIndex; 1620 1621 // Refocus and Gcam are modes that cannot be selected 1622 // from the mode list view, because they are not list items. 1623 // Check whether we should interpret MODULE_CRAFT as either. 1624 if (modeIndex == getResources().getInteger(R.integer.camera_mode_photo)) { 1625 boolean hdrPlusOn = mSettingsManager.isHdrPlusOn(); 1626 if (hdrPlusOn && GcamHelper.hasGcamCapture()) { 1627 modeIndex = getResources().getInteger(R.integer.camera_mode_gcam); 1628 } 1629 } 1630 1631 setModuleFromModeIndex(modeIndex); 1632 1633 mCameraAppUI.resetBottomControls(mCurrentModule, modeIndex); 1634 mCameraAppUI.addShutterListener(mCurrentModule); 1635 openModule(mCurrentModule); 1636 mCurrentModule.onOrientationChanged(mLastRawOrientation); 1637 // Store the module index so we can use it the next time the Camera 1638 // starts up. 1639 SharedPreferences prefs = PreferenceManager 1640 .getDefaultSharedPreferences(mAppContext); 1641 prefs.edit().putInt(CameraSettings.KEY_STARTUP_MODULE_INDEX, modeIndex).apply(); 1642 } 1643 1644 /** 1645 * Shows the settings dialog. 1646 */ 1647 @Override 1648 public void onSettingsSelected() { 1649 Intent intent = new Intent(this, CameraSettingsActivity.class); 1650 startActivity(intent); 1651 } 1652 1653 /** 1654 * Sets the mCurrentModuleIndex, creates a new module instance for the given 1655 * index an sets it as mCurrentModule. 1656 */ 1657 private void setModuleFromModeIndex(int modeIndex) { 1658 ModuleManagerImpl.ModuleAgent agent = mModuleManager.getModuleAgent(modeIndex); 1659 if (agent == null) { 1660 return; 1661 } 1662 if (!agent.requestAppForCamera()) { 1663 mCameraController.closeCamera(); 1664 } 1665 mCurrentModeIndex = agent.getModuleId(); 1666 mCurrentModule = (CameraModule) agent.createModule(this); 1667 } 1668 1669 @Override 1670 public SettingsManager getSettingsManager() { 1671 return mSettingsManager; 1672 } 1673 1674 @Override 1675 public CameraServices getServices() { 1676 return (CameraServices) getApplication(); 1677 } 1678 1679 public List<String> getSupportedModeNames() { 1680 List<Integer> indices = mModuleManager.getSupportedModeIndexList(); 1681 List<String> supported = new ArrayList<String>(); 1682 1683 for (Integer modeIndex : indices) { 1684 String name = CameraUtil.getCameraModeText(modeIndex, mAppContext); 1685 if (name != null && !name.equals("")) { 1686 supported.add(name); 1687 } 1688 } 1689 return supported; 1690 } 1691 1692 @Override 1693 public ButtonManager getButtonManager() { 1694 if (mButtonManager == null) { 1695 mButtonManager = new ButtonManager(this); 1696 } 1697 return mButtonManager; 1698 } 1699 1700 /** 1701 * Creates an AlertDialog appropriate for choosing whether to enable 1702 * location on the first run of the app. 1703 */ 1704 public AlertDialog getFirstTimeLocationAlert() { 1705 AlertDialog.Builder builder = new AlertDialog.Builder(this); 1706 builder = SettingsUtil.getFirstTimeLocationAlertBuilder(builder, new Callback<Boolean>() { 1707 @Override 1708 public void onCallback(Boolean locationOn) { 1709 mSettingsManager.setLocation(locationOn, mLocationManager); 1710 } 1711 }); 1712 if (builder != null) { 1713 return builder.create(); 1714 } else { 1715 return null; 1716 } 1717 } 1718 1719 /** 1720 * Launches an ACTION_EDIT intent for the given local data item. If 1721 * 'withTinyPlanet' is set, this will show a disambig dialog first to let 1722 * the user start either the tiny planet editor or another photo edior. 1723 * 1724 * @param data The data item to edit. 1725 */ 1726 public void launchEditor(LocalData data) { 1727 Intent intent = new Intent(Intent.ACTION_EDIT) 1728 .setDataAndType(data.getContentUri(), data.getMimeType()) 1729 .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 1730 try { 1731 launchActivityByIntent(intent); 1732 } catch (ActivityNotFoundException e) { 1733 launchActivityByIntent(Intent.createChooser(intent, null)); 1734 } 1735 } 1736 1737 @Override 1738 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { 1739 super.onCreateContextMenu(menu, v, menuInfo); 1740 1741 MenuInflater inflater = getMenuInflater(); 1742 inflater.inflate(R.menu.filmstrip_context_menu, menu); 1743 } 1744 1745 @Override 1746 public boolean onContextItemSelected(MenuItem item) { 1747 switch (item.getItemId()) { 1748 case R.id.tiny_planet_editor: 1749 mMyFilmstripBottomControlListener.onTinyPlanet(); 1750 return true; 1751 case R.id.photo_editor: 1752 mMyFilmstripBottomControlListener.onEdit(); 1753 return true; 1754 } 1755 return false; 1756 } 1757 1758 /** 1759 * Launch the tiny planet editor. 1760 * 1761 * @param data The data must be a 360 degree stereographically mapped 1762 * panoramic image. It will not be modified, instead a new item 1763 * with the result will be added to the filmstrip. 1764 */ 1765 public void launchTinyPlanetEditor(LocalData data) { 1766 TinyPlanetFragment fragment = new TinyPlanetFragment(); 1767 Bundle bundle = new Bundle(); 1768 bundle.putString(TinyPlanetFragment.ARGUMENT_URI, data.getContentUri().toString()); 1769 bundle.putString(TinyPlanetFragment.ARGUMENT_TITLE, data.getTitle()); 1770 fragment.setArguments(bundle); 1771 fragment.show(getFragmentManager(), "tiny_planet"); 1772 } 1773 1774 private void openModule(CameraModule module) { 1775 module.init(this, isSecureCamera(), isCaptureIntent()); 1776 module.resume(); 1777 int visibility = mFilmstripVisible ? ModuleController.VISIBILITY_HIDDEN 1778 : ModuleController.VISIBILITY_VISIBLE; 1779 module.onPreviewVisibilityChanged(visibility); 1780 } 1781 1782 private void closeModule(CameraModule module) { 1783 module.pause(); 1784 mCameraAppUI.clearModuleUI(); 1785 } 1786 1787 private void performDeletion() { 1788 if (!mPendingDeletion) { 1789 return; 1790 } 1791 hideUndoDeletionBar(false); 1792 mDataAdapter.executeDeletion(); 1793 } 1794 1795 public void showUndoDeletionBar() { 1796 if (mPendingDeletion) { 1797 performDeletion(); 1798 } 1799 Log.v(TAG, "showing undo bar"); 1800 mPendingDeletion = true; 1801 if (mUndoDeletionBar == null) { 1802 ViewGroup v = (ViewGroup) getLayoutInflater().inflate(R.layout.undo_bar, 1803 mAboveFilmstripControlLayout, true); 1804 mUndoDeletionBar = (ViewGroup) v.findViewById(R.id.camera_undo_deletion_bar); 1805 View button = mUndoDeletionBar.findViewById(R.id.camera_undo_deletion_button); 1806 button.setOnClickListener(new View.OnClickListener() { 1807 @Override 1808 public void onClick(View view) { 1809 mDataAdapter.undoDataRemoval(); 1810 hideUndoDeletionBar(true); 1811 } 1812 }); 1813 // Setting undo bar clickable to avoid touch events going through 1814 // the bar to the buttons (eg. edit button, etc) underneath the bar. 1815 mUndoDeletionBar.setClickable(true); 1816 // When there is user interaction going on with the undo button, we 1817 // do not want to hide the undo bar. 1818 button.setOnTouchListener(new View.OnTouchListener() { 1819 @Override 1820 public boolean onTouch(View v, MotionEvent event) { 1821 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { 1822 mIsUndoingDeletion = true; 1823 } else if (event.getActionMasked() == MotionEvent.ACTION_UP) { 1824 mIsUndoingDeletion = false; 1825 } 1826 return false; 1827 } 1828 }); 1829 } 1830 mUndoDeletionBar.setAlpha(0f); 1831 mUndoDeletionBar.setVisibility(View.VISIBLE); 1832 mUndoDeletionBar.animate().setDuration(200).alpha(1f).setListener(null).start(); 1833 } 1834 1835 private void hideUndoDeletionBar(boolean withAnimation) { 1836 Log.v(TAG, "Hiding undo deletion bar"); 1837 mPendingDeletion = false; 1838 if (mUndoDeletionBar != null) { 1839 if (withAnimation) { 1840 mUndoDeletionBar.animate().setDuration(200).alpha(0f) 1841 .setListener(new Animator.AnimatorListener() { 1842 @Override 1843 public void onAnimationStart(Animator animation) { 1844 // Do nothing. 1845 } 1846 1847 @Override 1848 public void onAnimationEnd(Animator animation) { 1849 mUndoDeletionBar.setVisibility(View.GONE); 1850 } 1851 1852 @Override 1853 public void onAnimationCancel(Animator animation) { 1854 // Do nothing. 1855 } 1856 1857 @Override 1858 public void onAnimationRepeat(Animator animation) { 1859 // Do nothing. 1860 } 1861 }).start(); 1862 } else { 1863 mUndoDeletionBar.setVisibility(View.GONE); 1864 } 1865 } 1866 } 1867 1868 @Override 1869 public void onOrientationChanged(int orientation) { 1870 // We keep the last known orientation. So if the user first orient 1871 // the camera then point the camera to floor or sky, we still have 1872 // the correct orientation. 1873 if (orientation == OrientationManager.ORIENTATION_UNKNOWN) { 1874 return; 1875 } 1876 mLastRawOrientation = orientation; 1877 if (mCurrentModule != null) { 1878 mCurrentModule.onOrientationChanged(orientation); 1879 } 1880 } 1881 1882 /** 1883 * Enable/disable swipe-to-filmstrip. Will always disable swipe if in 1884 * capture intent. 1885 * 1886 * @param enable {@code true} to enable swipe. 1887 */ 1888 public void setSwipingEnabled(boolean enable) { 1889 // TODO: Bring back the functionality. 1890 if (isCaptureIntent()) { 1891 // lockPreview(true); 1892 } else { 1893 // lockPreview(!enable); 1894 } 1895 } 1896 1897 // Accessor methods for getting latency times used in performance testing 1898 public long getFirstPreviewTime() { 1899 if (mCurrentModule instanceof PhotoModule) { 1900 long coverHiddenTime = getCameraAppUI().getCoverHiddenTime(); 1901 if (coverHiddenTime != -1) { 1902 return coverHiddenTime - mOnCreateTime; 1903 } 1904 } 1905 return -1; 1906 } 1907 1908 public long getAutoFocusTime() { 1909 return (mCurrentModule instanceof PhotoModule) ? 1910 ((PhotoModule) mCurrentModule).mAutoFocusTime : -1; 1911 } 1912 1913 public long getShutterLag() { 1914 return (mCurrentModule instanceof PhotoModule) ? 1915 ((PhotoModule) mCurrentModule).mShutterLag : -1; 1916 } 1917 1918 public long getShutterToPictureDisplayedTime() { 1919 return (mCurrentModule instanceof PhotoModule) ? 1920 ((PhotoModule) mCurrentModule).mShutterToPictureDisplayedTime : -1; 1921 } 1922 1923 public long getPictureDisplayedToJpegCallbackTime() { 1924 return (mCurrentModule instanceof PhotoModule) ? 1925 ((PhotoModule) mCurrentModule).mPictureDisplayedToJpegCallbackTime : -1; 1926 } 1927 1928 public long getJpegCallbackFinishTime() { 1929 return (mCurrentModule instanceof PhotoModule) ? 1930 ((PhotoModule) mCurrentModule).mJpegCallbackFinishTime : -1; 1931 } 1932 1933 public long getCaptureStartTime() { 1934 return (mCurrentModule instanceof PhotoModule) ? 1935 ((PhotoModule) mCurrentModule).mCaptureStartTime : -1; 1936 } 1937 1938 public boolean isRecording() { 1939 return (mCurrentModule instanceof VideoModule) ? 1940 ((VideoModule) mCurrentModule).isRecording() : false; 1941 } 1942 1943 public CameraManager.CameraOpenCallback getCameraOpenErrorCallback() { 1944 return mCameraController; 1945 } 1946 1947 // For debugging purposes only. 1948 public CameraModule getCurrentModule() { 1949 return mCurrentModule; 1950 } 1951 1952 /** 1953 * Reads the current location recording settings and passes it on to the 1954 * location manager. 1955 */ 1956 public void syncLocationManagerSetting() { 1957 mSettingsManager.syncLocationManager(mLocationManager); 1958 } 1959 1960 private void keepScreenOnForAWhile() { 1961 if (mKeepScreenOn) { 1962 return; 1963 } 1964 mMainHandler.removeMessages(MSG_CLEAR_SCREEN_ON_FLAG); 1965 getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 1966 mMainHandler.sendEmptyMessageDelayed(MSG_CLEAR_SCREEN_ON_FLAG, SCREEN_DELAY_MS); 1967 } 1968 1969 private void resetScreenOn() { 1970 mKeepScreenOn = false; 1971 mMainHandler.removeMessages(MSG_CLEAR_SCREEN_ON_FLAG); 1972 getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 1973 } 1974 1975 /** 1976 * @return {@code true} if the Gallery is launched successfully. 1977 */ 1978 private boolean startGallery() { 1979 if (mGalleryIntent == null) { 1980 return false; 1981 } 1982 try { 1983 UsageStatistics.changeScreen(NavigationChange.Mode.GALLERY, InteractionCause.BUTTON); 1984 Intent startGalleryIntent = new Intent(mGalleryIntent); 1985 int currentDataId = mFilmstripController.getCurrentId(); 1986 LocalData currentLocalData = mDataAdapter.getLocalData(currentDataId); 1987 if (currentLocalData != null) { 1988 GalleryHelper.setContentUri(startGalleryIntent, currentLocalData.getContentUri()); 1989 } 1990 launchActivityByIntent(startGalleryIntent); 1991 } catch (ActivityNotFoundException e) { 1992 Log.w(TAG, "Failed to launch gallery activity, closing"); 1993 } 1994 return false; 1995 } 1996 1997 private void setNfcBeamPushUriFromData(LocalData data) { 1998 final Uri uri = data.getContentUri(); 1999 if (uri != Uri.EMPTY) { 2000 mNfcPushUris[0] = uri; 2001 } else { 2002 mNfcPushUris[0] = null; 2003 } 2004 } 2005 2006 /** 2007 * Updates the visibility of the filmstrip bottom controls and action bar. 2008 */ 2009 private void updateUiByData(final int dataId) { 2010 final LocalData currentData = mDataAdapter.getLocalData(dataId); 2011 if (currentData == null) { 2012 Log.w(TAG, "Current data ID not found."); 2013 hideSessionProgress(); 2014 return; 2015 } 2016 updateActionBarMenu(currentData); 2017 2018 /* Bottom controls. */ 2019 updateBottomControlsByData(currentData); 2020 2021 if (isSecureCamera()) { 2022 // We cannot show buttons in secure camera since go to other 2023 // activities might create a security hole. 2024 mCameraAppUI.getFilmstripBottomControls().hideControls(); 2025 return; 2026 } 2027 2028 2029 setNfcBeamPushUriFromData(currentData); 2030 2031 if (!mDataAdapter.isMetadataUpdated(dataId)) { 2032 mDataAdapter.updateMetadata(dataId); 2033 } 2034 } 2035 2036 /** 2037 * Updates the bottom controls based on the data. 2038 */ 2039 private void updateBottomControlsByData(final LocalData currentData) { 2040 2041 final CameraAppUI.BottomPanel filmstripBottomPanel = 2042 mCameraAppUI.getFilmstripBottomControls(); 2043 filmstripBottomPanel.showControls(); 2044 filmstripBottomPanel.setEditButtonVisibility( 2045 currentData.isDataActionSupported(LocalData.DATA_ACTION_EDIT)); 2046 filmstripBottomPanel.setShareButtonVisibility( 2047 currentData.isDataActionSupported(LocalData.DATA_ACTION_SHARE)); 2048 filmstripBottomPanel.setDeleteButtonVisibility( 2049 currentData.isDataActionSupported(LocalData.DATA_ACTION_DELETE)); 2050 2051 /* Progress bar */ 2052 2053 Uri contentUri = currentData.getContentUri(); 2054 CaptureSessionManager sessionManager = getServices() 2055 .getCaptureSessionManager(); 2056 int sessionProgress = sessionManager.getSessionProgress(contentUri); 2057 2058 if (sessionProgress < 0) { 2059 hideSessionProgress(); 2060 } else { 2061 CharSequence progressMessage = sessionManager 2062 .getSessionProgressMessage(contentUri); 2063 showSessionProgress(progressMessage); 2064 updateSessionProgress(sessionProgress); 2065 } 2066 2067 /* View button */ 2068 2069 // We need to add this to a separate DB. 2070 final int viewButtonVisibility; 2071 if (PanoramaMetadataLoader.isPanoramaAndUseViewer(currentData)) { 2072 viewButtonVisibility = CameraAppUI.BottomPanel.VIEWER_PHOTO_SPHERE; 2073 } else if (RgbzMetadataLoader.hasRGBZData(currentData)) { 2074 viewButtonVisibility = CameraAppUI.BottomPanel.VIEWER_REFOCUS; 2075 } else { 2076 viewButtonVisibility = CameraAppUI.BottomPanel.VIEWER_NONE; 2077 } 2078 2079 filmstripBottomPanel.setTinyPlanetEnabled( 2080 PanoramaMetadataLoader.isPanorama360(currentData)); 2081 filmstripBottomPanel.setViewerButtonVisibility(viewButtonVisibility); 2082 } 2083 2084 private class PeekAnimationHandler extends Handler { 2085 private class DataAndCallback { 2086 LocalData mData; 2087 com.android.camera.util.Callback<Bitmap> mCallback; 2088 2089 public DataAndCallback(LocalData data, com.android.camera.util.Callback<Bitmap> 2090 callback) { 2091 mData = data; 2092 mCallback = callback; 2093 } 2094 } 2095 2096 public PeekAnimationHandler(Looper looper) { 2097 super(looper); 2098 } 2099 2100 /** 2101 * Starts the animation decoding job and posts a {@code Runnable} back 2102 * when when the decoding is done. 2103 * 2104 * @param data The data item to decode the thumbnail for. 2105 * @param callback {@link com.android.camera.util.Callback} after the 2106 * decoding is done. 2107 */ 2108 public void startDecodingJob(final LocalData data, 2109 final com.android.camera.util.Callback<Bitmap> callback) { 2110 PeekAnimationHandler.this.obtainMessage(0 /** dummy integer **/, 2111 new DataAndCallback(data, callback)).sendToTarget(); 2112 } 2113 2114 @Override 2115 public void handleMessage(Message msg) { 2116 final LocalData data = ((DataAndCallback) msg.obj).mData; 2117 final com.android.camera.util.Callback<Bitmap> callback = 2118 ((DataAndCallback) msg.obj).mCallback; 2119 if (data == null || callback == null) { 2120 return; 2121 } 2122 2123 final Bitmap bitmap; 2124 switch (data.getLocalDataType()) { 2125 case LocalData.LOCAL_IMAGE: 2126 case LocalData.LOCAL_IN_PROGRESS_DATA: 2127 FileInputStream stream; 2128 try { 2129 stream = new FileInputStream(data.getPath()); 2130 } catch (FileNotFoundException e) { 2131 Log.e(TAG, "File not found:" + data.getPath()); 2132 return; 2133 } 2134 Point dim = CameraUtil.resizeToFill(data.getWidth(), data.getHeight(), 2135 data.getRotation(), mAboveFilmstripControlLayout.getWidth(), 2136 mAboveFilmstripControlLayout.getMeasuredHeight()); 2137 if (data.getRotation() % 180 != 0) { 2138 int dummy = dim.x; 2139 dim.x = dim.y; 2140 dim.y = dummy; 2141 } 2142 bitmap = LocalDataUtil 2143 .loadImageThumbnailFromStream(stream, data.getWidth(), data.getHeight(), 2144 (int) (dim.x * 0.7f), (int) (dim.y * 0.7), 2145 data.getRotation(), MAX_PEEK_BITMAP_PIXELS); 2146 break; 2147 2148 case LocalData.LOCAL_VIDEO: 2149 bitmap = LocalDataUtil.loadVideoThumbnail(data.getPath()); 2150 break; 2151 2152 default: 2153 bitmap = null; 2154 break; 2155 } 2156 2157 if (bitmap == null) { 2158 return; 2159 } 2160 2161 mMainHandler.post(new Runnable() { 2162 @Override 2163 public void run() { 2164 callback.onCallback(bitmap); 2165 mCameraAppUI.startPeekAnimation(bitmap, true); 2166 } 2167 }); 2168 } 2169 } 2170 2171 private void showDetailsDialog(int dataId) { 2172 final LocalData data = mDataAdapter.getLocalData(dataId); 2173 if (data == null) { 2174 return; 2175 } 2176 MediaDetails details = data.getMediaDetails(getAndroidContext()); 2177 if (details == null) { 2178 return; 2179 } 2180 Dialog detailDialog = DetailsDialog.create(CameraActivity.this, details); 2181 detailDialog.show(); 2182 2183 } 2184 2185 /** 2186 * Show or hide action bar items depending on current data type. 2187 */ 2188 private void updateActionBarMenu(LocalData data) { 2189 if (mActionBarMenu == null) { 2190 return; 2191 } 2192 2193 MenuItem detailsMenuItem = mActionBarMenu.findItem(R.id.action_details); 2194 if (detailsMenuItem == null) { 2195 return; 2196 } 2197 2198 int type = data.getLocalDataType(); 2199 boolean showDetails = (type == LocalData.LOCAL_IMAGE) || (type == LocalData.LOCAL_VIDEO); 2200 detailsMenuItem.setVisible(showDetails); 2201 } 2202} 2203