CameraActivity.java revision 9cdfe00cf57f05f81e6d02ca050e6afd8cc4a25f
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.animation.Animator; 20import android.animation.AnimatorListenerAdapter; 21import android.animation.ObjectAnimator; 22import android.content.ComponentName; 23import android.content.Context; 24import android.content.Intent; 25import android.content.ServiceConnection; 26import android.content.pm.ActivityInfo; 27import android.content.res.Configuration; 28import android.graphics.drawable.Drawable; 29import android.os.Bundle; 30import android.os.IBinder; 31import android.provider.MediaStore; 32import android.provider.Settings; 33import android.view.KeyEvent; 34import android.view.LayoutInflater; 35import android.view.MotionEvent; 36import android.view.OrientationEventListener; 37import android.view.View; 38import android.view.ViewGroup; 39import android.view.Window; 40import android.view.WindowManager; 41import android.widget.FrameLayout; 42 43import com.android.camera.ui.CameraSwitcher; 44import com.android.camera.ui.RotatableLayout; 45import com.android.gallery3d.R; 46import com.android.gallery3d.app.PhotoPage; 47import com.android.gallery3d.common.ApiHelper; 48import com.android.gallery3d.util.LightCycleHelper; 49 50public class CameraActivity extends ActivityBase 51 implements CameraSwitcher.CameraSwitchListener { 52 public static final int PHOTO_MODULE_INDEX = 0; 53 public static final int VIDEO_MODULE_INDEX = 1; 54 public static final int PANORAMA_MODULE_INDEX = 2; 55 public static final int LIGHTCYCLE_MODULE_INDEX = 3; 56 57 CameraModule mCurrentModule; 58 private FrameLayout mFrame; 59 private ShutterButton mShutter; 60 private CameraSwitcher mSwitcher; 61 private View mCameraControls; 62 private View mControlsBackground; 63 private View mPieMenuButton; 64 private Drawable[] mDrawables; 65 private int mCurrentModuleIndex; 66 private MotionEvent mDown; 67 private boolean mAutoRotateScreen; 68 private int mHeightOrWidth = -1; 69 70 private MyOrientationEventListener mOrientationListener; 71 // The degrees of the device rotated clockwise from its natural orientation. 72 private int mLastRawOrientation = OrientationEventListener.ORIENTATION_UNKNOWN; 73 74 private MediaSaveService mMediaSaveService; 75 private ServiceConnection mConnection = new ServiceConnection() { 76 @Override 77 public void onServiceConnected(ComponentName className, IBinder b) { 78 mMediaSaveService = ((MediaSaveService.LocalBinder) b).getService(); 79 mCurrentModule.onMediaSaveServiceConnected(mMediaSaveService); 80 } 81 @Override 82 public void onServiceDisconnected(ComponentName className) { 83 mMediaSaveService = null; 84 }}; 85 86 private static final String TAG = "CAM_activity"; 87 88 private static final int[] DRAW_IDS = { 89 R.drawable.ic_switch_camera, 90 R.drawable.ic_switch_video, 91 R.drawable.ic_switch_pan, 92 R.drawable.ic_switch_photosphere 93 }; 94 95 @Override 96 public void onCreate(Bundle state) { 97 super.onCreate(state); 98 setContentView(R.layout.camera_main); 99 mFrame = (FrameLayout) findViewById(R.id.camera_app_root); 100 mDrawables = new Drawable[DRAW_IDS.length]; 101 for (int i = 0; i < DRAW_IDS.length; i++) { 102 mDrawables[i] = getResources().getDrawable(DRAW_IDS[i]); 103 } 104 init(); 105 if (MediaStore.INTENT_ACTION_VIDEO_CAMERA.equals(getIntent().getAction()) 106 || MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction())) { 107 mCurrentModule = new VideoModule(); 108 mCurrentModuleIndex = VIDEO_MODULE_INDEX; 109 } else { 110 mCurrentModule = new PhotoModule(); 111 mCurrentModuleIndex = PHOTO_MODULE_INDEX; 112 } 113 mCurrentModule.init(this, mFrame, true); 114 mSwitcher.setCurrentIndex(mCurrentModuleIndex); 115 mOrientationListener = new MyOrientationEventListener(this); 116 bindMediaSaveService(); 117 } 118 119 public void init() { 120 boolean landscape = Util.getDisplayRotation(this) % 180 == 90; 121 mControlsBackground = findViewById(R.id.blocker); 122 mCameraControls = findViewById(R.id.camera_controls); 123 mShutter = (ShutterButton) findViewById(R.id.shutter_button); 124 mSwitcher = (CameraSwitcher) findViewById(R.id.camera_switcher); 125 mPieMenuButton = findViewById(R.id.menu); 126 int totaldrawid = (LightCycleHelper.hasLightCycleCapture(this) 127 ? DRAW_IDS.length : DRAW_IDS.length - 1); 128 if (!ApiHelper.HAS_OLD_PANORAMA) totaldrawid--; 129 130 int[] drawids = new int[totaldrawid]; 131 int[] moduleids = new int[totaldrawid]; 132 int ix = 0; 133 for (int i = 0; i < mDrawables.length; i++) { 134 if (i == PANORAMA_MODULE_INDEX && !ApiHelper.HAS_OLD_PANORAMA) { 135 continue; // not enabled, so don't add to UI 136 } 137 if (i == LIGHTCYCLE_MODULE_INDEX && !LightCycleHelper.hasLightCycleCapture(this)) { 138 continue; // not enabled, so don't add to UI 139 } 140 moduleids[ix] = i; 141 drawids[ix++] = DRAW_IDS[i]; 142 } 143 mSwitcher.setIds(moduleids, drawids); 144 mSwitcher.setSwitchListener(this); 145 mSwitcher.setCurrentIndex(mCurrentModuleIndex); 146 } 147 148 @Override 149 public void onDestroy() { 150 unbindMediaSaveService(); 151 super.onDestroy(); 152 } 153 154 // Return whether the auto-rotate screen in system settings 155 // is turned on. 156 public boolean isAutoRotateScreen() { 157 return mAutoRotateScreen; 158 } 159 160 private class MyOrientationEventListener 161 extends OrientationEventListener { 162 public MyOrientationEventListener(Context context) { 163 super(context); 164 } 165 166 @Override 167 public void onOrientationChanged(int orientation) { 168 // We keep the last known orientation. So if the user first orient 169 // the camera then point the camera to floor or sky, we still have 170 // the correct orientation. 171 if (orientation == ORIENTATION_UNKNOWN) return; 172 mLastRawOrientation = orientation; 173 mCurrentModule.onOrientationChanged(orientation); 174 } 175 } 176 177 private ObjectAnimator mCameraSwitchAnimator; 178 179 @Override 180 public void onCameraSelected(final int i) { 181 if (mPaused) return; 182 if (i != mCurrentModuleIndex) { 183 mPaused = true; 184 CameraScreenNail screenNail = getCameraScreenNail(); 185 if (screenNail != null) { 186 if (mCameraSwitchAnimator != null && mCameraSwitchAnimator.isRunning()) { 187 mCameraSwitchAnimator.cancel(); 188 } 189 mCameraSwitchAnimator = ObjectAnimator.ofFloat( 190 screenNail, "alpha", screenNail.getAlpha(), 0f); 191 mCameraSwitchAnimator.addListener(new AnimatorListenerAdapter() { 192 @Override 193 public void onAnimationEnd(Animator animation) { 194 super.onAnimationEnd(animation); 195 doChangeCamera(i); 196 } 197 }); 198 mCameraSwitchAnimator.start(); 199 } else { 200 doChangeCamera(i); 201 } 202 } 203 } 204 205 private void doChangeCamera(int i) { 206 boolean canReuse = canReuseScreenNail(); 207 CameraHolder.instance().keep(); 208 closeModule(mCurrentModule); 209 mCurrentModuleIndex = i; 210 switch (i) { 211 case VIDEO_MODULE_INDEX: 212 mCurrentModule = new VideoModule(); 213 break; 214 case PHOTO_MODULE_INDEX: 215 mCurrentModule = new PhotoModule(); 216 break; 217 case PANORAMA_MODULE_INDEX: 218 mCurrentModule = new PanoramaModule(); 219 break; 220 case LIGHTCYCLE_MODULE_INDEX: 221 mCurrentModule = LightCycleHelper.createPanoramaModule(); 222 break; 223 } 224 showPieMenuButton(mCurrentModule.needsPieMenu()); 225 226 openModule(mCurrentModule, canReuse); 227 mCurrentModule.onOrientationChanged(mLastRawOrientation); 228 if (mMediaSaveService != null) { 229 mCurrentModule.onMediaSaveServiceConnected(mMediaSaveService); 230 } 231 getCameraScreenNail().setAlpha(0f); 232 getCameraScreenNail().setOnFrameDrawnOneShot(mOnFrameDrawn); 233 } 234 235 public void showPieMenuButton(boolean show) { 236 if (show) { 237 findViewById(R.id.blocker).setVisibility(View.VISIBLE); 238 findViewById(R.id.menu).setVisibility(View.VISIBLE); 239 findViewById(R.id.on_screen_indicators).setVisibility(View.VISIBLE); 240 } else { 241 findViewById(R.id.blocker).setVisibility(View.INVISIBLE); 242 findViewById(R.id.menu).setVisibility(View.INVISIBLE); 243 findViewById(R.id.on_screen_indicators).setVisibility(View.INVISIBLE); 244 } 245 } 246 247 private Runnable mOnFrameDrawn = new Runnable() { 248 249 @Override 250 public void run() { 251 runOnUiThread(mFadeInCameraScreenNail); 252 } 253 }; 254 255 private Runnable mFadeInCameraScreenNail = new Runnable() { 256 257 @Override 258 public void run() { 259 mCameraSwitchAnimator = ObjectAnimator.ofFloat( 260 getCameraScreenNail(), "alpha", 0f, 1f); 261 mCameraSwitchAnimator.setStartDelay(50); 262 mCameraSwitchAnimator.start(); 263 } 264 }; 265 266 @Override 267 public void onShowSwitcherPopup() { 268 mCurrentModule.onShowSwitcherPopup(); 269 } 270 271 private void openModule(CameraModule module, boolean canReuse) { 272 module.init(this, mFrame, canReuse && canReuseScreenNail()); 273 mPaused = false; 274 module.onResumeBeforeSuper(); 275 module.onResumeAfterSuper(); 276 } 277 278 private void closeModule(CameraModule module) { 279 module.onPauseBeforeSuper(); 280 module.onPauseAfterSuper(); 281 mFrame.removeAllViews(); 282 } 283 284 public ShutterButton getShutterButton() { 285 return mShutter; 286 } 287 288 public void hideUI() { 289 mCameraControls.setVisibility(View.INVISIBLE); 290 hideSwitcher(); 291 mShutter.setVisibility(View.GONE); 292 } 293 294 public void showUI() { 295 mCameraControls.setVisibility(View.VISIBLE); 296 showSwitcher(); 297 mShutter.setVisibility(View.VISIBLE); 298 // Force a layout change to show shutter button 299 mShutter.requestLayout(); 300 } 301 302 public void hideSwitcher() { 303 mSwitcher.closePopup(); 304 mSwitcher.setVisibility(View.INVISIBLE); 305 } 306 307 public void showSwitcher() { 308 if (mCurrentModule.needsSwitcher()) { 309 mSwitcher.setVisibility(View.VISIBLE); 310 } 311 } 312 313 public boolean isInCameraApp() { 314 return mShowCameraAppView; 315 } 316 317 @Override 318 public void onConfigurationChanged(Configuration config) { 319 super.onConfigurationChanged(config); 320 mCurrentModule.onConfigurationChanged(config); 321 } 322 323 @Override 324 public void onPause() { 325 mPaused = true; 326 mOrientationListener.disable(); 327 mCurrentModule.onPauseBeforeSuper(); 328 super.onPause(); 329 mCurrentModule.onPauseAfterSuper(); 330 } 331 332 @Override 333 public void onResume() { 334 mPaused = false; 335 if (Settings.System.getInt(getContentResolver(), 336 Settings.System.ACCELEROMETER_ROTATION, 0) == 0) {// auto-rotate off 337 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); 338 mAutoRotateScreen = false; 339 } else { 340 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR); 341 mAutoRotateScreen = true; 342 } 343 mOrientationListener.enable(); 344 mCurrentModule.onResumeBeforeSuper(); 345 super.onResume(); 346 mCurrentModule.onResumeAfterSuper(); 347 } 348 349 private void bindMediaSaveService() { 350 Intent intent = new Intent(this, MediaSaveService.class); 351 startService(intent); // start service before binding it so the 352 // service won't be killed if we unbind it. 353 bindService(intent, mConnection, Context.BIND_AUTO_CREATE); 354 } 355 356 private void unbindMediaSaveService() { 357 if (mMediaSaveService != null) { 358 mMediaSaveService.setListener(null); 359 } 360 if (mConnection != null) { 361 unbindService(mConnection); 362 } 363 } 364 365 @Override 366 protected void onFullScreenChanged(boolean full) { 367 if (full) { 368 showUI(); 369 } else { 370 hideUI(); 371 } 372 super.onFullScreenChanged(full); 373 if (ApiHelper.HAS_ROTATION_ANIMATION) { 374 setRotationAnimation(full); 375 } 376 mCurrentModule.onFullScreenChanged(full); 377 } 378 379 private void setRotationAnimation(boolean fullscreen) { 380 int rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE; 381 if (fullscreen) { 382 rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE; 383 } 384 Window win = getWindow(); 385 WindowManager.LayoutParams winParams = win.getAttributes(); 386 winParams.rotationAnimation = rotationAnimation; 387 win.setAttributes(winParams); 388 } 389 390 @Override 391 protected void onStop() { 392 super.onStop(); 393 mCurrentModule.onStop(); 394 getStateManager().clearTasks(); 395 } 396 397 @Override 398 protected void onNewIntent(Intent intent) { 399 super.onNewIntent(intent); 400 getStateManager().clearActivityResult(); 401 } 402 403 @Override 404 protected void installIntentFilter() { 405 super.installIntentFilter(); 406 mCurrentModule.installIntentFilter(); 407 } 408 409 @Override 410 protected void onActivityResult( 411 int requestCode, int resultCode, Intent data) { 412 // Only PhotoPage understands ProxyLauncher.RESULT_USER_CANCELED 413 if (resultCode == ProxyLauncher.RESULT_USER_CANCELED 414 && !(getStateManager().getTopState() instanceof PhotoPage)) { 415 resultCode = RESULT_CANCELED; 416 } 417 super.onActivityResult(requestCode, resultCode, data); 418 // Unmap cancel vs. reset 419 if (resultCode == ProxyLauncher.RESULT_USER_CANCELED) { 420 resultCode = RESULT_CANCELED; 421 } 422 mCurrentModule.onActivityResult(requestCode, resultCode, data); 423 } 424 425 // Preview area is touched. Handle touch focus. 426 // Touch to focus is handled by PreviewGestures, this function call 427 // is no longer needed. TODO: Clean it up in the next refactor 428 @Override 429 protected void onSingleTapUp(View view, int x, int y) { 430 } 431 432 @Override 433 public void onBackPressed() { 434 if (!mCurrentModule.onBackPressed()) { 435 super.onBackPressed(); 436 } 437 } 438 439 @Override 440 public boolean onKeyDown(int keyCode, KeyEvent event) { 441 return mCurrentModule.onKeyDown(keyCode, event) 442 || super.onKeyDown(keyCode, event); 443 } 444 445 @Override 446 public boolean onKeyUp(int keyCode, KeyEvent event) { 447 return mCurrentModule.onKeyUp(keyCode, event) 448 || super.onKeyUp(keyCode, event); 449 } 450 451 public void cancelActivityTouchHandling() { 452 if (mDown != null) { 453 MotionEvent cancel = MotionEvent.obtain(mDown); 454 cancel.setAction(MotionEvent.ACTION_CANCEL); 455 super.dispatchTouchEvent(cancel); 456 } 457 } 458 459 @Override 460 public boolean dispatchTouchEvent(MotionEvent m) { 461 if (m.getActionMasked() == MotionEvent.ACTION_DOWN) { 462 mDown = m; 463 } 464 if ((mSwitcher != null) && mSwitcher.showsPopup() && !mSwitcher.isInsidePopup(m)) { 465 return mSwitcher.onTouch(null, m); 466 } else if ((mSwitcher != null) && mSwitcher.isInsidePopup(m)) { 467 return superDispatchTouchEvent(m); 468 } else { 469 return mCurrentModule.dispatchTouchEvent(m); 470 } 471 } 472 473 @Override 474 public void startActivityForResult(Intent intent, int requestCode) { 475 Intent proxyIntent = new Intent(this, ProxyLauncher.class); 476 proxyIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); 477 proxyIntent.putExtra(Intent.EXTRA_INTENT, intent); 478 super.startActivityForResult(proxyIntent, requestCode); 479 } 480 481 public boolean superDispatchTouchEvent(MotionEvent m) { 482 return super.dispatchTouchEvent(m); 483 } 484 485 // Preview texture has been copied. Now camera can be released and the 486 // animation can be started. 487 @Override 488 public void onPreviewTextureCopied() { 489 mCurrentModule.onPreviewTextureCopied(); 490 } 491 492 @Override 493 public void onCaptureTextureCopied() { 494 mCurrentModule.onCaptureTextureCopied(); 495 } 496 497 @Override 498 public void onUserInteraction() { 499 super.onUserInteraction(); 500 mCurrentModule.onUserInteraction(); 501 } 502 503 @Override 504 protected boolean updateStorageHintOnResume() { 505 return mCurrentModule.updateStorageHintOnResume(); 506 } 507 508 @Override 509 public void updateCameraAppView() { 510 super.updateCameraAppView(); 511 mCurrentModule.updateCameraAppView(); 512 } 513 514 private boolean canReuseScreenNail() { 515 return mCurrentModuleIndex == PHOTO_MODULE_INDEX 516 || mCurrentModuleIndex == VIDEO_MODULE_INDEX 517 || mCurrentModuleIndex == LIGHTCYCLE_MODULE_INDEX; 518 } 519 520 @Override 521 public boolean isPanoramaActivity() { 522 return (mCurrentModuleIndex == PANORAMA_MODULE_INDEX); 523 } 524 525 // Accessor methods for getting latency times used in performance testing 526 public long getAutoFocusTime() { 527 return (mCurrentModule instanceof PhotoModule) ? 528 ((PhotoModule) mCurrentModule).mAutoFocusTime : -1; 529 } 530 531 public long getShutterLag() { 532 return (mCurrentModule instanceof PhotoModule) ? 533 ((PhotoModule) mCurrentModule).mShutterLag : -1; 534 } 535 536 public long getShutterToPictureDisplayedTime() { 537 return (mCurrentModule instanceof PhotoModule) ? 538 ((PhotoModule) mCurrentModule).mShutterToPictureDisplayedTime : -1; 539 } 540 541 public long getPictureDisplayedToJpegCallbackTime() { 542 return (mCurrentModule instanceof PhotoModule) ? 543 ((PhotoModule) mCurrentModule).mPictureDisplayedToJpegCallbackTime : -1; 544 } 545 546 public long getJpegCallbackFinishTime() { 547 return (mCurrentModule instanceof PhotoModule) ? 548 ((PhotoModule) mCurrentModule).mJpegCallbackFinishTime : -1; 549 } 550 551 public long getCaptureStartTime() { 552 return (mCurrentModule instanceof PhotoModule) ? 553 ((PhotoModule) mCurrentModule).mCaptureStartTime : -1; 554 } 555 556 public boolean isRecording() { 557 return (mCurrentModule instanceof VideoModule) ? 558 ((VideoModule) mCurrentModule).isRecording() : false; 559 } 560 561 public CameraScreenNail getCameraScreenNail() { 562 return (CameraScreenNail) mCameraScreenNail; 563 } 564 565 public MediaSaveService getMediaSaveService() { 566 return mMediaSaveService; 567 } 568} 569