Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2014 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.app.Activity; |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 20 | import android.content.Context; |
| 21 | import android.content.res.Configuration; |
| 22 | import android.graphics.Bitmap; |
| 23 | import android.graphics.Matrix; |
| 24 | import android.graphics.RectF; |
| 25 | import android.graphics.SurfaceTexture; |
| 26 | import android.hardware.Sensor; |
| 27 | import android.hardware.SensorEvent; |
| 28 | import android.hardware.SensorEventListener; |
| 29 | import android.hardware.SensorManager; |
| 30 | import android.net.Uri; |
| 31 | import android.os.Handler; |
| 32 | import android.provider.MediaStore; |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 33 | import android.view.KeyEvent; |
| 34 | import android.view.OrientationEventListener; |
| 35 | import android.view.Surface; |
| 36 | import android.view.TextureView; |
| 37 | import android.view.View; |
| 38 | import android.view.View.OnLayoutChangeListener; |
| 39 | |
| 40 | import com.android.camera.app.AppController; |
| 41 | import com.android.camera.app.CameraAppUI; |
| 42 | import com.android.camera.app.CameraAppUI.BottomBarUISpec; |
| 43 | import com.android.camera.app.MediaSaver; |
| 44 | import com.android.camera.debug.Log; |
| 45 | import com.android.camera.debug.Log.Tag; |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 46 | import com.android.camera.hardware.HardwareSpec; |
| 47 | import com.android.camera.module.ModuleController; |
| 48 | import com.android.camera.one.OneCamera; |
Andy Huibers | 30ffce0 | 2014-07-31 16:31:43 -0700 | [diff] [blame] | 49 | import com.android.camera.one.OneCamera.AutoFocusMode; |
| 50 | import com.android.camera.one.OneCamera.AutoFocusState; |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 51 | import com.android.camera.one.OneCamera.CaptureReadyCallback; |
| 52 | import com.android.camera.one.OneCamera.Facing; |
| 53 | import com.android.camera.one.OneCamera.OpenCallback; |
| 54 | import com.android.camera.one.OneCamera.PhotoCaptureParameters; |
Sascha Haeberling | 0cf4a02 | 2014-07-31 11:36:58 -0700 | [diff] [blame] | 55 | import com.android.camera.one.OneCamera.PhotoCaptureParameters.Flash; |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 56 | import com.android.camera.one.OneCameraManager; |
| 57 | import com.android.camera.remote.RemoteCameraModule; |
| 58 | import com.android.camera.session.CaptureSession; |
| 59 | import com.android.camera.settings.Keys; |
| 60 | import com.android.camera.settings.ResolutionUtil; |
| 61 | import com.android.camera.settings.SettingsManager; |
| 62 | import com.android.camera.ui.PreviewStatusListener; |
| 63 | import com.android.camera.ui.TouchCoordinate; |
| 64 | import com.android.camera.util.CameraUtil; |
Sascha Haeberling | e3dfd5a | 2014-08-05 15:54:42 -0700 | [diff] [blame] | 65 | import com.android.camera.util.Size; |
Andy Huibers | 30ffce0 | 2014-07-31 16:31:43 -0700 | [diff] [blame] | 66 | import com.android.camera.util.SystemProperties; |
| 67 | import com.android.camera.util.UsageStatistics; |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 68 | import com.android.camera2.R; |
| 69 | import com.android.ex.camera2.portability.CameraAgent.CameraProxy; |
| 70 | |
Sascha Haeberling | 59c784b | 2014-08-05 10:53:08 -0700 | [diff] [blame] | 71 | import java.io.File; |
| 72 | |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 73 | /** |
| 74 | * New Capture module that is made to support photo and video capture on top of |
| 75 | * the OneCamera API, to transparently support GCam. |
| 76 | * <p> |
| 77 | * This has been a re-write with pieces taken and improved from GCamModule and |
| 78 | * PhotoModule, which are to be retired eventually. |
| 79 | * <p> |
| 80 | * TODO: |
| 81 | * <ul> |
| 82 | * <li>Server-side logging |
| 83 | * <li>Focusing |
| 84 | * <li>Show location dialog |
| 85 | * <li>Show resolution dialog on certain devices |
| 86 | * <li>Store location |
| 87 | * <li>Timer |
Sascha Haeberling | 0cf4a02 | 2014-07-31 11:36:58 -0700 | [diff] [blame] | 88 | * <li>Capture intent |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 89 | * </ul> |
| 90 | */ |
Sascha Haeberling | 0cf4a02 | 2014-07-31 11:36:58 -0700 | [diff] [blame] | 91 | public class CaptureModule extends CameraModule |
| 92 | implements MediaSaver.QueueListener, |
| 93 | ModuleController, |
| 94 | OneCamera.PictureCallback, |
Andy Huibers | 30ffce0 | 2014-07-31 16:31:43 -0700 | [diff] [blame] | 95 | OneCamera.FocusStateListener, |
Sascha Haeberling | e3ad435 | 2014-08-15 10:52:37 -0700 | [diff] [blame^] | 96 | OneCamera.ReadyStateChangedListener, |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 97 | PreviewStatusListener.PreviewAreaChangedListener, |
Sascha Haeberling | 0cf4a02 | 2014-07-31 11:36:58 -0700 | [diff] [blame] | 98 | RemoteCameraModule, |
| 99 | SensorEventListener, |
| 100 | SettingsManager.OnSettingChangedListener, |
| 101 | TextureView.SurfaceTextureListener { |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 102 | |
| 103 | /** |
| 104 | * Called on layout changes. |
| 105 | */ |
| 106 | private final OnLayoutChangeListener mLayoutListener = new OnLayoutChangeListener() { |
| 107 | @Override |
| 108 | public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, |
| 109 | int oldTop, int oldRight, int oldBottom) { |
| 110 | int width = right - left; |
| 111 | int height = bottom - top; |
| 112 | updatePreviewTransform(width, height, false); |
| 113 | } |
| 114 | }; |
| 115 | |
| 116 | /** |
| 117 | * Called when the captured media has been saved. |
| 118 | */ |
| 119 | private final MediaSaver.OnMediaSavedListener mOnMediaSavedListener = |
| 120 | new MediaSaver.OnMediaSavedListener() { |
| 121 | @Override |
| 122 | public void onMediaSaved(Uri uri) { |
| 123 | if (uri != null) { |
| 124 | mAppController.notifyNewMedia(uri); |
| 125 | } |
| 126 | } |
| 127 | }; |
| 128 | |
| 129 | /** |
| 130 | * Called when the user pressed the back/front camera switch button. |
| 131 | */ |
| 132 | private final ButtonManager.ButtonCallback mCameraSwitchCallback = |
| 133 | new ButtonManager.ButtonCallback() { |
| 134 | @Override |
| 135 | public void onStateChanged(int cameraId) { |
| 136 | // At the time this callback is fired, the camera id |
| 137 | // has be set to the desired camera. |
| 138 | if (mPaused) { |
| 139 | return; |
| 140 | } |
| 141 | |
| 142 | mSettingsManager.set(mAppController.getModuleScope(), Keys.KEY_CAMERA_ID, |
| 143 | cameraId); |
| 144 | |
| 145 | Log.d(TAG, "Start to switch camera. cameraId=" + cameraId); |
| 146 | switchCamera(getFacingFromCameraId(cameraId)); |
| 147 | } |
| 148 | }; |
| 149 | |
Andy Huibers | 30ffce0 | 2014-07-31 16:31:43 -0700 | [diff] [blame] | 150 | /** |
| 151 | * Show AF target in center of preview and start animation. |
| 152 | */ |
| 153 | Runnable mShowAutoFocusTargetInCenterRunnable = new Runnable() { |
| 154 | @Override |
| 155 | public void run() { |
| 156 | mUI.setAutoFocusTarget(((int) (mPreviewArea.left + mPreviewArea.right)) / 2, |
| 157 | ((int) (mPreviewArea.top + mPreviewArea.bottom)) / 2); |
| 158 | mUI.showAutoFocusInProgress(); |
| 159 | } |
| 160 | }; |
| 161 | |
| 162 | /** |
| 163 | * Hide AF target UI element. |
| 164 | */ |
| 165 | Runnable mHideAutoFocusTargetRunnable = new Runnable() { |
| 166 | @Override |
| 167 | public void run() { |
| 168 | // showAutoFocusSuccess() just hides the AF UI. |
| 169 | mUI.showAutoFocusSuccess(); |
| 170 | } |
| 171 | }; |
| 172 | |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 173 | private static final Tag TAG = new Tag("CaptureModule"); |
| 174 | private static final String PHOTO_MODULE_STRING_ID = "PhotoModule"; |
| 175 | /** Enable additional debug output. */ |
| 176 | private static final boolean DEBUG = true; |
| 177 | /** |
| 178 | * This is the delay before we execute onResume tasks when coming from the |
| 179 | * lock screen, to allow time for onPause to execute. |
| 180 | * <p> |
| 181 | * TODO: Make sure this value is in sync with what we see on L. |
| 182 | */ |
| 183 | private static final int ON_RESUME_TASKS_DELAY_MSEC = 20; |
| 184 | |
Andy Huibers | 30ffce0 | 2014-07-31 16:31:43 -0700 | [diff] [blame] | 185 | /** System Properties switch to enable debugging focus UI. */ |
| 186 | private static final String PROP_FOCUS_DEBUG_UI_KEY = "persist.camera.focus_debug_ui"; |
| 187 | private static final String PROP_FOCUS_DEBUG_UI_OFF = "0"; |
| 188 | private static final boolean FOCUS_DEBUG_UI = !PROP_FOCUS_DEBUG_UI_OFF |
| 189 | .equals(SystemProperties.get(PROP_FOCUS_DEBUG_UI_KEY, PROP_FOCUS_DEBUG_UI_OFF)); |
| 190 | |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 191 | private final Object mDimensionLock = new Object(); |
| 192 | /** |
| 193 | * Lock for race conditions in the SurfaceTextureListener callbacks. |
| 194 | */ |
| 195 | private final Object mSurfaceLock = new Object(); |
| 196 | /** Controller giving us access to other services. */ |
| 197 | private final AppController mAppController; |
| 198 | /** The applications settings manager. */ |
| 199 | private final SettingsManager mSettingsManager; |
| 200 | /** Application context. */ |
| 201 | private final Context mContext; |
| 202 | private CaptureModuleUI mUI; |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 203 | /** The camera manager used to open cameras. */ |
| 204 | private OneCameraManager mCameraManager; |
| 205 | /** The currently opened camera device. */ |
| 206 | private OneCamera mCamera; |
| 207 | /** The direction the currently opened camera is facing to. */ |
| 208 | private Facing mCameraFacing = Facing.BACK; |
| 209 | /** The texture used to render the preview in. */ |
| 210 | private SurfaceTexture mPreviewTexture; |
| 211 | |
| 212 | /** State by the module state machine. */ |
| 213 | private static enum ModuleState { |
| 214 | IDLE, |
| 215 | WATCH_FOR_NEXT_FRAME_AFTER_PREVIEW_STARTED, |
| 216 | UPDATE_TRANSFORM_ON_NEXT_SURFACE_TEXTURE_UPDATE, |
| 217 | } |
| 218 | |
| 219 | /** The current state of the module. */ |
| 220 | private ModuleState mState = ModuleState.IDLE; |
| 221 | /** Current orientation of the device. */ |
| 222 | private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN; |
Andy Huibers | 30ffce0 | 2014-07-31 16:31:43 -0700 | [diff] [blame] | 223 | /** Current zoom value. */ |
Sascha Haeberling | 59c784b | 2014-08-05 10:53:08 -0700 | [diff] [blame] | 224 | private final float mZoomValue = 1f; |
Andy Huibers | 30ffce0 | 2014-07-31 16:31:43 -0700 | [diff] [blame] | 225 | |
| 226 | /** True if in AF tap-to-focus sequence. */ |
| 227 | private boolean mTapToFocusInProgress = false; |
| 228 | |
| 229 | /** Persistence of Tap to Focus target UI after scan complete. */ |
| 230 | private static final int FOCUS_HOLD_UI_MILLIS = 500; |
| 231 | /** Persistence of Tap to Focus target UI timeout. */ |
| 232 | private static final int FOCUS_HOLD_UI_TIMEOUT_MILLIS = 1500; |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 233 | |
| 234 | /** Accelerometer data. */ |
| 235 | private final float[] mGData = new float[3]; |
| 236 | /** Magnetic sensor data. */ |
| 237 | private final float[] mMData = new float[3]; |
| 238 | /** Temporary rotation matrix. */ |
| 239 | private final float[] mR = new float[16]; |
| 240 | /** Current compass heading. */ |
| 241 | private int mHeading = -1; |
| 242 | |
| 243 | /** Whether the module is paused right now. */ |
| 244 | private boolean mPaused; |
| 245 | |
| 246 | /** Whether this module was resumed from lockscreen capture intent. */ |
| 247 | private boolean mIsResumeFromLockScreen = false; |
| 248 | |
| 249 | private final Runnable mResumeTaskRunnable = new Runnable() { |
| 250 | @Override |
| 251 | public void run() { |
| 252 | onResumeTasks(); |
| 253 | } |
| 254 | }; |
| 255 | |
| 256 | /** Main thread handler. */ |
| 257 | private Handler mMainHandler; |
| 258 | |
| 259 | /** Current display rotation in degrees. */ |
| 260 | private int mDisplayRotation; |
Andy Huibers | 30ffce0 | 2014-07-31 16:31:43 -0700 | [diff] [blame] | 261 | /** Current screen width in pixels. */ |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 262 | private int mScreenWidth; |
Andy Huibers | 30ffce0 | 2014-07-31 16:31:43 -0700 | [diff] [blame] | 263 | /** Current screen height in pixels. */ |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 264 | private int mScreenHeight; |
Andy Huibers | 30ffce0 | 2014-07-31 16:31:43 -0700 | [diff] [blame] | 265 | /** Current width of preview frames from camera. */ |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 266 | private int mPreviewBufferWidth; |
Andy Huibers | 30ffce0 | 2014-07-31 16:31:43 -0700 | [diff] [blame] | 267 | /** Current height of preview frames from camera.. */ |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 268 | private int mPreviewBufferHeight; |
Andy Huibers | 30ffce0 | 2014-07-31 16:31:43 -0700 | [diff] [blame] | 269 | /** Area used by preview. */ |
| 270 | RectF mPreviewArea; |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 271 | |
| 272 | /** The current preview transformation matrix. */ |
| 273 | private Matrix mPreviewTranformationMatrix = new Matrix(); |
| 274 | /** TODO: This is N5 specific. */ |
| 275 | public static final float FULLSCREEN_ASPECT_RATIO = 16 / 9f; |
| 276 | |
Sascha Haeberling | 59c784b | 2014-08-05 10:53:08 -0700 | [diff] [blame] | 277 | /** A directory to store debug information in during development. */ |
| 278 | private final File mDebugDataDir; |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 279 | |
| 280 | /** CLEAN UP START */ |
| 281 | // private SoundPool mSoundPool; |
| 282 | // private int mCaptureStartSoundId; |
| 283 | // private static final int NO_SOUND_STREAM = -999; |
| 284 | // private final int mCaptureStartSoundStreamId = NO_SOUND_STREAM; |
| 285 | // private int mCaptureDoneSoundId; |
| 286 | // private SoundClips.Player mSoundPlayer; |
| 287 | // private boolean mFirstLayout; |
| 288 | // private int[] mTargetFPSRanges; |
| 289 | // private float mZoomValue; |
| 290 | // private int mSensorOrientation; |
| 291 | // private int mLensFacing; |
| 292 | // private volatile float mMaxZoomRatio = 1.0f; |
| 293 | // private String mFlashMode; |
| 294 | /** CLEAN UP END */ |
| 295 | |
| 296 | /** Constructs a new capture module. */ |
| 297 | public CaptureModule(AppController appController) { |
| 298 | super(appController); |
| 299 | mAppController = appController; |
| 300 | mContext = mAppController.getAndroidContext(); |
| 301 | mSettingsManager = mAppController.getSettingsManager(); |
Sascha Haeberling | 0cf4a02 | 2014-07-31 11:36:58 -0700 | [diff] [blame] | 302 | mSettingsManager.addListener(this); |
Sascha Haeberling | 59c784b | 2014-08-05 10:53:08 -0700 | [diff] [blame] | 303 | mDebugDataDir = mContext.getExternalCacheDir(); |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 304 | } |
| 305 | |
| 306 | @Override |
| 307 | public void init(CameraActivity activity, boolean isSecureCamera, boolean isCaptureIntent) { |
| 308 | Log.d(TAG, "init"); |
| 309 | mIsResumeFromLockScreen = isResumeFromLockscreen(activity); |
| 310 | mMainHandler = new Handler(activity.getMainLooper()); |
| 311 | mCameraManager = mAppController.getCameraManager(); |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 312 | mDisplayRotation = CameraUtil.getDisplayRotation(mContext); |
| 313 | mCameraFacing = getFacingFromCameraId(mSettingsManager.getInteger( |
| 314 | mAppController.getModuleScope(), |
| 315 | Keys.KEY_CAMERA_ID)); |
| 316 | mUI = new CaptureModuleUI(activity, this, mAppController.getModuleLayoutRoot(), |
| 317 | mLayoutListener); |
| 318 | mAppController.setPreviewStatusListener(mUI); |
| 319 | mPreviewTexture = mAppController.getCameraAppUI().getSurfaceTexture(); |
| 320 | if (mPreviewTexture != null) { |
| 321 | initSurface(mPreviewTexture); |
| 322 | } |
| 323 | } |
| 324 | |
| 325 | @Override |
| 326 | public void onShutterButtonFocus(boolean pressed) { |
| 327 | // TODO Auto-generated method stub |
| 328 | } |
| 329 | |
| 330 | @Override |
| 331 | public void onShutterCoordinate(TouchCoordinate coord) { |
| 332 | // TODO Auto-generated method stub |
| 333 | } |
| 334 | |
| 335 | @Override |
| 336 | public void onShutterButtonClick() { |
| 337 | // TODO: Add focusing. |
| 338 | if (mCamera == null) { |
| 339 | return; |
| 340 | } |
| 341 | |
| 342 | // Set up the capture session. |
Sascha Haeberling | 57bcd92 | 2014-07-31 16:05:57 -0700 | [diff] [blame] | 343 | long sessionTime = System.currentTimeMillis(); |
| 344 | String title = CameraUtil.createJpegName(sessionTime); |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 345 | CaptureSession session = getServices().getCaptureSessionManager() |
Sascha Haeberling | 57bcd92 | 2014-07-31 16:05:57 -0700 | [diff] [blame] | 346 | .createNewSession(title, sessionTime, null); |
| 347 | |
| 348 | // TODO: Add location. |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 349 | |
| 350 | // Set up the parameters for this capture. |
| 351 | PhotoCaptureParameters params = new PhotoCaptureParameters(); |
Sascha Haeberling | 0cf4a02 | 2014-07-31 11:36:58 -0700 | [diff] [blame] | 352 | params.title = title; |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 353 | params.callback = this; |
| 354 | params.orientation = getOrientation(); |
Sascha Haeberling | 0cf4a02 | 2014-07-31 11:36:58 -0700 | [diff] [blame] | 355 | params.flashMode = getFlashModeFromSettings(); |
Sascha Haeberling | 57bcd92 | 2014-07-31 16:05:57 -0700 | [diff] [blame] | 356 | params.heading = mHeading; |
Sascha Haeberling | 59c784b | 2014-08-05 10:53:08 -0700 | [diff] [blame] | 357 | params.debugDataFolder = mDebugDataDir; |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 358 | |
| 359 | // Take the picture. |
| 360 | mCamera.takePicture(params, session); |
| 361 | } |
| 362 | |
| 363 | @Override |
| 364 | public void onPreviewAreaChanged(RectF previewArea) { |
Andy Huibers | 30ffce0 | 2014-07-31 16:31:43 -0700 | [diff] [blame] | 365 | mPreviewArea = previewArea; |
Sascha Haeberling | 0cf4a02 | 2014-07-31 11:36:58 -0700 | [diff] [blame] | 366 | // mUI.updatePreviewAreaRect(previewArea); |
| 367 | // mUI.positionProgressOverlay(previewArea); |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 368 | } |
| 369 | |
| 370 | @Override |
| 371 | public void onSensorChanged(SensorEvent event) { |
| 372 | // This is literally the same as the GCamModule implementation. |
| 373 | int type = event.sensor.getType(); |
| 374 | float[] data; |
| 375 | if (type == Sensor.TYPE_ACCELEROMETER) { |
| 376 | data = mGData; |
| 377 | } else if (type == Sensor.TYPE_MAGNETIC_FIELD) { |
| 378 | data = mMData; |
| 379 | } else { |
| 380 | Log.w(TAG, String.format("Unexpected sensor type %s", event.sensor.getName())); |
| 381 | return; |
| 382 | } |
| 383 | for (int i = 0; i < 3; i++) { |
| 384 | data[i] = event.values[i]; |
| 385 | } |
| 386 | float[] orientation = new float[3]; |
| 387 | SensorManager.getRotationMatrix(mR, null, mGData, mMData); |
| 388 | SensorManager.getOrientation(mR, orientation); |
| 389 | mHeading = (int) (orientation[0] * 180f / Math.PI) % 360; |
| 390 | if (mHeading < 0) { |
| 391 | mHeading += 360; |
| 392 | } |
| 393 | } |
| 394 | |
| 395 | @Override |
| 396 | public void onAccuracyChanged(Sensor sensor, int accuracy) { |
| 397 | // TODO Auto-generated method stub |
| 398 | } |
| 399 | |
| 400 | @Override |
| 401 | public void onQueueStatus(boolean full) { |
| 402 | // TODO Auto-generated method stub |
| 403 | } |
| 404 | |
| 405 | @Override |
| 406 | public void onRemoteShutterPress() { |
| 407 | // TODO: Check whether shutter is enabled. |
| 408 | onShutterButtonClick(); |
| 409 | } |
| 410 | |
| 411 | @Override |
| 412 | public void onSurfaceTextureAvailable(final SurfaceTexture surface, int width, int height) { |
| 413 | Log.d(TAG, "onSurfaceTextureAvailable"); |
| 414 | // Force to re-apply transform matrix here as a workaround for |
| 415 | // b/11168275 |
| 416 | updatePreviewTransform(width, height, true); |
| 417 | initSurface(surface); |
| 418 | } |
| 419 | |
| 420 | public void initSurface(final SurfaceTexture surface) { |
| 421 | mPreviewTexture = surface; |
| 422 | closeCamera(); |
| 423 | |
| 424 | mCameraManager.open(mCameraFacing, getPictureSizeFromSettings(), new OpenCallback() { |
| 425 | @Override |
| 426 | public void onFailure() { |
| 427 | Log.e(TAG, "Could not open camera."); |
| 428 | mCamera = null; |
Sascha Haeberling | 0cf4a02 | 2014-07-31 11:36:58 -0700 | [diff] [blame] | 429 | mAppController.showErrorAndFinish(R.string.cannot_connect_camera); |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 430 | } |
| 431 | |
| 432 | @Override |
| 433 | public void onCameraOpened(final OneCamera camera) { |
| 434 | Log.d(TAG, "onCameraOpened: " + camera); |
| 435 | mCamera = camera; |
| 436 | updateBufferDimension(); |
| 437 | |
| 438 | // If the surface texture is not destroyed, it may have the last |
| 439 | // frame lingering. |
| 440 | // We need to hold off setting transform until preview is |
| 441 | // started. |
| 442 | resetDefaultBufferSize(); |
| 443 | mState = ModuleState.WATCH_FOR_NEXT_FRAME_AFTER_PREVIEW_STARTED; |
| 444 | |
| 445 | Log.d(TAG, "starting preview ..."); |
| 446 | |
| 447 | // TODO: Consider rolling these two calls into one. |
| 448 | camera.startPreview(new Surface(surface), new CaptureReadyCallback() { |
| 449 | |
| 450 | @Override |
| 451 | public void onSetupFailed() { |
| 452 | Log.e(TAG, "Could not set up preview."); |
| 453 | mCamera.close(null); |
| 454 | mCamera = null; |
Sascha Haeberling | 0cf4a02 | 2014-07-31 11:36:58 -0700 | [diff] [blame] | 455 | // TODO: Show an error message and exit. |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 456 | } |
| 457 | |
| 458 | @Override |
| 459 | public void onReadyForCapture() { |
Sascha Haeberling | 0cf4a02 | 2014-07-31 11:36:58 -0700 | [diff] [blame] | 460 | Log.d(TAG, "Ready for capture."); |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 461 | onPreviewStarted(); |
Andy Huibers | 30ffce0 | 2014-07-31 16:31:43 -0700 | [diff] [blame] | 462 | mCamera.setFocusStateListener(CaptureModule.this); |
Sascha Haeberling | e3ad435 | 2014-08-15 10:52:37 -0700 | [diff] [blame^] | 463 | mCamera.setReadyStateChangedListener(CaptureModule.this); |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 464 | } |
| 465 | }); |
| 466 | } |
| 467 | }); |
| 468 | } |
| 469 | |
| 470 | @Override |
| 471 | public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { |
| 472 | Log.d(TAG, "onSurfaceTextureSizeChanged"); |
| 473 | resetDefaultBufferSize(); |
| 474 | } |
| 475 | |
| 476 | @Override |
| 477 | public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { |
| 478 | Log.d(TAG, "onSurfaceTextureDestroyed"); |
| 479 | closeCamera(); |
| 480 | return true; |
| 481 | } |
| 482 | |
| 483 | @Override |
| 484 | public void onSurfaceTextureUpdated(SurfaceTexture surface) { |
| 485 | if (mState == ModuleState.UPDATE_TRANSFORM_ON_NEXT_SURFACE_TEXTURE_UPDATE) { |
| 486 | Log.d(TAG, "onSurfaceTextureUpdated --> updatePreviewTransform"); |
| 487 | mState = ModuleState.IDLE; |
| 488 | CameraAppUI appUI = mAppController.getCameraAppUI(); |
| 489 | updatePreviewTransform(appUI.getSurfaceWidth(), appUI.getSurfaceHeight(), true); |
| 490 | } |
| 491 | } |
| 492 | |
| 493 | @Override |
| 494 | public String getModuleStringIdentifier() { |
| 495 | return PHOTO_MODULE_STRING_ID; |
| 496 | } |
| 497 | |
| 498 | @Override |
| 499 | public void resume() { |
| 500 | // Add delay on resume from lock screen only, in order to to speed up |
| 501 | // the onResume --> onPause --> onResume cycle from lock screen. |
| 502 | // Don't do always because letting go of thread can cause delay. |
| 503 | if (mIsResumeFromLockScreen) { |
| 504 | Log.v(TAG, "Delayng onResumeTasks from lock screen. " + System.currentTimeMillis()); |
| 505 | // Note: onPauseAfterSuper() will delete this runnable, so we will |
| 506 | // at most have 1 copy queued up. |
| 507 | mMainHandler.postDelayed(mResumeTaskRunnable, ON_RESUME_TASKS_DELAY_MSEC); |
| 508 | } else { |
| 509 | onResumeTasks(); |
| 510 | } |
| 511 | } |
| 512 | |
| 513 | private void onResumeTasks() { |
| 514 | Log.d(TAG, "onResumeTasks + " + System.currentTimeMillis()); |
| 515 | mPaused = false; |
| 516 | mAppController.getCameraAppUI().onChangeCamera(); |
| 517 | mAppController.addPreviewAreaSizeChangedListener(this); |
| 518 | resetDefaultBufferSize(); |
| 519 | getServices().getRemoteShutterListener().onModuleReady(this); |
Sascha Haeberling | e3ad435 | 2014-08-15 10:52:37 -0700 | [diff] [blame^] | 520 | // TODO: Check if we can really take a photo right now (memory, camera |
| 521 | // state, ... ). |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 522 | mAppController.setShutterEnabled(true); |
| 523 | } |
| 524 | |
| 525 | @Override |
| 526 | public void pause() { |
| 527 | mPaused = true; |
| 528 | resetTextureBufferSize(); |
| 529 | closeCamera(); |
| 530 | // Remove delayed resume trigger, if it hasn't been executed yet. |
| 531 | mMainHandler.removeCallbacksAndMessages(null); |
| 532 | } |
| 533 | |
| 534 | @Override |
| 535 | public void destroy() { |
| 536 | } |
| 537 | |
| 538 | @Override |
| 539 | public void onLayoutOrientationChanged(boolean isLandscape) { |
| 540 | Log.d(TAG, "onLayoutOrientationChanged"); |
| 541 | } |
| 542 | |
| 543 | @Override |
| 544 | public void onOrientationChanged(int orientation) { |
| 545 | // We keep the last known orientation. So if the user first orient |
| 546 | // the camera then point the camera to floor or sky, we still have |
| 547 | // the correct orientation. |
| 548 | if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) { |
| 549 | return; |
| 550 | } |
| 551 | mOrientation = CameraUtil.roundOrientation(orientation, mOrientation); |
| 552 | } |
| 553 | |
| 554 | @Override |
| 555 | public void onCameraAvailable(CameraProxy cameraProxy) { |
| 556 | // Ignore since we manage the camera ourselves until we remove this. |
| 557 | } |
| 558 | |
| 559 | @Override |
| 560 | public void hardResetSettings(SettingsManager settingsManager) { |
| 561 | // TODO Auto-generated method stub |
| 562 | } |
| 563 | |
| 564 | @Override |
| 565 | public HardwareSpec getHardwareSpec() { |
| 566 | return new HardwareSpec() { |
| 567 | @Override |
| 568 | public boolean isFrontCameraSupported() { |
| 569 | return true; |
| 570 | } |
| 571 | |
| 572 | @Override |
| 573 | public boolean isHdrSupported() { |
| 574 | return false; |
| 575 | } |
| 576 | |
| 577 | @Override |
| 578 | public boolean isHdrPlusSupported() { |
| 579 | // TODO: Enable once we support this. |
| 580 | return false; |
| 581 | } |
| 582 | |
| 583 | @Override |
| 584 | public boolean isFlashSupported() { |
Sascha Haeberling | 0cf4a02 | 2014-07-31 11:36:58 -0700 | [diff] [blame] | 585 | return true; |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 586 | } |
| 587 | }; |
| 588 | } |
| 589 | |
| 590 | @Override |
| 591 | public BottomBarUISpec getBottomBarSpec() { |
| 592 | CameraAppUI.BottomBarUISpec bottomBarSpec = new CameraAppUI.BottomBarUISpec(); |
| 593 | bottomBarSpec.enableGridLines = true; |
| 594 | bottomBarSpec.enableCamera = true; |
| 595 | bottomBarSpec.cameraCallback = mCameraSwitchCallback; |
| 596 | // TODO: Enable once we support this. |
| 597 | bottomBarSpec.enableHdr = false; |
| 598 | // TODO: Enable once we support this. |
| 599 | bottomBarSpec.hdrCallback = null; |
| 600 | // TODO: Enable once we support this. |
| 601 | bottomBarSpec.enableSelfTimer = false; |
| 602 | bottomBarSpec.showSelfTimer = false; |
Sascha Haeberling | 0cf4a02 | 2014-07-31 11:36:58 -0700 | [diff] [blame] | 603 | // TODO: Deal with e.g. HDR+ if it doesn't support it. |
| 604 | bottomBarSpec.enableFlash = true; |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 605 | return bottomBarSpec; |
| 606 | } |
| 607 | |
| 608 | @Override |
| 609 | public boolean isUsingBottomBar() { |
| 610 | return true; |
| 611 | } |
| 612 | |
| 613 | @Override |
| 614 | public boolean onKeyDown(int keyCode, KeyEvent event) { |
| 615 | return false; |
| 616 | } |
| 617 | |
| 618 | @Override |
| 619 | public boolean onKeyUp(int keyCode, KeyEvent event) { |
| 620 | return false; |
| 621 | } |
| 622 | |
Andy Huibers | 30ffce0 | 2014-07-31 16:31:43 -0700 | [diff] [blame] | 623 | /** |
| 624 | * Focus sequence starts for zone around tap location for single tap. |
| 625 | */ |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 626 | @Override |
| 627 | public void onSingleTapUp(View view, int x, int y) { |
Andy Huibers | 30ffce0 | 2014-07-31 16:31:43 -0700 | [diff] [blame] | 628 | Log.v(TAG, "onSingleTapUp x=" + x + " y=" + y); |
| 629 | // TODO: This should query actual capability. |
| 630 | if (mCameraFacing == Facing.FRONT) { |
| 631 | return; |
| 632 | } |
| 633 | triggerFocusAtScreenCoord(x, y); |
| 634 | } |
| 635 | |
| 636 | // TODO: Consider refactoring FocusOverlayManager. |
| 637 | // Currently AF state transitions are controlled in OneCameraImpl. |
| 638 | // PhotoModule uses FocusOverlayManager which uses API1/portability |
| 639 | // logic and coordinates. |
| 640 | |
| 641 | private void triggerFocusAtScreenCoord(int x, int y) { |
| 642 | mTapToFocusInProgress = true; |
| 643 | // Show UI immediately even though scan has not started yet. |
| 644 | mUI.setAutoFocusTarget(x, y); |
| 645 | mUI.showAutoFocusInProgress(); |
| 646 | mMainHandler.removeCallbacks(mHideAutoFocusTargetRunnable); |
| 647 | mMainHandler.postDelayed(mHideAutoFocusTargetRunnable, FOCUS_HOLD_UI_TIMEOUT_MILLIS); |
| 648 | |
| 649 | // Normalize coordinates to [0,1] per CameraOne API. |
| 650 | float points[] = new float[2]; |
| 651 | points[0] = (x - mPreviewArea.left) / mPreviewArea.width(); |
| 652 | points[1] = (y - mPreviewArea.top) / mPreviewArea.height(); |
| 653 | |
| 654 | // Rotate coordinates to portrait orientation per CameraOne API. |
| 655 | Matrix rotationMatrix = new Matrix(); |
| 656 | rotationMatrix.setRotate(mDisplayRotation, 0.5f, 0.5f); |
| 657 | rotationMatrix.mapPoints(points); |
| 658 | mCamera.triggerFocusAndMeterAtPoint(points[0], points[1]); |
| 659 | |
| 660 | // Log touch (screen coordinates). |
| 661 | if (mZoomValue == 1f) { |
| 662 | TouchCoordinate touchCoordinate = new TouchCoordinate(x - mPreviewArea.left, |
| 663 | y - mPreviewArea.top, mPreviewArea.width(), mPreviewArea.height()); |
| 664 | // TODO: Add to logging: duration, rotation. |
| 665 | UsageStatistics.instance().tapToFocus(touchCoordinate, null); |
| 666 | } |
| 667 | } |
| 668 | |
| 669 | /** |
| 670 | * This AF status listener does two things: |
| 671 | * <ol> |
| 672 | * <li>Ends tap-to-focus period when mode goes from AUTO to CONTINUOUS_PICTURE.</li> |
| 673 | * <li>Updates AF UI if tap-to-focus is not in progress.</li> |
| 674 | * </ol> |
| 675 | */ |
Sascha Haeberling | 59c784b | 2014-08-05 10:53:08 -0700 | [diff] [blame] | 676 | @Override |
Andy Huibers | 30ffce0 | 2014-07-31 16:31:43 -0700 | [diff] [blame] | 677 | public void onFocusStatusUpdate(final AutoFocusMode mode, final AutoFocusState state) { |
| 678 | Log.v(TAG, "AF status is mode:" + mode + " state:" + state); |
| 679 | |
| 680 | if (FOCUS_DEBUG_UI) { |
| 681 | // TODO: Add debug circle radius+color UI to FocusOverlay. |
| 682 | // mMainHandler.post(...) |
| 683 | } |
| 684 | |
| 685 | // After tap to focus SCAN completes, clear UI after FOCUS_HOLD_UI_MILLIS. |
| 686 | if (mTapToFocusInProgress && mode == AutoFocusMode.AUTO && |
| 687 | (state == AutoFocusState.STOPPED_FOCUSED || |
| 688 | state == AutoFocusState.STOPPED_UNFOCUSED)) { |
| 689 | mMainHandler.postDelayed(new Runnable() { |
| 690 | @Override |
| 691 | public void run() { |
| 692 | mTapToFocusInProgress = false; |
| 693 | mMainHandler.removeCallbacks(mHideAutoFocusTargetRunnable); |
| 694 | mMainHandler.post(mHideAutoFocusTargetRunnable); |
| 695 | } |
| 696 | }, FOCUS_HOLD_UI_MILLIS); |
| 697 | } |
| 698 | |
| 699 | // Use the OneCamera auto focus callbacks to show the UI, except for |
| 700 | // tap to focus where we show UI right away at touch, and then turn |
| 701 | // it off early at 0.5 sec, before the focus lock expires at 3 sec. |
| 702 | if (!mTapToFocusInProgress) { |
| 703 | switch (state) { |
| 704 | case SCANNING: |
| 705 | mMainHandler.removeCallbacks(mHideAutoFocusTargetRunnable); |
| 706 | mMainHandler.post(mShowAutoFocusTargetInCenterRunnable); |
| 707 | break; |
| 708 | case STOPPED_FOCUSED: |
| 709 | case STOPPED_UNFOCUSED: |
| 710 | mMainHandler.removeCallbacks(mHideAutoFocusTargetRunnable); |
| 711 | mMainHandler.post(mHideAutoFocusTargetRunnable); |
| 712 | break; |
| 713 | } |
| 714 | } |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 715 | } |
| 716 | |
| 717 | @Override |
Sascha Haeberling | e3ad435 | 2014-08-15 10:52:37 -0700 | [diff] [blame^] | 718 | public void onReadyStateChanged(boolean readyForCapture) { |
| 719 | mAppController.setShutterEnabled(readyForCapture); |
| 720 | } |
| 721 | |
| 722 | @Override |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 723 | public String getPeekAccessibilityString() { |
| 724 | return mAppController.getAndroidContext() |
| 725 | .getResources().getString(R.string.photo_accessibility_peek); |
| 726 | } |
| 727 | |
| 728 | @Override |
| 729 | public void onThumbnailResult(Bitmap bitmap) { |
| 730 | // TODO |
| 731 | } |
| 732 | |
| 733 | @Override |
Sascha Haeberling | 57bcd92 | 2014-07-31 16:05:57 -0700 | [diff] [blame] | 734 | public void onPictureTaken(CaptureSession session) { |
Sascha Haeberling | 57bcd92 | 2014-07-31 16:05:57 -0700 | [diff] [blame] | 735 | } |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 736 | |
Sascha Haeberling | 57bcd92 | 2014-07-31 16:05:57 -0700 | [diff] [blame] | 737 | @Override |
| 738 | public void onPictureSaved(Uri uri) { |
| 739 | mAppController.notifyNewMedia(uri); |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 740 | } |
| 741 | |
| 742 | @Override |
| 743 | public void onTakePictureProgress(int progressPercent) { |
Sascha Haeberling | 0cf4a02 | 2014-07-31 11:36:58 -0700 | [diff] [blame] | 744 | // TODO once we have HDR+ hooked up. |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 745 | } |
| 746 | |
| 747 | @Override |
| 748 | public void onPictureTakenFailed() { |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 749 | } |
| 750 | |
Sascha Haeberling | 0cf4a02 | 2014-07-31 11:36:58 -0700 | [diff] [blame] | 751 | @Override |
| 752 | public void onSettingChanged(SettingsManager settingsManager, String key) { |
| 753 | // TODO Auto-generated method stub |
| 754 | } |
| 755 | |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 756 | /** |
| 757 | * Updates the preview transform matrix to adapt to the current preview |
| 758 | * width, height, and orientation. |
| 759 | */ |
| 760 | public void updatePreviewTransform() { |
| 761 | int width; |
| 762 | int height; |
| 763 | synchronized (mDimensionLock) { |
| 764 | width = mScreenWidth; |
| 765 | height = mScreenHeight; |
| 766 | } |
| 767 | updatePreviewTransform(width, height); |
| 768 | } |
| 769 | |
| 770 | /** |
| 771 | * Called when the preview started. Informs the app controller and queues a |
| 772 | * transform update when the next preview frame arrives. |
| 773 | */ |
| 774 | private void onPreviewStarted() { |
| 775 | if (mState == ModuleState.WATCH_FOR_NEXT_FRAME_AFTER_PREVIEW_STARTED) { |
| 776 | mState = ModuleState.UPDATE_TRANSFORM_ON_NEXT_SURFACE_TEXTURE_UPDATE; |
| 777 | } |
| 778 | mAppController.onPreviewStarted(); |
| 779 | } |
| 780 | |
| 781 | /** |
| 782 | * Update the preview transform based on the new dimensions. Will not force |
| 783 | * an update, if it's not necessary. |
| 784 | */ |
| 785 | private void updatePreviewTransform(int incomingWidth, int incomingHeight) { |
| 786 | updatePreviewTransform(incomingWidth, incomingHeight, false); |
| 787 | } |
| 788 | |
| 789 | /*** |
| 790 | * Update the preview transform based on the new dimensions. |
Andy Huibers | 30ffce0 | 2014-07-31 16:31:43 -0700 | [diff] [blame] | 791 | * TODO: Make work with all: aspect ratios/resolutions x screens/cameras. |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 792 | */ |
| 793 | private void updatePreviewTransform(int incomingWidth, int incomingHeight, |
| 794 | boolean forceUpdate) { |
| 795 | Log.d(TAG, "updatePreviewTransform: " + incomingWidth + " x " + incomingHeight); |
| 796 | |
| 797 | synchronized (mDimensionLock) { |
| 798 | int incomingRotation = CameraUtil |
| 799 | .getDisplayRotation(mContext); |
| 800 | // Check for an actual change: |
| 801 | if (mScreenHeight == incomingHeight && mScreenWidth == incomingWidth && |
| 802 | incomingRotation == mDisplayRotation && !forceUpdate) { |
| 803 | return; |
| 804 | } |
| 805 | // Update display rotation and dimensions |
| 806 | mDisplayRotation = incomingRotation; |
| 807 | mScreenWidth = incomingWidth; |
| 808 | mScreenHeight = incomingHeight; |
| 809 | updateBufferDimension(); |
| 810 | |
Sascha Haeberling | 0cf4a02 | 2014-07-31 11:36:58 -0700 | [diff] [blame] | 811 | mPreviewTranformationMatrix = mAppController.getCameraAppUI().getPreviewTransform( |
| 812 | mPreviewTranformationMatrix); |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 813 | int width = mScreenWidth; |
| 814 | int height = mScreenHeight; |
| 815 | |
| 816 | // Assumptions: |
| 817 | // - Aspect ratio for the sensor buffers is in landscape |
| 818 | // orientation, |
| 819 | // - Dimensions of buffers received are rotated to the natural |
| 820 | // device orientation. |
| 821 | // - The contents of each buffer are rotated by the inverse of |
| 822 | // the display rotation. |
| 823 | // - Surface scales the buffer to fit the current view bounds. |
| 824 | |
| 825 | // Get natural orientation and buffer dimensions |
| 826 | int naturalOrientation = CaptureModuleUtil |
| 827 | .getDeviceNaturalOrientation(mContext); |
| 828 | int effectiveWidth = mPreviewBufferWidth; |
| 829 | int effectiveHeight = mPreviewBufferHeight; |
| 830 | |
| 831 | if (DEBUG) { |
| 832 | Log.v(TAG, "Rotation: " + mDisplayRotation); |
| 833 | Log.v(TAG, "Screen Width: " + mScreenWidth); |
| 834 | Log.v(TAG, "Screen Height: " + mScreenHeight); |
| 835 | Log.v(TAG, "Buffer width: " + mPreviewBufferWidth); |
| 836 | Log.v(TAG, "Buffer height: " + mPreviewBufferHeight); |
| 837 | Log.v(TAG, "Natural orientation: " + naturalOrientation); |
| 838 | } |
| 839 | |
| 840 | // If natural orientation is portrait, rotate the buffer |
| 841 | // dimensions |
| 842 | if (naturalOrientation == Configuration.ORIENTATION_PORTRAIT) { |
| 843 | int temp = effectiveWidth; |
| 844 | effectiveWidth = effectiveHeight; |
| 845 | effectiveHeight = temp; |
| 846 | } |
| 847 | |
| 848 | // Find and center view rect and buffer rect |
| 849 | RectF viewRect = new RectF(0, 0, width, height); |
| 850 | RectF bufRect = new RectF(0, 0, effectiveWidth, effectiveHeight); |
| 851 | float centerX = viewRect.centerX(); |
| 852 | float centerY = viewRect.centerY(); |
| 853 | bufRect.offset(centerX - bufRect.centerX(), centerY - bufRect.centerY()); |
| 854 | |
| 855 | // Undo ScaleToFit.FILL done by the surface |
| 856 | mPreviewTranformationMatrix.setRectToRect(viewRect, bufRect, Matrix.ScaleToFit.FILL); |
| 857 | |
| 858 | // Rotate buffer contents to proper orientation |
Sascha Haeberling | 0cf4a02 | 2014-07-31 11:36:58 -0700 | [diff] [blame] | 859 | mPreviewTranformationMatrix.postRotate(getPreviewOrientation(mDisplayRotation), |
| 860 | centerX, centerY); |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 861 | |
| 862 | // TODO: This is probably only working for the N5. Need to test |
| 863 | // on a device like N10 with different sensor orientation. |
| 864 | if ((mDisplayRotation % 180) == 90) { |
| 865 | int temp = effectiveWidth; |
| 866 | effectiveWidth = effectiveHeight; |
| 867 | effectiveHeight = temp; |
| 868 | } |
| 869 | |
| 870 | boolean is16by9 = false; |
| 871 | |
| 872 | // TODO: BACK/FRONT. |
| 873 | Size pictureSize = getPictureSizeFromSettings(); |
| 874 | if (pictureSize != null) { |
| 875 | pictureSize = ResolutionUtil.getApproximateSize(pictureSize); |
Sascha Haeberling | 0cf4a02 | 2014-07-31 11:36:58 -0700 | [diff] [blame] | 876 | if (pictureSize.equals(new Size(16, 9))) { |
| 877 | is16by9 = true; |
| 878 | } |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 879 | } |
| 880 | |
| 881 | float scale; |
| 882 | if (is16by9) { |
| 883 | // We are going to be clipping off edges to achieve the 16 |
| 884 | // by 9 aspect ratio so we will choose the max here to fill, |
| 885 | // instead of fit. |
| 886 | scale = |
| 887 | Math.max(width / (float) effectiveWidth, height |
| 888 | / (float) effectiveHeight); |
| 889 | } else { |
| 890 | // Scale to fit view, cropping the longest dimension |
| 891 | scale = |
| 892 | Math.min(width / (float) effectiveWidth, height |
| 893 | / (float) effectiveHeight); |
| 894 | } |
| 895 | mPreviewTranformationMatrix.postScale(scale, scale, centerX, centerY); |
| 896 | |
Andy Huibers | 30ffce0 | 2014-07-31 16:31:43 -0700 | [diff] [blame] | 897 | // TODO: Take these quantities from mPreviewArea. |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 898 | float previewWidth = effectiveWidth * scale; |
| 899 | float previewHeight = effectiveHeight * scale; |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 900 | float previewCenterX = previewWidth / 2; |
| 901 | float previewCenterY = previewHeight / 2; |
Sascha Haeberling | 0cf4a02 | 2014-07-31 11:36:58 -0700 | [diff] [blame] | 902 | mPreviewTranformationMatrix.postTranslate(previewCenterX - centerX, previewCenterY |
| 903 | - centerY); |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 904 | |
| 905 | if (is16by9) { |
| 906 | float aspectRatio = FULLSCREEN_ASPECT_RATIO; |
| 907 | RectF renderedPreviewRect = mAppController.getFullscreenRect(); |
| 908 | float desiredPreviewWidth = Math.max(renderedPreviewRect.height(), |
| 909 | renderedPreviewRect.width()) * 1 / aspectRatio; |
| 910 | int letterBoxWidth = (int) Math.ceil((Math.min(renderedPreviewRect.width(), |
| 911 | renderedPreviewRect.height()) - desiredPreviewWidth) / 2.0f); |
| 912 | mAppController.getCameraAppUI().addLetterboxing(letterBoxWidth); |
| 913 | |
| 914 | float wOffset = -(previewWidth - renderedPreviewRect.width()) / 2.0f; |
| 915 | float hOffset = -(previewHeight - renderedPreviewRect.height()) / 2.0f; |
| 916 | mPreviewTranformationMatrix.postTranslate(wOffset, hOffset); |
Sascha Haeberling | 0cf4a02 | 2014-07-31 11:36:58 -0700 | [diff] [blame] | 917 | mAppController.updatePreviewTransformFullscreen(mPreviewTranformationMatrix, |
| 918 | aspectRatio); |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 919 | } else { |
| 920 | mAppController.updatePreviewTransform(mPreviewTranformationMatrix); |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 921 | mAppController.getCameraAppUI().hideLetterboxing(); |
| 922 | } |
| 923 | // if (mGcamProxy != null) { |
| 924 | // mGcamProxy.postSetAspectRatio(mFinalAspectRatio); |
| 925 | // } |
Sascha Haeberling | 0cf4a02 | 2014-07-31 11:36:58 -0700 | [diff] [blame] | 926 | // mUI.updatePreviewAreaRect(new RectF(0, 0, previewWidth, |
| 927 | // previewHeight)); |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 928 | |
| 929 | // TODO: Add face detection. |
| 930 | // Characteristics info = |
| 931 | // mapp.getCameraProvider().getCharacteristics(0); |
| 932 | // mUI.setupFaceDetection(CameraUtil.getDisplayOrientation(incomingRotation, |
| 933 | // info), false); |
| 934 | // updateCamera2FaceBoundTransform(new |
| 935 | // RectF(mEffectiveCropRegion), |
| 936 | // new RectF(0, 0, mBufferWidth, mBufferHeight), |
| 937 | // new RectF(0, 0, previewWidth, previewHeight), getRotation()); |
| 938 | } |
| 939 | } |
| 940 | |
| 941 | private void updateBufferDimension() { |
| 942 | if (mCamera == null) { |
| 943 | return; |
| 944 | } |
| 945 | |
| 946 | Size picked = CaptureModuleUtil.pickBufferDimensions( |
| 947 | mCamera.getSupportedSizes(), |
| 948 | mCamera.getFullSizeAspectRatio(), |
| 949 | mContext); |
| 950 | mPreviewBufferWidth = picked.getWidth(); |
| 951 | mPreviewBufferHeight = picked.getHeight(); |
| 952 | } |
| 953 | |
| 954 | /** |
| 955 | * Resets the default buffer size to the initially calculated size. |
| 956 | */ |
| 957 | private void resetDefaultBufferSize() { |
| 958 | synchronized (mSurfaceLock) { |
| 959 | if (mPreviewTexture != null) { |
| 960 | mPreviewTexture.setDefaultBufferSize(mPreviewBufferWidth, mPreviewBufferHeight); |
| 961 | } |
| 962 | } |
| 963 | } |
| 964 | |
| 965 | private void closeCamera() { |
| 966 | if (mCamera != null) { |
Andy Huibers | 30ffce0 | 2014-07-31 16:31:43 -0700 | [diff] [blame] | 967 | mCamera.setFocusStateListener(null); |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 968 | mCamera.close(null); |
| 969 | mCamera = null; |
| 970 | } |
| 971 | } |
| 972 | |
| 973 | private int getOrientation() { |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 974 | if (mAppController.isAutoRotateScreen()) { |
Sol Boucher | 73ec985 | 2014-07-24 23:59:21 -0700 | [diff] [blame] | 975 | return mDisplayRotation; |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 976 | } else { |
| 977 | return mOrientation; |
| 978 | } |
| 979 | } |
| 980 | |
| 981 | /** |
| 982 | * @return Whether we are resuming from within the lockscreen. |
| 983 | */ |
| 984 | private static boolean isResumeFromLockscreen(Activity activity) { |
| 985 | String action = activity.getIntent().getAction(); |
| 986 | return (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(action) |
| 987 | || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action)); |
| 988 | } |
| 989 | |
| 990 | private void switchCamera(Facing switchTo) { |
| 991 | if (mPaused || mCameraFacing == switchTo) { |
| 992 | return; |
| 993 | } |
| 994 | // TODO: Un-comment once we have timer back. |
| 995 | // cancelCountDown(); |
| 996 | |
| 997 | mAppController.freezeScreenUntilPreviewReady(); |
| 998 | |
| 999 | mCameraFacing = switchTo; |
| 1000 | initSurface(mPreviewTexture); |
| 1001 | |
| 1002 | // TODO: Un-comment once we have focus back. |
| 1003 | // if (mFocusManager != null) { |
| 1004 | // mFocusManager.removeMessages(); |
| 1005 | // } |
| 1006 | // mFocusManager.setMirror(mMirror); |
| 1007 | } |
| 1008 | |
| 1009 | private Size getPictureSizeFromSettings() { |
| 1010 | String pictureSizeKey = mCameraFacing == Facing.FRONT ? Keys.KEY_PICTURE_SIZE_FRONT |
| 1011 | : Keys.KEY_PICTURE_SIZE_BACK; |
| 1012 | return mSettingsManager.getSize(SettingsManager.SCOPE_GLOBAL, pictureSizeKey); |
| 1013 | } |
| 1014 | |
| 1015 | private int getPreviewOrientation(int deviceOrientationDegrees) { |
| 1016 | // Important: Camera2 buffers are already rotated to the natural |
| 1017 | // orientation of the device (at least for the back-camera). |
| 1018 | |
| 1019 | // TODO: Remove this hack for the front camera as soon as b/16637957 is |
| 1020 | // fixed. |
| 1021 | if (mCameraFacing == Facing.FRONT) { |
| 1022 | deviceOrientationDegrees += 180; |
| 1023 | } |
| 1024 | return (360 - deviceOrientationDegrees) % 360; |
| 1025 | } |
| 1026 | |
| 1027 | /** |
| 1028 | * Returns which way around the camera is facing, based on it's ID. |
| 1029 | * <p> |
| 1030 | * TODO: This needs to change so that we store the direction directly in the |
| 1031 | * settings, rather than a Camera ID. |
| 1032 | */ |
| 1033 | private static Facing getFacingFromCameraId(int cameraId) { |
| 1034 | return cameraId == 1 ? Facing.FRONT : Facing.BACK; |
| 1035 | } |
| 1036 | |
| 1037 | private void resetTextureBufferSize() { |
| 1038 | // Reset the default buffer sizes on the shared SurfaceTexture |
| 1039 | // so they are not scaled for gcam. |
| 1040 | // |
Sascha Haeberling | 0cf4a02 | 2014-07-31 11:36:58 -0700 | [diff] [blame] | 1041 | // According to the documentation for |
| 1042 | // SurfaceTexture.setDefaultBufferSize, |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 1043 | // photo and video based image producers (presumably only Camera 1 api), |
Sascha Haeberling | 0cf4a02 | 2014-07-31 11:36:58 -0700 | [diff] [blame] | 1044 | // override this buffer size. Any module that uses egl to render to a |
| 1045 | // SurfaceTexture must have these buffer sizes reset manually. Otherwise |
| 1046 | // the SurfaceTexture cannot be transformed by matrix set on the |
| 1047 | // TextureView. |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 1048 | if (mPreviewTexture != null) { |
| 1049 | mPreviewTexture.setDefaultBufferSize(mAppController.getCameraAppUI().getSurfaceWidth(), |
Sascha Haeberling | 0cf4a02 | 2014-07-31 11:36:58 -0700 | [diff] [blame] | 1050 | mAppController.getCameraAppUI().getSurfaceHeight()); |
| 1051 | } |
| 1052 | } |
| 1053 | |
| 1054 | /** |
| 1055 | * @return The currently set Flash settings. Defaults to AUTO if the setting |
| 1056 | * could not be parsed. |
| 1057 | */ |
| 1058 | private Flash getFlashModeFromSettings() { |
| 1059 | String flashSetting = mSettingsManager.getString(mAppController.getCameraScope(), |
| 1060 | Keys.KEY_FLASH_MODE); |
| 1061 | try { |
| 1062 | return Flash.valueOf(flashSetting.toUpperCase()); |
| 1063 | } catch (IllegalArgumentException ex) { |
| 1064 | Log.w(TAG, "Could not parse Flash Setting. Defaulting to AUTO."); |
| 1065 | return Flash.AUTO; |
Sascha Haeberling | 08b3c94 | 2014-07-30 17:48:52 -0700 | [diff] [blame] | 1066 | } |
| 1067 | } |
| 1068 | } |