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