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