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