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.content.Context; 20 import android.content.Intent; 21 import android.content.res.Configuration; 22 import android.graphics.drawable.Drawable; 23 import android.os.Bundle; 24 import android.provider.MediaStore; 25 import android.view.KeyEvent; 26 import android.view.LayoutInflater; 27 import android.view.MotionEvent; 28 import android.view.OrientationEventListener; 29 import android.view.View; 30 import android.view.ViewGroup; 31 import android.widget.FrameLayout; 32 33 import com.android.camera.ui.CameraSwitcher; 34 import com.android.gallery3d.app.PhotoPage; 35 import com.android.gallery3d.util.LightCycleHelper; 36 37 public class CameraActivity extends ActivityBase 38 implements CameraSwitcher.CameraSwitchListener { 39 public static final int PHOTO_MODULE_INDEX = 0; 40 public static final int VIDEO_MODULE_INDEX = 1; 41 public static final int PANORAMA_MODULE_INDEX = 2; 42 public static final int LIGHTCYCLE_MODULE_INDEX = 3; 43 44 CameraModule mCurrentModule; 45 private FrameLayout mFrame; 46 private ShutterButton mShutter; 47 private CameraSwitcher mSwitcher; 48 private View mShutterSwitcher; 49 private View mControlsBackground; 50 private Drawable[] mDrawables; 51 private int mCurrentModuleIndex; 52 private MotionEvent mDown; 53 54 private MyOrientationEventListener mOrientationListener; 55 // The degrees of the device rotated clockwise from its natural orientation. 56 private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN; 57 // The orientation compensation for icons. Eg: if the value 58 // is 90, the UI components should be rotated 90 degrees counter-clockwise. 59 private int mOrientationCompensation = 0; 60 61 private static final String TAG = "CAM_activity"; 62 63 private static final int[] DRAW_IDS = { 64 R.drawable.ic_switch_camera, 65 R.drawable.ic_switch_video, 66 R.drawable.ic_switch_pan, 67 R.drawable.ic_switch_photosphere 68 }; 69 70 @Override 71 public void onCreate(Bundle state) { 72 super.onCreate(state); 73 setContentView(R.layout.camera_main); 74 mFrame =(FrameLayout) findViewById(R.id.main_content); 75 mDrawables = new Drawable[DRAW_IDS.length]; 76 for (int i = 0; i < DRAW_IDS.length; i++) { 77 mDrawables[i] = getResources().getDrawable(DRAW_IDS[i]); 78 } 79 init(); 80 if (MediaStore.INTENT_ACTION_VIDEO_CAMERA.equals(getIntent().getAction()) 81 || MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction())) { 82 mCurrentModule = new VideoModule(); 83 mCurrentModuleIndex = VIDEO_MODULE_INDEX; 84 } else { 85 mCurrentModule = new PhotoModule(); 86 mCurrentModuleIndex = PHOTO_MODULE_INDEX; 87 } 88 mCurrentModule.init(this, mFrame, true); 89 mSwitcher.setCurrentIndex(mCurrentModuleIndex); 90 mOrientationListener = new MyOrientationEventListener(this); 91 } 92 93 public void init() { 94 mControlsBackground = findViewById(R.id.controls); 95 mShutterSwitcher = findViewById(R.id.camera_shutter_switcher); 96 mShutter = (ShutterButton) findViewById(R.id.shutter_button); 97 mSwitcher = (CameraSwitcher) findViewById(R.id.camera_switcher); 98 mSwitcher.setDrawIds(DRAW_IDS); 99 int[] drawids = new int[LightCycleHelper.hasLightCycleCapture(this) 100 ? DRAW_IDS.length : DRAW_IDS.length - 1]; 101 int ix = 0; 102 for (int i = 0; i < mDrawables.length; i++) { 103 if (i == LIGHTCYCLE_MODULE_INDEX && !LightCycleHelper.hasLightCycleCapture(this)) { 104 continue; // not enabled, so don't add to UI 105 } 106 drawids[ix++] = DRAW_IDS[i]; 107 } 108 mSwitcher.setDrawIds(drawids); 109 mSwitcher.setSwitchListener(this); 110 mSwitcher.setCurrentIndex(mCurrentModuleIndex); 111 } 112 113 private class MyOrientationEventListener 114 extends OrientationEventListener { 115 public MyOrientationEventListener(Context context) { 116 super(context); 117 } 118 119 @Override 120 public void onOrientationChanged(int orientation) { 121 // We keep the last known orientation. So if the user first orient 122 // the camera then point the camera to floor or sky, we still have 123 // the correct orientation. 124 if (orientation == ORIENTATION_UNKNOWN) return; 125 mOrientation = Util.roundOrientation(orientation, mOrientation); 126 // When the screen is unlocked, display rotation may change. Always 127 // calculate the up-to-date orientationCompensation. 128 int orientationCompensation = 129 (mOrientation + Util.getDisplayRotation(CameraActivity.this)) % 360; 130 // Rotate camera mode icons in the switcher 131 if (mOrientationCompensation != orientationCompensation) { 132 mOrientationCompensation = orientationCompensation; 133 } 134 mCurrentModule.onOrientationChanged(orientation); 135 } 136 } 137 138 @Override 139 public void onCameraSelected(int i) { 140 if (mPaused) return; 141 if (i != mCurrentModuleIndex) { 142 mPaused = true; 143 boolean canReuse = canReuseScreenNail(); 144 CameraHolder.instance().keep(); 145 closeModule(mCurrentModule); 146 mCurrentModuleIndex = i; 147 switch (i) { 148 case VIDEO_MODULE_INDEX: 149 mCurrentModule = new VideoModule(); 150 break; 151 case PHOTO_MODULE_INDEX: 152 mCurrentModule = new PhotoModule(); 153 break; 154 case PANORAMA_MODULE_INDEX: 155 mCurrentModule = new PanoramaModule(); 156 break; 157 case LIGHTCYCLE_MODULE_INDEX: 158 mCurrentModule = LightCycleHelper.createPanoramaModule(); 159 break; 160 } 161 openModule(mCurrentModule, canReuse); 162 mCurrentModule.onOrientationChanged(mOrientation); 163 } 164 } 165 166 @Override 167 public void onShowSwitcherPopup() { 168 mCurrentModule.onShowSwitcherPopup(); 169 } 170 171 private void openModule(CameraModule module, boolean canReuse) { 172 module.init(this, mFrame, canReuse && canReuseScreenNail()); 173 mPaused = false; 174 module.onResumeBeforeSuper(); 175 module.onResumeAfterSuper(); 176 } 177 178 private void closeModule(CameraModule module) { 179 module.onPauseBeforeSuper(); 180 module.onPauseAfterSuper(); 181 mFrame.removeAllViews(); 182 } 183 184 public ShutterButton getShutterButton() { 185 return mShutter; 186 } 187 188 public void hideUI() { 189 mControlsBackground.setVisibility(View.INVISIBLE); 190 hideSwitcher(); 191 mShutter.setVisibility(View.GONE); 192 } 193 194 public void showUI() { 195 mControlsBackground.setVisibility(View.VISIBLE); 196 showSwitcher(); 197 mShutter.setVisibility(View.VISIBLE); 198 } 199 200 public void hideSwitcher() { 201 mSwitcher.closePopup(); 202 mSwitcher.setVisibility(View.INVISIBLE); 203 } 204 205 public void showSwitcher() { 206 if (mCurrentModule.needsSwitcher()) { 207 mSwitcher.setVisibility(View.VISIBLE); 208 } 209 } 210 211 public boolean isInCameraApp() { 212 return mShowCameraAppView; 213 } 214 215 @Override 216 public void onConfigurationChanged(Configuration config) { 217 super.onConfigurationChanged(config); 218 219 ViewGroup appRoot = (ViewGroup) findViewById(R.id.content); 220 // remove old switcher, shutter and shutter icon 221 View cameraControlsView = findViewById(R.id.camera_shutter_switcher); 222 appRoot.removeView(cameraControlsView); 223 224 // create new layout with the current orientation 225 LayoutInflater inflater = getLayoutInflater(); 226 inflater.inflate(R.layout.camera_shutter_switcher, appRoot); 227 init(); 228 229 if (mShowCameraAppView) { 230 showUI(); 231 } else { 232 hideUI(); 233 } 234 mCurrentModule.onConfigurationChanged(config); 235 } 236 237 @Override 238 public void onPause() { 239 mPaused = true; 240 mOrientationListener.disable(); 241 mCurrentModule.onPauseBeforeSuper(); 242 super.onPause(); 243 mCurrentModule.onPauseAfterSuper(); 244 } 245 246 @Override 247 public void onResume() { 248 mPaused = false; 249 mOrientationListener.enable(); 250 mCurrentModule.onResumeBeforeSuper(); 251 super.onResume(); 252 mCurrentModule.onResumeAfterSuper(); 253 } 254 255 @Override 256 protected void onFullScreenChanged(boolean full) { 257 if (full) { 258 showUI(); 259 } else { 260 hideUI(); 261 } 262 super.onFullScreenChanged(full); 263 mCurrentModule.onFullScreenChanged(full); 264 } 265 266 @Override 267 protected void onStop() { 268 super.onStop(); 269 mCurrentModule.onStop(); 270 getStateManager().clearTasks(); 271 } 272 273 @Override 274 protected void onNewIntent(Intent intent) { 275 super.onNewIntent(intent); 276 getStateManager().clearActivityResult(); 277 } 278 279 @Override 280 protected void installIntentFilter() { 281 super.installIntentFilter(); 282 mCurrentModule.installIntentFilter(); 283 } 284 285 @Override 286 protected void onActivityResult( 287 int requestCode, int resultCode, Intent data) { 288 // Only PhotoPage understands ProxyLauncher.RESULT_USER_CANCELED 289 if (resultCode == ProxyLauncher.RESULT_USER_CANCELED 290 && !(getStateManager().getTopState() instanceof PhotoPage)) { 291 resultCode = RESULT_CANCELED; 292 } 293 super.onActivityResult(requestCode, resultCode, data); 294 // Unmap cancel vs. reset 295 if (resultCode == ProxyLauncher.RESULT_USER_CANCELED) { 296 resultCode = RESULT_CANCELED; 297 } 298 mCurrentModule.onActivityResult(requestCode, resultCode, data); 299 } 300 301 // Preview area is touched. Handle touch focus. 302 @Override 303 protected void onSingleTapUp(View view, int x, int y) { 304 mCurrentModule.onSingleTapUp(view, x, y); 305 } 306 307 @Override 308 public void onBackPressed() { 309 if (!mCurrentModule.onBackPressed()) { 310 super.onBackPressed(); 311 } 312 } 313 314 @Override 315 public boolean onKeyDown(int keyCode, KeyEvent event) { 316 return mCurrentModule.onKeyDown(keyCode, event) 317 || super.onKeyDown(keyCode, event); 318 } 319 320 @Override 321 public boolean onKeyUp(int keyCode, KeyEvent event) { 322 return mCurrentModule.onKeyUp(keyCode, event) 323 || super.onKeyUp(keyCode, event); 324 } 325 326 public void cancelActivityTouchHandling() { 327 if (mDown != null) { 328 MotionEvent cancel = MotionEvent.obtain(mDown); 329 cancel.setAction(MotionEvent.ACTION_CANCEL); 330 super.dispatchTouchEvent(cancel); 331 } 332 } 333 334 @Override 335 public boolean dispatchTouchEvent(MotionEvent m) { 336 if (m.getActionMasked() == MotionEvent.ACTION_DOWN) { 337 mDown = m; 338 } 339 if ((mSwitcher != null) && mSwitcher.showsPopup() && !mSwitcher.isInsidePopup(m)) { 340 return mSwitcher.onTouch(null, m); 341 } else { 342 return mShutterSwitcher.dispatchTouchEvent(m) 343 || mCurrentModule.dispatchTouchEvent(m); 344 } 345 } 346 347 @Override 348 public void startActivityForResult(Intent intent, int requestCode) { 349 Intent proxyIntent = new Intent(this, ProxyLauncher.class); 350 proxyIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); 351 proxyIntent.putExtra(Intent.EXTRA_INTENT, intent); 352 super.startActivityForResult(proxyIntent, requestCode); 353 } 354 355 public boolean superDispatchTouchEvent(MotionEvent m) { 356 return super.dispatchTouchEvent(m); 357 } 358 359 // Preview texture has been copied. Now camera can be released and the 360 // animation can be started. 361 @Override 362 public void onPreviewTextureCopied() { 363 mCurrentModule.onPreviewTextureCopied(); 364 } 365 366 @Override 367 public void onCaptureTextureCopied() { 368 mCurrentModule.onCaptureTextureCopied(); 369 } 370 371 @Override 372 public void onUserInteraction() { 373 super.onUserInteraction(); 374 mCurrentModule.onUserInteraction(); 375 } 376 377 @Override 378 protected boolean updateStorageHintOnResume() { 379 return mCurrentModule.updateStorageHintOnResume(); 380 } 381 382 @Override 383 public void updateCameraAppView() { 384 super.updateCameraAppView(); 385 mCurrentModule.updateCameraAppView(); 386 } 387 388 private boolean canReuseScreenNail() { 389 return mCurrentModuleIndex == PHOTO_MODULE_INDEX 390 || mCurrentModuleIndex == VIDEO_MODULE_INDEX; 391 } 392 393 @Override 394 public boolean isPanoramaActivity() { 395 return (mCurrentModuleIndex == PANORAMA_MODULE_INDEX); 396 } 397 398 // Accessor methods for getting latency times used in performance testing 399 public long getAutoFocusTime() { 400 return (mCurrentModule instanceof PhotoModule) ? 401 ((PhotoModule)mCurrentModule).mAutoFocusTime : -1; 402 } 403 404 public long getShutterLag() { 405 return (mCurrentModule instanceof PhotoModule) ? 406 ((PhotoModule)mCurrentModule).mShutterLag : -1; 407 } 408 409 public long getShutterToPictureDisplayedTime() { 410 return (mCurrentModule instanceof PhotoModule) ? 411 ((PhotoModule)mCurrentModule).mShutterToPictureDisplayedTime : -1; 412 } 413 414 public long getPictureDisplayedToJpegCallbackTime() { 415 return (mCurrentModule instanceof PhotoModule) ? 416 ((PhotoModule)mCurrentModule).mPictureDisplayedToJpegCallbackTime : -1; 417 } 418 419 public long getJpegCallbackFinishTime() { 420 return (mCurrentModule instanceof PhotoModule) ? 421 ((PhotoModule)mCurrentModule).mJpegCallbackFinishTime : -1; 422 } 423 424 public long getCaptureStartTime() { 425 return (mCurrentModule instanceof PhotoModule) ? 426 ((PhotoModule)mCurrentModule).mCaptureStartTime : -1; 427 } 428 429 public boolean isRecording() { 430 return (mCurrentModule instanceof VideoModule) ? 431 ((VideoModule) mCurrentModule).isRecording() : false; 432 } 433 434 public CameraScreenNail getCameraScreenNail() { 435 return (CameraScreenNail) mCameraScreenNail; 436 } 437 } 438