CameraActivity.java revision 394023fb7e3c8d07d4aeafc30bf4b66ed481ebb7
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.util.ApiHelper; 61import com.android.camera.ui.CameraSwitcher; 62import com.android.camera.ui.CameraSwitcher.CameraSwitchListener; 63import com.android.camera.ui.DetailsDialog; 64import com.android.camera.ui.FilmStripView; 65import com.android.camera.util.CameraUtil; 66import com.android.camera.util.PhotoSphereHelper.PanoramaViewHelper; 67import com.android.camera.util.PhotoSphereHelper; 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 80 // The intent extra for camera from secure lock screen. True if the gallery 81 // should only show newly captured pictures. sSecureAlbumId does not 82 // increment. This is used when switching between camera, camcorder, and 83 // panorama. If the extra is not set, it is in the normal camera mode. 84 public static final String SECURE_CAMERA_EXTRA = "secure_camera"; 85 86 // Supported operations at FilmStripView. Different data has different 87 // set of supported operations. 88 private static final int SUPPORT_DELETE = 1 << 0; 89 private static final int SUPPORT_ROTATE = 1 << 1; 90 private static final int SUPPORT_INFO = 1 << 2; 91 private static final int SUPPORT_CROP = 1 << 3; 92 private static final int SUPPORT_SETAS = 1 << 4; 93 private static final int SUPPORT_EDIT = 1 << 5; 94 private static final int SUPPORT_TRIM = 1 << 6; 95 private static final int SUPPORT_MUTE = 1 << 7; 96 private static final int SUPPORT_SHOW_ON_MAP = 1 << 8; 97 private static final int SUPPORT_ALL = 0xffffffff; 98 99 /** This data adapter is used by FilmStripView. */ 100 private LocalDataAdapter mDataAdapter; 101 /** This data adapter represents the real local camera data. */ 102 private LocalDataAdapter mWrappedDataAdapter; 103 104 private PanoramaStitchingManager mPanoramaManager; 105 private int mCurrentModuleIndex; 106 private CameraModule mCurrentModule; 107 private View mRootView; 108 private FilmStripView mFilmStripView; 109 private ProgressBar mBottomProgress; 110 private View mPanoStitchingPanel; 111 private int mResultCodeForTesting; 112 private Intent mResultDataForTesting; 113 private OnScreenHint mStorageHint; 114 private long mStorageSpace = Storage.LOW_STORAGE_THRESHOLD; 115 private boolean mAutoRotateScreen; 116 private boolean mSecureCamera; 117 // This is a hack to speed up the start of SecureCamera. 118 private static boolean sFirstStartAfterScreenOn = true; 119 private boolean mShowCameraPreview; 120 private int mLastRawOrientation; 121 private MyOrientationEventListener mOrientationListener; 122 private Handler mMainHandler; 123 private PanoramaViewHelper mPanoramaViewHelper; 124 private CameraPreviewData mCameraPreviewData; 125 private ActionBar mActionBar; 126 private Menu mActionBarMenu; 127 128 private class MyOrientationEventListener 129 extends OrientationEventListener { 130 public MyOrientationEventListener(Context context) { 131 super(context); 132 } 133 134 @Override 135 public void onOrientationChanged(int orientation) { 136 // We keep the last known orientation. So if the user first orient 137 // the camera then point the camera to floor or sky, we still have 138 // the correct orientation. 139 if (orientation == ORIENTATION_UNKNOWN) return; 140 mLastRawOrientation = orientation; 141 mCurrentModule.onOrientationChanged(orientation); 142 } 143 } 144 145 private MediaSaveService mMediaSaveService; 146 private ServiceConnection mConnection = new ServiceConnection() { 147 @Override 148 public void onServiceConnected(ComponentName className, IBinder b) { 149 mMediaSaveService = ((MediaSaveService.LocalBinder) b).getService(); 150 mCurrentModule.onMediaSaveServiceConnected(mMediaSaveService); 151 } 152 @Override 153 public void onServiceDisconnected(ComponentName className) { 154 mMediaSaveService = null; 155 }}; 156 157 // close activity when screen turns off 158 private BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() { 159 @Override 160 public void onReceive(Context context, Intent intent) { 161 finish(); 162 } 163 }; 164 165 private static BroadcastReceiver sScreenOffReceiver; 166 private static class ScreenOffReceiver extends BroadcastReceiver { 167 @Override 168 public void onReceive(Context context, Intent intent) { 169 sFirstStartAfterScreenOn = true; 170 } 171 } 172 173 public static boolean isFirstStartAfterScreenOn() { 174 return sFirstStartAfterScreenOn; 175 } 176 177 public static void resetFirstStartAfterScreenOn() { 178 sFirstStartAfterScreenOn = false; 179 } 180 181 private FilmStripView.Listener mFilmStripListener = 182 new FilmStripView.Listener() { 183 @Override 184 public void onDataPromoted(int dataID) { 185 removeData(dataID); 186 } 187 188 @Override 189 public void onDataDemoted(int dataID) { 190 removeData(dataID); 191 } 192 193 @Override 194 public void onDataFullScreenChange(int dataID, boolean full) { 195 } 196 197 @Override 198 public void onSwitchMode(boolean toCamera) { 199 mCurrentModule.onSwitchMode(toCamera); 200 if (toCamera) { 201 mActionBar.hide(); 202 } else { 203 mActionBar.show(); 204 } 205 } 206 207 @Override 208 public void onCurrentDataChanged(int dataID, boolean current) { 209 if (!current) { 210 hidePanoStitchingProgress(); 211 } else { 212 LocalData currentData = mDataAdapter.getLocalData(dataID); 213 if (currentData == null) { 214 Log.w(TAG, "Current data ID not found."); 215 hidePanoStitchingProgress(); 216 return; 217 } 218 updateActionBarMenu(dataID); 219 220 Uri contentUri = currentData.getContentUri(); 221 if (contentUri == null) { 222 hidePanoStitchingProgress(); 223 return; 224 } 225 int panoStitchingProgress = mPanoramaManager.getTaskProgress(contentUri); 226 if (panoStitchingProgress < 0) { 227 hidePanoStitchingProgress(); 228 return; 229 } 230 showPanoStitchingProgress(); 231 updateStitchingProgress(panoStitchingProgress); 232 } 233 } 234 235 @Override 236 public void onToggleActionBarVisibility() { 237 if (mActionBar.isShowing()) { 238 mActionBar.hide(); 239 } else { 240 mActionBar.show(); 241 } 242 } 243 }; 244 245 private void hidePanoStitchingProgress() { 246 mPanoStitchingPanel.setVisibility(View.GONE); 247 } 248 249 private void showPanoStitchingProgress() { 250 mPanoStitchingPanel.setVisibility(View.VISIBLE); 251 } 252 253 private void updateStitchingProgress(int progress) { 254 mBottomProgress.setProgress(progress); 255 } 256 257 /** 258 * According to the data type, make the menu items for supported operations 259 * visible. 260 * @param dataID the data ID of the current item. 261 */ 262 private void updateActionBarMenu(int dataID) { 263 LocalData currentData = mDataAdapter.getLocalData(dataID); 264 int type = currentData.getLocalDataType(); 265 266 if (mActionBarMenu == null) { 267 return; 268 } 269 270 int supported = 0; 271 switch (type) { 272 case LocalData.LOCAL_IMAGE: 273 supported |= SUPPORT_DELETE | SUPPORT_ROTATE | SUPPORT_INFO 274 | SUPPORT_CROP | SUPPORT_SETAS | SUPPORT_EDIT 275 | SUPPORT_SHOW_ON_MAP; 276 break; 277 case LocalData.LOCAL_VIDEO: 278 supported |= SUPPORT_DELETE | SUPPORT_INFO | SUPPORT_TRIM 279 | SUPPORT_MUTE; 280 break; 281 case LocalData.LOCAL_PHOTO_SPHERE: 282 supported |= SUPPORT_DELETE | SUPPORT_ROTATE | SUPPORT_INFO 283 | SUPPORT_CROP | SUPPORT_SETAS | SUPPORT_EDIT 284 | SUPPORT_SHOW_ON_MAP; 285 break; 286 default: 287 break; 288 } 289 290 setMenuItemVisible(mActionBarMenu, R.id.action_delete, 291 (supported & SUPPORT_DELETE) != 0); 292 setMenuItemVisible(mActionBarMenu, R.id.action_rotate_ccw, 293 (supported & SUPPORT_ROTATE) != 0); 294 setMenuItemVisible(mActionBarMenu, R.id.action_rotate_cw, 295 (supported & SUPPORT_ROTATE) != 0); 296 setMenuItemVisible(mActionBarMenu, R.id.action_crop, 297 (supported & SUPPORT_CROP) != 0); 298 setMenuItemVisible(mActionBarMenu, R.id.action_trim, 299 (supported & SUPPORT_TRIM) != 0); 300 setMenuItemVisible(mActionBarMenu, R.id.action_mute, 301 (supported & SUPPORT_MUTE) != 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 // TODO: add the functionality. 441 return true; 442 case R.id.action_edit: 443 // TODO: add the functionality. 444 return true; 445 case R.id.action_trim: 446 // TODO: add the functionality. 447 return true; 448 case R.id.action_mute: 449 // TODO: add the functionality. 450 return true; 451 case R.id.action_rotate_ccw: 452 // TODO: add the functionality. 453 return true; 454 case R.id.action_rotate_cw: 455 // TODO: add the functionality. 456 return true; 457 case R.id.action_crop: 458 // TODO: add the functionality. 459 return true; 460 case R.id.action_setas: 461 // TODO: add the functionality. 462 return true; 463 case R.id.action_details: 464 (new AsyncTask<Void, Void, MediaDetails>() { 465 @Override 466 protected MediaDetails doInBackground(Void... params) { 467 return localData.getMediaDetails(CameraActivity.this); 468 } 469 470 @Override 471 protected void onPostExecute(MediaDetails mediaDetails) { 472 DetailsDialog.create(CameraActivity.this, mediaDetails).show(); 473 } 474 }).execute(); 475 return true; 476 case R.id.action_show_on_map: 477 double[] latLong = localData.getLatLong(); 478 if (latLong != null) { 479 CameraUtil.showOnMap(this, latLong); 480 } 481 return true; 482 default: 483 return super.onOptionsItemSelected(item); 484 } 485 } 486 487 @Override 488 public void onCreate(Bundle state) { 489 super.onCreate(state); 490 getWindow().requestFeature(Window.FEATURE_ACTION_BAR); 491 setContentView(R.layout.camera_filmstrip); 492 mActionBar = getActionBar(); 493 // Hide action bar first since we are in full screen mode first. 494 mActionBar.hide(); 495 496 if (ApiHelper.HAS_ROTATION_ANIMATION) { 497 setRotationAnimation(); 498 } 499 // Check if this is in the secure camera mode. 500 Intent intent = getIntent(); 501 String action = intent.getAction(); 502 if (INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action) 503 || ACTION_IMAGE_CAPTURE_SECURE.equals(action)) { 504 mSecureCamera = true; 505 } else { 506 mSecureCamera = intent.getBooleanExtra(SECURE_CAMERA_EXTRA, false); 507 } 508 509 if (mSecureCamera) { 510 // Change the window flags so that secure camera can show when locked 511 Window win = getWindow(); 512 WindowManager.LayoutParams params = win.getAttributes(); 513 params.flags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; 514 win.setAttributes(params); 515 516 // Filter for screen off so that we can finish secure camera activity 517 // when screen is off. 518 IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF); 519 registerReceiver(mScreenOffReceiver, filter); 520 // TODO: This static screen off event receiver is a workaround to the 521 // double onResume() invocation (onResume->onPause->onResume). We should 522 // find a better solution to this. 523 if (sScreenOffReceiver == null) { 524 sScreenOffReceiver = new ScreenOffReceiver(); 525 registerReceiver(sScreenOffReceiver, filter); 526 } 527 } 528 mPanoramaManager = new PanoramaStitchingManager(CameraActivity.this); 529 mPanoramaManager.addTaskListener(mStitchingListener); 530 LayoutInflater inflater = getLayoutInflater(); 531 View rootLayout = inflater.inflate(R.layout.camera, null, false); 532 mRootView = rootLayout.findViewById(R.id.camera_app_root); 533 mPanoStitchingPanel = findViewById(R.id.pano_stitching_progress_panel); 534 mBottomProgress = (ProgressBar) findViewById(R.id.pano_stitching_progress_bar); 535 mCameraPreviewData = new CameraPreviewData(rootLayout, 536 FilmStripView.ImageData.SIZE_FULL, 537 FilmStripView.ImageData.SIZE_FULL); 538 // Put a CameraPreviewData at the first position. 539 mWrappedDataAdapter = new FixedFirstDataAdapter( 540 new CameraDataAdapter(new ColorDrawable( 541 getResources().getColor(R.color.photo_placeholder))), 542 mCameraPreviewData); 543 mFilmStripView = (FilmStripView) findViewById(R.id.filmstrip_view); 544 mFilmStripView.setViewGap( 545 getResources().getDimensionPixelSize(R.dimen.camera_film_strip_gap)); 546 mPanoramaViewHelper = new PanoramaViewHelper(this); 547 mPanoramaViewHelper.onCreate(); 548 mFilmStripView.setPanoramaViewHelper(mPanoramaViewHelper); 549 // Set up the camera preview first so the preview shows up ASAP. 550 mFilmStripView.setListener(mFilmStripListener); 551 if (MediaStore.INTENT_ACTION_VIDEO_CAMERA.equals(getIntent().getAction()) 552 || MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction())) { 553 mCurrentModule = new VideoModule(); 554 mCurrentModuleIndex = CameraSwitcher.VIDEO_MODULE_INDEX; 555 } else { 556 mCurrentModule = new PhotoModule(); 557 } 558 mCurrentModule.init(this, mRootView); 559 mOrientationListener = new MyOrientationEventListener(this); 560 mMainHandler = new Handler(getMainLooper()); 561 bindMediaSaveService(); 562 563 if (!mSecureCamera) { 564 mDataAdapter = mWrappedDataAdapter; 565 mFilmStripView.setDataAdapter(mDataAdapter); 566 mDataAdapter.requestLoad(getContentResolver()); 567 } else { 568 // Put a lock placeholder as the last image by setting its date to 0. 569 ImageView v = (ImageView) getLayoutInflater().inflate( 570 R.layout.secure_album_placeholder, null); 571 mDataAdapter = new FixedLastDataAdapter( 572 mWrappedDataAdapter, 573 new SimpleViewData( 574 v, 575 v.getDrawable().getIntrinsicWidth(), 576 v.getDrawable().getIntrinsicHeight(), 577 0, 0)); 578 // Flush out all the original data. 579 mDataAdapter.flush(); 580 mFilmStripView.setDataAdapter(mDataAdapter); 581 } 582 } 583 584 private void setRotationAnimation() { 585 int rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE; 586 rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE; 587 Window win = getWindow(); 588 WindowManager.LayoutParams winParams = win.getAttributes(); 589 winParams.rotationAnimation = rotationAnimation; 590 win.setAttributes(winParams); 591 } 592 593 @Override 594 public void onUserInteraction() { 595 super.onUserInteraction(); 596 mCurrentModule.onUserInteraction(); 597 } 598 599 @Override 600 public void onPause() { 601 mOrientationListener.disable(); 602 mCurrentModule.onPauseBeforeSuper(); 603 super.onPause(); 604 mCurrentModule.onPauseAfterSuper(); 605 } 606 607 @Override 608 public void onResume() { 609 if (Settings.System.getInt(getContentResolver(), 610 Settings.System.ACCELEROMETER_ROTATION, 0) == 0) {// auto-rotate off 611 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); 612 mAutoRotateScreen = false; 613 } else { 614 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR); 615 mAutoRotateScreen = true; 616 } 617 mOrientationListener.enable(); 618 mCurrentModule.onResumeBeforeSuper(); 619 super.onResume(); 620 mCurrentModule.onResumeAfterSuper(); 621 622 setSwipingEnabled(true); 623 } 624 625 @Override 626 public void onStart() { 627 super.onStart(); 628 629 mPanoramaViewHelper.onStart(); 630 } 631 632 @Override 633 protected void onStop() { 634 super.onStop(); 635 mPanoramaViewHelper.onStop(); 636 } 637 638 @Override 639 public void onDestroy() { 640 unbindMediaSaveService(); 641 if (mSecureCamera) unregisterReceiver(mScreenOffReceiver); 642 super.onDestroy(); 643 } 644 645 @Override 646 public void onConfigurationChanged(Configuration config) { 647 super.onConfigurationChanged(config); 648 mCurrentModule.onConfigurationChanged(config); 649 } 650 651 @Override 652 public boolean onKeyDown(int keyCode, KeyEvent event) { 653 if (mCurrentModule.onKeyDown(keyCode, event)) return true; 654 // Prevent software keyboard or voice search from showing up. 655 if (keyCode == KeyEvent.KEYCODE_SEARCH 656 || keyCode == KeyEvent.KEYCODE_MENU) { 657 if (event.isLongPress()) return true; 658 } 659 if (keyCode == KeyEvent.KEYCODE_MENU && mShowCameraPreview) { 660 return true; 661 } 662 663 return super.onKeyDown(keyCode, event); 664 } 665 666 @Override 667 public boolean onKeyUp(int keyCode, KeyEvent event) { 668 if (mCurrentModule.onKeyUp(keyCode, event)) return true; 669 if (keyCode == KeyEvent.KEYCODE_MENU && mShowCameraPreview) { 670 return true; 671 } 672 return super.onKeyUp(keyCode, event); 673 } 674 675 public boolean isAutoRotateScreen() { 676 return mAutoRotateScreen; 677 } 678 679 protected void updateStorageSpace() { 680 mStorageSpace = Storage.getAvailableSpace(); 681 } 682 683 protected long getStorageSpace() { 684 return mStorageSpace; 685 } 686 687 protected void updateStorageSpaceAndHint() { 688 updateStorageSpace(); 689 updateStorageHint(mStorageSpace); 690 } 691 692 protected void updateStorageHint() { 693 updateStorageHint(mStorageSpace); 694 } 695 696 protected boolean updateStorageHintOnResume() { 697 return true; 698 } 699 700 protected void updateStorageHint(long storageSpace) { 701 String message = null; 702 if (storageSpace == Storage.UNAVAILABLE) { 703 message = getString(R.string.no_storage); 704 } else if (storageSpace == Storage.PREPARING) { 705 message = getString(R.string.preparing_sd); 706 } else if (storageSpace == Storage.UNKNOWN_SIZE) { 707 message = getString(R.string.access_sd_fail); 708 } else if (storageSpace <= Storage.LOW_STORAGE_THRESHOLD) { 709 message = getString(R.string.spaceIsLow_content); 710 } 711 712 if (message != null) { 713 if (mStorageHint == null) { 714 mStorageHint = OnScreenHint.makeText(this, message); 715 } else { 716 mStorageHint.setText(message); 717 } 718 mStorageHint.show(); 719 } else if (mStorageHint != null) { 720 mStorageHint.cancel(); 721 mStorageHint = null; 722 } 723 } 724 725 protected void setResultEx(int resultCode) { 726 mResultCodeForTesting = resultCode; 727 setResult(resultCode); 728 } 729 730 protected void setResultEx(int resultCode, Intent data) { 731 mResultCodeForTesting = resultCode; 732 mResultDataForTesting = data; 733 setResult(resultCode, data); 734 } 735 736 public int getResultCode() { 737 return mResultCodeForTesting; 738 } 739 740 public Intent getResultData() { 741 return mResultDataForTesting; 742 } 743 744 public boolean isSecureCamera() { 745 return mSecureCamera; 746 } 747 748 @Override 749 public void onCameraSelected(int i) { 750 if (mCurrentModuleIndex == i) return; 751 752 CameraHolder.instance().keep(); 753 closeModule(mCurrentModule); 754 mCurrentModuleIndex = i; 755 switch (i) { 756 case CameraSwitcher.VIDEO_MODULE_INDEX: 757 mCurrentModule = new VideoModule(); 758 break; 759 case CameraSwitcher.PHOTO_MODULE_INDEX: 760 mCurrentModule = new PhotoModule(); 761 break; 762 case CameraSwitcher.LIGHTCYCLE_MODULE_INDEX: 763 mCurrentModule = PhotoSphereHelper.createPanoramaModule(); 764 break; 765 default: 766 break; 767 } 768 769 openModule(mCurrentModule); 770 mCurrentModule.onOrientationChanged(mLastRawOrientation); 771 if (mMediaSaveService != null) { 772 mCurrentModule.onMediaSaveServiceConnected(mMediaSaveService); 773 } 774 } 775 776 private void openModule(CameraModule module) { 777 module.init(this, mRootView); 778 module.onResumeBeforeSuper(); 779 module.onResumeAfterSuper(); 780 } 781 782 private void closeModule(CameraModule module) { 783 module.onPauseBeforeSuper(); 784 module.onPauseAfterSuper(); 785 ((ViewGroup) mRootView).removeAllViews(); 786 } 787 788 @Override 789 public void onShowSwitcherPopup() { 790 } 791 792 public void setSwipingEnabled(boolean enable) { 793 mCameraPreviewData.lockPreview(!enable); 794 } 795 796 // Accessor methods for getting latency times used in performance testing 797 public long getAutoFocusTime() { 798 return (mCurrentModule instanceof PhotoModule) ? 799 ((PhotoModule) mCurrentModule).mAutoFocusTime : -1; 800 } 801 802 public long getShutterLag() { 803 return (mCurrentModule instanceof PhotoModule) ? 804 ((PhotoModule) mCurrentModule).mShutterLag : -1; 805 } 806 807 public long getShutterToPictureDisplayedTime() { 808 return (mCurrentModule instanceof PhotoModule) ? 809 ((PhotoModule) mCurrentModule).mShutterToPictureDisplayedTime : -1; 810 } 811 812 public long getPictureDisplayedToJpegCallbackTime() { 813 return (mCurrentModule instanceof PhotoModule) ? 814 ((PhotoModule) mCurrentModule).mPictureDisplayedToJpegCallbackTime : -1; 815 } 816 817 public long getJpegCallbackFinishTime() { 818 return (mCurrentModule instanceof PhotoModule) ? 819 ((PhotoModule) mCurrentModule).mJpegCallbackFinishTime : -1; 820 } 821 822 public long getCaptureStartTime() { 823 return (mCurrentModule instanceof PhotoModule) ? 824 ((PhotoModule) mCurrentModule).mCaptureStartTime : -1; 825 } 826 827 public boolean isRecording() { 828 return (mCurrentModule instanceof VideoModule) ? 829 ((VideoModule) mCurrentModule).isRecording() : false; 830 } 831} 832