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