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