CameraActivity.java revision 91c25e3d33120d5466c42f17276565f99df79c56
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.MenuInflater; 60import android.view.MenuItem; 61import android.view.MotionEvent; 62import android.view.View; 63import android.view.ViewGroup; 64import android.view.Window; 65import android.view.WindowManager; 66import android.widget.FrameLayout; 67import android.widget.ImageView; 68import android.widget.ShareActionProvider; 69 70import com.android.camera.app.AppController; 71import com.android.camera.app.CameraAppUI; 72import com.android.camera.app.CameraController; 73import com.android.camera.app.CameraManager; 74import com.android.camera.app.CameraManagerFactory; 75import com.android.camera.app.CameraProvider; 76import com.android.camera.app.CameraServices; 77import com.android.camera.app.LocationManager; 78import com.android.camera.app.ModuleManagerImpl; 79import com.android.camera.app.OrientationManager; 80import com.android.camera.app.OrientationManagerImpl; 81import com.android.camera.data.CameraDataAdapter; 82import com.android.camera.data.FixedLastDataAdapter; 83import com.android.camera.data.InProgressDataWrapper; 84import com.android.camera.data.LocalData; 85import com.android.camera.data.LocalDataAdapter; 86import com.android.camera.data.LocalDataUtil; 87import com.android.camera.data.LocalMediaObserver; 88import com.android.camera.data.MediaDetails; 89import com.android.camera.data.PanoramaMetadataLoader; 90import com.android.camera.data.RgbzMetadataLoader; 91import com.android.camera.data.SimpleViewData; 92import com.android.camera.filmstrip.FilmstripContentPanel; 93import com.android.camera.filmstrip.FilmstripController; 94import com.android.camera.hardware.HardwareSpec; 95import com.android.camera.hardware.HardwareSpecImpl; 96import com.android.camera.module.ModuleController; 97import com.android.camera.module.ModulesInfo; 98import com.android.camera.session.CaptureSessionManager; 99import com.android.camera.session.CaptureSessionManager.SessionListener; 100import com.android.camera.settings.CameraSettingsActivity; 101import com.android.camera.settings.SettingsManager; 102import com.android.camera.settings.SettingsManager.SettingsCapabilities; 103import com.android.camera.settings.SettingsUtil; 104import com.android.camera.tinyplanet.TinyPlanetFragment; 105import com.android.camera.ui.DetailsDialog; 106import com.android.camera.ui.MainActivityLayout; 107import com.android.camera.ui.ModeListView; 108import com.android.camera.ui.ModeListView.ModeListVisibilityChangedListener; 109import com.android.camera.ui.PreviewStatusListener; 110import com.android.camera.util.ApiHelper; 111import com.android.camera.util.Callback; 112import com.android.camera.util.CameraUtil; 113import com.android.camera.util.FeedbackHelper; 114import com.android.camera.util.GalleryHelper; 115import com.android.camera.util.GcamHelper; 116import com.android.camera.util.IntentHelper; 117import com.android.camera.util.PhotoSphereHelper.PanoramaViewHelper; 118import com.android.camera.util.ReleaseDialogHelper; 119import com.android.camera.util.UsageStatistics; 120import com.android.camera.widget.FilmstripView; 121import com.android.camera2.R; 122import com.google.common.logging.eventprotos; 123import com.google.common.logging.eventprotos.CameraEvent.InteractionCause; 124import com.google.common.logging.eventprotos.NavigationChange; 125 126import java.io.File; 127import java.io.FileInputStream; 128import java.io.FileNotFoundException; 129import java.lang.ref.WeakReference; 130import java.util.ArrayList; 131import java.util.List; 132 133public class CameraActivity extends Activity 134 implements AppController, CameraManager.CameraOpenCallback, 135 ActionBar.OnMenuVisibilityListener, ShareActionProvider.OnShareTargetSelectedListener, 136 OrientationManager.OnOrientationChangeListener { 137 138 private static final String TAG = "CameraActivity"; 139 140 private static final String INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE = 141 "android.media.action.STILL_IMAGE_CAMERA_SECURE"; 142 public static final String ACTION_IMAGE_CAPTURE_SECURE = 143 "android.media.action.IMAGE_CAPTURE_SECURE"; 144 145 // The intent extra for camera from secure lock screen. True if the gallery 146 // should only show newly captured pictures. sSecureAlbumId does not 147 // increment. This is used when switching between camera, camcorder, and 148 // panorama. If the extra is not set, it is in the normal camera mode. 149 public static final String SECURE_CAMERA_EXTRA = "secure_camera"; 150 151 /** 152 * Request code from an activity we started that indicated that we do not 153 * want to reset the view to the preview in onResume. 154 */ 155 public static final int REQ_CODE_DONT_SWITCH_TO_PREVIEW = 142; 156 157 public static final int REQ_CODE_GCAM_DEBUG_POSTCAPTURE = 999; 158 159 private static final int MSG_CLEAR_SCREEN_ON_FLAG = 2; 160 private static final long SCREEN_DELAY_MS = 2 * 60 * 1000; // 2 mins. 161 private static final int MAX_PEEK_BITMAP_PIXELS = 1600000; // 1.6 * 4 MBs. 162 163 /** Should be used wherever a context is needed. */ 164 private Context mAppContext; 165 166 /** 167 * Whether onResume should reset the view to the preview. 168 */ 169 private boolean mResetToPreviewOnResume = true; 170 171 /** 172 * This data adapter is used by FilmStripView. 173 */ 174 private LocalDataAdapter mDataAdapter; 175 176 /** 177 * TODO: This should be moved to the app level. 178 */ 179 private SettingsManager mSettingsManager; 180 181 private ModeListView mModeListView; 182 private int mCurrentModeIndex; 183 private CameraModule mCurrentModule; 184 private ModuleManagerImpl mModuleManager; 185 private FrameLayout mAboveFilmstripControlLayout; 186 private FilmstripController mFilmstripController; 187 private boolean mFilmstripVisible; 188 private int mResultCodeForTesting; 189 private Intent mResultDataForTesting; 190 private OnScreenHint mStorageHint; 191 private long mStorageSpaceBytes = Storage.LOW_STORAGE_THRESHOLD_BYTES; 192 private boolean mAutoRotateScreen; 193 private boolean mSecureCamera; 194 private int mLastRawOrientation; 195 private OrientationManagerImpl mOrientationManager; 196 private LocationManager mLocationManager; 197 private ButtonManager mButtonManager; 198 private Handler mMainHandler; 199 private PanoramaViewHelper mPanoramaViewHelper; 200 private ActionBar mActionBar; 201 private ViewGroup mUndoDeletionBar; 202 private boolean mIsUndoingDeletion = false; 203 204 private final Uri[] mNfcPushUris = new Uri[1]; 205 206 private LocalMediaObserver mLocalImagesObserver; 207 private LocalMediaObserver mLocalVideosObserver; 208 209 private boolean mPendingDeletion = false; 210 211 private CameraController mCameraController; 212 private boolean mPaused; 213 private CameraAppUI mCameraAppUI; 214 215 private PeekAnimationHandler mPeekAnimationHandler; 216 private HandlerThread mPeekAnimationThread; 217 218 private FeedbackHelper mFeedbackHelper; 219 220 private Intent mGalleryIntent; 221 private long mOnCreateTime; 222 223 @Override 224 public CameraAppUI getCameraAppUI() { 225 return mCameraAppUI; 226 } 227 228 // close activity when screen turns off 229 private final BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() { 230 @Override 231 public void onReceive(Context context, Intent intent) { 232 finish(); 233 } 234 }; 235 236 /** 237 * Whether the screen is kept turned on. 238 */ 239 private boolean mKeepScreenOn; 240 private int mLastLayoutOrientation; 241 private final CameraAppUI.BottomPanel.Listener mMyFilmstripBottomControlListener = 242 new CameraAppUI.BottomPanel.Listener() { 243 244 /** 245 * If the current photo is a photo sphere, this will launch the 246 * Photo Sphere panorama viewer. 247 */ 248 @Override 249 public void onExternalViewer() { 250 if (mPanoramaViewHelper == null) { 251 return; 252 } 253 final LocalData data = getCurrentLocalData(); 254 if (data == null) { 255 return; 256 } 257 final Uri contentUri = data.getContentUri(); 258 if (contentUri == Uri.EMPTY) { 259 return; 260 } 261 262 if (PanoramaMetadataLoader.isPanoramaAndUseViewer(data)) { 263 mPanoramaViewHelper.showPanorama(CameraActivity.this, contentUri); 264 } else if (RgbzMetadataLoader.hasRGBZData(data)) { 265 mPanoramaViewHelper.showRgbz(contentUri); 266 } 267 } 268 269 @Override 270 public void onEdit() { 271 LocalData data = getCurrentLocalData(); 272 if (data == null) { 273 return; 274 } 275 launchEditor(data); 276 } 277 278 @Override 279 public void onTinyPlanet() { 280 LocalData data = getCurrentLocalData(); 281 if (data == null) { 282 return; 283 } 284 launchTinyPlanetEditor(data); 285 } 286 287 @Override 288 public void onDelete() { 289 final int currentDataId = getCurrentDataId(); 290 UsageStatistics.photoInteraction( 291 UsageStatistics.hashFileName(fileNameFromDataID(currentDataId)), 292 eventprotos.CameraEvent.InteractionType.DELETE, 293 InteractionCause.BUTTON); 294 removeData(currentDataId); 295 } 296 297 @Override 298 public void onShare() { 299 final LocalData data = getCurrentLocalData(); 300 301 // If applicable, show release information before this item 302 // is shared. 303 if (PanoramaMetadataLoader.isPanorama(data) 304 || RgbzMetadataLoader.hasRGBZData(data)) { 305 ReleaseDialogHelper.showReleaseInfoDialog(CameraActivity.this, 306 new Callback<Void>() { 307 @Override 308 public void onCallback(Void result) { 309 share(data); 310 } 311 }); 312 } else { 313 share(data); 314 } 315 } 316 317 private void share(LocalData data) { 318 Intent shareIntent = getShareIntentByData(data); 319 if (shareIntent != null) { 320 try { 321 launchActivityByIntent(shareIntent); 322 mCameraAppUI.getFilmstripBottomControls().setShareEnabled(false); 323 } catch (ActivityNotFoundException ex) { 324 // Nothing. 325 } 326 } 327 } 328 329 private int getCurrentDataId() { 330 return mFilmstripController.getCurrentId(); 331 } 332 333 private LocalData getCurrentLocalData() { 334 return mDataAdapter.getLocalData(getCurrentDataId()); 335 } 336 337 /** 338 * Sets up the share intent and NFC properly according to the 339 * data. 340 * 341 * @param data The data to be shared. 342 */ 343 private Intent getShareIntentByData(final LocalData data) { 344 Intent intent = null; 345 final Uri contentUri = data.getContentUri(); 346 if (PanoramaMetadataLoader.isPanorama360(data) && 347 data.getContentUri() != Uri.EMPTY) { 348 intent = new Intent(Intent.ACTION_SEND); 349 intent.setType("application/vnd.google.panorama360+jpg"); 350 intent.putExtra(Intent.EXTRA_STREAM, contentUri); 351 } else if (data.isDataActionSupported(LocalData.DATA_ACTION_SHARE)) { 352 final String mimeType = data.getMimeType(); 353 intent = getShareIntentFromType(mimeType); 354 if (intent != null) { 355 intent.putExtra(Intent.EXTRA_STREAM, contentUri); 356 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 357 } 358 } 359 return intent; 360 } 361 362 /** 363 * Get the share intent according to the mimeType 364 * 365 * @param mimeType The mimeType of current data. 366 * @return the video/image's ShareIntent or null if mimeType is 367 * invalid. 368 */ 369 private Intent getShareIntentFromType(String mimeType) { 370 // Lazily create the intent object. 371 Intent intent = new Intent(Intent.ACTION_SEND); 372 if (mimeType.startsWith("video/")) { 373 intent.setType("video/*"); 374 } else { 375 if (mimeType.startsWith("image/")) { 376 intent.setType("image/*"); 377 } else { 378 Log.w(TAG, "unsupported mimeType " + mimeType); 379 } 380 } 381 return intent; 382 } 383 }; 384 385 private ComboPreferences mPreferences; 386 private ContentResolver mContentResolver; 387 388 @Override 389 public void onCameraOpened(CameraManager.CameraProxy camera) { 390 /** 391 * The current UI requires that the flash option visibility in front-facing 392 * camera be 393 * * disabled if back facing camera supports flash 394 * * hidden if back facing camera does not support flash 395 * We save whether back facing camera supports flash because we cannot get 396 * this in front facing camera without a camera switch. 397 * 398 * If this preference is cleared, we also need to clear the camera facing 399 * setting so we default to opening the camera in back facing camera, and 400 * can save this flash support value again. 401 */ 402 if (!mSettingsManager.isSet(SettingsManager.SETTING_FLASH_SUPPORTED_BACK_CAMERA)) { 403 HardwareSpec hardware = new HardwareSpecImpl(camera.getParameters()); 404 mSettingsManager.setBoolean(SettingsManager.SETTING_FLASH_SUPPORTED_BACK_CAMERA, 405 hardware.isFlashSupported()); 406 } 407 408 if (!mModuleManager.getModuleAgent(mCurrentModeIndex).requestAppForCamera()) { 409 // We shouldn't be here. Just close the camera and leave. 410 camera.release(false); 411 throw new IllegalStateException("Camera opened but the module shouldn't be " + 412 "requesting"); 413 } 414 if (mCurrentModule != null) { 415 SettingsCapabilities capabilities = 416 SettingsUtil.getSettingsCapabilities(camera); 417 mSettingsManager.changeCamera(camera.getCameraId(), capabilities); 418 mCurrentModule.onCameraAvailable(camera); 419 } 420 mCameraAppUI.onChangeCamera(); 421 } 422 423 @Override 424 public void onCameraDisabled(int cameraId) { 425 UsageStatistics.cameraFailure(eventprotos.CameraFailure.FailureReason.SECURITY); 426 427 CameraUtil.showErrorAndFinish(this, R.string.camera_disabled); 428 } 429 430 @Override 431 public void onDeviceOpenFailure(int cameraId) { 432 UsageStatistics.cameraFailure(eventprotos.CameraFailure.FailureReason.OPEN_FAILURE); 433 434 CameraUtil.showErrorAndFinish(this, R.string.cannot_connect_camera); 435 } 436 437 @Override 438 public void onReconnectionFailure(CameraManager mgr) { 439 UsageStatistics.cameraFailure(eventprotos.CameraFailure.FailureReason.RECONNECT_FAILURE); 440 441 CameraUtil.showErrorAndFinish(this, R.string.cannot_connect_camera); 442 } 443 444 private static class MainHandler extends Handler { 445 final WeakReference<CameraActivity> mActivity; 446 447 public MainHandler(CameraActivity activity, Looper looper) { 448 super(looper); 449 mActivity = new WeakReference<CameraActivity>(activity); 450 } 451 452 @Override 453 public void handleMessage(Message msg) { 454 CameraActivity activity = mActivity.get(); 455 if (activity == null) { 456 return; 457 } 458 switch (msg.what) { 459 460 case MSG_CLEAR_SCREEN_ON_FLAG: { 461 if (!activity.mPaused) { 462 activity.getWindow().clearFlags( 463 WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 464 } 465 break; 466 } 467 } 468 } 469 } 470 471 private String fileNameFromDataID(int dataID) { 472 final LocalData localData = mDataAdapter.getLocalData(dataID); 473 474 File localFile = new File(localData.getPath()); 475 return localFile.getName(); 476 } 477 478 private final FilmstripContentPanel.Listener mFilmstripListener = 479 new FilmstripContentPanel.Listener() { 480 481 @Override 482 public void onSwipeOut() { 483 UsageStatistics.changeScreen(eventprotos.NavigationChange.Mode.PHOTO_CAPTURE, 484 eventprotos.CameraEvent.InteractionCause.SWIPE_RIGHT); 485 } 486 487 @Override 488 public void onSwipeOutBegin() { 489 mActionBar.hide(); 490 } 491 492 @Override 493 public void onFilmstripHidden() { 494 mFilmstripVisible = false; 495 CameraActivity.this.setFilmstripUiVisibility(false); 496 // When the user hide the filmstrip (either swipe out or 497 // tap on back key) we move to the first item so next time 498 // when the user swipe in the filmstrip, the most recent 499 // one is shown. 500 mFilmstripController.goToFirstItem(); 501 if (mCurrentModule != null) { 502 mCurrentModule 503 .onPreviewVisibilityChanged(ModuleController.VISIBILITY_VISIBLE); 504 } 505 } 506 507 @Override 508 public void onFilmstripShown() { 509 mFilmstripVisible = true; 510 updateUiByData(mFilmstripController.getCurrentId()); 511 if (mCurrentModule != null) { 512 mCurrentModule 513 .onPreviewVisibilityChanged(ModuleController.VISIBILITY_HIDDEN); 514 } 515 } 516 517 @Override 518 public void onFocusedDataLongPressed(int dataId) { 519 final LocalData data = mDataAdapter.getLocalData(dataId); 520 if (data == null) { 521 return; 522 } 523 MediaDetails details = data.getMediaDetails(getAndroidContext()); 524 if (details == null) { 525 return; 526 } 527 Dialog detailDialog = DetailsDialog.create(CameraActivity.this, details); 528 detailDialog.show(); 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 boolean currentActionBarVisibility = mActionBar.isShowing(); 668 mCameraAppUI.getFilmstripBottomControls().setVisible(visible); 669 if (visible != currentActionBarVisibility) { 670 if (visible) { 671 mActionBar.show(); 672 } else { 673 mActionBar.hide(); 674 } 675 } 676 } 677 678 private void hideSessionProgress() { 679 mCameraAppUI.getFilmstripBottomControls().showControls(); 680 } 681 682 private void showSessionProgress(CharSequence message) { 683 CameraAppUI.BottomPanel controls = mCameraAppUI.getFilmstripBottomControls(); 684 controls.setProgressText(message); 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 mOrientationManager.lockOrientation(); 942 } 943 944 @Override 945 public void unlockOrientation() { 946 mOrientationManager.unlockOrientation(); 947 } 948 949 /** 950 * Starts the filmstrip peek animation if the filmstrip is not visible. 951 * Only {@link LocalData#LOCAL_IMAGE}, {@link 952 * LocalData#LOCAL_IN_PROGRESS_DATA} and {@link 953 * LocalData#LOCAL_VIDEO} are supported. 954 * 955 * @param data The data to peek. 956 */ 957 private void startPeekAnimation(final LocalData data) { 958 if (mFilmstripVisible || mPeekAnimationHandler == null) { 959 return; 960 } 961 962 int dataType = data.getLocalDataType(); 963 if (dataType != LocalData.LOCAL_IMAGE && dataType != LocalData.LOCAL_IN_PROGRESS_DATA && 964 dataType != LocalData.LOCAL_VIDEO) { 965 return; 966 } 967 968 mPeekAnimationHandler.startDecodingJob(data, new Callback<Bitmap>() { 969 @Override 970 public void onCallback(Bitmap result) { 971 mCameraAppUI.startPeekAnimation(result, true); 972 } 973 }); 974 } 975 976 @Override 977 public void notifyNewMedia(Uri uri) { 978 ContentResolver cr = getContentResolver(); 979 String mimeType = cr.getType(uri); 980 if (LocalDataUtil.isMimeTypeVideo(mimeType)) { 981 sendBroadcast(new Intent(CameraUtil.ACTION_NEW_VIDEO, uri)); 982 mDataAdapter.addNewVideo(uri); 983 } else if (LocalDataUtil.isMimeTypeImage(mimeType)) { 984 CameraUtil.broadcastNewPicture(mAppContext, uri); 985 mDataAdapter.addNewPhoto(uri); 986 } else if (LocalDataUtil.isMimeTypePlaceHolder(mimeType)) { 987 mDataAdapter.addNewPhoto(uri); 988 } else { 989 android.util.Log.w(TAG, "Unknown new media with MIME type:" 990 + mimeType + ", uri:" + uri); 991 } 992 } 993 994 @Override 995 public void enableKeepScreenOn(boolean enabled) { 996 if (mPaused) { 997 return; 998 } 999 1000 mKeepScreenOn = enabled; 1001 if (mKeepScreenOn) { 1002 mMainHandler.removeMessages(MSG_CLEAR_SCREEN_ON_FLAG); 1003 getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 1004 } else { 1005 keepScreenOnForAWhile(); 1006 } 1007 } 1008 1009 @Override 1010 public CameraProvider getCameraProvider() { 1011 return mCameraController; 1012 } 1013 1014 private void removeData(int dataID) { 1015 mDataAdapter.removeData(dataID); 1016 if (mDataAdapter.getTotalNumber() > 1) { 1017 showUndoDeletionBar(); 1018 } else { 1019 // If camera preview is the only view left in filmstrip, 1020 // no need to show undo bar. 1021 mPendingDeletion = true; 1022 performDeletion(); 1023 if (mFilmstripVisible) { 1024 mCameraAppUI.getFilmstripContentPanel().animateHide(); 1025 } 1026 } 1027 } 1028 1029 @Override 1030 public boolean onOptionsItemSelected(MenuItem item) { 1031 // Handle presses on the action bar items 1032 switch (item.getItemId()) { 1033 case android.R.id.home: 1034 if (mFilmstripVisible && startGallery()) { 1035 return true; 1036 } 1037 onBackPressed(); 1038 return true; 1039 default: 1040 return super.onOptionsItemSelected(item); 1041 } 1042 } 1043 1044 private boolean isCaptureIntent() { 1045 if (MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction()) 1046 || MediaStore.ACTION_IMAGE_CAPTURE.equals(getIntent().getAction()) 1047 || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(getIntent().getAction())) { 1048 return true; 1049 } else { 1050 return false; 1051 } 1052 } 1053 1054 private final SettingsManager.StrictUpgradeCallback mStrictUpgradeCallback 1055 = new SettingsManager.StrictUpgradeCallback() { 1056 @Override 1057 public void upgrade(SettingsManager settingsManager, int version) { 1058 // Show the location dialog on upgrade if 1059 // (a) the user has never set this option (status quo). 1060 // (b) the user opt'ed out previously. 1061 if (settingsManager.isSet(SettingsManager.SETTING_RECORD_LOCATION) && 1062 !settingsManager.getBoolean(SettingsManager.SETTING_RECORD_LOCATION)) { 1063 settingsManager.remove(SettingsManager.SETTING_RECORD_LOCATION); 1064 } 1065 } 1066 }; 1067 1068 @Override 1069 public void onCreate(Bundle state) { 1070 super.onCreate(state); 1071 CameraPerformanceTracker.onEvent(CameraPerformanceTracker.ACTIVITY_START); 1072 mOnCreateTime = System.currentTimeMillis(); 1073 mAppContext = getApplicationContext(); 1074 GcamHelper.init(getContentResolver()); 1075 1076 getWindow().requestFeature(Window.FEATURE_ACTION_BAR); 1077 setContentView(R.layout.activity_main); 1078 mActionBar = getActionBar(); 1079 mActionBar.addOnMenuVisibilityListener(this); 1080 mMainHandler = new MainHandler(this, getMainLooper()); 1081 mCameraController = 1082 new CameraController(mAppContext, this, mMainHandler, 1083 CameraManagerFactory.getAndroidCameraManager()); 1084 mPreferences = new ComboPreferences(mAppContext); 1085 mContentResolver = this.getContentResolver(); 1086 1087 mSettingsManager = new SettingsManager(mAppContext, this, 1088 mCameraController.getNumberOfCameras(), mStrictUpgradeCallback); 1089 1090 // Remove this after we get rid of ComboPreferences. 1091 int cameraId = Integer.parseInt(mSettingsManager.get(SettingsManager.SETTING_CAMERA_ID)); 1092 mPreferences.setLocalId(mAppContext, cameraId); 1093 CameraSettings.upgradeGlobalPreferences(mPreferences, 1094 mCameraController.getNumberOfCameras()); 1095 // TODO: Try to move all the resources allocation to happen as soon as 1096 // possible so we can call module.init() at the earliest time. 1097 mModuleManager = new ModuleManagerImpl(); 1098 ModulesInfo.setupModules(mAppContext, mModuleManager); 1099 1100 mModeListView = (ModeListView) findViewById(R.id.mode_list_layout); 1101 mModeListView.init(mModuleManager.getSupportedModeIndexList()); 1102 if (ApiHelper.HAS_ROTATION_ANIMATION) { 1103 setRotationAnimation(); 1104 } 1105 mModeListView.setVisibilityChangedListener(new ModeListVisibilityChangedListener() { 1106 @Override 1107 public void onVisibilityChanged(boolean visible) { 1108 if (mCurrentModule != null) { 1109 int visibility = visible ? ModuleController.VISIBILITY_COVERED 1110 : ModuleController.VISIBILITY_VISIBLE; 1111 mCurrentModule.onPreviewVisibilityChanged(visibility); 1112 } 1113 } 1114 }); 1115 1116 // Check if this is in the secure camera mode. 1117 Intent intent = getIntent(); 1118 String action = intent.getAction(); 1119 if (INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action) 1120 || ACTION_IMAGE_CAPTURE_SECURE.equals(action)) { 1121 mSecureCamera = true; 1122 } else { 1123 mSecureCamera = intent.getBooleanExtra(SECURE_CAMERA_EXTRA, false); 1124 } 1125 1126 if (mSecureCamera) { 1127 // Foreground event caused by lock screen startup. 1128 // It is necessary to log this in onCreate, to avoid the 1129 // onResume->onPause->onResume sequence. 1130 UsageStatistics.foregrounded( 1131 eventprotos.ForegroundEvent.ForegroundSource.LOCK_SCREEN); 1132 1133 // Change the window flags so that secure camera can show when 1134 // locked 1135 Window win = getWindow(); 1136 WindowManager.LayoutParams params = win.getAttributes(); 1137 params.flags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; 1138 win.setAttributes(params); 1139 1140 // Filter for screen off so that we can finish secure camera 1141 // activity 1142 // when screen is off. 1143 IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF); 1144 registerReceiver(mScreenOffReceiver, filter); 1145 } 1146 mCameraAppUI = new CameraAppUI(this, 1147 (MainActivityLayout) findViewById(R.id.activity_root_view), isCaptureIntent()); 1148 1149 mCameraAppUI.setFilmstripBottomControlsListener(mMyFilmstripBottomControlListener); 1150 1151 mAboveFilmstripControlLayout = 1152 (FrameLayout) findViewById(R.id.camera_filmstrip_content_layout); 1153 1154 // Add the session listener so we can track the session progress 1155 // updates. 1156 getServices().getCaptureSessionManager().addSessionListener(mSessionListener); 1157 mFilmstripController = ((FilmstripView) findViewById(R.id.filmstrip_view)).getController(); 1158 mFilmstripController.setImageGap( 1159 getResources().getDimensionPixelSize(R.dimen.camera_film_strip_gap)); 1160 mPanoramaViewHelper = new PanoramaViewHelper(this); 1161 mPanoramaViewHelper.onCreate(); 1162 // Set up the camera preview first so the preview shows up ASAP. 1163 mDataAdapter = new CameraDataAdapter(mAppContext, 1164 new ColorDrawable(getResources().getColor(R.color.photo_placeholder))); 1165 mDataAdapter.setLocalDataListener(mLocalDataListener); 1166 1167 mCameraAppUI.getFilmstripContentPanel().setFilmstripListener(mFilmstripListener); 1168 1169 mLocationManager = new LocationManager(mAppContext); 1170 1171 int modeIndex = -1; 1172 int photoIndex = getResources().getInteger(R.integer.camera_mode_photo); 1173 int videoIndex = getResources().getInteger(R.integer.camera_mode_video); 1174 int gcamIndex = getResources().getInteger(R.integer.camera_mode_gcam); 1175 if (MediaStore.INTENT_ACTION_VIDEO_CAMERA.equals(getIntent().getAction()) 1176 || MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction())) { 1177 modeIndex = videoIndex; 1178 } else if (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(getIntent().getAction()) 1179 || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(getIntent() 1180 .getAction())) { 1181 modeIndex = photoIndex; 1182 if (mSettingsManager.getInt(SettingsManager.SETTING_STARTUP_MODULE_INDEX) 1183 == gcamIndex && GcamHelper.hasGcamCapture()) { 1184 modeIndex = gcamIndex; 1185 } 1186 } else if (MediaStore.ACTION_IMAGE_CAPTURE.equals(getIntent().getAction()) 1187 || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(getIntent().getAction())) { 1188 modeIndex = photoIndex; 1189 } else { 1190 // If the activity has not been started using an explicit intent, 1191 // read the module index from the last time the user changed modes 1192 modeIndex = mSettingsManager.getInt(SettingsManager.SETTING_STARTUP_MODULE_INDEX); 1193 if ((modeIndex == gcamIndex && 1194 !GcamHelper.hasGcamCapture()) || modeIndex < 0) { 1195 modeIndex = photoIndex; 1196 } 1197 } 1198 1199 mOrientationManager = new OrientationManagerImpl(this); 1200 mOrientationManager.addOnOrientationChangeListener(mMainHandler, this); 1201 1202 setModuleFromModeIndex(modeIndex); 1203 mCameraAppUI.prepareModuleUI(); 1204 mCurrentModule.init(this, isSecureCamera(), isCaptureIntent()); 1205 1206 if (!mSecureCamera) { 1207 mFilmstripController.setDataAdapter(mDataAdapter); 1208 if (!isCaptureIntent()) { 1209 mDataAdapter.requestLoad(); 1210 } 1211 } else { 1212 // Put a lock placeholder as the last image by setting its date to 1213 // 0. 1214 ImageView v = (ImageView) getLayoutInflater().inflate( 1215 R.layout.secure_album_placeholder, null); 1216 v.setOnClickListener(new View.OnClickListener() { 1217 @Override 1218 public void onClick(View view) { 1219 UsageStatistics.changeScreen(NavigationChange.Mode.GALLERY, 1220 InteractionCause.BUTTON); 1221 startGallery(); 1222 finish(); 1223 } 1224 }); 1225 mDataAdapter = new FixedLastDataAdapter( 1226 mAppContext, 1227 mDataAdapter, 1228 new SimpleViewData( 1229 v, 1230 v.getDrawable().getIntrinsicWidth(), 1231 v.getDrawable().getIntrinsicHeight(), 1232 0, 0)); 1233 // Flush out all the original data. 1234 mDataAdapter.flush(); 1235 mFilmstripController.setDataAdapter(mDataAdapter); 1236 } 1237 1238 setupNfcBeamPush(); 1239 1240 mLocalImagesObserver = new LocalMediaObserver(); 1241 mLocalVideosObserver = new LocalMediaObserver(); 1242 1243 getContentResolver().registerContentObserver( 1244 MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true, 1245 mLocalImagesObserver); 1246 getContentResolver().registerContentObserver( 1247 MediaStore.Video.Media.EXTERNAL_CONTENT_URI, true, 1248 mLocalVideosObserver); 1249 if (FeedbackHelper.feedbackAvailable()) { 1250 mFeedbackHelper = new FeedbackHelper(mAppContext); 1251 } 1252 } 1253 1254 private void setRotationAnimation() { 1255 int rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE; 1256 rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE; 1257 Window win = getWindow(); 1258 WindowManager.LayoutParams winParams = win.getAttributes(); 1259 winParams.rotationAnimation = rotationAnimation; 1260 win.setAttributes(winParams); 1261 } 1262 1263 @Override 1264 public void onUserInteraction() { 1265 super.onUserInteraction(); 1266 if (!isFinishing()) { 1267 keepScreenOnForAWhile(); 1268 } 1269 } 1270 1271 @Override 1272 public boolean dispatchTouchEvent(MotionEvent ev) { 1273 boolean result = super.dispatchTouchEvent(ev); 1274 if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) { 1275 // Real deletion is postponed until the next user interaction after 1276 // the gesture that triggers deletion. Until real deletion is 1277 // performed, users can click the undo button to bring back the 1278 // image that they chose to delete. 1279 if (mPendingDeletion && !mIsUndoingDeletion) { 1280 performDeletion(); 1281 } 1282 } 1283 return result; 1284 } 1285 1286 @Override 1287 public void onPause() { 1288 mPaused = true; 1289 mPeekAnimationHandler = null; 1290 mPeekAnimationThread.quitSafely(); 1291 mPeekAnimationThread = null; 1292 CameraPerformanceTracker.onEvent(CameraPerformanceTracker.ACTIVITY_PAUSE); 1293 1294 // Delete photos that are pending deletion 1295 performDeletion(); 1296 mCurrentModule.pause(); 1297 mOrientationManager.pause(); 1298 // Close the camera and wait for the operation done. 1299 mCameraController.closeCamera(); 1300 mPanoramaViewHelper.onPause(); 1301 1302 mLocalImagesObserver.setActivityPaused(true); 1303 mLocalVideosObserver.setActivityPaused(true); 1304 resetScreenOn(); 1305 super.onPause(); 1306 } 1307 1308 @Override 1309 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 1310 if (requestCode == REQ_CODE_DONT_SWITCH_TO_PREVIEW) { 1311 mResetToPreviewOnResume = false; 1312 } else { 1313 super.onActivityResult(requestCode, resultCode, data); 1314 } 1315 } 1316 1317 @Override 1318 public void onResume() { 1319 mPaused = false; 1320 CameraPerformanceTracker.onEvent(CameraPerformanceTracker.ACTIVITY_RESUME); 1321 1322 mLastLayoutOrientation = getResources().getConfiguration().orientation; 1323 1324 // TODO: Handle this in OrientationManager. 1325 // Auto-rotate off 1326 if (Settings.System.getInt(getContentResolver(), 1327 Settings.System.ACCELEROMETER_ROTATION, 0) == 0) { 1328 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); 1329 mAutoRotateScreen = false; 1330 } else { 1331 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR); 1332 mAutoRotateScreen = true; 1333 } 1334 1335 if (isCaptureIntent()) { 1336 // Foreground event caused by photo or video capure intent. 1337 UsageStatistics.foregrounded( 1338 eventprotos.ForegroundEvent.ForegroundSource.INTENT_PICKER); 1339 } else if (!mSecureCamera) { 1340 // Foreground event that is not caused by an intent. 1341 UsageStatistics.foregrounded( 1342 eventprotos.ForegroundEvent.ForegroundSource.ICON_LAUNCHER); 1343 } 1344 1345 Drawable galleryLogo; 1346 if (mSecureCamera) { 1347 mGalleryIntent = null; 1348 galleryLogo = null; 1349 } else { 1350 mGalleryIntent = IntentHelper.getDefaultGalleryIntent(mAppContext); 1351 galleryLogo = IntentHelper.getGalleryIcon(mAppContext, mGalleryIntent); 1352 } 1353 if (galleryLogo == null) { 1354 try { 1355 galleryLogo = getPackageManager().getActivityLogo(getComponentName()); 1356 } catch (PackageManager.NameNotFoundException e) { 1357 Log.e(TAG, "Can't get the activity logo"); 1358 } 1359 } 1360 if (mGalleryIntent != null) { 1361 mActionBar.setDisplayUseLogoEnabled(true); 1362 } 1363 mActionBar.setLogo(galleryLogo); 1364 mOrientationManager.resume(); 1365 super.onResume(); 1366 mPeekAnimationThread = new HandlerThread("Peek animation"); 1367 mPeekAnimationThread.start(); 1368 mPeekAnimationHandler = new PeekAnimationHandler(mPeekAnimationThread.getLooper()); 1369 mCurrentModule.resume(); 1370 setSwipingEnabled(true); 1371 1372 if (mResetToPreviewOnResume) { 1373 mCameraAppUI.resume(); 1374 } else { 1375 LocalData data = mDataAdapter.getLocalData(mFilmstripController.getCurrentId()); 1376 if (data != null) { 1377 mDataAdapter.refresh(data.getContentUri(), false); 1378 } 1379 } 1380 // The share button might be disabled to avoid double tapping. 1381 mCameraAppUI.getFilmstripBottomControls().setShareEnabled(true); 1382 // Default is showing the preview, unless disabled by explicitly 1383 // starting an activity we want to return from to the filmstrip rather 1384 // than the preview. 1385 mResetToPreviewOnResume = true; 1386 1387 if (mLocalVideosObserver.isMediaDataChangedDuringPause() 1388 || mLocalImagesObserver.isMediaDataChangedDuringPause()) { 1389 if (!mSecureCamera) { 1390 // If it's secure camera, requestLoad() should not be called 1391 // as it will load all the data. 1392 if (!mFilmstripVisible) { 1393 mDataAdapter.requestLoad(); 1394 } 1395 } 1396 } 1397 mLocalImagesObserver.setActivityPaused(false); 1398 mLocalVideosObserver.setActivityPaused(false); 1399 1400 keepScreenOnForAWhile(); 1401 1402 // Lights-out mode at all times. 1403 findViewById(R.id.activity_root_view) 1404 .setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE); 1405 mPanoramaViewHelper.onResume(); 1406 ReleaseDialogHelper.showReleaseInfoDialogOnStart(this, mSettingsManager); 1407 syncLocationManagerSetting(); 1408 } 1409 1410 @Override 1411 public void onStart() { 1412 super.onStart(); 1413 mPanoramaViewHelper.onStart(); 1414 } 1415 1416 @Override 1417 protected void onStop() { 1418 mPanoramaViewHelper.onStop(); 1419 if (mFeedbackHelper != null) { 1420 mFeedbackHelper.stopFeedback(); 1421 } 1422 1423 mLocationManager.disconnect(); 1424 CameraManagerFactory.recycle(); 1425 super.onStop(); 1426 } 1427 1428 @Override 1429 public void onDestroy() { 1430 if (mSecureCamera) { 1431 unregisterReceiver(mScreenOffReceiver); 1432 } 1433 mActionBar.removeOnMenuVisibilityListener(this); 1434 mSettingsManager.removeAllListeners(); 1435 mCameraController.removeCallbackReceiver(); 1436 getContentResolver().unregisterContentObserver(mLocalImagesObserver); 1437 getContentResolver().unregisterContentObserver(mLocalVideosObserver); 1438 getServices().getCaptureSessionManager().removeSessionListener(mSessionListener); 1439 mCameraAppUI.onDestroy(); 1440 mCameraController = null; 1441 mSettingsManager = null; 1442 mCameraAppUI = null; 1443 mOrientationManager = null; 1444 mButtonManager = null; 1445 super.onDestroy(); 1446 } 1447 1448 @Override 1449 public void onConfigurationChanged(Configuration config) { 1450 super.onConfigurationChanged(config); 1451 Log.v(TAG, "onConfigurationChanged"); 1452 if (config.orientation == Configuration.ORIENTATION_UNDEFINED) { 1453 return; 1454 } 1455 1456 if (mLastLayoutOrientation != config.orientation) { 1457 mLastLayoutOrientation = config.orientation; 1458 mCurrentModule.onLayoutOrientationChanged( 1459 mLastLayoutOrientation == Configuration.ORIENTATION_LANDSCAPE); 1460 } 1461 } 1462 1463 @Override 1464 public boolean onKeyDown(int keyCode, KeyEvent event) { 1465 if (!mFilmstripVisible) { 1466 if (mCurrentModule.onKeyDown(keyCode, event)) { 1467 return true; 1468 } 1469 // Prevent software keyboard or voice search from showing up. 1470 if (keyCode == KeyEvent.KEYCODE_SEARCH 1471 || keyCode == KeyEvent.KEYCODE_MENU) { 1472 if (event.isLongPress()) { 1473 return true; 1474 } 1475 } 1476 } 1477 1478 return super.onKeyDown(keyCode, event); 1479 } 1480 1481 @Override 1482 public boolean onKeyUp(int keyCode, KeyEvent event) { 1483 if (!mFilmstripVisible && mCurrentModule.onKeyUp(keyCode, event)) { 1484 return true; 1485 } 1486 return super.onKeyUp(keyCode, event); 1487 } 1488 1489 @Override 1490 public void onBackPressed() { 1491 if (!mCameraAppUI.onBackPressed()) { 1492 if (!mCurrentModule.onBackPressed()) { 1493 super.onBackPressed(); 1494 } 1495 } 1496 } 1497 1498 @Override 1499 public boolean isAutoRotateScreen() { 1500 // TODO: Move to OrientationManager. 1501 return mAutoRotateScreen; 1502 } 1503 1504 protected void updateStorageSpace() { 1505 mStorageSpaceBytes = Storage.getAvailableSpace(); 1506 } 1507 1508 protected long getStorageSpaceBytes() { 1509 return mStorageSpaceBytes; 1510 } 1511 1512 protected void updateStorageSpaceAndHint() { 1513 updateStorageSpace(); 1514 updateStorageHint(mStorageSpaceBytes); 1515 } 1516 1517 protected void updateStorageHint(long storageSpace) { 1518 String message = null; 1519 if (storageSpace == Storage.UNAVAILABLE) { 1520 message = getString(R.string.no_storage); 1521 } else if (storageSpace == Storage.PREPARING) { 1522 message = getString(R.string.preparing_sd); 1523 } else if (storageSpace == Storage.UNKNOWN_SIZE) { 1524 message = getString(R.string.access_sd_fail); 1525 } else if (storageSpace <= Storage.LOW_STORAGE_THRESHOLD_BYTES) { 1526 message = getString(R.string.spaceIsLow_content); 1527 } 1528 1529 if (message != null) { 1530 if (mStorageHint == null) { 1531 mStorageHint = OnScreenHint.makeText(mAppContext, message); 1532 } else { 1533 mStorageHint.setText(message); 1534 } 1535 mStorageHint.show(); 1536 } else if (mStorageHint != null) { 1537 mStorageHint.cancel(); 1538 mStorageHint = null; 1539 } 1540 } 1541 1542 protected void setResultEx(int resultCode) { 1543 mResultCodeForTesting = resultCode; 1544 setResult(resultCode); 1545 } 1546 1547 protected void setResultEx(int resultCode, Intent data) { 1548 mResultCodeForTesting = resultCode; 1549 mResultDataForTesting = data; 1550 setResult(resultCode, data); 1551 } 1552 1553 public int getResultCode() { 1554 return mResultCodeForTesting; 1555 } 1556 1557 public Intent getResultData() { 1558 return mResultDataForTesting; 1559 } 1560 1561 public boolean isSecureCamera() { 1562 return mSecureCamera; 1563 } 1564 1565 @Override 1566 public boolean isPaused() { 1567 return mPaused; 1568 } 1569 1570 @Override 1571 public void onModeSelected(int modeIndex) { 1572 if (mCurrentModeIndex == modeIndex) { 1573 return; 1574 } 1575 1576 CameraPerformanceTracker.onEvent(CameraPerformanceTracker.MODE_SWITCH_START); 1577 // Record last used camera mode for quick switching 1578 if (modeIndex == getResources().getInteger(R.integer.camera_mode_photo) 1579 || modeIndex == getResources().getInteger(R.integer.camera_mode_gcam)) { 1580 mSettingsManager.setInt(SettingsManager.SETTING_KEY_CAMERA_MODULE_LAST_USED_INDEX, 1581 modeIndex); 1582 } 1583 1584 closeModule(mCurrentModule); 1585 int oldModuleIndex = mCurrentModeIndex; 1586 1587 // Refocus and Gcam are modes that cannot be selected 1588 // from the mode list view, because they are not list items. 1589 // Check whether we should interpret MODULE_CRAFT as either. 1590 if (modeIndex == getResources().getInteger(R.integer.camera_mode_photo)) { 1591 boolean hdrPlusOn = mSettingsManager.isHdrPlusOn(); 1592 if (hdrPlusOn && GcamHelper.hasGcamCapture()) { 1593 modeIndex = getResources().getInteger(R.integer.camera_mode_gcam); 1594 } 1595 } 1596 1597 setModuleFromModeIndex(modeIndex); 1598 1599 mCameraAppUI.resetBottomControls(mCurrentModule, modeIndex); 1600 mCameraAppUI.addShutterListener(mCurrentModule); 1601 openModule(mCurrentModule); 1602 mCurrentModule.onOrientationChanged(mLastRawOrientation); 1603 // Store the module index so we can use it the next time the Camera 1604 // starts up. 1605 SharedPreferences prefs = PreferenceManager 1606 .getDefaultSharedPreferences(mAppContext); 1607 prefs.edit().putInt(CameraSettings.KEY_STARTUP_MODULE_INDEX, modeIndex).apply(); 1608 } 1609 1610 /** 1611 * Shows the settings dialog. 1612 */ 1613 @Override 1614 public void onSettingsSelected() { 1615 Intent intent = new Intent(this, CameraSettingsActivity.class); 1616 startActivity(intent); 1617 } 1618 1619 /** 1620 * Sets the mCurrentModuleIndex, creates a new module instance for the given 1621 * index an sets it as mCurrentModule. 1622 */ 1623 private void setModuleFromModeIndex(int modeIndex) { 1624 ModuleManagerImpl.ModuleAgent agent = mModuleManager.getModuleAgent(modeIndex); 1625 if (agent == null) { 1626 return; 1627 } 1628 if (!agent.requestAppForCamera()) { 1629 mCameraController.closeCamera(); 1630 } 1631 mCurrentModeIndex = agent.getModuleId(); 1632 mCurrentModule = (CameraModule) agent.createModule(this); 1633 } 1634 1635 @Override 1636 public SettingsManager getSettingsManager() { 1637 return mSettingsManager; 1638 } 1639 1640 @Override 1641 public CameraServices getServices() { 1642 return (CameraServices) getApplication(); 1643 } 1644 1645 public List<String> getSupportedModeNames() { 1646 List<Integer> indices = mModuleManager.getSupportedModeIndexList(); 1647 List<String> supported = new ArrayList<String>(); 1648 1649 for (Integer modeIndex : indices) { 1650 String name = CameraUtil.getCameraModeText(modeIndex, mAppContext); 1651 if (name != null && !name.equals("")) { 1652 supported.add(name); 1653 } 1654 } 1655 return supported; 1656 } 1657 1658 @Override 1659 public ButtonManager getButtonManager() { 1660 if (mButtonManager == null) { 1661 mButtonManager = new ButtonManager(this); 1662 } 1663 return mButtonManager; 1664 } 1665 1666 /** 1667 * Creates an AlertDialog appropriate for choosing whether to enable 1668 * location on the first run of the app. 1669 */ 1670 public AlertDialog getFirstTimeLocationAlert() { 1671 AlertDialog.Builder builder = new AlertDialog.Builder(this); 1672 builder = SettingsUtil.getFirstTimeLocationAlertBuilder(builder, new Callback<Boolean>() { 1673 @Override 1674 public void onCallback(Boolean locationOn) { 1675 mSettingsManager.setLocation(locationOn, mLocationManager); 1676 } 1677 }); 1678 if (builder != null) { 1679 return builder.create(); 1680 } else { 1681 return null; 1682 } 1683 } 1684 1685 /** 1686 * Launches an ACTION_EDIT intent for the given local data item. If 1687 * 'withTinyPlanet' is set, this will show a disambig dialog first to let 1688 * the user start either the tiny planet editor or another photo edior. 1689 * 1690 * @param data The data item to edit. 1691 */ 1692 public void launchEditor(LocalData data) { 1693 Intent intent = new Intent(Intent.ACTION_EDIT) 1694 .setDataAndType(data.getContentUri(), data.getMimeType()) 1695 .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 1696 try { 1697 launchActivityByIntent(intent); 1698 } catch (ActivityNotFoundException e) { 1699 launchActivityByIntent(Intent.createChooser(intent, null)); 1700 } 1701 } 1702 1703 @Override 1704 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { 1705 super.onCreateContextMenu(menu, v, menuInfo); 1706 1707 MenuInflater inflater = getMenuInflater(); 1708 inflater.inflate(R.menu.filmstrip_context_menu, menu); 1709 } 1710 1711 @Override 1712 public boolean onContextItemSelected(MenuItem item) { 1713 switch (item.getItemId()) { 1714 case R.id.tiny_planet_editor: 1715 mMyFilmstripBottomControlListener.onTinyPlanet(); 1716 return true; 1717 case R.id.photo_editor: 1718 mMyFilmstripBottomControlListener.onEdit(); 1719 return true; 1720 } 1721 return false; 1722 } 1723 1724 /** 1725 * Launch the tiny planet editor. 1726 * 1727 * @param data The data must be a 360 degree stereographically mapped 1728 * panoramic image. It will not be modified, instead a new item 1729 * with the result will be added to the filmstrip. 1730 */ 1731 public void launchTinyPlanetEditor(LocalData data) { 1732 TinyPlanetFragment fragment = new TinyPlanetFragment(); 1733 Bundle bundle = new Bundle(); 1734 bundle.putString(TinyPlanetFragment.ARGUMENT_URI, data.getContentUri().toString()); 1735 bundle.putString(TinyPlanetFragment.ARGUMENT_TITLE, data.getTitle()); 1736 fragment.setArguments(bundle); 1737 fragment.show(getFragmentManager(), "tiny_planet"); 1738 } 1739 1740 private void openModule(CameraModule module) { 1741 module.init(this, isSecureCamera(), isCaptureIntent()); 1742 module.resume(); 1743 int visibility = mFilmstripVisible ? ModuleController.VISIBILITY_HIDDEN 1744 : ModuleController.VISIBILITY_VISIBLE; 1745 module.onPreviewVisibilityChanged(visibility); 1746 } 1747 1748 private void closeModule(CameraModule module) { 1749 module.pause(); 1750 mCameraAppUI.clearModuleUI(); 1751 } 1752 1753 private void performDeletion() { 1754 if (!mPendingDeletion) { 1755 return; 1756 } 1757 hideUndoDeletionBar(false); 1758 mDataAdapter.executeDeletion(); 1759 } 1760 1761 public void showUndoDeletionBar() { 1762 if (mPendingDeletion) { 1763 performDeletion(); 1764 } 1765 Log.v(TAG, "showing undo bar"); 1766 mPendingDeletion = true; 1767 if (mUndoDeletionBar == null) { 1768 ViewGroup v = (ViewGroup) getLayoutInflater().inflate(R.layout.undo_bar, 1769 mAboveFilmstripControlLayout, true); 1770 mUndoDeletionBar = (ViewGroup) v.findViewById(R.id.camera_undo_deletion_bar); 1771 View button = mUndoDeletionBar.findViewById(R.id.camera_undo_deletion_button); 1772 button.setOnClickListener(new View.OnClickListener() { 1773 @Override 1774 public void onClick(View view) { 1775 mDataAdapter.undoDataRemoval(); 1776 hideUndoDeletionBar(true); 1777 } 1778 }); 1779 // Setting undo bar clickable to avoid touch events going through 1780 // the bar to the buttons (eg. edit button, etc) underneath the bar. 1781 mUndoDeletionBar.setClickable(true); 1782 // When there is user interaction going on with the undo button, we 1783 // do not want to hide the undo bar. 1784 button.setOnTouchListener(new View.OnTouchListener() { 1785 @Override 1786 public boolean onTouch(View v, MotionEvent event) { 1787 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { 1788 mIsUndoingDeletion = true; 1789 } else if (event.getActionMasked() == MotionEvent.ACTION_UP) { 1790 mIsUndoingDeletion = false; 1791 } 1792 return false; 1793 } 1794 }); 1795 } 1796 mUndoDeletionBar.setAlpha(0f); 1797 mUndoDeletionBar.setVisibility(View.VISIBLE); 1798 mUndoDeletionBar.animate().setDuration(200).alpha(1f).setListener(null).start(); 1799 } 1800 1801 private void hideUndoDeletionBar(boolean withAnimation) { 1802 Log.v(TAG, "Hiding undo deletion bar"); 1803 mPendingDeletion = false; 1804 if (mUndoDeletionBar != null) { 1805 if (withAnimation) { 1806 mUndoDeletionBar.animate().setDuration(200).alpha(0f) 1807 .setListener(new Animator.AnimatorListener() { 1808 @Override 1809 public void onAnimationStart(Animator animation) { 1810 // Do nothing. 1811 } 1812 1813 @Override 1814 public void onAnimationEnd(Animator animation) { 1815 mUndoDeletionBar.setVisibility(View.GONE); 1816 } 1817 1818 @Override 1819 public void onAnimationCancel(Animator animation) { 1820 // Do nothing. 1821 } 1822 1823 @Override 1824 public void onAnimationRepeat(Animator animation) { 1825 // Do nothing. 1826 } 1827 }).start(); 1828 } else { 1829 mUndoDeletionBar.setVisibility(View.GONE); 1830 } 1831 } 1832 } 1833 1834 @Override 1835 public void onOrientationChanged(int orientation) { 1836 // We keep the last known orientation. So if the user first orient 1837 // the camera then point the camera to floor or sky, we still have 1838 // the correct orientation. 1839 if (orientation == OrientationManager.ORIENTATION_UNKNOWN) { 1840 return; 1841 } 1842 mLastRawOrientation = orientation; 1843 if (mCurrentModule != null) { 1844 mCurrentModule.onOrientationChanged(orientation); 1845 } 1846 } 1847 1848 /** 1849 * Enable/disable swipe-to-filmstrip. Will always disable swipe if in 1850 * capture intent. 1851 * 1852 * @param enable {@code true} to enable swipe. 1853 */ 1854 public void setSwipingEnabled(boolean enable) { 1855 // TODO: Bring back the functionality. 1856 if (isCaptureIntent()) { 1857 // lockPreview(true); 1858 } else { 1859 // lockPreview(!enable); 1860 } 1861 } 1862 1863 // Accessor methods for getting latency times used in performance testing 1864 public long getFirstPreviewTime() { 1865 if (mCurrentModule instanceof PhotoModule) { 1866 long coverHiddenTime = getCameraAppUI().getCoverHiddenTime(); 1867 if (coverHiddenTime != -1) { 1868 return coverHiddenTime - mOnCreateTime; 1869 } 1870 } 1871 return -1; 1872 } 1873 1874 public long getAutoFocusTime() { 1875 return (mCurrentModule instanceof PhotoModule) ? 1876 ((PhotoModule) mCurrentModule).mAutoFocusTime : -1; 1877 } 1878 1879 public long getShutterLag() { 1880 return (mCurrentModule instanceof PhotoModule) ? 1881 ((PhotoModule) mCurrentModule).mShutterLag : -1; 1882 } 1883 1884 public long getShutterToPictureDisplayedTime() { 1885 return (mCurrentModule instanceof PhotoModule) ? 1886 ((PhotoModule) mCurrentModule).mShutterToPictureDisplayedTime : -1; 1887 } 1888 1889 public long getPictureDisplayedToJpegCallbackTime() { 1890 return (mCurrentModule instanceof PhotoModule) ? 1891 ((PhotoModule) mCurrentModule).mPictureDisplayedToJpegCallbackTime : -1; 1892 } 1893 1894 public long getJpegCallbackFinishTime() { 1895 return (mCurrentModule instanceof PhotoModule) ? 1896 ((PhotoModule) mCurrentModule).mJpegCallbackFinishTime : -1; 1897 } 1898 1899 public long getCaptureStartTime() { 1900 return (mCurrentModule instanceof PhotoModule) ? 1901 ((PhotoModule) mCurrentModule).mCaptureStartTime : -1; 1902 } 1903 1904 public boolean isRecording() { 1905 return (mCurrentModule instanceof VideoModule) ? 1906 ((VideoModule) mCurrentModule).isRecording() : false; 1907 } 1908 1909 public CameraManager.CameraOpenCallback getCameraOpenErrorCallback() { 1910 return mCameraController; 1911 } 1912 1913 // For debugging purposes only. 1914 public CameraModule getCurrentModule() { 1915 return mCurrentModule; 1916 } 1917 1918 /** 1919 * Reads the current location recording settings and passes it on to the 1920 * location manager. 1921 */ 1922 public void syncLocationManagerSetting() { 1923 mSettingsManager.syncLocationManager(mLocationManager); 1924 } 1925 1926 private void keepScreenOnForAWhile() { 1927 if (mKeepScreenOn) { 1928 return; 1929 } 1930 mMainHandler.removeMessages(MSG_CLEAR_SCREEN_ON_FLAG); 1931 getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 1932 mMainHandler.sendEmptyMessageDelayed(MSG_CLEAR_SCREEN_ON_FLAG, SCREEN_DELAY_MS); 1933 } 1934 1935 private void resetScreenOn() { 1936 mKeepScreenOn = false; 1937 mMainHandler.removeMessages(MSG_CLEAR_SCREEN_ON_FLAG); 1938 getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 1939 } 1940 1941 /** 1942 * @return {@code true} if the Gallery is launched successfully. 1943 */ 1944 private boolean startGallery() { 1945 if (mGalleryIntent == null) { 1946 return false; 1947 } 1948 try { 1949 UsageStatistics.changeScreen(NavigationChange.Mode.GALLERY, InteractionCause.BUTTON); 1950 Intent startGalleryIntent = new Intent(mGalleryIntent); 1951 int currentDataId = mFilmstripController.getCurrentId(); 1952 LocalData currentLocalData = mDataAdapter.getLocalData(currentDataId); 1953 if (currentLocalData != null) { 1954 GalleryHelper.setContentUri(startGalleryIntent, currentLocalData.getContentUri()); 1955 } 1956 launchActivityByIntent(startGalleryIntent); 1957 } catch (ActivityNotFoundException e) { 1958 Log.w(TAG, "Failed to launch gallery activity, closing"); 1959 } 1960 return false; 1961 } 1962 1963 private void setNfcBeamPushUriFromData(LocalData data) { 1964 final Uri uri = data.getContentUri(); 1965 if (uri != Uri.EMPTY) { 1966 mNfcPushUris[0] = uri; 1967 } else { 1968 mNfcPushUris[0] = null; 1969 } 1970 } 1971 1972 /** 1973 * Updates the visibility of the filmstrip bottom controls. 1974 */ 1975 private void updateUiByData(final int dataId) { 1976 if (isSecureCamera()) { 1977 // We cannot show buttons in secure camera since go to other 1978 // activities might create a security hole. 1979 return; 1980 } 1981 1982 final LocalData currentData = mDataAdapter.getLocalData(dataId); 1983 if (currentData == null) { 1984 Log.w(TAG, "Current data ID not found."); 1985 hideSessionProgress(); 1986 return; 1987 } 1988 1989 setNfcBeamPushUriFromData(currentData); 1990 1991 /* Bottom controls. */ 1992 1993 updateBottomControlsByData(currentData); 1994 if (!mDataAdapter.isMetadataUpdated(dataId)) { 1995 mDataAdapter.updateMetadata(dataId); 1996 } 1997 } 1998 1999 /** 2000 * Updates the bottom controls based on the data. 2001 */ 2002 private void updateBottomControlsByData(final LocalData currentData) { 2003 2004 final CameraAppUI.BottomPanel filmstripBottomPanel = 2005 mCameraAppUI.getFilmstripBottomControls(); 2006 filmstripBottomPanel.setEditButtonVisibility( 2007 currentData.isDataActionSupported(LocalData.DATA_ACTION_EDIT)); 2008 filmstripBottomPanel.setShareButtonVisibility( 2009 currentData.isDataActionSupported(LocalData.DATA_ACTION_SHARE)); 2010 filmstripBottomPanel.setDeleteButtonVisibility( 2011 currentData.isDataActionSupported(LocalData.DATA_ACTION_DELETE)); 2012 2013 /* Progress bar */ 2014 2015 Uri contentUri = currentData.getContentUri(); 2016 CaptureSessionManager sessionManager = getServices() 2017 .getCaptureSessionManager(); 2018 int sessionProgress = sessionManager.getSessionProgress(contentUri); 2019 2020 if (sessionProgress < 0) { 2021 hideSessionProgress(); 2022 } else { 2023 CharSequence progressMessage = sessionManager 2024 .getSessionProgressMessage(contentUri); 2025 showSessionProgress(progressMessage); 2026 updateSessionProgress(sessionProgress); 2027 } 2028 2029 /* View button */ 2030 2031 // We need to add this to a separate DB. 2032 final int viewButtonVisibility; 2033 if (PanoramaMetadataLoader.isPanoramaAndUseViewer(currentData)) { 2034 viewButtonVisibility = CameraAppUI.BottomPanel.VIEWER_PHOTO_SPHERE; 2035 } else if (RgbzMetadataLoader.hasRGBZData(currentData)) { 2036 viewButtonVisibility = CameraAppUI.BottomPanel.VIEWER_REFOCUS; 2037 } else { 2038 viewButtonVisibility = CameraAppUI.BottomPanel.VIEWER_NONE; 2039 } 2040 2041 filmstripBottomPanel.setTinyPlanetEnabled( 2042 PanoramaMetadataLoader.isPanorama360(currentData)); 2043 filmstripBottomPanel.setViewerButtonVisibility(viewButtonVisibility); 2044 } 2045 2046 private class PeekAnimationHandler extends Handler { 2047 private class DataAndCallback { 2048 LocalData mData; 2049 com.android.camera.util.Callback<Bitmap> mCallback; 2050 2051 public DataAndCallback(LocalData data, com.android.camera.util.Callback<Bitmap> 2052 callback) { 2053 mData = data; 2054 mCallback = callback; 2055 } 2056 } 2057 2058 public PeekAnimationHandler(Looper looper) { 2059 super(looper); 2060 } 2061 2062 /** 2063 * Starts the animation decoding job and posts a {@code Runnable} back 2064 * when when the decoding is done. 2065 * 2066 * @param data The data item to decode the thumbnail for. 2067 * @param callback {@link com.android.camera.util.Callback} after the 2068 * decoding is done. 2069 */ 2070 public void startDecodingJob(final LocalData data, 2071 final com.android.camera.util.Callback<Bitmap> callback) { 2072 PeekAnimationHandler.this.obtainMessage(0 /** dummy integer **/, 2073 new DataAndCallback(data, callback)).sendToTarget(); 2074 } 2075 2076 @Override 2077 public void handleMessage(Message msg) { 2078 final LocalData data = ((DataAndCallback) msg.obj).mData; 2079 final com.android.camera.util.Callback<Bitmap> callback = 2080 ((DataAndCallback) msg.obj).mCallback; 2081 if (data == null || callback == null) { 2082 return; 2083 } 2084 2085 final Bitmap bitmap; 2086 switch (data.getLocalDataType()) { 2087 case LocalData.LOCAL_IMAGE: 2088 case LocalData.LOCAL_IN_PROGRESS_DATA: 2089 FileInputStream stream; 2090 try { 2091 stream = new FileInputStream(data.getPath()); 2092 } catch (FileNotFoundException e) { 2093 Log.e(TAG, "File not found:" + data.getPath()); 2094 return; 2095 } 2096 Point dim = CameraUtil.resizeToFill(data.getWidth(), data.getHeight(), 2097 data.getRotation(), mAboveFilmstripControlLayout.getWidth(), 2098 mAboveFilmstripControlLayout.getMeasuredHeight()); 2099 if (data.getRotation() % 180 != 0) { 2100 int dummy = dim.x; 2101 dim.x = dim.y; 2102 dim.y = dummy; 2103 } 2104 bitmap = LocalDataUtil 2105 .loadImageThumbnailFromStream(stream, data.getWidth(), data.getHeight(), 2106 (int) (dim.x * 0.7f), (int) (dim.y * 0.7), 2107 data.getRotation(), MAX_PEEK_BITMAP_PIXELS); 2108 break; 2109 2110 case LocalData.LOCAL_VIDEO: 2111 bitmap = LocalDataUtil.loadVideoThumbnail(data.getPath()); 2112 break; 2113 2114 default: 2115 bitmap = null; 2116 break; 2117 } 2118 2119 if (bitmap == null) { 2120 return; 2121 } 2122 2123 mMainHandler.post(new Runnable() { 2124 @Override 2125 public void run() { 2126 callback.onCallback(bitmap); 2127 mCameraAppUI.startPeekAnimation(bitmap, true); 2128 } 2129 }); 2130 } 2131 } 2132} 2133