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