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