CameraActivity.java revision e857cc90a98e280e2008beb03e2446bd642d2ec4
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.app.ActionBar; 20import android.app.Activity; 21import android.content.BroadcastReceiver; 22import android.content.ComponentName; 23import android.content.ContentResolver; 24import android.content.Context; 25import android.content.Intent; 26import android.content.IntentFilter; 27import android.content.ServiceConnection; 28import android.content.pm.ActivityInfo; 29import android.content.res.Configuration; 30import android.graphics.drawable.ColorDrawable; 31import android.net.Uri; 32import android.os.AsyncTask; 33import android.os.Bundle; 34import android.os.Handler; 35import android.os.IBinder; 36import android.provider.MediaStore; 37import android.provider.Settings; 38import android.util.Log; 39import android.view.KeyEvent; 40import android.view.LayoutInflater; 41import android.view.Menu; 42import android.view.MenuInflater; 43import android.view.MenuItem; 44import android.view.OrientationEventListener; 45import android.view.View; 46import android.view.ViewGroup; 47import android.view.Window; 48import android.view.WindowManager; 49import android.widget.ImageView; 50import android.widget.ProgressBar; 51 52import com.android.camera.data.CameraDataAdapter; 53import com.android.camera.data.CameraPreviewData; 54import com.android.camera.data.FixedFirstDataAdapter; 55import com.android.camera.data.FixedLastDataAdapter; 56import com.android.camera.data.LocalData; 57import com.android.camera.data.LocalDataAdapter; 58import com.android.camera.data.MediaDetails; 59import com.android.camera.data.SimpleViewData; 60import com.android.camera.ui.CameraSwitcher; 61import com.android.camera.ui.CameraSwitcher.CameraSwitchListener; 62import com.android.camera.ui.DetailsDialog; 63import com.android.camera.ui.FilmStripView; 64import com.android.camera.util.ApiHelper; 65import com.android.camera.util.CameraUtil; 66import com.android.camera.util.PhotoSphereHelper; 67import com.android.camera.util.PhotoSphereHelper.PanoramaViewHelper; 68import com.android.camera2.R; 69 70public class CameraActivity extends Activity 71 implements CameraSwitchListener { 72 73 private static final String TAG = "CAM_Activity"; 74 75 private static final String INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE = 76 "android.media.action.STILL_IMAGE_CAMERA_SECURE"; 77 public static final String ACTION_IMAGE_CAPTURE_SECURE = 78 "android.media.action.IMAGE_CAPTURE_SECURE"; 79 public static final String ACTION_TRIM_VIDEO = 80 "com.android.camera.action.TRIM"; 81 public static final String MEDIA_ITEM_PATH = "media-item-path"; 82 83 // The intent extra for camera from secure lock screen. True if the gallery 84 // should only show newly captured pictures. sSecureAlbumId does not 85 // increment. This is used when switching between camera, camcorder, and 86 // panorama. If the extra is not set, it is in the normal camera mode. 87 public static final String SECURE_CAMERA_EXTRA = "secure_camera"; 88 89 // Supported operations at FilmStripView. Different data has different 90 // set of supported operations. 91 private static final int SUPPORT_DELETE = 1 << 0; 92 private static final int SUPPORT_ROTATE = 1 << 1; 93 private static final int SUPPORT_INFO = 1 << 2; 94 private static final int SUPPORT_CROP = 1 << 3; 95 private static final int SUPPORT_SETAS = 1 << 4; 96 private static final int SUPPORT_EDIT = 1 << 5; 97 private static final int SUPPORT_TRIM = 1 << 6; 98 private static final int SUPPORT_SHOW_ON_MAP = 1 << 7; 99 private static final int SUPPORT_ALL = 0xffffffff; 100 101 /** This data adapter is used by FilmStripView. */ 102 private LocalDataAdapter mDataAdapter; 103 /** This data adapter represents the real local camera data. */ 104 private LocalDataAdapter mWrappedDataAdapter; 105 106 private PanoramaStitchingManager mPanoramaManager; 107 private int mCurrentModuleIndex; 108 private CameraModule mCurrentModule; 109 private View mRootView; 110 private FilmStripView mFilmStripView; 111 private ProgressBar mBottomProgress; 112 private View mPanoStitchingPanel; 113 private int mResultCodeForTesting; 114 private Intent mResultDataForTesting; 115 private OnScreenHint mStorageHint; 116 private long mStorageSpace = Storage.LOW_STORAGE_THRESHOLD; 117 private boolean mAutoRotateScreen; 118 private boolean mSecureCamera; 119 // This is a hack to speed up the start of SecureCamera. 120 private static boolean sFirstStartAfterScreenOn = true; 121 private boolean mShowCameraPreview; 122 private int mLastRawOrientation; 123 private MyOrientationEventListener mOrientationListener; 124 private Handler mMainHandler; 125 private PanoramaViewHelper mPanoramaViewHelper; 126 private CameraPreviewData mCameraPreviewData; 127 private ActionBar mActionBar; 128 private Menu mActionBarMenu; 129 130 private class MyOrientationEventListener 131 extends OrientationEventListener { 132 public MyOrientationEventListener(Context context) { 133 super(context); 134 } 135 136 @Override 137 public void onOrientationChanged(int orientation) { 138 // We keep the last known orientation. So if the user first orient 139 // the camera then point the camera to floor or sky, we still have 140 // the correct orientation. 141 if (orientation == ORIENTATION_UNKNOWN) return; 142 mLastRawOrientation = orientation; 143 mCurrentModule.onOrientationChanged(orientation); 144 } 145 } 146 147 private MediaSaveService mMediaSaveService; 148 private ServiceConnection mConnection = new ServiceConnection() { 149 @Override 150 public void onServiceConnected(ComponentName className, IBinder b) { 151 mMediaSaveService = ((MediaSaveService.LocalBinder) b).getService(); 152 mCurrentModule.onMediaSaveServiceConnected(mMediaSaveService); 153 } 154 @Override 155 public void onServiceDisconnected(ComponentName className) { 156 mMediaSaveService = null; 157 }}; 158 159 // close activity when screen turns off 160 private BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() { 161 @Override 162 public void onReceive(Context context, Intent intent) { 163 finish(); 164 } 165 }; 166 167 private static BroadcastReceiver sScreenOffReceiver; 168 private static class ScreenOffReceiver extends BroadcastReceiver { 169 @Override 170 public void onReceive(Context context, Intent intent) { 171 sFirstStartAfterScreenOn = true; 172 } 173 } 174 175 public static boolean isFirstStartAfterScreenOn() { 176 return sFirstStartAfterScreenOn; 177 } 178 179 public static void resetFirstStartAfterScreenOn() { 180 sFirstStartAfterScreenOn = false; 181 } 182 183 private FilmStripView.Listener mFilmStripListener = 184 new FilmStripView.Listener() { 185 @Override 186 public void onDataPromoted(int dataID) { 187 removeData(dataID); 188 } 189 190 @Override 191 public void onDataDemoted(int dataID) { 192 removeData(dataID); 193 } 194 195 @Override 196 public void onDataFullScreenChange(int dataID, boolean full) { 197 } 198 199 @Override 200 public void onSwitchMode(boolean toCamera) { 201 mCurrentModule.onSwitchMode(toCamera); 202 if (toCamera) { 203 mActionBar.hide(); 204 } else { 205 mActionBar.show(); 206 } 207 } 208 209 @Override 210 public void onCurrentDataChanged(int dataID, boolean current) { 211 if (!current) { 212 hidePanoStitchingProgress(); 213 } else { 214 LocalData currentData = mDataAdapter.getLocalData(dataID); 215 if (currentData == null) { 216 Log.w(TAG, "Current data ID not found."); 217 hidePanoStitchingProgress(); 218 return; 219 } 220 updateActionBarMenu(dataID); 221 222 Uri contentUri = currentData.getContentUri(); 223 if (contentUri == null) { 224 hidePanoStitchingProgress(); 225 return; 226 } 227 int panoStitchingProgress = mPanoramaManager.getTaskProgress(contentUri); 228 if (panoStitchingProgress < 0) { 229 hidePanoStitchingProgress(); 230 return; 231 } 232 showPanoStitchingProgress(); 233 updateStitchingProgress(panoStitchingProgress); 234 } 235 } 236 237 @Override 238 public boolean onToggleActionBarVisibility() { 239 if (mActionBar.isShowing()) { 240 mActionBar.hide(); 241 } else { 242 mActionBar.show(); 243 } 244 return mActionBar.isShowing(); 245 } 246 }; 247 248 private void hidePanoStitchingProgress() { 249 mPanoStitchingPanel.setVisibility(View.GONE); 250 } 251 252 private void showPanoStitchingProgress() { 253 mPanoStitchingPanel.setVisibility(View.VISIBLE); 254 } 255 256 private void updateStitchingProgress(int progress) { 257 mBottomProgress.setProgress(progress); 258 } 259 260 /** 261 * According to the data type, make the menu items for supported operations 262 * visible. 263 * @param dataID the data ID of the current item. 264 */ 265 private void updateActionBarMenu(int dataID) { 266 LocalData currentData = mDataAdapter.getLocalData(dataID); 267 int type = currentData.getLocalDataType(); 268 269 if (mActionBarMenu == null) { 270 return; 271 } 272 273 int supported = 0; 274 switch (type) { 275 case LocalData.LOCAL_IMAGE: 276 supported |= SUPPORT_DELETE | SUPPORT_ROTATE | SUPPORT_INFO 277 | SUPPORT_CROP | SUPPORT_SETAS | SUPPORT_EDIT 278 | SUPPORT_SHOW_ON_MAP; 279 break; 280 case LocalData.LOCAL_VIDEO: 281 supported |= SUPPORT_DELETE | SUPPORT_INFO | SUPPORT_TRIM; 282 break; 283 case LocalData.LOCAL_PHOTO_SPHERE: 284 supported |= SUPPORT_DELETE | SUPPORT_ROTATE | SUPPORT_INFO 285 | SUPPORT_CROP | SUPPORT_SETAS | SUPPORT_EDIT 286 | SUPPORT_SHOW_ON_MAP; 287 break; 288 default: 289 break; 290 } 291 292 setMenuItemVisible(mActionBarMenu, R.id.action_delete, 293 (supported & SUPPORT_DELETE) != 0); 294 setMenuItemVisible(mActionBarMenu, R.id.action_rotate_ccw, 295 (supported & SUPPORT_ROTATE) != 0); 296 setMenuItemVisible(mActionBarMenu, R.id.action_rotate_cw, 297 (supported & SUPPORT_ROTATE) != 0); 298 setMenuItemVisible(mActionBarMenu, R.id.action_crop, 299 (supported & SUPPORT_CROP) != 0); 300 setMenuItemVisible(mActionBarMenu, R.id.action_trim, 301 (supported & SUPPORT_TRIM) != 0); 302 setMenuItemVisible(mActionBarMenu, R.id.action_setas, 303 (supported & SUPPORT_SETAS) != 0); 304 setMenuItemVisible(mActionBarMenu, R.id.action_edit, 305 (supported & SUPPORT_EDIT) != 0); 306 setMenuItemVisible(mActionBarMenu, R.id.action_details, 307 (supported & SUPPORT_INFO) != 0); 308 309 boolean itemHasLocation = currentData.getLatLong() != null; 310 setMenuItemVisible(mActionBarMenu, R.id.action_show_on_map, 311 itemHasLocation && (supported & SUPPORT_SHOW_ON_MAP) != 0); 312 } 313 314 private void setMenuItemVisible(Menu menu, int itemId, boolean visible) { 315 MenuItem item = menu.findItem(itemId); 316 if (item != null) 317 item.setVisible(visible); 318 } 319 320 private Runnable mDeletionRunnable = new Runnable() { 321 @Override 322 public void run() { 323 mDataAdapter.executeDeletion(CameraActivity.this); 324 } 325 }; 326 327 private ImageTaskManager.TaskListener mStitchingListener = 328 new ImageTaskManager.TaskListener() { 329 @Override 330 public void onTaskQueued(String filePath, final Uri imageUri) { 331 mMainHandler.post(new Runnable() { 332 @Override 333 public void run() { 334 notifyNewMedia(imageUri); 335 } 336 }); 337 } 338 339 @Override 340 public void onTaskDone(String filePath, final Uri imageUri) { 341 Log.v(TAG, "onTaskDone:" + filePath); 342 mMainHandler.post(new Runnable() { 343 @Override 344 public void run() { 345 int doneID = mDataAdapter.findDataByContentUri(imageUri); 346 int currentDataId = mFilmStripView.getCurrentId(); 347 348 if (currentDataId == doneID) { 349 hidePanoStitchingProgress(); 350 updateStitchingProgress(0); 351 } 352 353 mDataAdapter.refresh(getContentResolver(), imageUri); 354 } 355 }); 356 } 357 358 @Override 359 public void onTaskProgress( 360 String filePath, final Uri imageUri, final int progress) { 361 mMainHandler.post(new Runnable() { 362 @Override 363 public void run() { 364 int currentDataId = mFilmStripView.getCurrentId(); 365 if (currentDataId == -1) { 366 return; 367 } 368 if (imageUri.equals( 369 mDataAdapter.getLocalData(currentDataId).getContentUri())) { 370 updateStitchingProgress(progress); 371 } 372 } 373 }); 374 } 375 }; 376 377 public MediaSaveService getMediaSaveService() { 378 return mMediaSaveService; 379 } 380 381 public void notifyNewMedia(Uri uri) { 382 ContentResolver cr = getContentResolver(); 383 String mimeType = cr.getType(uri); 384 if (mimeType.startsWith("video/")) { 385 sendBroadcast(new Intent(CameraUtil.ACTION_NEW_VIDEO, uri)); 386 mDataAdapter.addNewVideo(cr, uri); 387 } else if (mimeType.startsWith("image/")) { 388 CameraUtil.broadcastNewPicture(this, uri); 389 mDataAdapter.addNewPhoto(cr, uri); 390 } else if (mimeType.startsWith("application/stitching-preview")) { 391 mDataAdapter.addNewPhoto(cr, uri); 392 } else { 393 android.util.Log.w(TAG, "Unknown new media with MIME type:" 394 + mimeType + ", uri:" + uri); 395 } 396 } 397 398 private void removeData(int dataID) { 399 mDataAdapter.removeData(CameraActivity.this, dataID); 400 mMainHandler.removeCallbacks(mDeletionRunnable); 401 mMainHandler.postDelayed(mDeletionRunnable, 3000); 402 } 403 404 private void bindMediaSaveService() { 405 Intent intent = new Intent(this, MediaSaveService.class); 406 startService(intent); // start service before binding it so the 407 // service won't be killed if we unbind it. 408 bindService(intent, mConnection, Context.BIND_AUTO_CREATE); 409 } 410 411 private void unbindMediaSaveService() { 412 if (mMediaSaveService != null) { 413 mMediaSaveService.setListener(null); 414 } 415 if (mConnection != null) { 416 unbindService(mConnection); 417 } 418 } 419 420 @Override 421 public boolean onCreateOptionsMenu(Menu menu) { 422 // Inflate the menu items for use in the action bar 423 MenuInflater inflater = getMenuInflater(); 424 inflater.inflate(R.menu.operations, menu); 425 mActionBarMenu = menu; 426 return super.onCreateOptionsMenu(menu); 427 } 428 429 @Override 430 public boolean onOptionsItemSelected(MenuItem item) { 431 int currentDataId = mFilmStripView.getCurrentId(); 432 if (currentDataId < 0) { 433 return false; 434 } 435 final LocalData localData = mDataAdapter.getLocalData(currentDataId); 436 437 // Handle presses on the action bar items 438 switch (item.getItemId()) { 439 case R.id.action_delete: 440 removeData(currentDataId); 441 return true; 442 case R.id.action_edit: 443 launchEditor(localData); 444 return true; 445 case R.id.action_trim: 446 // This is going to be handled by the Gallery app. 447 Intent intent = new Intent(ACTION_TRIM_VIDEO); 448 LocalData currentData = mDataAdapter.getLocalData( 449 mFilmStripView.getCurrentId()); 450 intent.setData(currentData.getContentUri()); 451 // We need the file path to wrap this into a RandomAccessFile. 452 intent.putExtra(MEDIA_ITEM_PATH, currentData.getPath()); 453 startActivity(intent); 454 return true; 455 case R.id.action_rotate_ccw: 456 // TODO: add the functionality. 457 return true; 458 case R.id.action_rotate_cw: 459 // TODO: add the functionality. 460 return true; 461 case R.id.action_crop: 462 // TODO: add the functionality. 463 return true; 464 case R.id.action_setas: 465 // TODO: add the functionality. 466 return true; 467 case R.id.action_details: 468 (new AsyncTask<Void, Void, MediaDetails>() { 469 @Override 470 protected MediaDetails doInBackground(Void... params) { 471 return localData.getMediaDetails(CameraActivity.this); 472 } 473 474 @Override 475 protected void onPostExecute(MediaDetails mediaDetails) { 476 DetailsDialog.create(CameraActivity.this, mediaDetails).show(); 477 } 478 }).execute(); 479 return true; 480 case R.id.action_show_on_map: 481 double[] latLong = localData.getLatLong(); 482 if (latLong != null) { 483 CameraUtil.showOnMap(this, latLong); 484 } 485 return true; 486 default: 487 return super.onOptionsItemSelected(item); 488 } 489 } 490 491 @Override 492 public void onCreate(Bundle state) { 493 super.onCreate(state); 494 getWindow().requestFeature(Window.FEATURE_ACTION_BAR); 495 setContentView(R.layout.camera_filmstrip); 496 mActionBar = getActionBar(); 497 // Hide action bar first since we are in full screen mode first. 498 mActionBar.hide(); 499 500 if (ApiHelper.HAS_ROTATION_ANIMATION) { 501 setRotationAnimation(); 502 } 503 // Check if this is in the secure camera mode. 504 Intent intent = getIntent(); 505 String action = intent.getAction(); 506 if (INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action) 507 || ACTION_IMAGE_CAPTURE_SECURE.equals(action)) { 508 mSecureCamera = true; 509 } else { 510 mSecureCamera = intent.getBooleanExtra(SECURE_CAMERA_EXTRA, false); 511 } 512 513 if (mSecureCamera) { 514 // Change the window flags so that secure camera can show when locked 515 Window win = getWindow(); 516 WindowManager.LayoutParams params = win.getAttributes(); 517 params.flags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; 518 win.setAttributes(params); 519 520 // Filter for screen off so that we can finish secure camera activity 521 // when screen is off. 522 IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF); 523 registerReceiver(mScreenOffReceiver, filter); 524 // TODO: This static screen off event receiver is a workaround to the 525 // double onResume() invocation (onResume->onPause->onResume). We should 526 // find a better solution to this. 527 if (sScreenOffReceiver == null) { 528 sScreenOffReceiver = new ScreenOffReceiver(); 529 registerReceiver(sScreenOffReceiver, filter); 530 } 531 } 532 mPanoramaManager = new PanoramaStitchingManager(CameraActivity.this); 533 mPanoramaManager.addTaskListener(mStitchingListener); 534 LayoutInflater inflater = getLayoutInflater(); 535 View rootLayout = inflater.inflate(R.layout.camera, null, false); 536 mRootView = rootLayout.findViewById(R.id.camera_app_root); 537 mPanoStitchingPanel = findViewById(R.id.pano_stitching_progress_panel); 538 mBottomProgress = (ProgressBar) findViewById(R.id.pano_stitching_progress_bar); 539 mCameraPreviewData = new CameraPreviewData(rootLayout, 540 FilmStripView.ImageData.SIZE_FULL, 541 FilmStripView.ImageData.SIZE_FULL); 542 // Put a CameraPreviewData at the first position. 543 mWrappedDataAdapter = new FixedFirstDataAdapter( 544 new CameraDataAdapter(new ColorDrawable( 545 getResources().getColor(R.color.photo_placeholder))), 546 mCameraPreviewData); 547 mFilmStripView = (FilmStripView) findViewById(R.id.filmstrip_view); 548 mFilmStripView.setViewGap( 549 getResources().getDimensionPixelSize(R.dimen.camera_film_strip_gap)); 550 mPanoramaViewHelper = new PanoramaViewHelper(this); 551 mPanoramaViewHelper.onCreate(); 552 mFilmStripView.setPanoramaViewHelper(mPanoramaViewHelper); 553 // Set up the camera preview first so the preview shows up ASAP. 554 mFilmStripView.setListener(mFilmStripListener); 555 if (MediaStore.INTENT_ACTION_VIDEO_CAMERA.equals(getIntent().getAction()) 556 || MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction())) { 557 mCurrentModule = new VideoModule(); 558 mCurrentModuleIndex = CameraSwitcher.VIDEO_MODULE_INDEX; 559 } else { 560 mCurrentModule = new PhotoModule(); 561 } 562 mCurrentModule.init(this, mRootView); 563 mOrientationListener = new MyOrientationEventListener(this); 564 mMainHandler = new Handler(getMainLooper()); 565 bindMediaSaveService(); 566 567 if (!mSecureCamera) { 568 mDataAdapter = mWrappedDataAdapter; 569 mFilmStripView.setDataAdapter(mDataAdapter); 570 mDataAdapter.requestLoad(getContentResolver()); 571 } else { 572 // Put a lock placeholder as the last image by setting its date to 0. 573 ImageView v = (ImageView) getLayoutInflater().inflate( 574 R.layout.secure_album_placeholder, null); 575 mDataAdapter = new FixedLastDataAdapter( 576 mWrappedDataAdapter, 577 new SimpleViewData( 578 v, 579 v.getDrawable().getIntrinsicWidth(), 580 v.getDrawable().getIntrinsicHeight(), 581 0, 0)); 582 // Flush out all the original data. 583 mDataAdapter.flush(); 584 mFilmStripView.setDataAdapter(mDataAdapter); 585 } 586 } 587 588 private void setRotationAnimation() { 589 int rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE; 590 rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE; 591 Window win = getWindow(); 592 WindowManager.LayoutParams winParams = win.getAttributes(); 593 winParams.rotationAnimation = rotationAnimation; 594 win.setAttributes(winParams); 595 } 596 597 @Override 598 public void onUserInteraction() { 599 super.onUserInteraction(); 600 mCurrentModule.onUserInteraction(); 601 } 602 603 @Override 604 public void onPause() { 605 mOrientationListener.disable(); 606 mCurrentModule.onPauseBeforeSuper(); 607 super.onPause(); 608 mCurrentModule.onPauseAfterSuper(); 609 } 610 611 @Override 612 public void onResume() { 613 if (Settings.System.getInt(getContentResolver(), 614 Settings.System.ACCELEROMETER_ROTATION, 0) == 0) {// auto-rotate off 615 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); 616 mAutoRotateScreen = false; 617 } else { 618 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR); 619 mAutoRotateScreen = true; 620 } 621 mOrientationListener.enable(); 622 mCurrentModule.onResumeBeforeSuper(); 623 super.onResume(); 624 mCurrentModule.onResumeAfterSuper(); 625 626 setSwipingEnabled(true); 627 } 628 629 @Override 630 public void onStart() { 631 super.onStart(); 632 mPanoramaViewHelper.onStart(); 633 } 634 635 @Override 636 protected void onStop() { 637 super.onStop(); 638 mPanoramaViewHelper.onStop(); 639 } 640 641 @Override 642 public void onDestroy() { 643 unbindMediaSaveService(); 644 if (mSecureCamera) unregisterReceiver(mScreenOffReceiver); 645 super.onDestroy(); 646 } 647 648 @Override 649 public void onConfigurationChanged(Configuration config) { 650 super.onConfigurationChanged(config); 651 mCurrentModule.onConfigurationChanged(config); 652 } 653 654 @Override 655 public boolean onKeyDown(int keyCode, KeyEvent event) { 656 if (mCurrentModule.onKeyDown(keyCode, event)) return true; 657 // Prevent software keyboard or voice search from showing up. 658 if (keyCode == KeyEvent.KEYCODE_SEARCH 659 || keyCode == KeyEvent.KEYCODE_MENU) { 660 if (event.isLongPress()) return true; 661 } 662 if (keyCode == KeyEvent.KEYCODE_MENU && mShowCameraPreview) { 663 return true; 664 } 665 666 return super.onKeyDown(keyCode, event); 667 } 668 669 @Override 670 public boolean onKeyUp(int keyCode, KeyEvent event) { 671 if (mCurrentModule.onKeyUp(keyCode, event)) return true; 672 if (keyCode == KeyEvent.KEYCODE_MENU && mShowCameraPreview) { 673 return true; 674 } 675 return super.onKeyUp(keyCode, event); 676 } 677 678 public boolean isAutoRotateScreen() { 679 return mAutoRotateScreen; 680 } 681 682 protected void updateStorageSpace() { 683 mStorageSpace = Storage.getAvailableSpace(); 684 } 685 686 protected long getStorageSpace() { 687 return mStorageSpace; 688 } 689 690 protected void updateStorageSpaceAndHint() { 691 updateStorageSpace(); 692 updateStorageHint(mStorageSpace); 693 } 694 695 protected void updateStorageHint() { 696 updateStorageHint(mStorageSpace); 697 } 698 699 protected boolean updateStorageHintOnResume() { 700 return true; 701 } 702 703 protected void updateStorageHint(long storageSpace) { 704 String message = null; 705 if (storageSpace == Storage.UNAVAILABLE) { 706 message = getString(R.string.no_storage); 707 } else if (storageSpace == Storage.PREPARING) { 708 message = getString(R.string.preparing_sd); 709 } else if (storageSpace == Storage.UNKNOWN_SIZE) { 710 message = getString(R.string.access_sd_fail); 711 } else if (storageSpace <= Storage.LOW_STORAGE_THRESHOLD) { 712 message = getString(R.string.spaceIsLow_content); 713 } 714 715 if (message != null) { 716 if (mStorageHint == null) { 717 mStorageHint = OnScreenHint.makeText(this, message); 718 } else { 719 mStorageHint.setText(message); 720 } 721 mStorageHint.show(); 722 } else if (mStorageHint != null) { 723 mStorageHint.cancel(); 724 mStorageHint = null; 725 } 726 } 727 728 protected void setResultEx(int resultCode) { 729 mResultCodeForTesting = resultCode; 730 setResult(resultCode); 731 } 732 733 protected void setResultEx(int resultCode, Intent data) { 734 mResultCodeForTesting = resultCode; 735 mResultDataForTesting = data; 736 setResult(resultCode, data); 737 } 738 739 public int getResultCode() { 740 return mResultCodeForTesting; 741 } 742 743 public Intent getResultData() { 744 return mResultDataForTesting; 745 } 746 747 public boolean isSecureCamera() { 748 return mSecureCamera; 749 } 750 751 @Override 752 public void onCameraSelected(int i) { 753 if (mCurrentModuleIndex == i) return; 754 755 CameraHolder.instance().keep(); 756 closeModule(mCurrentModule); 757 mCurrentModuleIndex = i; 758 switch (i) { 759 case CameraSwitcher.VIDEO_MODULE_INDEX: 760 mCurrentModule = new VideoModule(); 761 break; 762 case CameraSwitcher.PHOTO_MODULE_INDEX: 763 mCurrentModule = new PhotoModule(); 764 break; 765 case CameraSwitcher.LIGHTCYCLE_MODULE_INDEX: 766 mCurrentModule = PhotoSphereHelper.createPanoramaModule(); 767 break; 768 default: 769 break; 770 } 771 772 openModule(mCurrentModule); 773 mCurrentModule.onOrientationChanged(mLastRawOrientation); 774 if (mMediaSaveService != null) { 775 mCurrentModule.onMediaSaveServiceConnected(mMediaSaveService); 776 } 777 } 778 779 /** 780 * Launches an ACTION_EDIT intent for the given local data item. 781 */ 782 public void launchEditor(LocalData data) { 783 Intent intent = new Intent(Intent.ACTION_EDIT) 784 .setDataAndType(data.getContentUri(), data.getMimeType()) 785 .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 786 startActivity(Intent.createChooser(intent, null)); 787 } 788 789 private void openModule(CameraModule module) { 790 module.init(this, mRootView); 791 module.onResumeBeforeSuper(); 792 module.onResumeAfterSuper(); 793 } 794 795 private void closeModule(CameraModule module) { 796 module.onPauseBeforeSuper(); 797 module.onPauseAfterSuper(); 798 ((ViewGroup) mRootView).removeAllViews(); 799 } 800 801 @Override 802 public void onShowSwitcherPopup() { 803 } 804 805 public void setSwipingEnabled(boolean enable) { 806 mCameraPreviewData.lockPreview(!enable); 807 } 808 809 // Accessor methods for getting latency times used in performance testing 810 public long getAutoFocusTime() { 811 return (mCurrentModule instanceof PhotoModule) ? 812 ((PhotoModule) mCurrentModule).mAutoFocusTime : -1; 813 } 814 815 public long getShutterLag() { 816 return (mCurrentModule instanceof PhotoModule) ? 817 ((PhotoModule) mCurrentModule).mShutterLag : -1; 818 } 819 820 public long getShutterToPictureDisplayedTime() { 821 return (mCurrentModule instanceof PhotoModule) ? 822 ((PhotoModule) mCurrentModule).mShutterToPictureDisplayedTime : -1; 823 } 824 825 public long getPictureDisplayedToJpegCallbackTime() { 826 return (mCurrentModule instanceof PhotoModule) ? 827 ((PhotoModule) mCurrentModule).mPictureDisplayedToJpegCallbackTime : -1; 828 } 829 830 public long getJpegCallbackFinishTime() { 831 return (mCurrentModule instanceof PhotoModule) ? 832 ((PhotoModule) mCurrentModule).mJpegCallbackFinishTime : -1; 833 } 834 835 public long getCaptureStartTime() { 836 return (mCurrentModule instanceof PhotoModule) ? 837 ((PhotoModule) mCurrentModule).mCaptureStartTime : -1; 838 } 839 840 public boolean isRecording() { 841 return (mCurrentModule instanceof VideoModule) ? 842 ((VideoModule) mCurrentModule).isRecording() : false; 843 } 844} 845