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