Made changes for AffdexMe to work with SDK v2.01

This commit is contained in:
acasallas 2015-08-27 12:07:37 -04:00
parent 96f9d55ebe
commit 301ba88795
37 changed files with 410 additions and 183 deletions

View File

@ -14,6 +14,7 @@
<uses-feature android:name="android.hardware.camera.front" android:required="false"/>
<application
android:name="com.affectiva.errorreporting.CustomApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:hardwareAccelerated="true"
@ -38,6 +39,16 @@
android:value="com.affectiva.affdexme.MainActivity" />
</activity>
<activity
android:name="com.affectiva.errorreporting.ErrorReporter"
android:theme="@android:style/Theme.DeviceDefault"
android:textAppearance="@android:style/TextAppearance.Large">
<intent-filter>
<action android:name="com.affectiva.REPORT_ERROR" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -25,13 +25,17 @@ import com.affectiva.android.affdex.sdk.detector.Face;
*/
public class DrawingView extends SurfaceView implements SurfaceHolder.Callback {
class PointFArraySharer {
PointF[] nextPointsToDraw = null;
}
//Inner Thread class
class DrawingThread extends Thread{
private SurfaceHolder mSurfaceHolder;
private Paint circlePaint;
private Paint boxPaint;
private boolean stopFlag = false; //boolean to indicate when thread has been told to stop
private PointF[] nextPointsToDraw = null; //holds a reference to the most recent set of points returned by CameraDetector, passed in by main thread
private volatile boolean stopFlag = false; //boolean to indicate when thread has been told to stop
private final PointFArraySharer sharer;
private DrawingViewConfig config;
private final long drawPeriod = 33; //draw at 30 fps
@ -52,6 +56,7 @@ public class DrawingView extends SurfaceView implements SurfaceHolder.Callback {
boxPaint.setStyle(Paint.Style.STROKE);
config = con;
sharer = new PointFArraySharer();
setThickness(config.drawThickness);
}
@ -84,7 +89,9 @@ public class DrawingView extends SurfaceView implements SurfaceHolder.Callback {
//Updates thread with latest points returned by the onImageResults() event.
public void updatePoints(PointF[] pointList) {
nextPointsToDraw = pointList;
synchronized (sharer) {
sharer.nextPointsToDraw = pointList;
}
}
void setThickness(int thickness) {
@ -93,7 +100,9 @@ public class DrawingView extends SurfaceView implements SurfaceHolder.Callback {
//Inform thread face detection has stopped, so array of points is no longer valid.
public void invalidatePoints() {
nextPointsToDraw = null;
synchronized (sharer) {
sharer.nextPointsToDraw = null;
}
}
@Override
@ -101,7 +110,6 @@ public class DrawingView extends SurfaceView implements SurfaceHolder.Callback {
android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
while(!stopFlag) {
long startTime = SystemClock.elapsedRealtime(); //get time at the start of thread loop
/**
* We use SurfaceHolder.lockCanvas() to get the canvas that draws to the SurfaceView.
@ -114,9 +122,7 @@ public class DrawingView extends SurfaceView implements SurfaceHolder.Callback {
if (c!= null) {
synchronized (mSurfaceHolder) {
c.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); //clear previous dots
if ((nextPointsToDraw != null) ) {
draw(c);
}
draw(c);
}
}
}
@ -126,23 +132,18 @@ public class DrawingView extends SurfaceView implements SurfaceHolder.Callback {
}
}
//send thread to sleep so we don't draw faster than the requested 'drawPeriod'.
long sleepTime = drawPeriod - (SystemClock.elapsedRealtime() - startTime);
try {
if(sleepTime>0){
this.sleep(sleepTime);
}
} catch (InterruptedException ex) {
Log.e(LOG_TAG,ex.getMessage());
}
}
config = null; //nullify object to avoid memory leak
}
void draw(Canvas c) {
//Save our own reference to the list of points, in case the previous reference is overwritten by the main thread.
PointF[] points = nextPointsToDraw;
PointF[] points;
synchronized (sharer) {
if (sharer.nextPointsToDraw == null)
return;
points = sharer.nextPointsToDraw;
}
//Coordinates around which to draw bounding box.
float leftBx = config.surfaceViewWidth;
@ -218,10 +219,9 @@ public class DrawingView extends SurfaceView implements SurfaceHolder.Callback {
private int surfaceViewHeight = 0;
private float screenToImageRatio = 0;
private int drawThickness = 0;
private boolean isImageDimensionsNeeded = true;
private boolean isSurfaceViewDimensionsNeeded = true;
private boolean isDrawPointsEnabled = true; //by default, have the drawing thread draw tracking dots
private boolean isDrawMeasurementsEnabled = false;
private boolean isDimensionsNeeded = true;
private Paint textPaint;
private int textSize;
@ -235,32 +235,16 @@ public class DrawingView extends SurfaceView implements SurfaceHolder.Callback {
this.upperTextSpacing = upperTextSpacing;
}
public void updateImageDimensions(int w, int h) {
if ( (w <= 0) || (h <= 0)) {
throw new IllegalArgumentException("Image Dimensions must be positive.");
public void updateViewDimensions(int surfaceViewWidth, int surfaceViewHeight, int imageWidth, int imageHeight) {
if (surfaceViewWidth <= 0 || surfaceViewHeight <= 0 || imageWidth <= 0 || imageHeight <= 0) {
throw new IllegalArgumentException("All dimensions submitted to updateViewDimensions() must be positive");
}
imageWidth = w;
imageHeight = h;
if (!isSurfaceViewDimensionsNeeded) {
screenToImageRatio = (float)surfaceViewWidth / (float)imageWidth;
}
isImageDimensionsNeeded = false;
}
public void updateSurfaceViewDimensions(int w, int h) {
if ( (w <= 0) || (h <= 0)) {
throw new IllegalArgumentException("SurfaceView Dimensions must be positive.");
}
surfaceViewWidth = w;
surfaceViewHeight = h;
if (!isImageDimensionsNeeded) {
screenToImageRatio = (float)surfaceViewWidth / (float)imageWidth;
}
isSurfaceViewDimensionsNeeded = false;
this.imageWidth = imageWidth;
this.imageHeight = imageHeight;
this.surfaceViewWidth = surfaceViewWidth;
this.surfaceViewHeight = surfaceViewHeight;
screenToImageRatio = (float)surfaceViewWidth / imageWidth;
isDimensionsNeeded = false;
}
public void setDrawThickness(int t) {
@ -365,29 +349,17 @@ public class DrawingView extends SurfaceView implements SurfaceHolder.Callback {
}
}
public boolean isImageDimensionsNeeded() {
return drawingViewConfig.isImageDimensionsNeeded;
public boolean isDimensionsNeeded() {
return drawingViewConfig.isDimensionsNeeded;
}
public boolean isSurfaceDimensionsNeeded() {
return drawingViewConfig.isSurfaceViewDimensionsNeeded;
public void invalidateDimensions() {
drawingViewConfig.isDimensionsNeeded = true;
}
public void invalidateImageDimensions() {
drawingViewConfig.isImageDimensionsNeeded = true;
}
public void updateImageDimensions(int w, int h) {
public void updateViewDimensions(int surfaceViewWidth, int surfaceViewHeight, int imageWidth, int imageHeight) {
try {
drawingViewConfig.updateImageDimensions(w, h);
} catch (Exception e) {
Log.e(LOG_TAG,e.getMessage());
}
}
public void updateSurfaceViewDimensions(int w, int h) {
try {
drawingViewConfig.updateSurfaceViewDimensions(w, h);
drawingViewConfig.updateViewDimensions(surfaceViewWidth,surfaceViewHeight,imageWidth,imageHeight);
} catch (Exception e) {
Log.e(LOG_TAG,e.getMessage());
}

View File

@ -67,13 +67,15 @@ import com.affectiva.android.affdex.sdk.detector.Face;
*/
public class MainActivity extends Activity
implements Detector.FaceListener, Detector.ImageListener, View.OnTouchListener, CameraDetector.CameraSurfaceViewListener {
implements Detector.FaceListener, Detector.ImageListener, View.OnTouchListener, CameraDetector.OnCameraEventListener {
private static final String LOG_TAG = "Affectiva";
public static final int NUM_METRICS_DISPLAYED = 6;
//Affectiva SDK Object
private CameraDetector detector = null;
//TODO: License file in byte form. Should NOT be included in released sample code.
private byte[] licenseBytes = {123,34,116,111,107,101,110,34,58,32,34,102,98,51,51,101,102,57,98,102,98,49,57,53,100,97,99,55,97,53,99,48,98,50,56,54,54,51,51,48,56,52,100,102,56,48,57,55,52,101,99,99,57,98,51,54,97,97,101,51,57,99,97,51,98,97,53,54,57,50,49,102,56,49,53,34,44,34,108,105,99,101,110,115,111,114,34,58,32,34,65,102,102,101,99,116,105,118,97,32,73,110,99,46,34,44,34,101,120,112,105,114,101,115,34,58,32,34,50,48,57,57,45,48,49,45,48,49,34,44,34,100,101,118,101,108,111,112,101,114,73,100,34,58,32,34,65,102,102,101,99,116,105,118,97,45,105,110,116,101,114,110,97,108,34,44,34,115,111,102,116,119,97,114,101,34,58,32,34,65,102,102,100,101,120,32,83,68,75,34,125};
//MetricsManager View UI Objects
private RelativeLayout metricViewLayout;
@ -93,6 +95,7 @@ public class MainActivity extends Activity
private SurfaceView cameraView; //SurfaceView used to display camera images
private DrawingView drawingView; //SurfaceView containing its own thread, used to draw facial tracking dots
private ImageButton settingsButton;
private ImageButton cameraButton;
//Application settings variables
private int detectorProcessRate;
@ -105,7 +108,12 @@ public class MainActivity extends Activity
private float numberOfFrames = 0;
private long timeToUpdate = 0;
//Camera-related variables
private boolean isFrontFacingCameraDetected = true;
private boolean isBackFacingCameraDetected = true;
int cameraPreviewWidth = 0;
int cameraPreviewHeight = 0;
CameraDetector.CameraType cameraType;
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -115,20 +123,33 @@ public class MainActivity extends Activity
initializeUI();
/**
* We check to make sure the device has a front-facing camera.
* If it does not, we obscure the app with a notice informing the user they cannot
* use the app.
*/
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT)) {
isFrontFacingCameraDetected = false;
determineCameraAvailability();
initializeCameraDetector();
}
/**
* We check to make sure the device has a front-facing camera.
* If it does not, we obscure the app with a notice informing the user they cannot
* use the app.
*/
void determineCameraAvailability() {
PackageManager manager = getPackageManager();
isFrontFacingCameraDetected = manager.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT);
isBackFacingCameraDetected = manager.hasSystemFeature(PackageManager.FEATURE_CAMERA);
if (!isFrontFacingCameraDetected && !isBackFacingCameraDetected) {
progressBar.setVisibility(View.INVISIBLE);
pleaseWaitTextView.setVisibility(View.INVISIBLE);
TextView notFoundTextView = (TextView) findViewById(R.id.not_found_textview);
notFoundTextView.setVisibility(View.VISIBLE);
}
initializeCameraDetector();
//TODO: change this to be taken from settings
if (isBackFacingCameraDetected)
cameraType = CameraDetector.CameraType.CAMERA_BACK;
if (isFrontFacingCameraDetected)
cameraType = CameraDetector.CameraType.CAMERA_FRONT;
}
void initializeUI() {
@ -145,6 +166,7 @@ public class MainActivity extends Activity
cameraView = (SurfaceView) findViewById(R.id.camera_preview);
drawingView = (DrawingView) findViewById(R.id.drawing_view);
settingsButton = (ImageButton) findViewById(R.id.settings_button);
cameraButton = (ImageButton) findViewById(R.id.camera_button);
progressBar = (ProgressBar) findViewById(R.id.progress_bar);
pleaseWaitTextView = (TextView) findViewById(R.id.please_wait_textview);
@ -216,12 +238,14 @@ public class MainActivity extends Activity
* the camera. If a SurfaceView is passed in as the last argument to the constructor,
* that view will be painted with what the camera sees.
*/
detector = new CameraDetector(this, CameraDetector.CameraType.CAMERA_FRONT, cameraView);
detector = new CameraDetector(this, cameraType, cameraView);
//TODO: this method SHOULD NOT be included in sample code release (customer should enter their own license file).
detector.setLicenseStream(new BufferedReader(new InputStreamReader(new ByteArrayInputStream(licenseBytes))));
// NOTE: uncomment the line below and replace "YourLicenseFile" with your license file, which should be stored in /assets/Affdex/
//detector.setLicensePath("YourLicenseFile");
detector.setImageListener(this);
detector.setFaceListener(this);
detector.setCameraDetectorDimensionsListener(this);
detector.setOnCameraEventListener(this);
}
/*
@ -329,8 +353,10 @@ public class MainActivity extends Activity
}
void mainWindowResumedTasks() {
startCamera();
if (!drawingView.isSurfaceDimensionsNeeded()) {
startDetector();
if (!drawingView.isDimensionsNeeded()) {
progressBarLayout.setVisibility(View.GONE);
}
resetFPSCalculations();
@ -341,14 +367,14 @@ public class MainActivity extends Activity
setMenuVisible(false);
}
}
},5000);
}, 5000);
}
void startCamera() {
//this app will always detect valence (it will also always detect measurements, but measurement don't need to be enabled)
detector.setDetectValence(true);
void startDetector() {
if (!isBackFacingCameraDetected && !isFrontFacingCameraDetected)
return; //without any cameras detected, we cannot proceed
detector.setDetectValence(true); //this app will always detect valence
if (!detector.isRunning()) {
try {
detector.start();
@ -356,9 +382,10 @@ public class MainActivity extends Activity
Log.e(LOG_TAG, e.getMessage());
}
}
}
@Override
public void onFaceDetectionStarted() {
leftMetricsLayout.animate().alpha(1); //make left and right metrics appear
@ -375,8 +402,7 @@ public class MainActivity extends Activity
void performFaceDetectionStoppedTasks() {
leftMetricsLayout.animate().alpha(0); //make left and right metrics disappear
rightMetricsLayout.animate().alpha(0);
drawingView.invalidatePoints(); //inform the drawing thread that the latest facial tracking points are now invalid
drawingView.updatePoints(null);
resetFPSCalculations(); //Since the FPS may be different whether a face is being tracked or not, reset variables.
}
@ -385,14 +411,6 @@ public class MainActivity extends Activity
*/
@Override
public void onImageResults(List<Face> faces, Frame image, float timeStamp) {
/**
* If the flag indicating that we still need to know the size of the camera frames, call calculateImageDimensions().
* The flag is a boolean stored in our drawingView object, retrieved through DrawingView.isImageDimensionsNeeded().
*/
if (drawingView.isImageDimensionsNeeded() ) {
calculateImageDimensions(image);
}
//If the faces object is null, we received an unprocessed frame
if (faces == null) {
return;
@ -403,6 +421,7 @@ public class MainActivity extends Activity
//If faces.size() is 0, we received a frame in which no face was detected
if (faces.size() == 0) {
drawingView.updatePoints(null); //the drawingView takes null points to mean it doesn't have to draw anything
return;
}
@ -448,54 +467,6 @@ public class MainActivity extends Activity
metricDisplay.setScore(score);
}
/**
* In this method, we inform our drawingView of the size of the incoming camera images.
* We also set the thickness (which controls the size of the dots and bounding box) based on a reference thickness, which
* should be the same whether the device is landscape or portrait.
*/
void calculateImageDimensions(Frame image){
///referenceDimension will be used to determine the size of the facial tracking dots
float referenceDimension = activityLayout.getHeight();
//get size of frames being passed by camera
int imageWidth = image.getWidth();
int imageHeight = image.getHeight();
/**
* If device is rotated vertically, reverse the width and height returned by the Frame object,
* and switch the dimension we consider to be the reference dimension.
*/
if ((ROTATE.BY_90_CW == image.getTargetRotation()) || (ROTATE.BY_90_CCW == image.getTargetRotation())) {
int temp = imageWidth;
imageWidth = imageHeight;
imageHeight = temp;
referenceDimension = activityLayout.getWidth();
}
drawingView.updateImageDimensions(imageWidth, imageHeight);
drawingView.setThickness((int) (referenceDimension / 160f));
}
/**
* This method is called when the SDK has corrected the aspect ratio of the SurfaceView. We use this information to resize
* our mainLayout ViewGroup so the UI fits snugly around the SurfaceView. We also update our drawingView object, so the tracking dots
* are drawn in the correct coordinates.
*/
@Override
public void onSurfaceViewAspectRatioChanged(int width, int height) {
drawingView.updateSurfaceViewDimensions(width,height);
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) mainLayout.getLayoutParams();
params.height = height;
params.width = width;
mainLayout.setLayoutParams(params);
//Now that our main layout has been resized, we can remove the progress bar that was obscuring the screen (its purpose was to obscure the resizing of the SurfaceView)
progressBarLayout.setVisibility(View.GONE);
}
/**
* FPS measurement simply uses SystemClock to measure how many frames were processed since
* the FPS variables were last reset.
@ -522,23 +493,27 @@ public class MainActivity extends Activity
public void onPause() {
super.onPause();
progressBarLayout.setVisibility(View.VISIBLE);
stopCamera();
performFaceDetectionStoppedTasks();
stopDetector();
}
private void stopCamera() {
performFaceDetectionStoppedTasks();
void stopDetector() {
if (detector.isRunning()) {
try {
detector.stop();
} catch (Exception e) {
Log.e(LOG_TAG,e.getMessage());
}
}
detector.setDetectAllEmotions(false);
detector.setDetectAllExpressions(false);
try {
detector.stop();
} catch (Exception e) {
Log.e(LOG_TAG, e.getMessage());
}
}
/**
* When the user taps the screen, hide the menu if it is visible and show it if it is hidden.
* **/
@ -547,6 +522,7 @@ public class MainActivity extends Activity
isMenuVisible = b;
if (b) {
settingsButton.setVisibility(View.VISIBLE);
cameraButton.setVisibility(View.VISIBLE);
//We display the navigation bar again
getWindow().getDecorView().setSystemUiVisibility(
@ -567,6 +543,7 @@ public class MainActivity extends Activity
settingsButton.setVisibility(View.INVISIBLE);
cameraButton.setVisibility(View.INVISIBLE);
}
}
@ -614,6 +591,86 @@ public class MainActivity extends Activity
public void settings_button_click(View view) {
startActivity(new Intent(this,SettingsActivity.class));
}
@Override
public void onCameraStarted(boolean b, Throwable throwable) {
if (throwable != null) {
Toast.makeText(this,"Failed to start camera.",Toast.LENGTH_LONG).show();
}
}
@Override
public void onCameraSizeSelected(int cameraWidth, int cameraHeight, ROTATE rotation) {
if (rotation == ROTATE.BY_90_CCW || rotation == ROTATE.BY_90_CW) {
cameraPreviewWidth = cameraHeight;
cameraPreviewHeight = cameraWidth;
} else {
cameraPreviewWidth = cameraWidth;
cameraPreviewHeight = cameraHeight;
}
drawingView.setThickness((int)(cameraWidth/100f));
mainLayout.post(new Runnable() {
@Override
public void run() {
int layoutWidth = mainLayout.getWidth();
int layoutHeight = mainLayout.getHeight();
if (cameraPreviewWidth == 0 || cameraPreviewHeight == 0 || layoutWidth == 0 || layoutHeight == 0)
return;
float layoutAspectRatio = (float)layoutWidth/layoutHeight;
float cameraPreviewAspectRatio = (float)cameraPreviewWidth/cameraPreviewHeight;
int newWidth;
int newHeight;
if (cameraPreviewAspectRatio > layoutAspectRatio) {
newWidth = layoutWidth;
newHeight =(int) (layoutWidth / cameraPreviewAspectRatio);
} else {
newWidth = (int) (layoutHeight * cameraPreviewAspectRatio);
newHeight = layoutHeight;
}
drawingView.updateViewDimensions(newWidth,newHeight,cameraPreviewWidth,cameraPreviewHeight);
ViewGroup.LayoutParams params = mainLayout.getLayoutParams();
params.height = newHeight;
params.width = newWidth;
mainLayout.setLayoutParams(params);
//Now that our main layout has been resized, we can remove the progress bar that was obscuring the screen (its purpose was to obscure the resizing of the SurfaceView)
progressBarLayout.setVisibility(View.GONE);
}
});
}
public void camera_button_click(View view) {
if (cameraType == CameraDetector.CameraType.CAMERA_FRONT) {
if (isBackFacingCameraDetected) {
cameraType = CameraDetector.CameraType.CAMERA_BACK;
} else {
Toast.makeText(this,"No back-facing camera found",Toast.LENGTH_LONG).show();
}
} else if (cameraType == CameraDetector.CameraType.CAMERA_BACK) {
if (isFrontFacingCameraDetected) {
cameraType = CameraDetector.CameraType.CAMERA_FRONT;
} else {
Toast.makeText(this,"No front-facing camera found",Toast.LENGTH_LONG).show();
}
}
performFaceDetectionStoppedTasks();
try {
detector.setCameraType(cameraType);
} catch (Exception e) {
Log.e(LOG_TAG,e.getMessage());
}
}
}

View File

@ -0,0 +1,44 @@
package com.affectiva.errorreporting;
import android.app.Application;
import android.content.Intent;
/**
* Created by Alan on 8/21/2015.
*/
public class CustomApplication extends Application {
static volatile boolean wasErrorActivityStarted = false;
static final boolean enableCustomErrorMessage = false;
Thread.UncaughtExceptionHandler exceptionHandler;
@Override
public void onCreate ()
{
super.onCreate();
exceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
// Setup handler for uncaught exceptions.
Thread.setDefaultUncaughtExceptionHandler (new Thread.UncaughtExceptionHandler()
{
@Override
public void uncaughtException (Thread thread, Throwable e)
{
handleUncaughtException (thread, e);
}
});
}
public void handleUncaughtException (Thread thread, Throwable e)
{
if (!wasErrorActivityStarted && enableCustomErrorMessage) {
Intent intent = new Intent();
intent.setAction("com.affectiva.REPORT_ERROR"); // see step 5.
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // required when starting from Application
intent.putExtra("affdexme_error", e);
startActivity(intent);
wasErrorActivityStarted = true;
}
exceptionHandler.uncaughtException(thread,e);
}
}

View File

@ -0,0 +1,66 @@
package com.affectiva.errorreporting;
import android.app.Activity;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import com.affectiva.affdexme.R;
import java.text.DateFormat;
import java.util.Date;
/**
* Created by Alan on 8/21/2015.
*/
public class ErrorReporter extends Activity implements View.OnClickListener {
String errorMessage;
Button sendButton;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE); // make a dialog without a titlebar
setContentView(R.layout.error_reporter);
Throwable error = (Throwable) getIntent().getSerializableExtra("affdexme_error");
if (error != null) {
StringBuilder builder = new StringBuilder();
builder.append("AffdexMe Error Report:");
builder.append(DateFormat.getDateTimeInstance().format(new Date()));
builder.append("\n");
builder.append(error.getMessage());
builder.append(("\n"));
StackTraceElement[] stackTraceElements = error.getStackTrace();
for (StackTraceElement element : stackTraceElements) {
builder.append("\n");
builder.append(element.toString());
}
errorMessage = builder.toString();
} else {
errorMessage = "Failed to catch error.";
}
sendButton = (Button) findViewById(R.id.send_button);
sendButton.setOnClickListener(this);
}
@Override
public void onClick(View v) {
Intent intent = new Intent (Intent.ACTION_SEND);
intent.setType("plain/text");
intent.putExtra(Intent.EXTRA_EMAIL, new String[]{"sdk@affectiva.com"});
intent.putExtra(Intent.EXTRA_SUBJECT, "AffdexMe Crash Report");
intent.putExtra(Intent.EXTRA_TEXT, errorMessage); // do this so some email clients don't complain about empty body.
startActivity(intent);
finish();
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 365 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 379 B

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 B

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 305 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 451 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 472 B

View File

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 411 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 472 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 674 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 713 B

View File

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/transluscent_circle"
android:maxLevel="0"/>
<item android:drawable="@drawable/ic_switch_camera_black_48dp"
android:maxLevel="1"
/>
</layer-list>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/transluscent_circle"
android:maxLevel="0"/>
<item android:drawable="@drawable/ic_switch_camera_white_48dp"
android:maxLevel="1"
/>
</layer-list>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:drawable="@drawable/camera_button_pressed"/>
<item android:drawable="@drawable/camera_button_not_pressed" />
</selector>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/transluscent_circle"
android:maxLevel="0"/>
<item android:drawable="@drawable/ic_more_vert_black_48dp"
android:maxLevel="1"
/>
</layer-list>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/transluscent_circle"
android:maxLevel="0"/>
<item android:drawable="@drawable/ic_more_vert_white_48dp"
android:maxLevel="1"
/>
</layer-list>

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:drawable="@drawable/settings_button_pressed"/>
<item android:drawable="@drawable/settings_button" />
<item android:drawable="@drawable/settings_button_not_pressed" />
</selector>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid
android:color="#88FFFFFF"/>
<corners
android:radius="24dp"/>
<size android:height="48dp"
android:width="48dp"/>
</shape>

View File

@ -1,7 +1,9 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:custom="http://schemas.android.com/apk/res-auto"
android:layout_gravity="center"
android:layout_width="match_parent"
android:id="@+id/main_layout"
android:layout_height="match_parent" tools:context=".MainActivity" android:focusable="true"
android:focusableInTouchMode="true"
android:keepScreenOn="true"
@ -12,40 +14,45 @@
android:layout_centerInParent="true"
android:id="@+id/camera_preview"
/>
<RelativeLayout
android:layout_height="match_parent"
<com.affectiva.affdexme.DrawingView
android:layout_width="match_parent"
android:id="@+id/main_layout"
android:layout_height="match_parent"
android:layout_centerInParent="true"
>
<com.affectiva.affdexme.DrawingView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
custom:measurements_text_size="@dimen/measurements_text_size"
custom:measurements_upper_spacing="@dimen/measurements_upper_text_spacing"
custom:measurements_lower_spacing="@dimen/measurements_lower_text_spacing"
custom:measurements_color="#DDDDDD"
custom:measurements_text_border_color="@color/letter_gray"
custom:measurements_text_border_thickness="@integer/measurements_text_border_thickness"
android:id="@+id/drawing_view"/>
<include layout="@layout/metric_layout"
android:id="@+id/metric_view_group"
/>
<ImageButton
android:layout_width="@dimen/settings_button_size"
android:layout_height="@dimen/settings_button_size"
android:background="@null"
android:src="@drawable/settings_button_selector"
android:scaleType="fitCenter"
android:layout_margin="@dimen/settings_button_margin"
android:contentDescription="@string/settings_content_description"
android:layout_alignParentRight="true"
android:layout_below="@id/metric_view_group"
android:id="@+id/settings_button"
android:onClick="settings_button_click"/>
</RelativeLayout>
custom:measurements_text_size="@dimen/measurements_text_size"
custom:measurements_upper_spacing="@dimen/measurements_upper_text_spacing"
custom:measurements_lower_spacing="@dimen/measurements_lower_text_spacing"
custom:measurements_color="#DDDDDD"
custom:measurements_text_border_color="@color/letter_gray"
custom:measurements_text_border_thickness="@integer/measurements_text_border_thickness"
android:id="@+id/drawing_view"/>
<include layout="@layout/metric_layout"
android:id="@+id/metric_view_group"
/>
<ImageButton
android:layout_width="@dimen/settings_button_size"
android:layout_height="@dimen/settings_button_size"
android:background="@null"
android:src="@drawable/settings_button_selector"
android:scaleType="fitCenter"
android:layout_margin="@dimen/settings_button_margin"
android:contentDescription="@string/settings_content_description"
android:layout_alignParentRight="true"
android:layout_below="@id/metric_view_group"
android:id="@+id/settings_button"
android:onClick="settings_button_click"/>
<ImageButton
android:layout_width="@dimen/settings_button_size"
android:layout_height="@dimen/settings_button_size"
android:background="@null"
android:src="@drawable/camera_button_selector"
android:scaleType="fitCenter"
android:layout_margin="@dimen/settings_button_margin"
android:contentDescription="Switch camera button"
android:layout_alignParentRight="true"
android:layout_below="@id/settings_button"
android:id="@+id/camera_button"
android:onClick="camera_button_click"/>
<RelativeLayout
android:layout_width="match_parent"

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:padding="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:id="@+id/send_button"
android:text="Send"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:gravity="center"
android:textSize="20sp"
android:layout_centerHorizontal="true"
android:layout_above="@id/send_button"
android:text="It looks like AffdexMe has crashed! Click below to send an automated report, then try restarting the app."/>
</RelativeLayout>