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