summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorByunghun Jeon <bjeon@codeaurora.org>2016-06-08 14:41:47 -0700
committerSteve Kondik <steve@cyngn.com>2016-08-21 18:46:32 -0700
commitc456f576b25812e11c6205149b234937ba9c1b5b (patch)
treecc436a34aa196ef2baee8b634850b8de33f4b4ce
parent3d1baa4bbfe7b889e955228ee09ce0eb92fe4e49 (diff)
downloadandroid_packages_apps_Snap-c456f576b25812e11c6205149b234937ba9c1b5b.zip
android_packages_apps_Snap-c456f576b25812e11c6205149b234937ba9c1b5b.tar.gz
android_packages_apps_Snap-c456f576b25812e11c6205149b234937ba9c1b5b.tar.bz2
SnapdragonCamera: Add face detection to camera2
Add face detection to camera2. Receive face data from camera2 and use faceView UI to draw it Change-Id: Id9c49ab6dd73de316398c57981cc0b9df0400b45 CRs-Fixed: 1025797
-rw-r--r--res/layout/capture_module.xml11
-rw-r--r--res/values/camera2arrays.xml10
-rw-r--r--res/xml/capture_preferences.xml8
-rw-r--r--src/com/android/camera/CaptureModule.java172
-rw-r--r--src/com/android/camera/CaptureUI.java59
-rw-r--r--src/com/android/camera/SettingsManager.java22
-rw-r--r--src/com/android/camera/ui/Camera2FaceView.java147
-rw-r--r--src/com/android/camera/ui/FaceView.java34
8 files changed, 388 insertions, 75 deletions
diff --git a/res/layout/capture_module.xml b/res/layout/capture_module.xml
index 3b4f025..07594ce 100644
--- a/res/layout/capture_module.xml
+++ b/res/layout/capture_module.xml
@@ -51,6 +51,17 @@
android:layout_height="match_parent"
android:background="@android:color/black" />
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <com.android.camera.ui.Camera2FaceView
+ android:id="@+id/face_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="gone"/>
+ </FrameLayout>
+
<com.android.camera.ui.RenderOverlay
android:id="@+id/render_overlay"
android:layout_width="match_parent"
diff --git a/res/values/camera2arrays.xml b/res/values/camera2arrays.xml
index b2cb979..ac569f4 100644
--- a/res/values/camera2arrays.xml
+++ b/res/values/camera2arrays.xml
@@ -785,4 +785,14 @@ for time lapse recording -->
<item>@string/pref_video_time_lapse_frame_interval_54000000</item>
<item>@string/pref_video_time_lapse_frame_interval_86400000</item>
</string-array>
+
+ <string-array name="pref_camera2_facedetection_entries" translatable="false">
+ <item>@string/pref_camera_facedetection_entry_off</item>
+ <item>@string/pref_camera_facedetection_entry_on</item>
+ </string-array>
+
+ <string-array name="pref_camera2_facedetection_entryvalues" translatable="false">
+ <item>off</item>
+ <item>on</item>
+ </string-array>
</resources>
diff --git a/res/xml/capture_preferences.xml b/res/xml/capture_preferences.xml
index 697770b..06f816c 100644
--- a/res/xml/capture_preferences.xml
+++ b/res/xml/capture_preferences.xml
@@ -258,4 +258,12 @@
camera:entryValues="@array/pref_camera2_video_time_lapse_frame_interval_entryvalues"
camera:key="pref_camera2_video_time_lapse_frame_interval_key"
camera:title="@string/pref_video_time_lapse_frame_interval_title"/>
+
+ <IconListPreference
+ camera:defaultValue="@string/pref_camera_facedetection_default"
+ camera:entries="@array/pref_camera2_facedetection_entries"
+ camera:entryValues="@array/pref_camera2_facedetection_entryvalues"
+ camera:key="pref_camera2_facedetection_key"
+ camera:singleIcon="@drawable/ic_settings_facerec"
+ camera:title="@string/pref_camera_facedetection_title"/>
</PreferenceGroup>
diff --git a/src/com/android/camera/CaptureModule.java b/src/com/android/camera/CaptureModule.java
index 336e7b4..9fa4789 100644
--- a/src/com/android/camera/CaptureModule.java
+++ b/src/com/android/camera/CaptureModule.java
@@ -388,7 +388,7 @@ public class CaptureModule implements CameraModule, PhotoController,
private CameraCaptureSession.CaptureCallback mCaptureCallback
= new CameraCaptureSession.CaptureCallback() {
- private void updateState(CaptureResult result) {
+ private void processCaptureResult(CaptureResult result) {
int id = (int) result.getRequest().getTag();
if (!mFirstPreviewLoaded) {
@@ -408,58 +408,7 @@ public class CaptureModule implements CameraModule, PhotoController,
}
mPreviewCaptureResult = result;
- switch (mState[id]) {
- case STATE_PREVIEW: {
- break;
- }
- case STATE_WAITING_LOCK: {
- Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);
- Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
- Log.d(TAG, "STATE_WAITING_LOCK id: " + id + " afState:" + afState + " aeState:" + aeState);
- // AF_PASSIVE is added for continous auto focus mode
- if (CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState ||
- CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState ||
- CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED == afState ||
- CaptureResult.CONTROL_AF_STATE_PASSIVE_UNFOCUSED == afState ||
- (mLockRequestHashCode[id] == result.getRequest().hashCode() &&
- afState == CaptureResult.CONTROL_AF_STATE_INACTIVE)) {
- // CONTROL_AE_STATE can be null on some devices
- if (aeState == null || (aeState == CaptureResult
- .CONTROL_AE_STATE_CONVERGED) && isFlashOff(id)) {
- mState[id] = STATE_PICTURE_TAKEN;
- captureStillPicture(id);
- } else {
- runPrecaptureSequence(id);
- }
- }
- break;
- }
- case STATE_WAITING_PRECAPTURE: {
- // CONTROL_AE_STATE can be null on some devices
- Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
- Log.d(TAG, "STATE_WAITING_PRECAPTURE id: " + id + " aeState:" + aeState);
- if (aeState == null ||
- aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE ||
- aeState == CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED ||
- aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) {
- if (mPrecaptureRequestHashCode[id] == result.getRequest().hashCode())
- mState[id] = STATE_WAITING_NON_PRECAPTURE;
- }
- break;
- }
- case STATE_WAITING_NON_PRECAPTURE: {
- // CONTROL_AE_STATE can be null on some devices
- Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
- Log.d(TAG, "STATE_WAITING_NON_PRECAPTURE id: " + id + " aeState:" + aeState);
- if (aeState == null || aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) {
- mState[id] = STATE_PICTURE_TAKEN;
- captureStillPicture(id);
- }
- break;
- }
- case STATE_WAITING_TOUCH_FOCUS:
- break;
- }
+ updateCaptureStateMachine(id, result);
}
@Override
@@ -468,6 +417,8 @@ public class CaptureModule implements CameraModule, PhotoController,
CaptureResult partialResult) {
int id = (int) partialResult.getRequest().getTag();
if (id == getMainCameraId()) updateFocusStateChange(partialResult);
+ Face[] faces = partialResult.get(CaptureResult.STATISTICS_FACES);
+ updateFaceView(faces);
}
@Override
@@ -476,7 +427,9 @@ public class CaptureModule implements CameraModule, PhotoController,
TotalCaptureResult result) {
int id = (int) result.getRequest().getTag();
if (id == getMainCameraId()) updateFocusStateChange(result);
- updateState(result);
+ Face[] faces = result.get(CaptureResult.STATISTICS_FACES);
+ updateFaceView(faces);
+ processCaptureResult(result);
}
};
@@ -540,6 +493,67 @@ public class CaptureModule implements CameraModule, PhotoController,
};
+ private void updateCaptureStateMachine(int id, CaptureResult result) {
+ switch (mState[id]) {
+ case STATE_PREVIEW: {
+ break;
+ }
+ case STATE_WAITING_LOCK: {
+ Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);
+ Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
+ Log.d(TAG, "STATE_WAITING_LOCK id: " + id + " afState:" + afState + " aeState:" + aeState);
+ // AF_PASSIVE is added for continous auto focus mode
+ if (CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState ||
+ CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState ||
+ CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED == afState ||
+ CaptureResult.CONTROL_AF_STATE_PASSIVE_UNFOCUSED == afState ||
+ (mLockRequestHashCode[id] == result.getRequest().hashCode() &&
+ afState == CaptureResult.CONTROL_AF_STATE_INACTIVE)) {
+ // CONTROL_AE_STATE can be null on some devices
+ if (aeState == null || (aeState == CaptureResult
+ .CONTROL_AE_STATE_CONVERGED) && isFlashOff(id)) {
+ mState[id] = STATE_PICTURE_TAKEN;
+ captureStillPicture(id);
+ } else {
+ runPrecaptureSequence(id);
+ }
+ }
+ break;
+ }
+ case STATE_WAITING_PRECAPTURE: {
+ // CONTROL_AE_STATE can be null on some devices
+ Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
+ Log.d(TAG, "STATE_WAITING_PRECAPTURE id: " + id + " aeState:" + aeState);
+ if (aeState == null ||
+ aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE ||
+ aeState == CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED ||
+ aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) {
+ if (mPrecaptureRequestHashCode[id] == result.getRequest().hashCode())
+ mState[id] = STATE_WAITING_NON_PRECAPTURE;
+ }
+ break;
+ }
+ case STATE_WAITING_NON_PRECAPTURE: {
+ // CONTROL_AE_STATE can be null on some devices
+ Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
+ Log.d(TAG, "STATE_WAITING_NON_PRECAPTURE id: " + id + " aeState:" + aeState);
+ if (aeState == null || aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) {
+ mState[id] = STATE_PICTURE_TAKEN;
+ captureStillPicture(id);
+ }
+ break;
+ }
+ case STATE_WAITING_TOUCH_FOCUS:
+ break;
+ }
+ }
+
+ public void startFaceDetection() {
+ mUI.onStartFaceDetection(mDisplayOrientation,
+ mSettingsManager.isFacingFront(getMainCameraId()),
+ mSettingsManager.getSensorActiveArraySize(getMainCameraId()));
+ }
+
private boolean isMonoPreviewOn() {
String value = mSettingsManager.getValue(SettingsManager.KEY_MONO_PREVIEW);
if (value == null) return false;
@@ -672,6 +686,8 @@ public class CaptureModule implements CameraModule, PhotoController,
mCaptureSession[id] = cameraCaptureSession;
mPreviewSession = cameraCaptureSession;
initializePreviewConfiguration(id);
+ setDisplayOrientation();
+ updateFaceDetection();
try {
if (isBackCamera() && getCameraMode() == DUAL_MODE) {
linkBayerMono(id);
@@ -1324,6 +1340,7 @@ public class CaptureModule implements CameraModule, PhotoController,
private void applyCommonSettings(CaptureRequest.Builder builder, int id) {
builder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
builder.set(CaptureRequest.CONTROL_AF_MODE, mControlAFMode);
+ applyFaceDetection(builder);
applyFlash(builder, id);
applyWhiteBalance(builder);
applyExposure(builder);
@@ -1409,6 +1426,7 @@ public class CaptureModule implements CameraModule, PhotoController,
@Override
public void onPauseBeforeSuper() {
mPaused = true;
+ mUI.onPause();
if (mIsRecordingVideo) {
stopRecordingVideo(getMainCameraId());
}
@@ -1426,7 +1444,6 @@ public class CaptureModule implements CameraModule, PhotoController,
mUI.hideSurfaceView();
mFirstPreviewLoaded = false;
stopBackgroundThread();
- mUI.onPause();
}
@Override
@@ -1530,6 +1547,7 @@ public class CaptureModule implements CameraModule, PhotoController,
@Override
public void onConfigurationChanged(Configuration config) {
+ Log.v(TAG, "onConfigurationChanged");
setDisplayOrientation();
}
@@ -1653,7 +1671,11 @@ public class CaptureModule implements CameraModule, PhotoController,
return;
}
Log.d(TAG, "onSingleTapUp " + x + " " + y);
+ int[] newXY = {x, y};
+ if (!mUI.isOverSurfaceView(newXY)) return;
mUI.setFocusPosition(x, y);
+ x = newXY[0];
+ y = newXY[1];
mUI.onFocusStarted();
if (isBackCamera()) {
switch (getCameraMode()) {
@@ -1709,6 +1731,17 @@ public class CaptureModule implements CameraModule, PhotoController,
return false;
}
+ private void updateFaceView(final Face[] faces) {
+ if (faces != null) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mUI.onFaceDetection(faces);
+ }
+ });
+ }
+ }
+
@Override
public void onCountDownFinished() {
mUI.showUIAfterCountDown();
@@ -2549,6 +2582,14 @@ public class CaptureModule implements CameraModule, PhotoController,
}
}
+ private void applyFaceDetection(CaptureRequest.Builder request) {
+ String value = mSettingsManager.getValue(SettingsManager.KEY_FACE_DETECTION);
+ if (value != null) {
+ request.set(CaptureRequest.STATISTICS_FACE_DETECT_MODE,
+ CaptureRequest.STATISTICS_FACE_DETECT_MODE_SIMPLE);
+ }
+ }
+
private void applyFlash(CaptureRequest.Builder request, int id) {
if (mSettingsManager.isFlashSupported(id)) {
String value = mSettingsManager.getValue(SettingsManager.KEY_FLASH_MODE);
@@ -2701,6 +2742,9 @@ public class CaptureModule implements CameraModule, PhotoController,
updatePictureSize();
if (count == 0) restart();
return;
+ case SettingsManager.KEY_FACE_DETECTION:
+ updateFaceDetection();
+ break;
case SettingsManager.KEY_CAMERA_ID:
case SettingsManager.KEY_MONO_ONLY:
case SettingsManager.KEY_CLEARSIGHT:
@@ -2762,6 +2806,21 @@ public class CaptureModule implements CameraModule, PhotoController,
}
}
+ private void updateFaceDetection() {
+ final String value = mSettingsManager.getValue(SettingsManager.KEY_FACE_DETECTION);
+ mActivity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ if (value == null || value.equals("off")) mUI.onStopFaceDetection();
+ else {
+ mUI.onStartFaceDetection(mDisplayOrientation,
+ mSettingsManager.isFacingFront(getMainCameraId()),
+ mSettingsManager.getSensorActiveArraySize(getMainCameraId()));
+ }
+ }
+ });
+ }
+
private int mCurrentMode;
private boolean checkNeedToRestart(String value) {
@@ -2794,6 +2853,9 @@ public class CaptureModule implements CameraModule, PhotoController,
private Size getOptimalPreviewSize(Size pictureSize, Size[] prevSizes, int screenW, int
screenH) {
+ if (pictureSize.getWidth() <= screenH && pictureSize.getHeight() <= screenW) {
+ return pictureSize;
+ }
Size optimal = prevSizes[0];
float ratio = (float) pictureSize.getWidth() / pictureSize.getHeight();
for (Size prevSize: prevSizes) {
diff --git a/src/com/android/camera/CaptureUI.java b/src/com/android/camera/CaptureUI.java
index f10be57..c6576d7 100644
--- a/src/com/android/camera/CaptureUI.java
+++ b/src/com/android/camera/CaptureUI.java
@@ -23,6 +23,7 @@ import android.animation.Animator;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Point;
+import android.graphics.Rect;
import android.graphics.drawable.AnimationDrawable;
import android.hardware.Camera.Face;
import android.text.TextUtils;
@@ -42,6 +43,7 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.camera.ui.AutoFitSurfaceView;
+import com.android.camera.ui.Camera2FaceView;
import com.android.camera.ui.CameraControls;
import com.android.camera.ui.CountDownView;
import com.android.camera.ui.FocusIndicator;
@@ -95,6 +97,7 @@ public class CaptureUI implements FocusOverlayManager.FocusUI,
SettingsManager.KEY_WHITE_BALANCE,
SettingsManager.KEY_CAMERA2,
SettingsManager.KEY_MAKEUP,
+ SettingsManager.KEY_FACE_DETECTION,
SettingsManager.KEY_VIDEO_FLASH_MODE,
SettingsManager.KEY_VIDEO_DURATION,
SettingsManager.KEY_VIDEO_QUALITY
@@ -133,6 +136,7 @@ public class CaptureUI implements FocusOverlayManager.FocusUI,
private SettingsManager mSettingsManager;
private ImageView mThumbnail;
+ private Camera2FaceView mFaceView;
private SurfaceHolder.Callback callback = new SurfaceHolder.Callback() {
@@ -239,6 +243,18 @@ public class CaptureUI implements FocusOverlayManager.FocusUI,
mSurfaceView2.setZOrderMediaOverlay(true);
mSurfaceHolder = mSurfaceView.getHolder();
mSurfaceHolder.addCallback(callback);
+ mSurfaceView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right,
+ int bottom, int oldLeft, int oldTop, int oldRight,
+ int oldBottom) {
+ int width = right - left;
+ int height = bottom - top;
+ if (mFaceView != null) {
+ mFaceView.onSurfaceTextureSizeChanged(width, height);
+ }
+ }
+ });
mSurfaceHolder2 = mSurfaceView2.getHolder();
mSurfaceHolder2.addCallback(callback2);
@@ -297,6 +313,7 @@ public class CaptureUI implements FocusOverlayManager.FocusUI,
muteButton.setVisibility(View.GONE);
mCameraControls = (CameraControls) mRootView.findViewById(R.id.camera_controls);
+ mFaceView = (Camera2FaceView) mRootView.findViewById(R.id.face_view);
Point size = new Point();
mActivity.getWindowManager().getDefaultDisplay().getSize(size);
@@ -1302,6 +1319,7 @@ public class CaptureUI implements FocusOverlayManager.FocusUI,
public void onPause() {
cancelCountDown();
collapseCameraControls();
+ if (mFaceView != null) mFaceView.clear();
}
public boolean collapseCameraControls() {
@@ -1314,15 +1332,16 @@ public class CaptureUI implements FocusOverlayManager.FocusUI,
}
private FocusIndicator getFocusIndicator() {
- return mPieRenderer;
+ return (mFaceView != null && mFaceView.faceExists()) ? mFaceView : mPieRenderer;
}
@Override
public boolean hasFaces() {
- return false;
+ return (mFaceView != null && mFaceView.faceExists());
}
public void clearFaces() {
+ if (mFaceView != null) mFaceView.clear();
}
@Override
@@ -1362,16 +1381,31 @@ public class CaptureUI implements FocusOverlayManager.FocusUI,
public void resumeFaceDetection() {
}
- public void onStartFaceDetection(int orientation, boolean mirror) {
+ public void onStartFaceDetection(int orientation, boolean mirror, Rect cameraBound) {
+ mFaceView.setBlockDraw(false);
+ mFaceView.clear();
+ mFaceView.setVisibility(View.VISIBLE);
+ mFaceView.setDisplayOrientation(orientation);
+ mFaceView.setMirror(mirror);
+ mFaceView.setCameraBound(cameraBound);
+ mFaceView.resume();
}
public void onStopFaceDetection() {
+ if (mFaceView != null) {
+ mFaceView.setBlockDraw(true);
+ mFaceView.clear();
+ }
}
@Override
public void onFaceDetection(Face[] faces, CameraManager.CameraProxy camera) {
}
+ public void onFaceDetection(android.hardware.camera2.params.Face[] faces) {
+ mFaceView.setFaces(faces);
+ }
+
public Point getSurfaceViewSize() {
Point point = new Point();
if (mSurfaceView != null) point.set(mSurfaceView.getWidth(), mSurfaceView.getHeight());
@@ -1415,6 +1449,9 @@ public class CaptureUI implements FocusOverlayManager.FocusUI,
mRecordingTimeRect.setOrientation(orientation, false);
}
}
+ if (mFaceView != null) {
+ mFaceView.setDisplayRotation(orientation);
+ }
if (mCountDownView != null)
mCountDownView.setOrientation(orientation);
RotateTextToast.setOrientation(orientation);
@@ -1442,12 +1479,28 @@ public class CaptureUI implements FocusOverlayManager.FocusUI,
mModule.onSingleTapUp(view, x, y);
}
+ public boolean isOverSurfaceView(int[] xy) {
+ int x = xy[0];
+ int y = xy[1];
+ int[] surfaceViewLocation = new int[2];
+ mSurfaceView.getLocationInWindow(surfaceViewLocation);
+ int surfaceViewX = surfaceViewLocation[0];
+ int surfaceViewY = surfaceViewLocation[1];
+ xy[0] = x - surfaceViewX;
+ xy[1] = y - surfaceViewY;
+ return (x > surfaceViewX) && (x < surfaceViewX + mSurfaceView.getWidth())
+ && (y > surfaceViewY) && (y < surfaceViewY + mSurfaceView.getHeight());
+ }
+
public void onPreviewFocusChanged(boolean previewFocused) {
if (previewFocused) {
showUI();
} else {
hideUI();
}
+ if (mFaceView != null) {
+ mFaceView.setBlockDraw(!previewFocused);
+ }
if (mGestures != null) {
mGestures.setEnabled(previewFocused);
}
diff --git a/src/com/android/camera/SettingsManager.java b/src/com/android/camera/SettingsManager.java
index 7909541..57dc624 100644
--- a/src/com/android/camera/SettingsManager.java
+++ b/src/com/android/camera/SettingsManager.java
@@ -103,6 +103,7 @@ public class SettingsManager implements ListMenu.SettingsListener {
public static final String KEY_VIDEO_ROTATION = "pref_camera2_video_rotation_key";
public static final String KEY_VIDEO_TIME_LAPSE_FRAME_INTERVAL =
"pref_camera2_video_time_lapse_frame_interval_key";
+ public static final String KEY_FACE_DETECTION = "pref_camera2_facedetection_key";
private static final String TAG = "SnapCam_SettingsManager";
private static SettingsManager sInstance;
@@ -464,6 +465,7 @@ public class SettingsManager implements ListMenu.SettingsListener {
ListPreference audioEncoder = mPreferenceGroup.findPreference(KEY_AUDIO_ENCODER);
ListPreference noiseReduction = mPreferenceGroup.findPreference(KEY_NOISE_REDUCTION);
ListPreference videoFlash = mPreferenceGroup.findPreference(KEY_VIDEO_FLASH_MODE);
+ ListPreference faceDetection = mPreferenceGroup.findPreference(KEY_FACE_DETECTION);
if (whiteBalance != null) {
CameraSettings.filterUnsupportedOptions(mPreferenceGroup,
@@ -534,6 +536,11 @@ public class SettingsManager implements ListMenu.SettingsListener {
if (!isFlashAvailable(cameraId))
removePreference(mPreferenceGroup, KEY_VIDEO_FLASH_MODE);
}
+
+ if (faceDetection != null) {
+ if (!isFaceDetectionSupported(cameraId))
+ removePreference(mPreferenceGroup, KEY_FACE_DETECTION);
+ }
}
private void buildExposureCompensation(int cameraId) {
@@ -682,6 +689,21 @@ public class SettingsManager implements ListMenu.SettingsListener {
}
}
+ public boolean isFaceDetectionSupported(int id) {
+ int[] faceDetection = mCharacteristics.get(id).get
+ (CameraCharacteristics.STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES);
+ for (int value: faceDetection) {
+ if (value == CameraMetadata.STATISTICS_FACE_DETECT_MODE_SIMPLE)
+ return true;
+ }
+ return false;
+ }
+
+ public boolean isFacingFront(int id) {
+ int facing = mCharacteristics.get(id).get(CameraCharacteristics.LENS_FACING);
+ return facing == CameraCharacteristics.LENS_FACING_FRONT;
+ }
+
public boolean isFlashSupported(int id) {
return mCharacteristics.get(id).get(CameraCharacteristics.FLASH_INFO_AVAILABLE) &&
mValuesMap.get(KEY_FLASH_MODE) != null;
diff --git a/src/com/android/camera/ui/Camera2FaceView.java b/src/com/android/camera/ui/Camera2FaceView.java
new file mode 100644
index 0000000..40a3469
--- /dev/null
+++ b/src/com/android/camera/ui/Camera2FaceView.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.android.camera.ui;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.hardware.camera2.params.Face;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.util.Log;
+
+import com.android.camera.util.CameraUtil;
+
+public class Camera2FaceView extends FaceView {
+
+ private Face[] mFaces;
+ private Face[] mPendingFaces;
+ private Rect mCameraBound;
+ private Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_SWITCH_FACES:
+ mStateSwitchPending = false;
+ mFaces = mPendingFaces;
+ invalidate();
+ break;
+ }
+ }
+ };
+
+ public Camera2FaceView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void setCameraBound(Rect cameraBound) {
+ mCameraBound = cameraBound;
+ }
+
+ public void setFaces(Face[] faces) {
+ if (LOGV) Log.v(TAG, "Num of faces=" + faces.length);
+ if (mPause) return;
+ if (mFaces != null) {
+ if ((faces.length > 0 && mFaces.length == 0)
+ || (faces.length == 0 && mFaces.length > 0)) {
+ mPendingFaces = faces;
+ if (!mStateSwitchPending) {
+ mStateSwitchPending = true;
+ mHandler.sendEmptyMessageDelayed(MSG_SWITCH_FACES, SWITCH_DELAY);
+ }
+ return;
+ }
+ }
+ if (mStateSwitchPending) {
+ mStateSwitchPending = false;
+ mHandler.removeMessages(MSG_SWITCH_FACES);
+ }
+ mFaces = faces;
+ if (!mBlocked && (mFaces != null) && (mFaces.length > 0) && mCameraBound != null) {
+ invalidate();
+ }
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (!mBlocked && (mFaces != null) && (mFaces.length > 0) && mCameraBound != null) {
+ int rw, rh;
+ rw = mUncroppedWidth;
+ rh = mUncroppedHeight;
+ if (((rh > rw) && ((mDisplayOrientation == 0) || (mDisplayOrientation == 180)))
+ || ((rw > rh) && ((mDisplayOrientation == 90) || (mDisplayOrientation == 270)))) {
+ int temp = rw;
+ rw = rh;
+ rh = temp;
+ }
+ CameraUtil.prepareMatrix(mMatrix, mMirror, mDisplayOrientation, rw, rh);
+
+ // mMatrix assumes that the face coordinates are from -1000 to 1000.
+ // so translate the face coordination to match the assumption.
+ Matrix translateMatrix = new Matrix();
+ translateMatrix.preTranslate(-mCameraBound.width() / 2f, -mCameraBound.height() / 2f);
+ translateMatrix.postScale(2000f / mCameraBound.width(), 2000f / mCameraBound.height());
+
+ int dx = (getWidth() - rw) / 2;
+ ;
+ int dy = (getHeight() - rh) / 2;
+
+ // Focus indicator is directional. Rotate the matrix and the canvas
+ // so it looks correctly in all orientations.
+ canvas.save();
+ mMatrix.postRotate(mOrientation); // postRotate is clockwise
+ canvas.rotate(-mOrientation); // rotate is counter-clockwise (for canvas)
+ for (int i = 0; i < mFaces.length; i++) {
+ if (mFaces[i].getScore() < 50) continue;
+ Rect faceBound = mFaces[i].getBounds();
+ faceBound.offset(-mCameraBound.left, -mCameraBound.top);
+ mRect.set(faceBound);
+ translateMatrix.mapRect(mRect);
+ mMatrix.mapRect(mRect);
+ mPaint.setColor(mColor);
+ mRect.offset(dx, dy);
+
+ canvas.drawOval(mRect, mPaint);
+ }
+ canvas.restore();
+ }
+ super.onDraw(canvas);
+ }
+
+ @Override
+ public void clear() {
+ // Face indicator is displayed during preview. Do not clear the
+ // drawable.
+ mColor = mFocusingColor;
+ mFaces = null;
+ invalidate();
+ }
+}
diff --git a/src/com/android/camera/ui/FaceView.java b/src/com/android/camera/ui/FaceView.java
index 0bdf0de..dfa84ec 100644
--- a/src/com/android/camera/ui/FaceView.java
+++ b/src/com/android/camera/ui/FaceView.java
@@ -39,41 +39,41 @@ import org.codeaurora.camera.ExtendedFace;
public class FaceView extends View
implements FocusIndicator, Rotatable,
PhotoUI.SurfaceTextureSizeChangedListener {
- private static final String TAG = "CAM FaceView";
- private final boolean LOGV = false;
+ protected static final String TAG = "CAM FaceView";
+ protected final boolean LOGV = false;
// The value for android.hardware.Camera.setDisplayOrientation.
- private int mDisplayOrientation;
+ protected int mDisplayOrientation;
// The orientation compensation for the face indicator to make it look
// correctly in all device orientations. Ex: if the value is 90, the
// indicator should be rotated 90 degrees counter-clockwise.
- private int mOrientation;
- private boolean mMirror;
- private boolean mPause;
- private Matrix mMatrix = new Matrix();
- private RectF mRect = new RectF();
+ protected int mOrientation;
+ protected boolean mMirror;
+ protected boolean mPause;
+ protected Matrix mMatrix = new Matrix();
+ protected RectF mRect = new RectF();
// As face detection can be flaky, we add a layer of filtering on top of it
// to avoid rapid changes in state (eg, flickering between has faces and
// not having faces)
private Face[] mFaces;
private Face[] mPendingFaces;
- private int mColor;
- private final int mFocusingColor;
+ protected int mColor;
+ protected final int mFocusingColor;
private final int mFocusedColor;
private final int mFailColor;
- private Paint mPaint;
- private volatile boolean mBlocked;
+ protected Paint mPaint;
+ protected volatile boolean mBlocked;
- private int mUncroppedWidth;
- private int mUncroppedHeight;
+ protected int mUncroppedWidth;
+ protected int mUncroppedHeight;
private final int smile_threashold_no_smile = 30;
private final int smile_threashold_small_smile = 60;
private final int blink_threshold = 60;
- private static final int MSG_SWITCH_FACES = 1;
- private static final int SWITCH_DELAY = 70;
+ protected static final int MSG_SWITCH_FACES = 1;
+ protected static final int SWITCH_DELAY = 70;
private int mDisplayRotation = 0;
- private boolean mStateSwitchPending = false;
+ protected boolean mStateSwitchPending = false;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {