CameraActivity.java revision a0bd080a978fd7e9a67436b412f929c5b3cf8854
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.Bundle; 33import android.os.Handler; 34import android.os.IBinder; 35import android.provider.Settings; 36import android.util.Log; 37import android.view.KeyEvent; 38import android.view.LayoutInflater; 39import android.view.OrientationEventListener; 40import android.view.View; 41import android.view.ViewGroup; 42import android.view.Window; 43import android.view.WindowManager; 44import android.widget.ImageView; 45import android.widget.ProgressBar; 46 47import com.android.camera.data.CameraDataAdapter; 48import com.android.camera.data.CameraPreviewData; 49import com.android.camera.data.FixedFirstDataAdapter; 50import com.android.camera.data.FixedLastDataAdapter; 51import com.android.camera.data.LocalData; 52import com.android.camera.data.LocalDataAdapter; 53import com.android.camera.data.SimpleViewData; 54import com.android.camera.util.ApiHelper; 55import com.android.camera.ui.CameraSwitcher; 56import com.android.camera.ui.CameraSwitcher.CameraSwitchListener; 57import com.android.camera.ui.FilmStripView; 58import com.android.camera.util.CameraUtil; 59import com.android.camera.util.PhotoSphereHelper.PanoramaViewHelper; 60import com.android.camera.util.PhotoSphereHelper; 61import com.android.camera.util.RefocusHelper; 62import com.android.camera2.R; 63 64public class CameraActivity extends Activity 65 implements CameraSwitchListener { 66 67 private static final String TAG = "CAM_Activity"; 68 69 private static final String INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE = 70 "android.media.action.STILL_IMAGE_CAMERA_SECURE"; 71 public static final String ACTION_IMAGE_CAPTURE_SECURE = 72 "android.media.action.IMAGE_CAPTURE_SECURE"; 73 74 // The intent extra for camera from secure lock screen. True if the gallery 75 // should only show newly captured pictures. sSecureAlbumId does not 76 // increment. This is used when switching between camera, camcorder, and 77 // panorama. If the extra is not set, it is in the normal camera mode. 78 public static final String SECURE_CAMERA_EXTRA = "secure_camera"; 79 80 /** This data adapter is used by FilmStirpView. */ 81 private LocalDataAdapter mDataAdapter; 82 /** This data adapter represents the real local camera data. */ 83 private LocalDataAdapter mWrappedDataAdapter; 84 85 private PanoramaStitchingManager mPanoramaManager; 86 private int mCurrentModuleIndex; 87 private CameraModule mCurrentModule; 88 private View mRootView; 89 private FilmStripView mFilmStripView; 90 private ProgressBar mBottomProgress; 91 private View mPanoStitchingPanel; 92 private int mResultCodeForTesting; 93 private Intent mResultDataForTesting; 94 private OnScreenHint mStorageHint; 95 private long mStorageSpace = Storage.LOW_STORAGE_THRESHOLD; 96 private boolean mAutoRotateScreen; 97 private boolean mSecureCamera; 98 // This is a hack to speed up the start of SecureCamera. 99 private static boolean sFirstStartAfterScreenOn = true; 100 private boolean mShowCameraPreview; 101 private int mLastRawOrientation; 102 private MyOrientationEventListener mOrientationListener; 103 private Handler mMainHandler; 104 private PanoramaViewHelper mPanoramaViewHelper; 105 private CameraPreviewData mCameraPreviewData; 106 private ActionBar mActionBar; 107 108 private class MyOrientationEventListener 109 extends OrientationEventListener { 110 public MyOrientationEventListener(Context context) { 111 super(context); 112 } 113 114 @Override 115 public void onOrientationChanged(int orientation) { 116 // We keep the last known orientation. So if the user first orient 117 // the camera then point the camera to floor or sky, we still have 118 // the correct orientation. 119 if (orientation == ORIENTATION_UNKNOWN) return; 120 mLastRawOrientation = orientation; 121 mCurrentModule.onOrientationChanged(orientation); 122 } 123 } 124 125 private MediaSaveService mMediaSaveService; 126 private ServiceConnection mConnection = new ServiceConnection() { 127 @Override 128 public void onServiceConnected(ComponentName className, IBinder b) { 129 mMediaSaveService = ((MediaSaveService.LocalBinder) b).getService(); 130 mCurrentModule.onMediaSaveServiceConnected(mMediaSaveService); 131 } 132 @Override 133 public void onServiceDisconnected(ComponentName className) { 134 mMediaSaveService = null; 135 }}; 136 137 // close activity when screen turns off 138 private BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() { 139 @Override 140 public void onReceive(Context context, Intent intent) { 141 finish(); 142 } 143 }; 144 145 private static BroadcastReceiver sScreenOffReceiver; 146 private static class ScreenOffReceiver extends BroadcastReceiver { 147 @Override 148 public void onReceive(Context context, Intent intent) { 149 sFirstStartAfterScreenOn = true; 150 } 151 } 152 153 public static boolean isFirstStartAfterScreenOn() { 154 return sFirstStartAfterScreenOn; 155 } 156 157 public static void resetFirstStartAfterScreenOn() { 158 sFirstStartAfterScreenOn = false; 159 } 160 161 private FilmStripView.Listener mFilmStripListener = 162 new FilmStripView.Listener() { 163 @Override 164 public void onDataPromoted(int dataID) { 165 removeData(dataID); 166 } 167 168 @Override 169 public void onDataDemoted(int dataID) { 170 removeData(dataID); 171 } 172 173 @Override 174 public void onDataFullScreenChange(int dataID, boolean full) { 175 } 176 177 @Override 178 public void onSwitchMode(boolean toCamera) { 179 mCurrentModule.onSwitchMode(toCamera); 180 if (toCamera) { 181 mActionBar.hide(); 182 } else { 183 mActionBar.show(); 184 } 185 } 186 187 @Override 188 public void onCurrentDataChanged(int dataID, boolean current) { 189 if (!current) { 190 hidePanoStitchingProgress(); 191 } else { 192 LocalData currentData = mDataAdapter.getLocalData(dataID); 193 if (currentData == null) { 194 Log.w(TAG, "Current data ID not found."); 195 hidePanoStitchingProgress(); 196 return; 197 } 198 Uri contentUri = currentData.getContentUri(); 199 if (contentUri == null) { 200 hidePanoStitchingProgress(); 201 return; 202 } 203 int panoStitchingProgress = mPanoramaManager.getTaskProgress(contentUri); 204 if (panoStitchingProgress < 0) { 205 hidePanoStitchingProgress(); 206 return; 207 } 208 showPanoStitchingProgress(); 209 updateStitchingProgress(panoStitchingProgress); 210 } 211 } 212 }; 213 214 private void hidePanoStitchingProgress() { 215 mPanoStitchingPanel.setVisibility(View.GONE); 216 } 217 218 private void showPanoStitchingProgress() { 219 mPanoStitchingPanel.setVisibility(View.VISIBLE); 220 } 221 222 private void updateStitchingProgress(int progress) { 223 mBottomProgress.setProgress(progress); 224 } 225 226 private Runnable mDeletionRunnable = new Runnable() { 227 @Override 228 public void run() { 229 mDataAdapter.executeDeletion(CameraActivity.this); 230 } 231 }; 232 233 private ImageTaskManager.TaskListener mStitchingListener = 234 new ImageTaskManager.TaskListener() { 235 @Override 236 public void onTaskQueued(String filePath, final Uri imageUri) { 237 mMainHandler.post(new Runnable() { 238 @Override 239 public void run() { 240 notifyNewMedia(imageUri); 241 } 242 }); 243 } 244 245 @Override 246 public void onTaskDone(String filePath, final Uri imageUri) { 247 Log.v(TAG, "onTaskDone:" + filePath); 248 mMainHandler.post(new Runnable() { 249 @Override 250 public void run() { 251 int doneID = mDataAdapter.findDataByContentUri(imageUri); 252 int currentDataId = mFilmStripView.getCurrentId(); 253 254 if (currentDataId == doneID) { 255 hidePanoStitchingProgress(); 256 updateStitchingProgress(0); 257 } 258 259 mDataAdapter.refresh(getContentResolver(), imageUri); 260 } 261 }); 262 } 263 264 @Override 265 public void onTaskProgress( 266 String filePath, final Uri imageUri, final int progress) { 267 mMainHandler.post(new Runnable() { 268 @Override 269 public void run() { 270 int currentDataId = mFilmStripView.getCurrentId(); 271 if (currentDataId == -1) { 272 return; 273 } 274 if (imageUri.equals( 275 mDataAdapter.getLocalData(currentDataId).getContentUri())) { 276 updateStitchingProgress(progress); 277 } 278 } 279 }); 280 } 281 }; 282 283 public MediaSaveService getMediaSaveService() { 284 return mMediaSaveService; 285 } 286 287 public void notifyNewMedia(Uri uri) { 288 ContentResolver cr = getContentResolver(); 289 String mimeType = cr.getType(uri); 290 if (mimeType.startsWith("video/")) { 291 sendBroadcast(new Intent(CameraUtil.ACTION_NEW_VIDEO, uri)); 292 mDataAdapter.addNewVideo(cr, uri); 293 } else if (mimeType.startsWith("image/")) { 294 CameraUtil.broadcastNewPicture(this, uri); 295 mDataAdapter.addNewPhoto(cr, uri); 296 } else if (mimeType.startsWith("application/stitching-preview")) { 297 mDataAdapter.addNewPhoto(cr, uri); 298 } else { 299 android.util.Log.w(TAG, "Unknown new media with MIME type:" 300 + mimeType + ", uri:" + uri); 301 } 302 } 303 304 private void removeData(int dataID) { 305 mDataAdapter.removeData(CameraActivity.this, dataID); 306 mMainHandler.removeCallbacks(mDeletionRunnable); 307 mMainHandler.postDelayed(mDeletionRunnable, 3000); 308 } 309 310 private void bindMediaSaveService() { 311 Intent intent = new Intent(this, MediaSaveService.class); 312 startService(intent); // start service before binding it so the 313 // service won't be killed if we unbind it. 314 bindService(intent, mConnection, Context.BIND_AUTO_CREATE); 315 } 316 317 private void unbindMediaSaveService() { 318 if (mMediaSaveService != null) { 319 mMediaSaveService.setListener(null); 320 } 321 if (mConnection != null) { 322 unbindService(mConnection); 323 } 324 } 325 326 @Override 327 public void onCreate(Bundle state) { 328 super.onCreate(state); 329 setContentView(R.layout.camera_filmstrip); 330 mActionBar = getActionBar(); 331 // Hide action bar first since we are in full screen mode first. 332 mActionBar.hide(); 333 334 if (ApiHelper.HAS_ROTATION_ANIMATION) { 335 setRotationAnimation(); 336 } 337 // Check if this is in the secure camera mode. 338 Intent intent = getIntent(); 339 String action = intent.getAction(); 340 if (INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action) 341 || ACTION_IMAGE_CAPTURE_SECURE.equals(action)) { 342 mSecureCamera = true; 343 } else { 344 mSecureCamera = intent.getBooleanExtra(SECURE_CAMERA_EXTRA, false); 345 } 346 347 if (mSecureCamera) { 348 // Change the window flags so that secure camera can show when locked 349 Window win = getWindow(); 350 WindowManager.LayoutParams params = win.getAttributes(); 351 params.flags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; 352 win.setAttributes(params); 353 354 // Filter for screen off so that we can finish secure camera activity 355 // when screen is off. 356 IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF); 357 registerReceiver(mScreenOffReceiver, filter); 358 // TODO: This static screen off event receiver is a workaround to the 359 // double onResume() invocation (onResume->onPause->onResume). We should 360 // find a better solution to this. 361 if (sScreenOffReceiver == null) { 362 sScreenOffReceiver = new ScreenOffReceiver(); 363 registerReceiver(sScreenOffReceiver, filter); 364 } 365 } 366 mPanoramaManager = new PanoramaStitchingManager(CameraActivity.this); 367 mPanoramaManager.addTaskListener(mStitchingListener); 368 LayoutInflater inflater = getLayoutInflater(); 369 View rootLayout = inflater.inflate(R.layout.camera, null, false); 370 mRootView = rootLayout.findViewById(R.id.camera_app_root); 371 mPanoStitchingPanel = findViewById(R.id.pano_stitching_progress_panel); 372 mBottomProgress = (ProgressBar) findViewById(R.id.pano_stitching_progress_bar); 373 mCameraPreviewData = new CameraPreviewData(rootLayout, 374 FilmStripView.ImageData.SIZE_FULL, 375 FilmStripView.ImageData.SIZE_FULL); 376 // Put a CameraPreviewData at the first position. 377 mWrappedDataAdapter = new FixedFirstDataAdapter( 378 new CameraDataAdapter(new ColorDrawable( 379 getResources().getColor(R.color.photo_placeholder))), 380 mCameraPreviewData); 381 mFilmStripView = (FilmStripView) findViewById(R.id.filmstrip_view); 382 mFilmStripView.setViewGap( 383 getResources().getDimensionPixelSize(R.dimen.camera_film_strip_gap)); 384 mPanoramaViewHelper = new PanoramaViewHelper(this); 385 mPanoramaViewHelper.onCreate(); 386 mFilmStripView.setPanoramaViewHelper(mPanoramaViewHelper); 387 // Set up the camera preview first so the preview shows up ASAP. 388 mFilmStripView.setListener(mFilmStripListener); 389 mCurrentModule = new PhotoModule(); 390 mCurrentModule.init(this, mRootView); 391 mOrientationListener = new MyOrientationEventListener(this); 392 mMainHandler = new Handler(getMainLooper()); 393 bindMediaSaveService(); 394 395 if (!mSecureCamera) { 396 mDataAdapter = mWrappedDataAdapter; 397 mDataAdapter.requestLoad(getContentResolver()); 398 } else { 399 // Put a lock placeholder as the last image by setting its date to 0. 400 ImageView v = (ImageView) getLayoutInflater().inflate( 401 R.layout.secure_album_placeholder, null); 402 mDataAdapter = new FixedLastDataAdapter( 403 mWrappedDataAdapter, 404 new SimpleViewData( 405 v, 406 v.getDrawable().getIntrinsicWidth(), 407 v.getDrawable().getIntrinsicHeight(), 408 0, 0)); 409 // Flush out all the original data. 410 mDataAdapter.flush(); 411 } 412 mFilmStripView.setDataAdapter(mDataAdapter); 413 } 414 415 private void setRotationAnimation() { 416 int rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE; 417 rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE; 418 Window win = getWindow(); 419 WindowManager.LayoutParams winParams = win.getAttributes(); 420 winParams.rotationAnimation = rotationAnimation; 421 win.setAttributes(winParams); 422 } 423 424 @Override 425 public void onUserInteraction() { 426 super.onUserInteraction(); 427 mCurrentModule.onUserInteraction(); 428 } 429 430 @Override 431 public void onPause() { 432 mOrientationListener.disable(); 433 mCurrentModule.onPauseBeforeSuper(); 434 super.onPause(); 435 mCurrentModule.onPauseAfterSuper(); 436 } 437 438 @Override 439 public void onResume() { 440 if (Settings.System.getInt(getContentResolver(), 441 Settings.System.ACCELEROMETER_ROTATION, 0) == 0) {// auto-rotate off 442 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); 443 mAutoRotateScreen = false; 444 } else { 445 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR); 446 mAutoRotateScreen = true; 447 } 448 mOrientationListener.enable(); 449 mCurrentModule.onResumeBeforeSuper(); 450 super.onResume(); 451 mCurrentModule.onResumeAfterSuper(); 452 453 setSwipingEnabled(true); 454 } 455 456 @Override 457 public void onStart() { 458 super.onStart(); 459 460 mPanoramaViewHelper.onStart(); 461 } 462 463 @Override 464 protected void onStop() { 465 super.onStop(); 466 mPanoramaViewHelper.onStop(); 467 } 468 469 @Override 470 public void onDestroy() { 471 unbindMediaSaveService(); 472 if (mSecureCamera) unregisterReceiver(mScreenOffReceiver); 473 super.onDestroy(); 474 } 475 476 @Override 477 public void onConfigurationChanged(Configuration config) { 478 super.onConfigurationChanged(config); 479 mCurrentModule.onConfigurationChanged(config); 480 } 481 482 @Override 483 public boolean onKeyDown(int keyCode, KeyEvent event) { 484 if (mCurrentModule.onKeyDown(keyCode, event)) return true; 485 // Prevent software keyboard or voice search from showing up. 486 if (keyCode == KeyEvent.KEYCODE_SEARCH 487 || keyCode == KeyEvent.KEYCODE_MENU) { 488 if (event.isLongPress()) return true; 489 } 490 if (keyCode == KeyEvent.KEYCODE_MENU && mShowCameraPreview) { 491 return true; 492 } 493 494 return super.onKeyDown(keyCode, event); 495 } 496 497 @Override 498 public boolean onKeyUp(int keyCode, KeyEvent event) { 499 if (mCurrentModule.onKeyUp(keyCode, event)) return true; 500 if (keyCode == KeyEvent.KEYCODE_MENU && mShowCameraPreview) { 501 return true; 502 } 503 return super.onKeyUp(keyCode, event); 504 } 505 506 public boolean isAutoRotateScreen() { 507 return mAutoRotateScreen; 508 } 509 510 protected void updateStorageSpace() { 511 mStorageSpace = Storage.getAvailableSpace(); 512 } 513 514 protected long getStorageSpace() { 515 return mStorageSpace; 516 } 517 518 protected void updateStorageSpaceAndHint() { 519 updateStorageSpace(); 520 updateStorageHint(mStorageSpace); 521 } 522 523 protected void updateStorageHint() { 524 updateStorageHint(mStorageSpace); 525 } 526 527 protected boolean updateStorageHintOnResume() { 528 return true; 529 } 530 531 protected void updateStorageHint(long storageSpace) { 532 String message = null; 533 if (storageSpace == Storage.UNAVAILABLE) { 534 message = getString(R.string.no_storage); 535 } else if (storageSpace == Storage.PREPARING) { 536 message = getString(R.string.preparing_sd); 537 } else if (storageSpace == Storage.UNKNOWN_SIZE) { 538 message = getString(R.string.access_sd_fail); 539 } else if (storageSpace <= Storage.LOW_STORAGE_THRESHOLD) { 540 message = getString(R.string.spaceIsLow_content); 541 } 542 543 if (message != null) { 544 if (mStorageHint == null) { 545 mStorageHint = OnScreenHint.makeText(this, message); 546 } else { 547 mStorageHint.setText(message); 548 } 549 mStorageHint.show(); 550 } else if (mStorageHint != null) { 551 mStorageHint.cancel(); 552 mStorageHint = null; 553 } 554 } 555 556 protected void setResultEx(int resultCode) { 557 mResultCodeForTesting = resultCode; 558 setResult(resultCode); 559 } 560 561 protected void setResultEx(int resultCode, Intent data) { 562 mResultCodeForTesting = resultCode; 563 mResultDataForTesting = data; 564 setResult(resultCode, data); 565 } 566 567 public int getResultCode() { 568 return mResultCodeForTesting; 569 } 570 571 public Intent getResultData() { 572 return mResultDataForTesting; 573 } 574 575 public boolean isSecureCamera() { 576 return mSecureCamera; 577 } 578 579 @Override 580 public void onCameraSelected(int i) { 581 if (mCurrentModuleIndex == i) return; 582 583 CameraHolder.instance().keep(); 584 closeModule(mCurrentModule); 585 mCurrentModuleIndex = i; 586 switch (i) { 587 case CameraSwitcher.VIDEO_MODULE_INDEX: 588 mCurrentModule = new VideoModule(); 589 break; 590 case CameraSwitcher.PHOTO_MODULE_INDEX: 591 mCurrentModule = new PhotoModule(); 592 break; 593 case CameraSwitcher.LIGHTCYCLE_MODULE_INDEX: 594 mCurrentModule = PhotoSphereHelper.createPanoramaModule(); 595 break; 596 case CameraSwitcher.REFOCUS_MODULE_INDEX: 597 mCurrentModule = RefocusHelper.createRefocusModule(); 598 break; 599 default: 600 break; 601 } 602 603 openModule(mCurrentModule); 604 mCurrentModule.onOrientationChanged(mLastRawOrientation); 605 if (mMediaSaveService != null) { 606 mCurrentModule.onMediaSaveServiceConnected(mMediaSaveService); 607 } 608 } 609 610 private void openModule(CameraModule module) { 611 module.init(this, mRootView); 612 module.onResumeBeforeSuper(); 613 module.onResumeAfterSuper(); 614 } 615 616 private void closeModule(CameraModule module) { 617 module.onPauseBeforeSuper(); 618 module.onPauseAfterSuper(); 619 ((ViewGroup) mRootView).removeAllViews(); 620 } 621 622 @Override 623 public void onShowSwitcherPopup() { 624 } 625 626 public void setSwipingEnabled(boolean enable) { 627 mCameraPreviewData.lockPreview(!enable); 628 } 629 630 // Accessor methods for getting latency times used in performance testing 631 public long getAutoFocusTime() { 632 return (mCurrentModule instanceof PhotoModule) ? 633 ((PhotoModule) mCurrentModule).mAutoFocusTime : -1; 634 } 635 636 public long getShutterLag() { 637 return (mCurrentModule instanceof PhotoModule) ? 638 ((PhotoModule) mCurrentModule).mShutterLag : -1; 639 } 640 641 public long getShutterToPictureDisplayedTime() { 642 return (mCurrentModule instanceof PhotoModule) ? 643 ((PhotoModule) mCurrentModule).mShutterToPictureDisplayedTime : -1; 644 } 645 646 public long getPictureDisplayedToJpegCallbackTime() { 647 return (mCurrentModule instanceof PhotoModule) ? 648 ((PhotoModule) mCurrentModule).mPictureDisplayedToJpegCallbackTime : -1; 649 } 650 651 public long getJpegCallbackFinishTime() { 652 return (mCurrentModule instanceof PhotoModule) ? 653 ((PhotoModule) mCurrentModule).mJpegCallbackFinishTime : -1; 654 } 655 656 public long getCaptureStartTime() { 657 return (mCurrentModule instanceof PhotoModule) ? 658 ((PhotoModule) mCurrentModule).mCaptureStartTime : -1; 659 } 660 661 public boolean isRecording() { 662 return (mCurrentModule instanceof VideoModule) ? 663 ((VideoModule) mCurrentModule).isRecording() : false; 664 } 665} 666