import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.location.Location;
-import android.media.AudioManager;
import android.media.CameraProfile;
-import android.media.SoundPool;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import com.android.ex.camera2.portability.CameraAgent.CameraPictureCallback;
import com.android.ex.camera2.portability.CameraAgent.CameraProxy;
import com.android.ex.camera2.portability.CameraAgent.CameraShutterCallback;
+import com.android.ex.camera2.portability.CameraAgent.CameraStartPreviewCallback;
import com.android.ex.camera2.portability.CameraCapabilities;
import com.android.ex.camera2.portability.CameraDeviceInfo.Characteristics;
import com.android.ex.camera2.portability.CameraSettings;
RemoteCameraModule,
CountDownView.OnCountDownStatusListener {
- private static final String PHOTO_MODULE_STRING_ID = "PhotoModule";
+ public static final String PHOTO_MODULE_STRING_ID = "PhotoModule";
private static final Log.Tag TAG = new Log.Tag(PHOTO_MODULE_STRING_ID);
// needed to be updated in mUpdateSet.
private int mUpdateSet;
- private int mZoomValue; // The current zoom value.
+ private float mZoomValue; // The current zoom ratio.
private int mTimerDuration;
/** Set when a volume button is clicked to take photo */
private boolean mVolumeButtonClickedFlag = false;
private FocusOverlayManager mFocusManager;
private final int mGcamModeIndex;
- private final CountdownSoundPlayer mCountdownSoundPlayer = new CountdownSoundPlayer();
+ private SoundPlayer mCountdownSoundPlayer;
private CameraCapabilities.SceneMode mSceneMode;
// in the new module. The new module will set the enabled/disabled
// of this button when the module's preferred camera becomes available.
ButtonManager buttonManager = mActivity.getButtonManager();
- buttonManager.disableButton(ButtonManager.BUTTON_HDR_PLUS);
+
+ buttonManager.disableButtonClick(ButtonManager.BUTTON_HDR_PLUS);
mAppController.getCameraAppUI().freezeScreenUntilPreviewReady();
// Do not post this to avoid this module switch getting interleaved with
// other button callbacks.
mActivity.onModeSelected(mGcamModeIndex);
+
+ buttonManager.enableButtonClick(ButtonManager.BUTTON_HDR_PLUS);
}
}
mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
mSensorManager = (SensorManager) (mActivity.getSystemService(Context.SENSOR_SERVICE));
mUI.setCountdownFinishedListener(this);
+ mCountdownSoundPlayer = new SoundPlayer(mAppController.getAndroidContext());
// TODO: Make this a part of app controller API.
View cancelButton = mActivity.findViewById(R.id.shutter_cancel_button);
private void onPreviewStarted() {
mAppController.onPreviewStarted();
+ mAppController.setShutterEnabled(true);
setCameraState(IDLE);
startFaceDetection();
settingsFirstRun();
}, createAspectRatioDialogCallback());
} else {
// App upgrade. Only show aspect ratio selection.
- mUI.showAspectRatioDialog(createAspectRatioDialogCallback());
+ boolean wasShown = mUI.showAspectRatioDialog(createAspectRatioDialogCallback());
+ if (!wasShown) {
+ // If the dialog was not shown, set this flag to true so that we
+ // never have to check for it again. It means that we don't need
+ // to show the dialog on this device.
+ mActivity.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL,
+ Keys.KEY_USER_SELECTED_ASPECT_RATIO, true);
+ }
}
}
Log.i(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId);
closeCamera();
mCameraId = mPendingSwitchCameraId;
+
settingsManager.set(mAppController.getModuleScope(), Keys.KEY_CAMERA_ID, mCameraId);
requestCameraOpen();
mUI.clearFaces();
* device, using {@link GservicesHelper} to choose between API-1 and API-2.
*/
private void requestCameraOpen() {
+ Log.v(TAG, "requestCameraOpen");
mActivity.getCameraProvider().requestCamera(mCameraId,
GservicesHelper.useCamera2ApiThroughPortabilityLayer(mActivity));
}
@Override
public void onStateChanged(int state) {
SettingsManager settingsManager = mActivity.getSettingsManager();
- if (GcamHelper.hasGcamCapture()) {
+ if (GcamHelper.hasGcamAsSeparateModule()) {
// Set the camera setting to default backfacing.
settingsManager.setToDefault(mAppController.getModuleScope(),
Keys.KEY_CAMERA_ID);
// PhotoModule should hard reset HDR+ to off,
// and HDR to off if HDR+ is supported.
settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR_PLUS, false);
- if (GcamHelper.hasGcamCapture()) {
+ if (GcamHelper.hasGcamAsSeparateModule()) {
settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR, false);
}
}
int xOffset = (originalWidth - newWidth)/2;
int yOffset = (originalHeight - newHeight)/2;
+ // For some reason L needs this to work.
+ // This code is only run on the Nexus 5.
+ // TODO: Determine why this is needed.
+ if (Build.VERSION.SDK_INT >= 21 || Build.VERSION.CODENAME.equals("L")) {
+ Log.v(TAG,"xOffset = " + xOffset);
+ Log.v(TAG,"yOffset = " + yOffset);
+ xOffset *= 2;
+ yOffset = 0;
+ Log.v(TAG,"new xOffset = " + xOffset);
+ Log.v(TAG,"new yOffset = " + yOffset);
+ }
+
if (xOffset < 0 || yOffset < 0) {
return dataBundle;
}
int orientation = Exif.getOrientation(exif);
- float zoomValue = 0f;
+ float zoomValue = 1.0f;
if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
- int zoomIndex = mCameraSettings.getCurrentZoomIndex();
- List<Integer> zoomRatios = mCameraCapabilities.getZoomRatioList();
- if (zoomRatios != null && zoomIndex < zoomRatios.size()) {
- zoomValue = 0.01f * zoomRatios.get(zoomIndex);
- }
+ zoomValue = mCameraSettings.getCurrentZoomRatio();
}
-
boolean hdrOn = CameraCapabilities.SceneMode.HDR == mSceneMode;
String flashSetting =
mActivity.getSettingsManager().getString(mAppController.getCameraScope(),
animateAfterShutter();
}
- // Set rotation and gps data.
- int orientation;
-
- if (mActivity.isAutoRotateScreen()) {
- orientation = mDisplayRotation;
- } else {
- orientation = mOrientation;
- }
- Characteristics info =
- mActivity.getCameraProvider().getCharacteristics(mCameraId);
- mJpegRotation = info.getJpegOrientation(orientation);
Location loc = mActivity.getLocationManager().getCurrentLocation();
CameraUtil.setGpsParameters(mCameraSettings, loc);
mCameraDevice.applySettings(mCameraSettings);
+ // Set JPEG orientation. Even if screen UI is locked in portrait, camera orientation should
+ // still match device orientation (e.g., users should always get landscape photos while
+ // capturing by putting device in landscape.)
+ int orientation = mActivity.isAutoRotateScreen() ? mDisplayRotation : mOrientation;
+ Characteristics info = mActivity.getCameraProvider().getCharacteristics(mCameraId);
+ mJpegRotation = info.getJpegOrientation(orientation);
+ mCameraDevice.setJpegOrientation(mJpegRotation);
+
// We don't want user to press the button again while taking a
// multi-second HDR photo.
mAppController.setShutterEnabled(false);
@Override
public void onOrientationChanged(int orientation) {
- // We keep the last known orientation. So if the user first orient
- // the camera then point the camera to floor or sky, we still have
- // the correct orientation.
if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) {
return;
}
- mOrientation = CameraUtil.roundOrientation(orientation, mOrientation);
+
+ // TODO: Document orientation compute logic and unify them in OrientationManagerImpl.
+ // b/17443789
+ // Flip to counter-clockwise orientation.
+ mOrientation = (360 - orientation) % 360;
}
@Override
public void onCameraAvailable(CameraProxy cameraProxy) {
+ Log.v(TAG, "onCameraAvailable");
if (mPaused) {
return;
}
initializeCapabilities();
// Reset zoom value index.
- mZoomValue = 0;
+ mZoomValue = 1.0f;
if (mFocusManager == null) {
initializeFocusManager();
}
outputStream.write(data);
outputStream.close();
+ Log.v(TAG, "saved result to URI: " + mSaveUri);
mActivity.setResultEx(Activity.RESULT_OK);
mActivity.finish();
} catch (IOException ex) {
+ Log.w(TAG, "exception saving result to URI: " + mSaveUri, ex);
// ignore exception
} finally {
CameraUtil.closeSilently(outputStream);
int orientation = Exif.getOrientation(exif);
Bitmap bitmap = CameraUtil.makeBitmap(data, 50 * 1024);
bitmap = CameraUtil.rotate(bitmap, orientation);
+ Log.v(TAG, "inlined bitmap into capture intent result");
mActivity.setResultEx(Activity.RESULT_OK,
new Intent("inline-data").putExtra("data", bitmap));
mActivity.finish();
tempStream.write(data);
tempStream.close();
tempUri = Uri.fromFile(path);
+ Log.v(TAG, "wrote temp file for cropping to: " + sTempCropFilename);
} catch (FileNotFoundException ex) {
+ Log.w(TAG, "error writing temp cropping file to: " + sTempCropFilename, ex);
mActivity.setResultEx(Activity.RESULT_CANCELED);
mActivity.finish();
return;
} catch (IOException ex) {
+ Log.w(TAG, "error writing temp cropping file to: " + sTempCropFilename, ex);
mActivity.setResultEx(Activity.RESULT_CANCELED);
mActivity.finish();
return;
newExtras.putString("circleCrop", "true");
}
if (mSaveUri != null) {
+ Log.v(TAG, "setting output of cropped file to: " + mSaveUri);
newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri);
} else {
newExtras.putBoolean(CameraUtil.KEY_RETURN_DATA, true);
cropIntent.setData(tempUri);
cropIntent.putExtras(newExtras);
-
+ Log.v(TAG, "starting CROP intent for capture");
mActivity.startActivityForResult(cropIntent, REQUEST_CROP);
}
}
@Override
public void onRemainingSecondsChanged(int remainingSeconds) {
- mCountdownSoundPlayer.onRemainingSecondsChanged(remainingSeconds);
+ if (remainingSeconds == 1) {
+ mCountdownSoundPlayer.play(R.raw.beep_twice, 0.6f);
+ } else if (remainingSeconds == 2 || remainingSeconds == 3) {
+ mCountdownSoundPlayer.play(R.raw.beep_once, 0.6f);
+ }
}
@Override
}
Log.v(TAG, "Executing onResumeTasks.");
- mCountdownSoundPlayer.loadSounds();
+ mCountdownSoundPlayer.loadSound(R.raw.beep_once);
+ mCountdownSoundPlayer.loadSound(R.raw.beep_twice);
if (mFocusManager != null) {
// If camera is not open when resume is called, focus manager will
// not be initialized yet, in which case it will start listening to
requestCameraOpen();
mJpegPictureCallbackTime = 0;
- mZoomValue = 0;
+ mZoomValue = 1.0f;
mOnResumeTime = SystemClock.uptimeMillis();
checkDisplayRotation();
mCameraDevice.setPreviewTexture(mActivity.getCameraAppUI().getSurfaceTexture());
Log.i(TAG, "startPreview");
- mCameraDevice.startPreview();
+ mCameraDevice.startPreviewWithCallback(mHandler, new CameraStartPreviewCallback() {
+ @Override
+ public void onPreviewStarted() {
+ mFocusManager.onPreviewStarted();
+ PhotoModule.this.onPreviewStarted();
+ }
+ });
- mFocusManager.onPreviewStarted();
- onPreviewStarted();
SessionStatsCollector.instance().previewActive(true);
if (mSnapshotOnIdle) {
mHandler.post(mDoSnapRunnable);
private void updateCameraParametersZoom() {
// Set zoom.
if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
- mCameraSettings.setZoomIndex(mZoomValue);
+ mCameraSettings.setZoomRatio(mZoomValue);
}
}
(double) size.width() / size.height());
Size original = mCameraSettings.getCurrentPreviewSize();
if (!optimalSize.equals(original)) {
+ Log.v(TAG, "setting preview size");
mCameraSettings.setPreviewSize(optimalSize);
// Zoom related settings will be changed for different preview
}
if (optimalSize.width() != 0 && optimalSize.height() != 0) {
+ Log.v(TAG, "updating aspect ratio");
mUI.updatePreviewAspectRatio((float) optimalSize.width()
/ (float) optimalSize.height());
}
- Log.i(TAG, "Preview size is " + optimalSize);
+ Log.d(TAG, "Preview size is " + optimalSize);
}
private void updateParametersPictureQuality() {
mCameraCapabilities.supports(CameraCapabilities.FocusMode.CONTINUOUS_PICTURE);
}
- // TODO: Use zoomRatio device API rather than deprecated zoomIndex
@Override
- public int onZoomChanged(int index) {
+ public void onZoomChanged(float ratio) {
// Not useful to change zoom value when the activity is paused.
if (mPaused) {
- return index;
+ return;
}
- mZoomValue = index;
+ mZoomValue = ratio;
if (mCameraSettings == null || mCameraDevice == null) {
- return index;
+ return;
}
// Set zoom parameters asynchronously
- mCameraSettings.setZoomIndex(mZoomValue);
+ mCameraSettings.setZoomRatio(mZoomValue);
mCameraDevice.applySettings(mCameraSettings);
- CameraSettings settings = mCameraDevice.getSettings();
- if (settings != null) {
- return settings.getCurrentZoomIndex();
- }
- return index;
}
@Override
}
});
}
-
- /**
- * This class manages the loading/releasing/playing of the sounds needed for
- * countdown timer.
- */
- private class CountdownSoundPlayer {
- private SoundPool mSoundPool;
- private int mBeepOnce;
- private int mBeepTwice;
-
- void loadSounds() {
- // Load the beeps.
- if (mSoundPool == null) {
- mSoundPool = new SoundPool(1, AudioManager.STREAM_NOTIFICATION, 0);
- mBeepOnce = mSoundPool.load(mAppController.getAndroidContext(), R.raw.beep_once, 1);
- mBeepTwice = mSoundPool.load(mAppController.getAndroidContext(), R.raw.beep_twice, 1);
- }
- }
-
- void onRemainingSecondsChanged(int newVal) {
- if (mSoundPool == null) {
- Log.e(TAG, "Cannot play sound - they have not been loaded.");
- return;
- }
- if (newVal == 1) {
- mSoundPool.play(mBeepTwice, 1.0f, 1.0f, 0, 0, 1.0f);
- } else if (newVal == 2 || newVal == 3) {
- mSoundPool.play(mBeepOnce, 1.0f, 1.0f, 0, 0, 1.0f);
- }
- }
-
- void release() {
- if (mSoundPool != null) {
- mSoundPool.release();
- mSoundPool = null;
- }
- }
- }
}