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