Merge pull request #1 from Affectiva/master

Merge CT pack update
This commit is contained in:
Abdelrahman Mahmoud 2015-09-03 17:37:04 -04:00
commit 5faffd33dd
103 changed files with 410 additions and 1878 deletions

8
.gitignore vendored
View file

@ -25,11 +25,3 @@ proguard/
# Log Files # Log Files
*.log *.log
#Affectiva SDK files
AffdexMe/app/libs/*
AffdexMe/app/src/main/jniLibs/*
AffdexMe/app/src/main/assets/Affdex

View file

@ -88,9 +88,11 @@
</content> </content>
<orderEntry type="jdk" jdkName="Android API 22 Platform (1)" jdkType="Android SDK" /> <orderEntry type="jdk" jdkName="Android API 22 Platform (1)" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" exported="" name="Affdex-sdk-1.2-SNAPSHOT-javadoc" level="project" />
<orderEntry type="library" exported="" name="dagger-1.2.2" level="project" /> <orderEntry type="library" exported="" name="dagger-1.2.2" level="project" />
<orderEntry type="library" exported="" name="Affdex-sdk" level="project" />
<orderEntry type="library" exported="" name="javax.inject-1" level="project" /> <orderEntry type="library" exported="" name="javax.inject-1" level="project" />
<orderEntry type="library" exported="" name="Affdex-sdk-javadoc" level="project" /> <orderEntry type="library" exported="" name="Affdex-sdk-1.2-SNAPSHOT" level="project" />
<orderEntry type="library" exported="" name="gson-2.3" level="project" />
<orderEntry type="library" exported="" name="flurry-analytics-4.1.0" level="project" />
</component> </component>
</module> </module>

View file

@ -8,8 +8,8 @@ android {
applicationId "com.affectiva.affdexme" applicationId "com.affectiva.affdexme"
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 22 targetSdkVersion 22
versionCode 18 versionCode 14
versionName "2.0.18b" versionName "1.0.14b"
} }
buildTypes { buildTypes {
release { release {
@ -20,9 +20,19 @@ android {
} }
dependencies { dependencies {
//gson is necessary for the license to be parsed
compile 'com.google.code.gson:gson:2.3'
//include the Affdex SDK jars //include the Affdex SDK jars
compile files('libs/Affdex-sdk.jar') compile files('libs/Affdex-sdk-1.2-SNAPSHOT.jar')
compile files('libs/Affdex-sdk-javadoc.jar') compile files('libs/Affdex-sdk-1.2-SNAPSHOT-javadoc.jar')
compile files('libs/dagger-1.2.2.jar') compile files('libs/dagger-1.2.2.jar')
compile files('libs/flurry-analytics-4.1.0.jar')
compile files('libs/javax.inject-1.jar') compile files('libs/javax.inject-1.jar')
//although the use of the CameraDetector class in this project does not require it, you may have to include
//the following dependencies if you use other features of the Affdex SDK
//compile 'com.android.support:support-v4:22.2.0'
//compile 'com.google.android.gms:play-services:7.5.0'
} }

View file

@ -12,8 +12,6 @@
#prevent proguard from warning us about not including the GooglePlay dependency #prevent proguard from warning us about not including the GooglePlay dependency
-dontwarn ** -dontwarn **
-keepattributes Exceptions,InnerClasses,Signature,Deprecated, SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
#keep all classes (otherwise Proguard may remove classes that use reflection, injection, Gson, etc...) #keep all classes (otherwise Proguard may remove classes that use reflection, injection, Gson, etc...)
-keep class sun.** -keep class sun.**
-keepclassmembers class sun.** {*;} -keepclassmembers class sun.** {*;}
@ -27,20 +25,19 @@
#keep certain class members (otherwise Proguard would strip the members of these classes) #keep certain class members (otherwise Proguard would strip the members of these classes)
-keep class com.** -keep class com.**
-keepclassmembers class !com.affectiva.affdexme.MainActivity,!com.affectiva.android.affdex.sdk.detector.Detector {*;} -keepclassmembers class com.affectiva.android.affdex.sdk.detector.A* { *; }
-keepclassmembers class com.affectiva.android.affdex.sdk.detector.Detector { -keepclassmembers class com.affectiva.android.affdex.sdk.detector.B* { *; }
public void setDetect**; -keepclassmembers class com.affectiva.android.affdex.sdk.detector.I* { *; }
} -keepclassmembers class com.affectiva.android.affdex.sdk.detector.L* { *; }
-keepclassmembers class com.affectiva.android.affdex.sdk.Frame { *; }
# Dagger
-dontwarn dagger.internal.codegen.** -keepclassmembers class com.affectiva.affdexme.DrawingView {*;}
-keepclassmembers,allowobfuscation class * { -keepclassmembers class com.affectiva.affdexme.MetricView {*;}
-keepclassmembers class com.affectiva.affdexme.GradientMetricView {*;}
-keepclassmembers class * {
@javax.inject.* *; @javax.inject.* *;
@dagger.* *; @dagger.* *;
<init>(); <init>();
} }
-keep class dagger.* { *; }
-keep class javax.inject.* { *; }
-keep class * extends dagger.internal.Binding
-keep class * extends dagger.internal.ModuleAdapter
-keep class * extends dagger.internal.StaticInjection

View file

@ -16,28 +16,19 @@
<application <application
android:allowBackup="true" android:allowBackup="true"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:hardwareAccelerated="true" android:label="@string/app_name"
android:label="@string/app_name"> android:theme="@style/AppTheme">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:configChanges="screenSize|keyboardHidden|orientation" android:configChanges="screenSize|keyboardHidden|orientation"
android:screenOrientation="sensorPortrait" android:screenOrientation="sensorPortrait"
android:theme="@style/MainActivityTheme" android:windowSoftInputMode="stateHidden"
android:label="@string/app_name" > android:label="@string/app_name" >
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity
android:name=".SettingsActivity"
android:parentActivityName="com.affectiva.affdexme.MainActivity"
android:theme="@style/EditPreferencesTheme">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.affectiva.affdexme.MainActivity" />
</activity>
</application> </application>
</manifest> </manifest>

View file

@ -1,14 +1,12 @@
package com.affectiva.affdexme; package com.affectiva.affdexme;
import android.content.Context; import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.PixelFormat; import android.graphics.PixelFormat;
import android.graphics.PointF; import android.graphics.PointF;
import android.graphics.PorterDuff; import android.graphics.PorterDuff;
import android.graphics.Typeface;
import android.os.Process; import android.os.Process;
import android.os.SystemClock; import android.os.SystemClock;
import android.util.AttributeSet; import android.util.AttributeSet;
@ -16,8 +14,6 @@ import android.util.Log;
import android.view.SurfaceHolder; import android.view.SurfaceHolder;
import android.view.SurfaceView; import android.view.SurfaceView;
import com.affectiva.android.affdex.sdk.detector.Face;
/** /**
* This class contains a SurfaceView and its own thread that draws to it. * This class contains a SurfaceView and its own thread that draws to it.
@ -35,18 +31,12 @@ public class DrawingView extends SurfaceView implements SurfaceHolder.Callback {
private DrawingViewConfig config; private DrawingViewConfig config;
private final long drawPeriod = 33; //draw at 30 fps private final long drawPeriod = 33; //draw at 30 fps
private final int TEXT_RAISE = 10;
String roll = "";
String yaw = "";
String pitch = "";
String interOcDis = "";
public DrawingThread(SurfaceHolder surfaceHolder, DrawingViewConfig con) { public DrawingThread(SurfaceHolder surfaceHolder, DrawingViewConfig con) {
mSurfaceHolder = surfaceHolder; mSurfaceHolder = surfaceHolder;
circlePaint = new Paint(); circlePaint = new Paint();
circlePaint.setColor(Color.WHITE); circlePaint.setColor(Color.WHITE);
boxPaint = new Paint(); boxPaint = new Paint();
boxPaint.setColor(Color.WHITE); boxPaint.setColor(Color.WHITE);
boxPaint.setStyle(Paint.Style.STROKE); boxPaint.setStyle(Paint.Style.STROKE);
@ -54,24 +44,20 @@ public class DrawingView extends SurfaceView implements SurfaceHolder.Callback {
config = con; config = con;
setThickness(config.drawThickness); setThickness(config.drawThickness);
} }
void setMetrics(float roll, float yaw, float pitch, float interOcDis, float valence) { /**
//format string for our DrawingView to use when ready * Used to set the valence score, which determines the color of the bounding box.
this.roll = String.format("%.2f",roll); * **/
this.yaw = String.format("%.2f",yaw); void setScore(float s) {
this.pitch = String.format("%.2f",pitch); if (s > 0) {
this.interOcDis = String.format("%.2f",interOcDis); float colorScore = ((100f-s)/100f)*255;
//prepare the color of the bounding box using the valence score. Red for -100, White for 0, and Green for +100, with linear interpolation in between.
if (valence > 0) {
float colorScore = ((100f-valence)/100f)*255;
boxPaint.setColor(Color.rgb((int)colorScore,255,(int)colorScore)); boxPaint.setColor(Color.rgb((int)colorScore,255,(int)colorScore));
} else { } else {
float colorScore = ((100f+valence)/100f)*255; float colorScore = ((100f+s)/100f)*255;
boxPaint.setColor(Color.rgb(255, (int) colorScore, (int) colorScore)); boxPaint.setColor(Color.rgb(255, (int) colorScore, (int) colorScore));
} }
} }
public void stopThread() { public void stopThread() {
@ -114,7 +100,7 @@ public class DrawingView extends SurfaceView implements SurfaceHolder.Callback {
if (c!= null) { if (c!= null) {
synchronized (mSurfaceHolder) { synchronized (mSurfaceHolder) {
c.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); //clear previous dots c.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); //clear previous dots
if ((nextPointsToDraw != null) ) { if (config.isDrawPointsEnabled && (nextPointsToDraw != null) ) {
draw(c); draw(c);
} }
} }
@ -145,69 +131,31 @@ public class DrawingView extends SurfaceView implements SurfaceHolder.Callback {
PointF[] points = nextPointsToDraw; PointF[] points = nextPointsToDraw;
//Coordinates around which to draw bounding box. //Coordinates around which to draw bounding box.
float leftBx = config.surfaceViewWidth; float leftBx = config.imageWidth;
float rightBx = 0; float rightBx = 0;
float topBx = config.surfaceViewHeight; float topBx = config.imageHeight;
float botBx = 0; float botBx = 0;
//Iterate through all the points given to us by the CameraDetector object
for (int i = 0; i < points.length; i++) { for (int i = 0; i < points.length; i++) {
//transform from the camera coordinates to our screen coordinates
//The camera preview is displayed as a mirror, so X pts have to be mirrored back.
float x = (config.imageWidth - points[i].x - 1) * config.screenToImageRatio;
float y = (points[i].y)* config.screenToImageRatio;
//We determine the left-most, top-most, right-most, and bottom-most points to draw the bounding box around. //We determine the left-most, top-most, right-most, and bottom-most points to draw the bounding box around.
if (x < leftBx) if (points[i].x < leftBx)
leftBx = x; leftBx = points[i].x;
if (x > rightBx) if (points[i].x > rightBx)
rightBx = x; rightBx = points[i].x;
if (y < topBx) if (points[i].y < topBx)
topBx = y; topBx = points[i].y;
if (y > botBx) if (points[i].y > botBx)
botBx = y; botBx = points[i].y;
//Draw facial tracking dots. //Draw facial tracking dots.
if (config.isDrawPointsEnabled) { //The camera preview is displayed as a mirror, so X pts have to be reversed
c.drawCircle(x, y, config.drawThickness, circlePaint); c.drawCircle((config.imageWidth - points[i].x - 1) * config.screenToImageRatio, (points[i].y)* config.screenToImageRatio, config.drawThickness, circlePaint);
}
} }
//Draw the bounding box. //Draw the bounding box.
if (config.isDrawPointsEnabled) { c.drawRect((config.imageWidth - leftBx - 1) * config.screenToImageRatio, topBx * config.screenToImageRatio, (config.imageWidth - rightBx - 1) * config.screenToImageRatio, botBx * config.screenToImageRatio, boxPaint);
c.drawRect(leftBx, topBx, rightBx, botBx, boxPaint);
}
//Draw the measurement metrics, with a dark border around the words to make them visible for users of all skin colors.
if (config.isDrawMeasurementsEnabled) {
float centerBx = (leftBx + rightBx) / 2;
float upperText = topBx - TEXT_RAISE;
c.drawText("PITCH", centerBx, upperText - config.textSize,config.textBorderPaint);
c.drawText("PITCH", centerBx, upperText - config.textSize, config.textPaint);
c.drawText(pitch,centerBx ,upperText ,config.textBorderPaint);
c.drawText(pitch, centerBx, upperText, config.textPaint);
float upperLeft = centerBx - config.upperTextSpacing;
c.drawText("YAW", upperLeft , upperText - config.textSize , config.textBorderPaint);
c.drawText("YAW", upperLeft, upperText - config.textSize, config.textPaint);
c.drawText(yaw, upperLeft , upperText , config.textBorderPaint);
c.drawText(yaw, upperLeft, upperText, config.textPaint);
float upperRight = centerBx + config.upperTextSpacing;
c.drawText("ROLL", upperRight , upperText - config.textSize , config.textBorderPaint);
c.drawText("ROLL", upperRight, upperText - config.textSize, config.textPaint);
c.drawText(roll, upperRight , upperText , config.textBorderPaint);
c.drawText(roll, upperRight, upperText, config.textPaint);
c.drawText("INTEROCULAR DISTANCE", centerBx , botBx + config.textSize , config.textBorderPaint);
c.drawText("INTEROCULAR DISTANCE", centerBx, botBx + config.textSize, config.textPaint);
c.drawText(interOcDis,centerBx , botBx + config.textSize*2 , config.textBorderPaint);
c.drawText(interOcDis, centerBx, botBx + config.textSize * 2, config.textPaint);
}
} }
} }
@ -221,19 +169,6 @@ public class DrawingView extends SurfaceView implements SurfaceHolder.Callback {
private boolean isImageDimensionsNeeded = true; private boolean isImageDimensionsNeeded = true;
private boolean isSurfaceViewDimensionsNeeded = true; private boolean isSurfaceViewDimensionsNeeded = true;
private boolean isDrawPointsEnabled = true; //by default, have the drawing thread draw tracking dots private boolean isDrawPointsEnabled = true; //by default, have the drawing thread draw tracking dots
private boolean isDrawMeasurementsEnabled = false;
private Paint textPaint;
private int textSize;
private Paint textBorderPaint;
private int upperTextSpacing;
public void setMeasurementMetricConfigs(Paint textPaint, Paint dropShadowPaint, int textSize, int upperTextSpacing) {
this.textPaint = textPaint;
this.textSize = textSize;
this.textBorderPaint = dropShadowPaint;
this.upperTextSpacing = upperTextSpacing;
}
public void updateImageDimensions(int w, int h) { public void updateImageDimensions(int w, int h) {
@ -276,7 +211,6 @@ public class DrawingView extends SurfaceView implements SurfaceHolder.Callback {
//Class variables of DrawingView class //Class variables of DrawingView class
private SurfaceHolder surfaceHolder; private SurfaceHolder surfaceHolder;
private DrawingThread drawingThread; //DrawingThread object private DrawingThread drawingThread; //DrawingThread object
private Typeface typeface;
private DrawingViewConfig drawingViewConfig; private DrawingViewConfig drawingViewConfig;
private static String LOG_TAG = "AffdexMe"; private static String LOG_TAG = "AffdexMe";
@ -284,60 +218,25 @@ public class DrawingView extends SurfaceView implements SurfaceHolder.Callback {
//three constructors required of any custom view //three constructors required of any custom view
public DrawingView(Context context) { public DrawingView(Context context) {
super(context); super(context);
initView(context, null); initView();
} }
public DrawingView(Context context, AttributeSet attrs) { public DrawingView(Context context, AttributeSet attrs) {
super(context, attrs); super(context, attrs);
initView(context, attrs); initView();
} }
public DrawingView(Context context, AttributeSet attrs, int defStyle) { public DrawingView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle); super(context, attrs, defStyle);
initView(context, attrs); initView();
} }
void initView(Context context, AttributeSet attrs){ void initView(){
surfaceHolder = getHolder(); //The SurfaceHolder object will be used by the thread to request canvas to draw on SurfaceView surfaceHolder = getHolder(); //The SurfaceHolder object will be used by the thread to request canvas to draw on SurfaceView
surfaceHolder.setFormat(PixelFormat.TRANSPARENT); //set to Transparent so this surfaceView does not obscure the one it is overlaying (the one displaying the camera). surfaceHolder.setFormat(PixelFormat.TRANSPARENT); //set to Transparent so this surfaceView does not obscure the one it is overlaying (the one displaying the camera).
surfaceHolder.addCallback(this); //become a Listener to the three events below that SurfaceView throws surfaceHolder.addCallback(this); //become a Listener to the three events below that SurfaceView throws
drawingViewConfig = new DrawingViewConfig(); drawingViewConfig = new DrawingViewConfig();
//default values
int upperTextSpacing = 15;
int textSize = 15;
Paint measurementTextPaint = new Paint();
measurementTextPaint.setStyle(Paint.Style.FILL);
measurementTextPaint.setTextAlign(Paint.Align.CENTER);
Paint dropShadow = new Paint();
dropShadow.setColor(Color.BLACK);
dropShadow.setStyle(Paint.Style.STROKE);
dropShadow.setTextAlign(Paint.Align.CENTER);
//load and parse XML attributes
if (attrs != null) {
TypedArray a = getContext().obtainStyledAttributes(attrs,R.styleable.drawing_view_attributes,0,0);
upperTextSpacing = a.getDimensionPixelSize(R.styleable.drawing_view_attributes_measurements_upper_spacing,upperTextSpacing);
measurementTextPaint.setColor(a.getColor(R.styleable.drawing_view_attributes_measurements_color,Color.WHITE));
dropShadow.setColor(a.getColor(R.styleable.drawing_view_attributes_measurements_text_border_color,Color.BLACK));
dropShadow.setStrokeWidth(a.getInteger(R.styleable.drawing_view_attributes_measurements_text_border_thickness,5));
textSize = a.getDimensionPixelSize(R.styleable.drawing_view_attributes_measurements_text_size,textSize);
measurementTextPaint.setTextSize(textSize);
dropShadow.setTextSize(textSize);
a.recycle();
}
drawingViewConfig.setMeasurementMetricConfigs(measurementTextPaint, dropShadow, textSize, upperTextSpacing);
drawingThread = new DrawingThread(surfaceHolder, drawingViewConfig); drawingThread = new DrawingThread(surfaceHolder, drawingViewConfig);
} }
public void setTypeface(Typeface face) {
drawingViewConfig.textPaint.setTypeface(face);
drawingViewConfig.textBorderPaint.setTypeface(face);
}
@Override @Override
public void surfaceCreated(SurfaceHolder holder) { public void surfaceCreated(SurfaceHolder holder) {
if (drawingThread.isStopped()) { if (drawingThread.isStopped()) {
@ -410,16 +309,9 @@ public class DrawingView extends SurfaceView implements SurfaceHolder.Callback {
return drawingViewConfig.isDrawPointsEnabled; return drawingViewConfig.isDrawPointsEnabled;
} }
public void setDrawMeasurementsEnabled(boolean b) { //The methods below simply delegate to the drawingThread object
drawingViewConfig.isDrawMeasurementsEnabled = b; public void setScore(float s) {
} drawingThread.setScore(s);
public boolean getDrawMeasurementsEnabled() {
return drawingViewConfig.isDrawMeasurementsEnabled;
}
public void setMetrics(float roll, float yaw, float pitch, float interOcDis, float valence) {
drawingThread.setMetrics(roll,yaw,pitch,interOcDis,valence);
} }
public void updatePoints(PointF[] points) { public void updatePoints(PointF[] points) {

View file

@ -0,0 +1,48 @@
package com.affectiva.affdexme;
import android.content.Context;
import android.graphics.Color;
import android.util.AttributeSet;
/**
* GradientMetricView is used to display the valence metric and adds functionality of allowing
* the bar's shade of color to scale with the metric's value, rather than just being red or green.
*/
public class GradientMetricView extends MetricView {
//Three Constructors required of any custom view:
public GradientMetricView(Context context) {
super(context);
}
public GradientMetricView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public GradientMetricView(Context context, AttributeSet attrs, int styleID){
super(context, attrs, styleID);
}
/**
* As in MetricView, we set our text to display the score and size the colored bar appropriately.
* In this class, however, we let the score determine the color of the bar (shades of red for negative
* and shades of green for positive).
*/
@Override
public void setScore(float s) {
text = String.format("%d%%", (int)s);
if (s > 0) {
left = midX - (halfWidth * (s / 100));
right = midX + (halfWidth * (s / 100));
} else {
left = midX - (halfWidth * (-s / 100));
right = midX + (halfWidth * (-s / 100));
}
if (s > 0) {
float colorScore = ((100f-s)/100f)*255;
boxPaint.setColor(Color.rgb((int)colorScore,255,(int)colorScore));
} else {
float colorScore = ((100f+s)/100f)*255;
boxPaint.setColor(Color.rgb(255,(int)colorScore,(int)colorScore));
}
invalidate(); //instruct Android to re-draw our view, now that the text has changed
}
}

View file

@ -1,13 +1,12 @@
package com.affectiva.affdexme; package com.affectiva.affdexme;
import android.app.Activity; import android.app.Activity;
import android.content.Intent; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.graphics.Typeface; import android.graphics.Typeface;
import android.os.Bundle; import android.os.Bundle;
import android.os.SystemClock; import android.os.SystemClock;
import android.preference.PreferenceManager;
import android.util.Log; import android.util.Log;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.MotionEvent; import android.view.MotionEvent;
@ -15,17 +14,18 @@ import android.view.SurfaceView;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.WindowManager; import android.view.WindowManager;
import android.widget.ImageButton; import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.util.List; import java.util.List;
import com.affectiva.android.affdex.sdk.Frame; import com.affectiva.android.affdex.sdk.Frame;
@ -67,24 +67,37 @@ import com.affectiva.android.affdex.sdk.detector.Face;
*/ */
public class MainActivity extends Activity public class MainActivity extends Activity
implements Detector.FaceListener, Detector.ImageListener, View.OnTouchListener, CameraDetector.CameraSurfaceViewListener { implements Detector.FaceListener, Detector.ImageListener, TextView.OnEditorActionListener, View.OnTouchListener, CameraDetector.CameraSurfaceViewListener {
private static final String LOG_TAG = "Affectiva"; private static final String LOG_TAG = "Affectiva";
public static final int NUM_METRICS_DISPLAYED = 6;
//Affectiva SDK Object //Affectiva SDK Object
private CameraDetector detector = null; private CameraDetector detector = null;
//MetricsManager View UI Objects //Metrics View UI Objects
private RelativeLayout metricViewLayout; private RelativeLayout metricViewLayout;
private LinearLayout leftMetricsLayout; private LinearLayout leftMetricsLayout;
private LinearLayout rightMetricsLayout; private LinearLayout rightMetricsLayout;
private MetricDisplay[] metricDisplays; private MetricView smilePct;
private TextView[] metricNames; private MetricView browRaisePct;
private MetricView browFurrowPct;
private MetricView frownPct;
private MetricView valencePct;
private MetricView engagementPct;
private TextView fpsName; private TextView fpsName;
private TextView fpsPct; private TextView fpsPct;
private TextView pleaseWaitTextView; private TextView smileName;
private ProgressBar progressBar; private TextView browRaiseName;
private TextView browFurrowName;
private TextView frownName;
private TextView valenceName;
private TextView engagementName;
//Menu UI Objects
private RelativeLayout menuLayout;
private EditText fpsEditText;
private CheckBox fpsCheckbox;
private CheckBox trackingCheckbox;
private TextView fpsEditTextName;
//Other UI objects //Other UI objects
private ViewGroup activityLayout; //top-most ViewGroup in which all content resides private ViewGroup activityLayout; //top-most ViewGroup in which all content resides
@ -92,13 +105,15 @@ public class MainActivity extends Activity
private RelativeLayout progressBarLayout; //layout used to show progress circle while camera is starting private RelativeLayout progressBarLayout; //layout used to show progress circle while camera is starting
private SurfaceView cameraView; //SurfaceView used to display camera images private SurfaceView cameraView; //SurfaceView used to display camera images
private DrawingView drawingView; //SurfaceView containing its own thread, used to draw facial tracking dots private DrawingView drawingView; //SurfaceView containing its own thread, used to draw facial tracking dots
private ImageButton settingsButton;
//The Shared Preferences object is used to restore/save settings when activity is created/destroyed
private final String PREFS_NAME = "AffdexMe";
SharedPreferences sharedPreferences;
//Application settings variables //Application settings variables
private int detectorProcessRate; private int detectorProcessRate = 20;
private boolean isMenuVisible = false; private boolean isMenuVisible = false;
private boolean isFPSVisible = false; private boolean isFPSVisible = false;
private boolean isMenuShowingForFirstTime = true;
//Frames Per Second (FPS) variables //Frames Per Second (FPS) variables
private long firstSystemTime = 0; private long firstSystemTime = 0;
@ -113,8 +128,6 @@ public class MainActivity extends Activity
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); //To maximize UI space, we declare our app to be full-screen getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); //To maximize UI space, we declare our app to be full-screen
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
initializeUI();
/** /**
* We check to make sure the device has a front-facing camera. * 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 * If it does not, we obscure the app with a notice informing the user they cannot
@ -122,12 +135,12 @@ public class MainActivity extends Activity
*/ */
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT)) { if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT)) {
isFrontFacingCameraDetected = false; isFrontFacingCameraDetected = false;
progressBar.setVisibility(View.INVISIBLE);
pleaseWaitTextView.setVisibility(View.INVISIBLE);
TextView notFoundTextView = (TextView) findViewById(R.id.not_found_textview); TextView notFoundTextView = (TextView) findViewById(R.id.not_found_textview);
notFoundTextView.setVisibility(View.VISIBLE); notFoundTextView.setVisibility(View.VISIBLE);
} }
initializeUI();
initializeCameraDetector(); initializeCameraDetector();
} }
@ -140,42 +153,47 @@ public class MainActivity extends Activity
leftMetricsLayout = (LinearLayout) findViewById(R.id.left_metrics); leftMetricsLayout = (LinearLayout) findViewById(R.id.left_metrics);
rightMetricsLayout = (LinearLayout) findViewById(R.id.right_metrics); rightMetricsLayout = (LinearLayout) findViewById(R.id.right_metrics);
mainLayout = (RelativeLayout) findViewById(R.id.main_layout); mainLayout = (RelativeLayout) findViewById(R.id.main_layout);
menuLayout = (RelativeLayout) findViewById(R.id.affdexme_menu);
smilePct = (MetricView) findViewById(R.id.smile_pct);
browRaisePct = (MetricView) findViewById(R.id.brow_raise_pct);
browFurrowPct = (MetricView) findViewById(R.id.brow_furrow_pct);
frownPct = (MetricView) findViewById(R.id.frown_pct);
valencePct = (MetricView) findViewById(R.id.valence_pct);
engagementPct = (MetricView) findViewById(R.id.engagement_pct);
fpsPct = (TextView) findViewById(R.id.fps_value); fpsPct = (TextView) findViewById(R.id.fps_value);
smileName = (TextView) findViewById(R.id.smile_name);
browRaiseName = (TextView) findViewById(R.id.brow_raise_name);
browFurrowName = (TextView) findViewById(R.id.brow_furrow_name);
frownName = (TextView) findViewById(R.id.frown_name);
valenceName = (TextView) findViewById(R.id.valence_name);
engagementName = (TextView) findViewById(R.id.engagement_name);
fpsName = (TextView) findViewById(R.id.fps_name); fpsName = (TextView) findViewById(R.id.fps_name);
fpsEditText = (EditText) findViewById(R.id.fps_edittext);
fpsEditTextName = (TextView) findViewById(R.id.fps_edittext_name);
fpsCheckbox = (CheckBox) findViewById(R.id.fps_checkbox);
trackingCheckbox = (CheckBox) findViewById(R.id.tracking_checkbox);
cameraView = (SurfaceView) findViewById(R.id.camera_preview); cameraView = (SurfaceView) findViewById(R.id.camera_preview);
drawingView = (DrawingView) findViewById(R.id.drawing_view); drawingView = (DrawingView) findViewById(R.id.drawing_view);
settingsButton = (ImageButton) findViewById(R.id.settings_button);
progressBar = (ProgressBar) findViewById(R.id.progress_bar);
pleaseWaitTextView = (TextView) findViewById(R.id.please_wait_textview);
//Initialize views to display metrics
metricNames = new TextView[NUM_METRICS_DISPLAYED];
metricNames[0] = (TextView) findViewById(R.id.metric_name_0);
metricNames[1] = (TextView) findViewById(R.id.metric_name_1);
metricNames[2] = (TextView) findViewById(R.id.metric_name_2);
metricNames[3] = (TextView) findViewById(R.id.metric_name_3);
metricNames[4] = (TextView) findViewById(R.id.metric_name_4);
metricNames[5] = (TextView) findViewById(R.id.metric_name_5);
metricDisplays = new MetricDisplay[NUM_METRICS_DISPLAYED];
metricDisplays[0] = (MetricDisplay) findViewById(R.id.metric_pct_0);
metricDisplays[1] = (MetricDisplay) findViewById(R.id.metric_pct_1);
metricDisplays[2] = (MetricDisplay) findViewById(R.id.metric_pct_2);
metricDisplays[3] = (MetricDisplay) findViewById(R.id.metric_pct_3);
metricDisplays[4] = (MetricDisplay) findViewById(R.id.metric_pct_4);
metricDisplays[5] = (MetricDisplay) findViewById(R.id.metric_pct_5);
//Load Application Font and set UI Elements to use it //Load Application Font and set UI Elements to use it
Typeface face = Typeface.createFromAsset(getAssets(), "fonts/Square.ttf"); Typeface face = Typeface.createFromAsset(getAssets(), "fonts/Square.ttf");
for (TextView textView : metricNames) { smilePct.setTypeface(face);
textView.setTypeface(face); browRaisePct.setTypeface(face);
} browFurrowPct.setTypeface(face);
for (MetricDisplay metricDisplay : metricDisplays) { frownPct.setTypeface(face);
metricDisplay.setTypeface(face); valencePct.setTypeface(face);
} engagementPct.setTypeface(face);
smileName.setTypeface(face);
browRaiseName.setTypeface(face);
browFurrowName.setTypeface(face);
frownName.setTypeface(face);
valenceName.setTypeface(face);
engagementName.setTypeface(face);
fpsPct.setTypeface(face); fpsPct.setTypeface(face);
fpsName.setTypeface(face); fpsName.setTypeface(face);
drawingView.setTypeface(face); fpsEditTextName.setTypeface(face);
pleaseWaitTextView.setTypeface(face); fpsCheckbox.setTypeface(face);
trackingCheckbox.setTypeface(face);
//Hide left and right metrics by default (will be made visible when face detection starts) //Hide left and right metrics by default (will be made visible when face detection starts)
leftMetricsLayout.setAlpha(0); leftMetricsLayout.setAlpha(0);
@ -192,6 +210,18 @@ public class MainActivity extends Activity
//Attach event listeners to the menu and edit box //Attach event listeners to the menu and edit box
activityLayout.setOnTouchListener(this); activityLayout.setOnTouchListener(this);
menuLayout.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
/*
* This method effectively blocks the mainLayout from receiving a touch event
* when the menu is pressed. This is to prevent the menu from closing if the user accidentally touches it
* when aiming for a checkbox or edit box.
*/
return true;
}
});
fpsEditText.setOnEditorActionListener(this);
/* /*
* This app sets the View.SYSTEM_UI_FLAG_HIDE_NAVIGATION flag. Unfortunately, this flag causes * This app sets the View.SYSTEM_UI_FLAG_HIDE_NAVIGATION flag. Unfortunately, this flag causes
@ -217,8 +247,20 @@ public class MainActivity extends Activity
* that view will be painted with what the camera sees. * that view will be painted with what the camera sees.
*/ */
detector = new CameraDetector(this, CameraDetector.CameraType.CAMERA_FRONT, cameraView); detector = new CameraDetector(this, CameraDetector.CameraType.CAMERA_FRONT, cameraView);
// NOTE: uncomment the line below and replace "YourLicenseFile" with your license file, which should be stored in /assets/Affdex/ // NOTE: uncomment the line below and replace "YourLicenseFile" with your license file, which should be stored in /assets/Affdex/
//detector.setLicensePath("YourLicenseFile"); //detector.setLicensePath("YourLicenseFile");
// We want to detect all expressions, so turn on all classifiers.
detector.setDetectSmile(true);
detector.setDetectBrowFurrow(true);
detector.setDetectBrowRaise(true);
detector.setDetectEngagement(true);
detector.setDetectValence(true);
detector.setDetectLipCornerDepressor(true);
detector.setMaxProcessRate(detectorProcessRate);
detector.setImageListener(this); detector.setImageListener(this);
detector.setFaceListener(this); detector.setFaceListener(this);
detector.setCameraDetectorDimensionsListener(this); detector.setCameraDetectorDimensionsListener(this);
@ -231,75 +273,33 @@ public class MainActivity extends Activity
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
restoreApplicationSettings(); restoreApplicationSettings();
setMenuVisible(true); setMenuVisible(false); //always make the menu invisible by default
isMenuShowingForFirstTime = true;
} }
/* /*
* We use the Shared Preferences object to restore application settings. * We use the Shared Preferences object to restore application settings.
*/ */
public void restoreApplicationSettings() { public void restoreApplicationSettings() {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); sharedPreferences = getSharedPreferences(PREFS_NAME, 0);
//restore camera processing rate detectorProcessRate = sharedPreferences.getInt("rate", detectorProcessRate); //restore camera processing rate
detectorProcessRate = PreferencesUtils.getFrameProcessingRate(sharedPreferences); fpsEditText.setText(String.valueOf(detectorProcessRate));
detector.setMaxProcessRate(detectorProcessRate);
if (sharedPreferences.getBoolean("fps",isFPSVisible)) { //restore isFPSMetricVisible if (sharedPreferences.getBoolean("fps",isFPSVisible)) { //restore isFPSMetricVisible
fpsCheckbox.setChecked(true);
setFPSVisible(true); setFPSVisible(true);
} else { } else {
fpsCheckbox.setChecked(false);
setFPSVisible(false); setFPSVisible(false);
} }
if (sharedPreferences.getBoolean("track",drawingView.getDrawPointsEnabled())) { //restore isTrackingDotsVisible if (sharedPreferences.getBoolean("track",drawingView.getDrawPointsEnabled())) { //restore isTrackingDotsVisible
setTrackPoints(true); setTrackPoints(true);
trackingCheckbox.setChecked(true);
} else { } else {
setTrackPoints(false); setTrackPoints(false);
trackingCheckbox.setChecked(false);
} }
if (sharedPreferences.getBoolean("measurements",drawingView.getDrawMeasurementsEnabled())) { //restore show measurements
setShowMeasurements(true);
} else {
setShowMeasurements(false);
}
//populate metric displays
for (int n = 0; n < NUM_METRICS_DISPLAYED; n++) {
activateMetric(n,PreferencesUtils.getMetricFromPrefs(sharedPreferences, n));
}
}
/**
* Populates a TextView to display a metric name and readies a MetricDisplay to display the value.
* Uses reflection to:
* -enable the corresponding metric in the Detector object by calling Detector.setDetect<MetricName>()
* -save the Method object that will be invoked on the Face object received in onImageResults() to get the metric score
*/
void activateMetric(int index, MetricsManager.Metrics metric) {
metricNames[index].setText(MetricsManager.getUpperCaseName(metric));
Method getFaceScoreMethod = null; //The method that will be used to get a metric score
try {
//Enable metric detection
Detector.class.getMethod("setDetect" + MetricsManager.getCamelCase(metric), boolean.class).invoke(detector, true);
if (metric.getType() == MetricsManager.MetricType.Emotion) {
getFaceScoreMethod = Face.Emotions.class.getMethod("get" + MetricsManager.getCamelCase(metric), null);
//The MetricDisplay for Valence is unique; it shades it color depending on the metric value
if (metric == MetricsManager.Emotions.VALENCE) {
metricDisplays[index].setIsShadedMetricView(true);
} else {
metricDisplays[index].setIsShadedMetricView(false);
}
} else if (metric.getType() == MetricsManager.MetricType.Expression) {
getFaceScoreMethod = Face.Expressions.class.getMethod("get" + MetricsManager.getCamelCase(metric),null);
}
} catch (Exception e) {
Log.e(LOG_TAG,String.format("Error using reflection to generate methods for %s",metric.toString()));
}
metricDisplays[index].setMetricToDisplay(metric, getFaceScoreMethod);
} }
/** /**
@ -312,43 +312,27 @@ public class MainActivity extends Activity
} }
/** /**
* We want to start the camera as late as possible, so it does not freeze the application before it has been visually resumed. * We start the camera as soon as the application has been given focus, which occurs as soon as the application has
* We thus post a runnable that will take care of starting the camera. * been opened or reopened. Although this can also occur when the application regains focus after a dialog box has been closed,
* the startCamera() method will not start the camera if it is already running.
* We also reset variables used to calculate the Processed Frames Per Second. * We also reset variables used to calculate the Processed Frames Per Second.
*/ */
@Override @Override
public void onWindowFocusChanged(boolean hasFocus) { public void onWindowFocusChanged(boolean hasFocus) {
if (hasFocus && isFrontFacingCameraDetected) {
cameraView.post(new Runnable() {
@Override
public void run() {
mainWindowResumedTasks();
}
});
}
}
void mainWindowResumedTasks() { if (hasFocus && isFrontFacingCameraDetected) {
startCamera(); startCamera();
if (!drawingView.isSurfaceDimensionsNeeded()) { if (!drawingView.isSurfaceDimensionsNeeded()) {
progressBarLayout.setVisibility(View.GONE); progressBarLayout.setVisibility(View.GONE);
} }
resetFPSCalculations(); resetFPSCalculations();
cameraView.postDelayed(new Runnable() {
@Override
public void run() {
if (isMenuShowingForFirstTime) {
setMenuVisible(false);
} }
} }
},5000);
}
void startCamera() { 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);
if (!detector.isRunning()) { if (!detector.isRunning()) {
try { try {
detector.start(); detector.start();
@ -363,7 +347,6 @@ public class MainActivity extends Activity
public void onFaceDetectionStarted() { public void onFaceDetectionStarted() {
leftMetricsLayout.animate().alpha(1); //make left and right metrics appear leftMetricsLayout.animate().alpha(1); //make left and right metrics appear
rightMetricsLayout.animate().alpha(1); rightMetricsLayout.animate().alpha(1);
resetFPSCalculations(); //Since the FPS may be different whether a face is being tracked or not, reset variables. resetFPSCalculations(); //Since the FPS may be different whether a face is being tracked or not, reset variables.
} }
@ -375,7 +358,6 @@ public class MainActivity extends Activity
void performFaceDetectionStoppedTasks() { void performFaceDetectionStoppedTasks() {
leftMetricsLayout.animate().alpha(0); //make left and right metrics disappear leftMetricsLayout.animate().alpha(0); //make left and right metrics disappear
rightMetricsLayout.animate().alpha(0); rightMetricsLayout.animate().alpha(0);
drawingView.invalidatePoints(); //inform the drawing thread that the latest facial tracking points are now invalid drawingView.invalidatePoints(); //inform the drawing thread that the latest facial tracking points are now invalid
resetFPSCalculations(); //Since the FPS may be different whether a face is being tracked or not, reset variables. resetFPSCalculations(); //Since the FPS may be different whether a face is being tracked or not, reset variables.
} }
@ -411,47 +393,29 @@ public class MainActivity extends Activity
Face face = faces.get(0); Face face = faces.get(0);
//update metrics with latest face information. The metrics are displayed on a MetricView, a custom view with a .setScore() method. //update metrics with latest face information. The metrics are displayed on a MetricView, a custom view with a .setScore() method.
for (MetricDisplay metricDisplay : metricDisplays) { smilePct.setScore(face.getSmileScore());
updateMetricScore(metricDisplay,face); browRaisePct.setScore(face.getBrowRaiseScore());
} browFurrowPct.setScore(face.getBrowFurrowScore());
engagementPct.setScore(face.getEngagementScore());
frownPct.setScore(face.getLipCornerDepressorScore());
float valenceScore = face.getValenceScore();
valencePct.setScore(valenceScore);
/** /**
* If the user has selected to have facial tracking dots or measurements drawn, we use face.getFacePoints() to send those points * If the user has selected to have facial tracking dots drawn, we use face.getFacePoints() to send those points
* to our drawing thread and also inform the thread what the valence score was, as that will determine the color * to our drawing thread and also inform the thread what the valence score was, as that will determine the color
* of the bounding box. * of the bounding box.
*/ */
if (drawingView.getDrawPointsEnabled() || drawingView.getDrawMeasurementsEnabled()) { if (drawingView.getDrawPointsEnabled()) {
drawingView.setMetrics(face.measurements.orientation.getRoll(), face.measurements.orientation.getYaw(), face.measurements.orientation.getPitch(), face.measurements.getInterocularDistance(), face.emotions.getValence()); drawingView.setScore(valenceScore);
drawingView.updatePoints(face.getFacePoints()); drawingView.updatePoints(face.getFacePoints());
} }
} }
/** /**
* Use the method that we saved in activateMetric() to get the metric score and display it * In this method, we update our drawingView to contain the dimensions of the frames coming from the camera so that drawingView
*/ * can correctly draw the tracking dots. We also call drawingView.setThickness(), which sets the size of the tracking dots and the
void updateMetricScore(MetricDisplay metricDisplay, Face face) { * thickness of the bounding box.
MetricsManager.Metrics metric = metricDisplay.getMetricToDisplay();
float score = Float.NaN;
try {
if (metric.getType() == MetricsManager.MetricType.Emotion) {
score = (Float) metricDisplay.getFaceScoreMethod().invoke(face.emotions,null);
metricDisplay.setScore(score);
} else if (metric.getType() == MetricsManager.MetricType.Expression) {
score = (Float) metricDisplay.getFaceScoreMethod().invoke(face.expressions,null);
}
} catch (Exception e) {
Log.e(LOG_TAG,String.format("Error using reflecting to get %s score from face.",metric.toString()));
}
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){ void calculateImageDimensions(Frame image){
///referenceDimension will be used to determine the size of the facial tracking dots ///referenceDimension will be used to determine the size of the facial tracking dots
@ -522,15 +486,12 @@ public class MainActivity extends Activity
public void onPause() { public void onPause() {
super.onPause(); super.onPause();
progressBarLayout.setVisibility(View.VISIBLE); progressBarLayout.setVisibility(View.VISIBLE);
saveApplicationSettings();
stopCamera(); stopCamera();
} }
private void stopCamera() { private void stopCamera() {
performFaceDetectionStoppedTasks(); performFaceDetectionStoppedTasks();
detector.setDetectAllEmotions(false);
detector.setDetectAllExpressions(false);
try { try {
detector.stop(); detector.stop();
} catch (Exception e) { } catch (Exception e) {
@ -538,15 +499,57 @@ public class MainActivity extends Activity
} }
} }
/**
* We use the SharedPreferences object to save application settings.
**/
public void saveApplicationSettings() {
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putBoolean("fps", isFPSVisible);
editor.putBoolean("track", drawingView.getDrawPointsEnabled());
editor.putInt("rate", detectorProcessRate);
editor.commit();
}
public void fps_checkbox_click(View view) {
setFPSVisible(((CheckBox) view).isChecked());
}
public void tracking_checkbox_click(View view) {
setTrackPoints(((CheckBox) view).isChecked());
}
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
/**
* When a user has selected the Edit box to change the number of frames the detector processes per second
* and presses the 'DONE' button, the below block will be executed.
* */
if (actionId == EditorInfo.IME_ACTION_DONE) {
int parsedInt = 0;
try {
parsedInt = Integer.parseInt(v.getText().toString());
} catch (Exception e) {
v.setText(String.valueOf(detectorProcessRate));
return false;
}
if (parsedInt > 0) {
detectorProcessRate = parsedInt;
detector.setMaxProcessRate(detectorProcessRate);
resetFPSCalculations(); //reset FPS variables, since changing the process rate should change the FPS.
} else {
v.setText(String.valueOf(detectorProcessRate));
}
}
return false; //return false regardless, so Android closes the keyboard when user presses 'DONE'
}
/** /**
* When the user taps the screen, hide the menu if it is visible and show it if it is hidden. * When the user taps the screen, hide the menu if it is visible and show it if it is hidden.
* **/ * **/
void setMenuVisible(boolean b){ void setMenuVisible(boolean b){
isMenuShowingForFirstTime = false;
isMenuVisible = b; isMenuVisible = b;
if (b) { if (b) {
settingsButton.setVisibility(View.VISIBLE); menuLayout.setVisibility(View.VISIBLE);
//We display the navigation bar again //We display the navigation bar again
getWindow().getDecorView().setSystemUiVisibility( getWindow().getDecorView().setSystemUiVisibility(
@ -555,6 +558,9 @@ public class MainActivity extends Activity
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
} }
else { else {
InputMethodManager imm = (InputMethodManager)getSystemService(
Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(fpsEditText.getWindowToken(), 0);
//We hide the navigation bar //We hide the navigation bar
getWindow().getDecorView().setSystemUiVisibility( getWindow().getDecorView().setSystemUiVisibility(
@ -566,7 +572,7 @@ public class MainActivity extends Activity
| View.SYSTEM_UI_FLAG_IMMERSIVE); | View.SYSTEM_UI_FLAG_IMMERSIVE);
settingsButton.setVisibility(View.INVISIBLE); menuLayout.setVisibility(View.INVISIBLE);
} }
} }
@ -588,10 +594,6 @@ public class MainActivity extends Activity
drawingView.setDrawPointsEnabled(b); drawingView.setDrawPointsEnabled(b);
} }
void setShowMeasurements(boolean b) {
drawingView.setDrawMeasurementsEnabled(b);
}
void setFPSVisible(boolean b) { void setFPSVisible(boolean b) {
isFPSVisible = b; isFPSVisible = b;
if (b) { if (b) {
@ -610,10 +612,6 @@ public class MainActivity extends Activity
} }
return false; return false;
} }
public void settings_button_click(View view) {
startActivity(new Intent(this,SettingsActivity.class));
}
} }

View file

@ -1,599 +0,0 @@
package com.affectiva.affdexme;
import android.app.Activity;
import android.app.Fragment;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.graphics.SurfaceTexture;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.GridLayout;
import android.widget.TextView;
import static com.affectiva.affdexme.MainActivity.NUM_METRICS_DISPLAYED;
import java.util.ArrayList;
import java.util.HashMap;
/**
* A fragment to display a graphical menu which allows the user to select which metrics to display.
*
*/
public class MetricSelectionFragment extends Fragment implements View.OnClickListener {
final static String LOG_TAG = "Affectiva";
int numberOfSelectedItems = 0;
int messageAtOrUnderLimitColor;
int messageOverLimitColor;
SharedPreferences sharedPreferences;
TextView metricChooserTextView;
GridLayout gridLayout;
Button clearAllButton;
HashMap<MetricsManager.Metrics, MetricSelector> metricSelectors = new HashMap<>();
//An inner class object to control video playback in the metricSelectors
MetricSelectionFragmentMediaPlayer fragmentMediaPlayer;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View fragmentLayout = inflater.inflate(R.layout.metric_chooser, container, false);
initUI(fragmentLayout);
fragmentMediaPlayer = new MetricSelectionFragmentMediaPlayer();
restoreSettings(savedInstanceState);
//We post the methods used to populate the gridLayout view so that they run when gridLayout has been added to the layout and sized.
gridLayout.post(new Runnable() {
@Override
public void run() {
populateGrid();
updateAllGridItems();
}
});
return fragmentLayout;
}
void initUI(View fragmentLayout) {
gridLayout = (GridLayout) fragmentLayout.findViewById(R.id.metric_chooser_gridlayout);
metricChooserTextView = (TextView) fragmentLayout.findViewById(R.id.metrics_chooser_textview);
clearAllButton = (Button) fragmentLayout.findViewById(R.id.clear_all_button);
clearAllButton.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
clearItems();
}
}
);
Resources res = getResources();
messageAtOrUnderLimitColor = res.getColor(R.color.white);
messageOverLimitColor = res.getColor(R.color.red);
}
/**
* A method to populate the metricSelectors array using information from either a saved instance bundle (if the activity is being re-created)
* or sharedPreferences (if the activity is being created for the first time)
*/
void restoreSettings(Bundle bundle) {
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
Activity hostActivity = getActivity();
LayoutInflater inflater = hostActivity.getLayoutInflater();
Resources res = getResources();
String packageName = hostActivity.getPackageName();
//populate metricSelectors list with objects
for (MetricsManager.Metrics metric : MetricsManager.getAllMetrics()) {
metricSelectors.put(metric, new MetricSelector(hostActivity, inflater, res, packageName, metric));
}
if (bundle != null) { //if we were passed a bundle, use its data to configure the MetricSelectors
for (MetricsManager.Metrics metric : MetricsManager.getAllMetrics()) {
if (bundle.getBoolean(metric.toString(),false)) {
selectItem(metricSelectors.get(metric),true,false);
}
}
} else { //otherwise, we pull the data from application preferences
for (int i = 0; i < NUM_METRICS_DISPLAYED; i++) {
MetricsManager.Metrics chosenMetric = PreferencesUtils.getMetricFromPrefs(sharedPreferences, i);
selectItem(metricSelectors.get(chosenMetric),true,false);
}
}
}
@Override
public void onSaveInstanceState(Bundle bundle) {
//save whether each MetricSelector has been selected, using the metric name as the key
for (MetricsManager.Metrics metric : MetricsManager.getAllMetrics()) {
bundle.putBoolean(metric.toString(), metricSelectors.get(metric).getIsSelected());
}
super.onSaveInstanceState(bundle);
}
/**
* When the app is minimized, the active TextureView in fragmentMediaPlayer is destroyed, but throws an
* exception if we try to remove it from its parent while in the destroyed state. Since fragmentMediaPlayer
* begins assuming the TextureView is not attached to any parent, we run through all MetricSelectors and command
* them to let go of the TextureView if they are the current parent.
*/
@Override
public void onResume() {
super.onResume();
for (MetricsManager.Metrics metric : MetricsManager.getAllMetrics()) {
fragmentMediaPlayer.stopMetricSelectorPlayback(metricSelectors.get(metric));
}
}
@Override
public void onPause() {
super.onPause();
saveSettings();
}
/* Our goal in this method is to ensure that 6 and only 6 metrics are saved in Preferences. We attempt to fill all 6 slots
with metrics chosen by the user, but if the user did not choose 6 slots, we fill the remaining slots with any other metrics.
*/
void saveSettings() {
ArrayList<MetricsManager.Metrics> selectedMetrics = new ArrayList<>();
//Add all selected metrics
for (MetricsManager.Metrics metric : MetricsManager.getAllMetrics()) {
if (metricSelectors.get(metric).getIsSelected()) {
selectedMetrics.add(metric);
if (selectedMetrics.size() >= NUM_METRICS_DISPLAYED) {
break;
}
}
}
//fill remaining empty slots
if (selectedMetrics.size() < NUM_METRICS_DISPLAYED) {
for (MetricsManager.Metrics metric : MetricsManager.getAllMetrics()) {
if (!selectedMetrics.contains(metric)) {
selectedMetrics.add(metric);
if (selectedMetrics.size() >= NUM_METRICS_DISPLAYED) {
break;
}
}
}
}
//save list into application preferences
SharedPreferences.Editor editor = sharedPreferences.edit();
for (int n = 0; n < selectedMetrics.size(); n++) {
PreferencesUtils.saveMetricToPrefs(editor, n, selectedMetrics.get(n));
}
editor.commit();
}
/* While Android offers a GridView which can automatically populate a grid from an array, we wished to divide our grid items into 'Emotions' and 'Expressions'
categories. Therefore, we implement a scrollable GridLayout.
Furthermore, while we wished for the grid items to take up the entire grid, Android versions lower than 21 do not support the concept of weights in a GridLayout,
so we manually size the grid items in this method.
Note that since this method is posted as a runnable of gridLayout, it should only be run once gridLayout has been added to the layout and sized.
*/
void populateGrid() {
LayoutInflater inflater = getActivity().getLayoutInflater();
Resources res = getResources();
int minColumnWidth = res.getDimensionPixelSize(R.dimen.metric_chooser_column_width);
//calculate number of columns
int gridWidth = gridLayout.getWidth();
int numColumns = gridWidth / minColumnWidth; //intentional integer division
if (numColumns <= 0) {
Log.e(LOG_TAG, "Desired Column Width too large! Unable to populate Grid");
return;
}
int columnWidth = (int)((float) gridWidth / (float)numColumns);
//This integer reference will be used across methods to keep track of how many rows we have created.
//Each method we pass it into leaves it at a value indicating the next row number that views should be added to.
IntRef currentRow = new IntRef();
addHeader("Emotions", currentRow, numColumns, inflater);
addGridItems(currentRow, numColumns, inflater, res, columnWidth, MetricsManager.Emotions.values());
addHeader("Expressions", currentRow, numColumns, inflater);
addGridItems(currentRow, numColumns, inflater, res, columnWidth, MetricsManager.Expressions.values());
gridLayout.setColumnCount(numColumns);
gridLayout.setRowCount(currentRow.value);
}
//adds a header (consisting of a TextView and border line) to the grid
void addHeader(String name, IntRef currentRow, int numColumns, LayoutInflater inflater) {
View header = inflater.inflate(R.layout.grid_header, null);
//each header should take up one row and all available columns
GridLayout.LayoutParams params = new GridLayout.LayoutParams(GridLayout.spec(currentRow.value, 1), GridLayout.spec(0, numColumns));
params.width = gridLayout.getWidth();
header.setLayoutParams(params);
gridLayout.addView(header);
((TextView) header.findViewById(R.id.header_text)).setText(name);
currentRow.value += 1; //point currentRow to row where next views should be added
}
//adds a set of metrics (using the data from the MetricsManager object from index 'start' to index 'end')
void addGridItems(IntRef currentRow, int numColumns, LayoutInflater inflater, Resources res, int size, MetricsManager.Metrics[] metricsToAdd) {
//keeps track of the column we are adding to
int col = -1; //start col at -1 so it becomes 0 during first iteration of for loop
for (MetricsManager.Metrics metric : metricsToAdd) {
col += 1;
if (col >= numColumns) {
col = 0;
currentRow.value += 1;
}
MetricSelector item = metricSelectors.get(metric);
GridLayout.LayoutParams params = new GridLayout.LayoutParams();
params.width = size;
params.height = size;
params.columnSpec = GridLayout.spec(col);
params.rowSpec = GridLayout.spec(currentRow.value);
item.setLayoutParams(params);
item.setOnClickListener(this);
gridLayout.addView(item);
}
currentRow.value +=1; //point currentRow to row where next views should be added
}
@Override
public void onClick(View v) {
MetricSelector item = (MetricSelector) v;
selectItem(item, !item.getIsSelected(), true); //select item if de-selected, and vice-versa
updateAllGridItems(); //each click will result in all items being updated
}
/* Updates numberOfSelectedItems as well as the message presented by the text at the top of the activity
*/
void selectItem(MetricSelector metricSelector, boolean isSelected, boolean playVideo) {
//update numberOfSelectedItems
boolean wasSelected = metricSelector.getIsSelected();
if (!wasSelected && isSelected) {
numberOfSelectedItems += 1;
if (playVideo) {
fragmentMediaPlayer.startMetricSelectorPlayback(metricSelector);
}
} else if (wasSelected && !isSelected) {
numberOfSelectedItems -= 1;
if (playVideo) {
fragmentMediaPlayer.stopMetricSelectorPlayback(metricSelector);
}
}
metricSelector.setIsSelected(isSelected);
//Create and display message at the top
/*String dMetricsChosen;
if (numberOfSelectedItems == 1) {
dMetricsChosen = "1 metric chosen.";
} else {
dMetricsChosen = String.format("%d metrics chosen.",numberOfSelectedItems);
}*/
if (numberOfSelectedItems == 1) {
metricChooserTextView.setText("1 metric chosen.");
} else {
metricChooserTextView.setText(String.format("%d metrics chosen.",numberOfSelectedItems));
}
if (numberOfSelectedItems <= NUM_METRICS_DISPLAYED) {
metricChooserTextView.setTextColor(messageAtOrUnderLimitColor);
} else {
metricChooserTextView.setTextColor(messageOverLimitColor);
}
/*if (numberOfSelectedItems < NUM_METRICS_DISPLAYED) {
metricChooserTextView.setTextColor(messageAtOrUnderLimitColor);
metricChooserTextView.setText(String.format("%s Choose %d more.", dMetricsChosen, NUM_METRICS_DISPLAYED - numberOfSelectedItems));
} else if (numberOfSelectedItems == NUM_METRICS_DISPLAYED) {
metricChooserTextView.setTextColor(messageAtOrUnderLimitColor);
metricChooserTextView.setText(dMetricsChosen);
} else {
metricChooserTextView.setTextColor(messageOverLimitColor);
metricChooserTextView.setText(String.format("%s Please de-select %d.", dMetricsChosen, numberOfSelectedItems - NUM_METRICS_DISPLAYED));
}*/
}
void clearItems() {
for (MetricsManager.Metrics metric : MetricsManager.getAllMetrics()) {
selectItem(metricSelectors.get(metric),false,true);
}
updateAllGridItems();
}
//loop through all grid items, and update those which are tagged with an Integer (those which represent selectable metrics).
void updateAllGridItems() {
for (MetricsManager.Metrics metric : MetricsManager.getAllMetrics()) {
metricSelectors.get(metric).setUnderOrOverLimit(numberOfSelectedItems <= NUM_METRICS_DISPLAYED);
}
}
@Override
public void onDestroy() {
super.onDestroy();
fragmentMediaPlayer.destroy();
}
//IntRef represents a reference to a mutable integer value
//It is used to keep track of how many rows have been created in the populateGrid() method
class IntRef {
public int value;
public IntRef() {
value = 0;
}
}
/**
* The MetricSelector objects in this fragment will play a video when selected. To keep memory usage low, we use only one MediaPlayer
* object to control video playback. Video is rendered on a single TextureView.
* Chain of events that lead to video playback:
* -When a MetricSelector is clicked, the MediaPlayer.setDataSource() is called to set the video file
* -The TextureView is added to the view hierarchy of the MetricSelector, causing the onSurfaceTextureAvailable callback to fire
* -The TextureView is bound to the MediaPlayer through MediaPlayer.setSurface(), then MediaPlayer.prepareAsync() is called
* -Once preparation is complete, MediaPlayer.start() is called
* -MediaPlayer.stop() will be called when playback finishes or the item has been de-selected, at which point the TextureView will
* be removed from the MetricSelector's view hierarchy, causing onSurfaceTextureDestroyed(), where we call MediaPlayer.setSurface(null)
*/
class MetricSelectionFragmentMediaPlayer {
SafeMediaPlayer safePlayer;
TextureView textureView;
MetricSelector videoPlayingSelector;
public MetricSelectionFragmentMediaPlayer() {
safePlayer = new SafeMediaPlayer(getActivity());
safePlayer.setOnPreparedListener(new OnSafeMediaPlayerPreparedListener() {
@Override
public void onSafeMediaPlayerPrepared() {
safePlayer.start();
if (!greaterThanHoneyComb()) {
safePlayer.seekTo(1);
}
}
});
/**
* Although it is best to remove the MetricSelector video cover upon reception of the
* VIDEO_RENDERING event, this event is only available on SDK 17 and above.
*/
if (greaterThanHoneyComb()) {
safePlayer.setOnInfoListener(new MediaPlayer.OnInfoListener() {
@Override
public boolean onInfo(MediaPlayer mp, int what, int extra) {
if (what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) {
videoPlayingSelector.removeCover();
}
return false;
}
});
} else {
safePlayer.setOnSeekListener(new MediaPlayer.OnSeekCompleteListener() {
@Override
public void onSeekComplete(MediaPlayer mp) {
videoPlayingSelector.removeCover();
}
});
}
safePlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
Uri nextVideoUri = videoPlayingSelector.getNextVideoResourceURI();
if (nextVideoUri == null) {
endVideoPlayback();
} else {
safePlayer.stopAndReset();
safePlayer.setDataSource(nextVideoUri);
safePlayer.prepareAsync();
}
}
});
textureView = new TextureView(getActivity());
textureView.setVisibility(View.GONE);
textureView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
safePlayer.setSurface(new Surface(surface));
safePlayer.prepareAsync();
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
safePlayer.stopAndReset();
safePlayer.setSurface(null);
return false;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
});
}
private void startVideoPlayback(MetricSelector metricSelector) {
videoPlayingSelector = metricSelector;
videoPlayingSelector.initIndex();
safePlayer.setDataSource(metricSelector.getNextVideoResourceURI());
metricSelector.displayVideo(textureView); //will cause onSurfaceTextureAvailable to fire
}
private void endVideoPlayback() {
videoPlayingSelector.displayCover();
safePlayer.stopAndReset();
videoPlayingSelector.removeVideo(); //will cause onSurfaceTextureDestroyed() to fire
}
void startMetricSelectorPlayback(MetricSelector metricSelector) {
if (videoPlayingSelector != null) {
endVideoPlayback(); //stop previous video
}
startVideoPlayback(metricSelector);
}
void stopMetricSelectorPlayback(MetricSelector metricSelector) {
if (metricSelector == videoPlayingSelector) { //if de-selected item is a playing video, stop it
endVideoPlayback();
}
}
public void destroy() {
safePlayer.release(); //release resources of media player
textureView = null;
}
boolean greaterThanHoneyComb() {
return Build.VERSION.SDK_INT >= 17;
}
}
/**
* These are not all the MediaPlayer states defined by Android, but they are all the ones we are interested in.
* Note that SafeMediaPlayer never stays in the STOPPED state, so we don't include it.
*/
enum MediaPlayerState {
IDLE, INIT, PREPARED, PLAYING
};
interface OnSafeMediaPlayerPreparedListener {
void onSafeMediaPlayerPrepared();
}
/**
* A Facade to ensure our MediaPlayer does not throw an error due to an invalid state change.
*/
class SafeMediaPlayer {
MediaPlayerState state;
MediaPlayer mediaPlayer;
Activity activity;
OnSafeMediaPlayerPreparedListener listener = null;
public SafeMediaPlayer(Activity activity) {
mediaPlayer = new MediaPlayer();
state = MediaPlayerState.IDLE;
this.activity = activity;
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
state = MediaPlayerState.PREPARED;
if (listener != null) {
listener.onSafeMediaPlayerPrepared();
}
}
});
}
void setDataSource(Uri source) {
if (state == MediaPlayerState.IDLE) {
try {
mediaPlayer.setDataSource(activity, source);
} catch (Exception e) {
Log.e(LOG_TAG, e.getMessage());
return; //If unable to setup video, exit function
}
state = MediaPlayerState.INIT;
}
}
void prepareAsync() {
if (state == MediaPlayerState.INIT) {
mediaPlayer.prepareAsync();
}
}
void start() {
if (state == MediaPlayerState.PREPARED) {
mediaPlayer.start();
state = MediaPlayerState.PLAYING;
}
}
void seekTo(int msec) {
if (state == MediaPlayerState.PREPARED || state == MediaPlayerState.PLAYING) {
mediaPlayer.seekTo(msec);
}
}
void stopAndReset() {
if (state == MediaPlayerState.PLAYING || state == MediaPlayerState.PREPARED) {
mediaPlayer.stop();
}
mediaPlayer.reset(); //can be called from any state
state = MediaPlayerState.IDLE;
}
void setOnPreparedListener(OnSafeMediaPlayerPreparedListener listener) {
this.listener = listener;
}
//The rest of the methods are delegation methods
void setSurface(Surface surface) {
mediaPlayer.setSurface(surface);
}
void setOnCompletionListener(MediaPlayer.OnCompletionListener listener) {
mediaPlayer.setOnCompletionListener(listener);
}
void setOnSeekListener(MediaPlayer.OnSeekCompleteListener listener) {
mediaPlayer.setOnSeekCompleteListener(listener);
}
void setOnInfoListener(MediaPlayer.OnInfoListener listener) {
mediaPlayer.setOnInfoListener(listener);
}
void release() {
mediaPlayer.release();
mediaPlayer = null;
}
}
}

View file

@ -1,172 +0,0 @@
package com.affectiva.affdexme;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Color;
import android.net.Uri;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.TextureView;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
/**
* A view representing a metric that can be selected by the user. Meant for use by MetricSelectionFragment.
* This view not only changes color when selected, but also plays a video. To save resources, only one MetricSelector
* object plays a video at a time, so playback is coordinated by the MetricSelectionFragment object.
*/
public class MetricSelector extends FrameLayout {
private boolean isMetricSelected;
private MetricsManager.Metrics metric;
TextureView textureView;
TextView gridItemTextView;
ImageView imageView;
ImageView imageViewBeneath;
RelativeLayout backgroundLayout;
int itemNotSelectedColor;
int itemSelectedColor;
int itemSelectedOverLimitColor;
Uri[] videoResourceURIs;
int videoResourceURIIndex;
TextView videoOverlay;
int picId;
public MetricSelector(Activity hostActivity, LayoutInflater inflater, Resources res, String packageName, MetricsManager.Metrics metric) {
super(hostActivity);
this.metric = metric;
this.isMetricSelected = false;
initContent(inflater, res, packageName);
}
void initContent(LayoutInflater inflater, Resources res, String packageName) {
View content = inflater.inflate(R.layout.grid_item, this, true);
String resourceName = MetricsManager.getLowerCaseName(metric);
videoOverlay = (TextView) content.findViewById(R.id.video_overlay);
int videoId = res.getIdentifier(resourceName,"raw",packageName);
if (metric == MetricsManager.Emotions.VALENCE) {
videoResourceURIs = new Uri[2];
videoResourceURIs[0] = Uri.parse(String.format("android.resource://%s/%d", packageName, videoId ));
videoResourceURIs[1] = Uri.parse(String.format("android.resource://%s/%d", packageName, res.getIdentifier(resourceName+"0","raw",packageName)));
} else {
videoResourceURIs = new Uri[1];
videoResourceURIs[0] = Uri.parse(String.format("android.resource://%s/%d", packageName, videoId));
}
videoResourceURIIndex = 0;
//set up image
picId = res.getIdentifier(resourceName, "drawable", packageName);
imageView = (ImageView) content.findViewById(R.id.grid_item_image_view);
imageViewBeneath = (ImageView) content.findViewById(R.id.grid_item_image_view_beneath);
imageView.setImageResource(picId);
imageViewBeneath.setImageResource(picId);
imageViewBeneath.setVisibility(GONE);
backgroundLayout = (RelativeLayout) content.findViewById(R.id.grid_item_background);
gridItemTextView = (TextView) content.findViewById(R.id.grid_item_text);
gridItemTextView.setText(MetricsManager.getCapitalizedName(metric));
itemSelectedOverLimitColor = res.getColor(R.color.grid_item_chosen_over_limit);
itemNotSelectedColor = res.getColor(R.color.grid_item_not_chosen);
itemSelectedColor = res.getColor(R.color.grid_item_chosen);
}
boolean getIsSelected() {
return this.isMetricSelected;
}
void setIsSelected(boolean isSelected) {
this.isMetricSelected = isSelected;
}
void removeCover() {
imageViewBeneath.setVisibility(VISIBLE);
imageView.setVisibility(GONE);
}
void displayCover() {
imageViewBeneath.setVisibility(GONE);
imageView.setVisibility(VISIBLE);
}
void displayVideo(TextureView videoView) {
textureView = videoView;
backgroundLayout.addView(textureView, 1);
textureView.setVisibility(VISIBLE);
videoOverlay.setVisibility(VISIBLE);
}
void removeVideo() {
if (textureView != null) {
textureView.setVisibility(GONE);
backgroundLayout.removeView(textureView);
textureView = null;
}
videoOverlay.setVisibility(GONE);
}
MetricsManager.Metrics getMetric() {
return metric;
}
void initIndex() {
videoResourceURIIndex = 0;
}
Uri getNextVideoResourceURI() {
if (metric == MetricsManager.Emotions.VALENCE) {
if (videoResourceURIIndex == 0) {
videoOverlay.setText("NEGATIVE");
videoOverlay.setTextColor(Color.RED);
} else {
videoOverlay.setText("POSITIVE");
videoOverlay.setTextColor(Color.GREEN);
}
}
videoResourceURIIndex += 1;
if (videoResourceURIIndex > videoResourceURIs.length) {
return null;
} else {
return videoResourceURIs[videoResourceURIIndex - 1];
}
}
/**
* Changes the appearance of the grid item to indicate whether too many metrics have been selected
*/
void setUnderOrOverLimit(boolean atOrUnderLimit) {
if (isMetricSelected) {
if (atOrUnderLimit) {
gridItemTextView.setBackgroundColor(itemSelectedColor);
backgroundLayout.setBackgroundColor(itemSelectedColor);
} else {
gridItemTextView.setBackgroundColor(itemSelectedOverLimitColor);
backgroundLayout.setBackgroundColor(itemSelectedOverLimitColor);
}
} else {
gridItemTextView.setBackgroundColor(itemNotSelectedColor);
backgroundLayout.setBackgroundColor(itemNotSelectedColor);
}
}
}

View file

@ -9,15 +9,11 @@ import android.graphics.Typeface;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.View; import android.view.View;
import java.lang.Math; import java.lang.Math;
import java.lang.reflect.Method;
/** /**
* The MetricView class is used to display metric scores on top of colored bars whose color depend on the score. * The MetricView class is used to display metric scores on top of colored bars whose color depend on the score.
*/ */
public class MetricDisplay extends View { public class MetricView extends View {
MetricsManager.Metrics metricToDisplay; //indicates which of the 24 Affectiva Emotions and Expressions this view is displaying
Method faceScoreMethod;
float midX = 0; //coordinates of the center of the view float midX = 0; //coordinates of the center of the view
float midY = 0; float midY = 0;
@ -30,28 +26,20 @@ public class MetricDisplay extends View {
float right = 0; float right = 0;
float top = 0; float top = 0;
float textBottom = 0; //tells our view where to draw the baseline of the font float textBottom = 0; //tells our view where to draw the baseline of the font
boolean isShadedMetricView = false;
public MetricDisplay(Context context) { public MetricView(Context context) {
super(context); super(context);
initResources(context,null); initResources(context,null);
} }
public MetricDisplay(Context context, AttributeSet attrs) { public MetricView(Context context, AttributeSet attrs) {
super(context,attrs); super(context,attrs);
initResources(context,attrs); initResources(context,attrs);
} }
public MetricDisplay(Context context, AttributeSet attrs, int styleID){ public MetricView(Context context, AttributeSet attrs, int styleID){
super(context, attrs, styleID); super(context, attrs, styleID);
initResources(context,attrs); initResources(context,attrs);
} }
void setIsShadedMetricView(boolean b) {
this.isShadedMetricView = b;
if (!b) {
boxPaint.setColor(Color.GREEN);
}
}
void initResources(Context context, AttributeSet attrs) { void initResources(Context context, AttributeSet attrs) {
boxPaint = new Paint(); boxPaint = new Paint();
@ -86,48 +74,14 @@ public class MetricDisplay extends View {
} }
public void setMetricToDisplay(MetricsManager.Metrics metricToDisplay, Method faceScoreMethod) {
this.metricToDisplay = metricToDisplay;
this.faceScoreMethod = faceScoreMethod;
}
public MetricsManager.Metrics getMetricToDisplay() {
return this.metricToDisplay;
}
public Method getFaceScoreMethod() {
return this.faceScoreMethod;
}
public void setTypeface(Typeface face) { public void setTypeface(Typeface face) {
textPaint.setTypeface(face); textPaint.setTypeface(face);
} }
public void setScore(float s){ public void setScore(float s){
text = String.format("%.0f%%", s); //change the text of the view text = String.format("%.0f%%", s); //change the text of the view
//shading mode is turned on for Valence, which causes this view to shade its color according
//to the value of 's'
if (isShadedMetricView) {
if (s > 0) {
left = midX - (halfWidth * (s / 100));
right = midX + (halfWidth * (s / 100));
} else {
left = midX - (halfWidth * (-s / 100));
right = midX + (halfWidth * (-s / 100));
}
if (s > 0) {
float colorScore = ((100f-s)/100f)*255;
boxPaint.setColor(Color.rgb((int)colorScore,255,(int)colorScore));
} else {
float colorScore = ((100f+s)/100f)*255;
boxPaint.setColor(Color.rgb(255,(int)colorScore,(int)colorScore));
}
} else {
left = midX - (halfWidth * (s / 100)); //change the coordinates at which the colored bar will be drawn left = midX - (halfWidth * (s / 100)); //change the coordinates at which the colored bar will be drawn
right = midX + (halfWidth * (s / 100)); right = midX + (halfWidth * (s / 100));
}
invalidate(); //instruct Android to re-draw our view, now that the text has changed invalidate(); //instruct Android to re-draw our view, now that the text has changed
} }

View file

@ -1,140 +0,0 @@
package com.affectiva.affdexme;
import java.lang.StringBuilder;
/**
* A class containing:
* -enumerations representing the Emotion and Expressions featured in the Affectiva SDK.
* -a Metric interface to allow easy iteration through all Expressions and Emotions
* -utility methods for converting a Metric into several types of strings
*/
public class MetricsManager {
private static Metrics[] allMetrics;
static {
Emotions[] emotions = Emotions.values();
Expressions[] expressions = Expressions.values();
allMetrics = new Metrics[emotions.length + expressions.length];
System.arraycopy(emotions,0,allMetrics,0,emotions.length);
System.arraycopy(expressions,0,allMetrics,emotions.length,expressions.length);
}
static Metrics[] getAllMetrics() {
return allMetrics;
}
enum MetricType {Emotion, Expression};
interface Metrics {
MetricType getType();
}
enum Emotions implements Metrics {
ANGER,
DISGUST,
FEAR,
JOY,
SADNESS,
SURPRISE,
CONTEMPT,
ENGAGEMENT,
VALENCE;
@Override
public MetricType getType() {
return MetricType.Emotion;
}
}
enum Expressions implements Metrics {
ATTENTION,
BROW_FURROW,
BROW_RAISE,
CHIN_RAISE,
EYE_CLOSURE,
INNER_BROW_RAISE,
LIP_CORNER_DEPRESSOR,
LIP_PRESS,
LIP_PUCKER,
LIP_SUCK,
MOUTH_OPEN,
NOSE_WRINKLE,
SMILE,
SMIRK,
UPPER_LIP_RAISE;
@Override
public MetricType getType() {
return MetricType.Expression;
}
}
//Used for displays
static String getUpperCaseName(Metrics metric) {
if (metric == Expressions.LIP_CORNER_DEPRESSOR) {
return "FROWN";
} else {
return metric.toString().replace("_", " ");
}
}
//Used for MetricSelectionFragment
//This method is optimized for strings of the form SOME_METRIC_NAME, which all metric names currently are
static String getCapitalizedName(Metrics metric) {
if (metric == Expressions.LIP_CORNER_DEPRESSOR) {
return "Frown";
}
String original = metric.toString();
StringBuilder builder = new StringBuilder();
boolean canBeLowerCase = false;
for (int n = 0; n < original.length(); n++) {
char c = original.charAt(n);
if (c == '_') {
builder.append(' ');
canBeLowerCase = false;
} else {
if (canBeLowerCase) {
builder.append(Character.toLowerCase(c));
} else {
builder.append(c);
canBeLowerCase = true;
}
}
}
return builder.toString();
}
//Used to load resource files
static String getLowerCaseName(Metrics metric) {
return metric.toString().toLowerCase();
}
//Used to construct method names for reflection
static String getCamelCase(Metrics metric) {
String metricString = metric.toString();
StringBuilder builder = new StringBuilder();
builder.append(Character.toUpperCase(metricString.charAt(0)));
if (metricString.length() > 1) {
for (int n = 1; n < metricString.length(); n++ ){
char c = metricString.charAt(n);
if (c == '_') {
n += 1;
if (n < metricString.length()) {
builder.append(metricString.charAt(n));
}
} else {
builder.append(Character.toLowerCase(metricString.charAt(n)));
}
}
}
return builder.toString();
}
}

View file

@ -1,95 +0,0 @@
package com.affectiva.affdexme;
import android.content.SharedPreferences;
/**
* A helper class to translate strings held in preferences into values to be used by the application.
*/
public class PreferencesUtils {
static final int DEFAULT_FPS = 20;
/**
* Attempt to parse and return FPS set by user. If the FPS is invalid, we set it to be the default FPS.
*/
public static int getFrameProcessingRate(SharedPreferences pref) {
String rateString = pref.getString("rate", String.valueOf(DEFAULT_FPS));
int toReturn;
try {
toReturn = Integer.parseInt(rateString);
} catch (Exception e) {
saveFrameProcessingRate(pref,DEFAULT_FPS);
return DEFAULT_FPS;
}
if (toReturn > 0) {
return toReturn;
} else {
saveFrameProcessingRate(pref,DEFAULT_FPS);
return DEFAULT_FPS;
}
}
private static void saveFrameProcessingRate(SharedPreferences pref, int rate) {
SharedPreferences.Editor editor = pref.edit();
editor.putString("rate",String.valueOf(rate));
editor.commit();
}
public static MetricsManager.Metrics getMetricFromPrefs(SharedPreferences pref, int index) {
MetricsManager.Metrics metric;
try {
String stringFromPref = pref.getString(String.format("metric_display_%d", index),defaultMetric(index).toString());
metric = parseSavedMetric(stringFromPref );
} catch (IllegalArgumentException e) {
metric = defaultMetric(index);
SharedPreferences.Editor editor = pref.edit();
editor.putString(String.format("metric_display_%d", index),defaultMetric(index).toString());
editor.commit();
}
return metric;
}
public static void saveMetricToPrefs(SharedPreferences.Editor editor , int index, MetricsManager.Metrics metric) {
editor.putString(String.format("metric_display_%d", index), metric.toString());
}
static private MetricsManager.Metrics defaultMetric(int index) {
switch (index) {
case 0:
return MetricsManager.Emotions.ANGER;
case 1:
return MetricsManager.Emotions.DISGUST;
case 2:
return MetricsManager.Emotions.FEAR;
case 3:
return MetricsManager.Emotions.JOY;
case 4:
return MetricsManager.Emotions.SADNESS;
case 5:
return MetricsManager.Emotions.SURPRISE;
}
return MetricsManager.Emotions.ANGER;
}
/**
* We attempt to parse the string as an Emotion or, failing that, as an Expression.
*/
static MetricsManager.Metrics parseSavedMetric(String metricString) throws IllegalArgumentException{
try {
MetricsManager.Emotions emotion;
emotion = MetricsManager.Emotions.valueOf(metricString);
return emotion;
} catch (IllegalArgumentException emotionParseFailed) {
try {
MetricsManager.Expressions expression;
expression = MetricsManager.Expressions.valueOf(metricString);
return expression;
} catch (IllegalArgumentException expressionParseFailed) {
throw new IllegalArgumentException("String did not match an emotion or expression");
}
}
}
}

View file

@ -1,72 +0,0 @@
package com.affectiva.affdexme;
import android.app.ActionBar;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.preference.PreferenceActivity;
import android.preference.PreferenceFragment;
import android.R.id.*;
import android.view.MenuItem;
import java.util.List;
//Activity which works with standard Android Preferences framework to display the preference headers.
public class SettingsActivity extends PreferenceActivity {
@Override
public void onCreate(Bundle savedBundleInstance) {
super.onCreate(savedBundleInstance);
ActionBar actionBar = getActionBar();
actionBar.setIcon(
new ColorDrawable(getResources().getColor(android.R.color.transparent)));
//actionBar.setDisplayHomeAsUpEnabled(true);
}
/*
@Override
public boolean onNavigateUp() {
this.onBackPressed();
return true;
}*/
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
// Respond to the action bar's Up/Home button
case android.R.id.home:
this.onBackPressed();
return true;
}
return super.onOptionsItemSelected(item);
}
//Populate the activity with the top-level headers.
@Override
public void onBuildHeaders(List<Header> target) {
loadHeadersFromResource(R.xml.preference_headers, target);
}
//Boilerplate method, required by Android API
@Override
protected boolean isValidFragment(String fragmentName) {
if (SettingsFragment.class.getName().equals(fragmentName) || MetricSelectionFragment.class.getName().equals(fragmentName)) {
return(true);
}
return(false);
}
//This fragment shows the preferences for the first header.
public static class SettingsFragment extends PreferenceFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Load the preferences from an XML resource
addPreferencesFromResource(R.xml.settings_preferences);
}
}
//The second fragment is defined in a separate file.
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 250 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 201 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 232 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 290 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 232 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -1,5 +0,0 @@
<?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" />
</selector>

View file

@ -1,7 +1,5 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
xmlns:custom="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent" tools:context=".MainActivity" android:focusable="true" android:layout_height="match_parent" tools:context=".MainActivity" android:focusable="true"
android:focusableInTouchMode="true" android:focusableInTouchMode="true"
android:keepScreenOn="true" android:keepScreenOn="true"
@ -23,28 +21,14 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_centerInParent="true" 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"/> android:id="@+id/drawing_view"/>
<include layout="@layout/metric_layout" <include layout="@layout/metric_layout"
android:id="@+id/metric_view_group" android:id="@+id/metric_view_group"
/> />
<ImageButton <include layout="@layout/menu_layout"
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:layout_below="@id/metric_view_group"
android:id="@+id/settings_button" android:layout_width="fill_parent"
android:onClick="settings_button_click"/> android:layout_height="wrap_content"/>
</RelativeLayout> </RelativeLayout>
<RelativeLayout <RelativeLayout
@ -52,24 +36,12 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:id="@+id/progress_bar_cover" android:id="@+id/progress_bar_cover"
android:background="@color/black"> android:background="@color/black">
<ProgressBar
<TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_centerVertical="true" style="@android:style/Widget.DeviceDefault.ProgressBar"
android:id="@+id/please_wait_textview" android:layout_centerInParent="true"
android:text="@string/loading" android:indeterminate="true"/>
android:textSize="@dimen/please_wait_textview_size"
android:layout_centerHorizontal="true"
/>
<ProgressBar
android:layout_width="@dimen/please_wait_textview_size"
android:layout_height="@dimen/please_wait_textview_size"
android:layout_toLeftOf="@id/please_wait_textview"
android:layout_centerVertical="true"
android:indeterminate="true"
android:paddingRight="10dp"
android:id="@+id/progress_bar"/>
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"

View file

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/header_text"
android:textSize="@dimen/header_text_size"
android:layout_marginLeft="5dp"/>
<View
android:layout_width="match_parent"
android:layout_height="2dp"
android:background="@color/affectiva_orange"
android:layout_margin="5dp"/>
</LinearLayout>

View file

@ -1,39 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent"
android:padding="5dp">
<RelativeLayout
android:layout_width="match_parent" android:layout_height="match_parent"
android:id="@+id/grid_item_background"
android:paddingLeft="4dp" android:paddingBottom="8dp" android:paddingRight="4dp">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/grid_item_image_view_beneath"
/>
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/grid_item_image_view"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/grid_item_text"
android:textSize="@dimen/grid_item_metric_name"
android:gravity="center"
android:textColor="@color/white"
android:layout_alignParentTop="true"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:id="@+id/video_overlay"
android:textSize="@dimen/grid_item_chooser_text_size"
android:gravity="center"
android:textStyle="bold"
android:padding="2dp"/>
</RelativeLayout>
</FrameLayout>

View file

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/affdexme_menu"
android:background="@color/transparent_overlay"
android:visibility="invisible"
>
<View
android:id="@+id/menu_border"
android:layout_width="fill_parent"
android:layout_marginLeft="30dp"
android:layout_marginRight="30dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_height="1dp"
android:background="@color/letter_gray"/>
<TableLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/menu_border"
android:id="@+id/menu_table_layout"
android:stretchColumns="0,1">
<TableRow>
<CheckBox
style="@style/optionsStyle"
android:layout_gravity="center"
android:text="@string/show_fps"
android:id="@+id/fps_checkbox"
android:onClick="fps_checkbox_click"
/>
<CheckBox
style="@style/optionsStyle"
android:layout_gravity="center"
android:text="@string/show_tracking"
android:id="@+id/tracking_checkbox"
android:onClick="tracking_checkbox_click"
/>
</TableRow>
</TableLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_below="@id/menu_table_layout">
<TextView
style="@style/optionsStyle"
android:text="@string/processed_frames_per_second"
android:id="@+id/fps_edittext_name"
/>
<EditText
style="@style/optionsStyle"
android:inputType="number"
android:ems="3"
android:maxLength="2"
android:id="@+id/fps_edittext"
android:imeOptions="actionDone" />
</LinearLayout>
</RelativeLayout>

View file

@ -1,47 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent"
android:orientation="vertical"
android:id="@+id/metric_chooser_root_layout"
android:soundEffectsEnabled="true">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="2dp"
android:paddingBottom="2dp"
android:textSize="@dimen/metric_chooser_text_size"
android:text="@string/choose_six_message"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="5dp"
>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/clear_all"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:id="@+id/clear_all_button"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toLeftOf="@id/clear_all_button"
android:textSize="@dimen/metric_chooser_text_size"
android:id="@+id/metrics_chooser_textview"
android:text="@string/metric_chooser_default_message"
android:textColor="@color/white"
android:layout_centerVertical="true"/>
</RelativeLayout>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<GridLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/metric_chooser_gridlayout"
android:orientation="horizontal"
android:columnOrderPreserved="true"
/>
</ScrollView>
</LinearLayout>

View file

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout <RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:affdex="http://schemas.android.com/apk/res-auto"
android:orientation="horizontal" android:orientation="horizontal"
android:layout_height="@dimen/metric_viewgroup" android:layout_height="@dimen/metric_viewgroup"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -12,12 +13,11 @@
<!-- Logo--> <!-- Logo-->
<ImageView <ImageView
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_width="@dimen/logo_width" android:layout_width="@dimen/image_width"
android:layout_centerInParent="true" android:layout_centerInParent="true"
android:src="@drawable/affectiva_logo_clear_background" android:src="@drawable/affectiva_logo_clear_background"
android:contentDescription="@string/affectiva_logo_content_description"
android:id="@+id/affectiva_logo" /> android:id="@+id/affectiva_logo" />
<!-- Left MetricsManager--> <!-- Left Metrics-->
<LinearLayout <LinearLayout
android:orientation="vertical" android:orientation="vertical"
android:paddingLeft="@dimen/metric_panel_padding" android:paddingLeft="@dimen/metric_panel_padding"
@ -27,23 +27,26 @@
android:id="@+id/left_metrics"> android:id="@+id/left_metrics">
<TextView <TextView
style="@style/metricName" style="@style/metricName"
android:id="@+id/metric_name_0" android:text="@string/smile"
android:id="@+id/smile_name"
/> />
<com.affectiva.affdexme.MetricDisplay <com.affectiva.affdexme.MetricView
style="@style/metricPct" style="@style/metricPct"
android:id="@+id/metric_pct_0" /> android:id="@+id/smile_pct" />
<TextView <TextView
style="@style/metricName" style="@style/metricName"
android:id="@+id/metric_name_1" /> android:text="@string/brow_raise"
<com.affectiva.affdexme.MetricDisplay android:id="@+id/brow_raise_name" />
<com.affectiva.affdexme.MetricView
style="@style/metricPct" style="@style/metricPct"
android:id="@+id/metric_pct_1" /> android:id="@+id/brow_raise_pct" />
<TextView <TextView
style="@style/metricName" style="@style/metricName"
android:id="@+id/metric_name_2" /> android:text="@string/brow_furrow"
<com.affectiva.affdexme.MetricDisplay android:id="@+id/brow_furrow_name" />
<com.affectiva.affdexme.MetricView
style="@style/metricPct" style="@style/metricPct"
android:id="@+id/metric_pct_2" /> android:id="@+id/brow_furrow_pct" />
</LinearLayout> </LinearLayout>
<!-- FPS Counter--> <!-- FPS Counter-->
<LinearLayout <LinearLayout
@ -67,11 +70,10 @@
android:gravity="left|bottom" android:gravity="left|bottom"
android:id="@+id/fps_value" android:id="@+id/fps_value"
android:textSize="@dimen/pct_text_size" android:textSize="@dimen/pct_text_size"
android:textColor="@color/letter_gray"
android:layout_weight="1" android:layout_weight="1"
/> />
</LinearLayout> </LinearLayout>
<!-- Right MetricsManager--> <!-- Right Metrics-->
<LinearLayout <LinearLayout
android:orientation="vertical" android:orientation="vertical"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -81,21 +83,24 @@
android:id="@+id/right_metrics"> android:id="@+id/right_metrics">
<TextView <TextView
style="@style/metricName" style="@style/metricName"
android:id="@+id/metric_name_3" /> android:text="@string/frown"
<com.affectiva.affdexme.MetricDisplay android:id="@+id/frown_name" />
<com.affectiva.affdexme.MetricView
style="@style/metricPct" style="@style/metricPct"
android:id="@+id/metric_pct_3" /> android:id="@+id/frown_pct" />
<TextView <TextView
style="@style/metricName" style="@style/metricName"
android:id="@+id/metric_name_4" /> android:text="@string/valence"
<com.affectiva.affdexme.MetricDisplay android:id="@+id/valence_name" />
<com.affectiva.affdexme.GradientMetricView
style="@style/metricPct" style="@style/metricPct"
android:id="@+id/metric_pct_4" /> android:id="@+id/valence_pct" />
<TextView <TextView
style="@style/metricName" style="@style/metricName"
android:id="@+id/metric_name_5" /> android:text="@string/engagement"
<com.affectiva.affdexme.MetricDisplay android:id="@+id/engagement_name" />
<com.affectiva.affdexme.MetricView
style="@style/metricPct" style="@style/metricPct"
android:id="@+id/metric_pct_5" /> android:id="@+id/engagement_pct" />
</LinearLayout> </LinearLayout>
</RelativeLayout> </RelativeLayout>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View file

@ -5,35 +5,6 @@
<dimen name="metric_view_bar_length">130dp</dimen> <dimen name="metric_view_bar_length">130dp</dimen>
<dimen name="bottom_padding">10dp</dimen> <dimen name="bottom_padding">10dp</dimen>
<dimen name="metric_viewgroup">190dp</dimen> <dimen name="metric_viewgroup">190dp</dimen>
<dimen name="logo_width">180dp</dimen> <dimen name="image_width">180dp</dimen>
<dimen name="metric_panel_padding">15dp</dimen> <dimen name="metric_panel_padding">15dp</dimen>
<dimen name="please_wait_textview_size">28sp</dimen>
<dimen name="settings_button_size">64dp</dimen>
<dimen name="settings_button_margin">12dp</dimen>
<dimen name="measurements_text_size">21sp</dimen>
<dimen name="measurements_upper_text_spacing">70dp</dimen>
<dimen name="measurements_lower_text_spacing">100dp</dimen>
<integer name="measurements_text_border_thickness">5</integer>
<dimen name="metric_chooser_text_size">24dp</dimen>
<dimen name="header_text_size">26dp</dimen>
<dimen name="grid_item_checkbox_size">40dp</dimen>
<dimen name="grid_item_chooser_text_size">19sp</dimen>
<dimen name="grid_item_left_padding">5dp</dimen>
<dimen name="grid_item_bottom_padding">10dp</dimen>
<dimen name="grid_item_metric_name">18sp</dimen>
<dimen name="metric_chooser_column_width">180dp</dimen>
</resources> </resources>

View file

@ -5,30 +5,6 @@
<dimen name="metric_view_bar_length">80dp</dimen> <dimen name="metric_view_bar_length">80dp</dimen>
<dimen name="bottom_padding">5dp</dimen> <dimen name="bottom_padding">5dp</dimen>
<dimen name="metric_viewgroup">130dp</dimen> <dimen name="metric_viewgroup">130dp</dimen>
<dimen name="logo_width">120dp</dimen> <dimen name="image_width">120dp</dimen>
<dimen name="metric_panel_padding">10dp</dimen> <dimen name="metric_panel_padding">10dp</dimen>
<dimen name="please_wait_textview_size">20sp</dimen>
<dimen name="settings_button_size">48dp</dimen>
<dimen name="settings_button_margin">10dp</dimen>
<dimen name="measurements_text_size">17sp</dimen>
<dimen name="measurements_upper_text_spacing">60dp</dimen>
<dimen name="measurements_lower_text_spacing">80dp</dimen>
<integer name="measurements_text_border_thickness">3</integer>
<dimen name="grid_item_chooser_text_size">15sp</dimen>
<dimen name="grid_item_checkbox_size">30dp</dimen>
<dimen name="header_text_size">21dp</dimen>
<dimen name="metric_chooser_text_size">18dp</dimen>
<dimen name="grid_item_left_padding">4dp</dimen>
<dimen name="grid_item_bottom_padding">8dp</dimen>
<dimen name="grid_item_metric_name">15sp</dimen>
<dimen name="metric_chooser_column_width">140dp</dimen>
</resources> </resources>

View file

@ -5,29 +5,6 @@
<dimen name="metric_view_bar_length">70dp</dimen> <dimen name="metric_view_bar_length">70dp</dimen>
<dimen name="bottom_padding">5dp</dimen> <dimen name="bottom_padding">5dp</dimen>
<dimen name="metric_viewgroup">130dp</dimen> <dimen name="metric_viewgroup">130dp</dimen>
<dimen name="logo_width">100dp</dimen> <dimen name="image_width">100dp</dimen>
<dimen name="metric_panel_padding">8dp</dimen> <dimen name="metric_panel_padding">8dp</dimen>
<dimen name="please_wait_textview_size">17sp</dimen>
<dimen name="settings_button_size">36dp</dimen>
<dimen name="settings_button_margin">7dp</dimen>
<dimen name="grid_item_chooser_text_size">12sp</dimen>
<dimen name="grid_item_checkbox_size">50dp</dimen>
<dimen name="measurements_text_size">13sp</dimen>
<dimen name="measurements_upper_text_spacing">50dp</dimen>
<dimen name="measurements_lower_text_spacing">70dp</dimen>
<integer name="measurements_text_border_thickness">3</integer>
<dimen name="header_text_size">17dp</dimen>
<dimen name="metric_chooser_text_size">13dp</dimen>
<dimen name="grid_item_left_padding">3dp</dimen>
<dimen name="grid_item_bottom_padding">6dp</dimen>
<dimen name="grid_item_metric_name">11sp</dimen>
<dimen name="metric_chooser_column_width">120dp</dimen>
</resources> </resources>

View file

@ -6,32 +6,6 @@
<dimen name="metric_view_bar_length">140dp</dimen> <dimen name="metric_view_bar_length">140dp</dimen>
<dimen name="bottom_padding">15dp</dimen> <dimen name="bottom_padding">15dp</dimen>
<dimen name="metric_viewgroup">220dp</dimen> <dimen name="metric_viewgroup">220dp</dimen>
<dimen name="logo_width">250dp</dimen> <dimen name="image_width">250dp</dimen>
<dimen name="metric_panel_padding">25dp</dimen> <dimen name="metric_panel_padding">25dp</dimen>
<dimen name="please_wait_textview_size">35sp</dimen>
<dimen name="settings_size">72dp</dimen>
<dimen name="settings_button_margin">15dp</dimen>
<dimen name="measurements_text_size">30sp</dimen>
<dimen name="measurements_upper_text_spacing">110dp</dimen>
<dimen name="measurements_lower_text_spacing">130dp</dimen>
<integer name="measurements_text_border_thickness">7</integer>
<dimen name="metric_chooser_text_size">26dp</dimen>
<dimen name="header_text_size">30dp</dimen>
<dimen name="grid_item_checkbox_size">60dp</dimen>
<dimen name="grid_item_chooser_text_size">24sp</dimen>
<dimen name="grid_item_left_padding">6dp</dimen>
<dimen name="grid_item_bottom_padding">12dp</dimen>
<dimen name="grid_item_metric_name">22sp</dimen>
<dimen name="metric_chooser_column_width">200dp</dimen>
</resources> </resources>

View file

@ -6,12 +6,4 @@
<attr name="barLength" format="dimension"/> <attr name="barLength" format="dimension"/>
<attr name="textDepth" format="dimension"/> <attr name="textDepth" format="dimension"/>
</declare-styleable> </declare-styleable>
<declare-styleable name="drawing_view_attributes">
<attr name="measurements_text_size" format="dimension" />
<attr name="measurements_upper_spacing" format="dimension"/>
<attr name="measurements_lower_spacing" format="dimension"/>
<attr name="measurements_color" format="color"/>
<attr name="measurements_text_border_color" format="color"/>
<attr name="measurements_text_border_thickness" format="float"/>
</declare-styleable>
</resources> </resources>

View file

@ -2,12 +2,6 @@
<resources> <resources>
<color name="transparent_overlay">#55ffffff</color> <color name="transparent_overlay">#55ffffff</color>
<color name="letter_gray">#514a40</color> <color name="letter_gray">#514a40</color>
<color name="affectiva_orange">#ff8000</color> <color name="letter_orange">#ff8000</color>
<color name="black">#000000</color> <color name="black">#000000</color>
<color name="white">#ffffff</color>
<color name="red">#cf1a0b</color>
<color name="grid_item_not_chosen">#646464</color>
<color name="grid_item_chosen">#009600</color>
<color name="grid_item_chosen_over_limit">#cf1a0b</color>
</resources> </resources>

View file

@ -5,32 +5,6 @@
<dimen name="metric_view_bar_length">80dp</dimen> <dimen name="metric_view_bar_length">80dp</dimen>
<dimen name="bottom_padding">5dp</dimen> <dimen name="bottom_padding">5dp</dimen>
<dimen name="metric_viewgroup">140dp</dimen> <dimen name="metric_viewgroup">140dp</dimen>
<dimen name="logo_width">120dp</dimen> <dimen name="image_width">120dp</dimen>
<dimen name="metric_panel_padding">10dp</dimen> <dimen name="metric_panel_padding">10dp</dimen>
<dimen name="please_wait_textview_size">20sp</dimen>
<!--TODO: create dimensions for other screen sizes -->
<dimen name="settings_button_size">48dp</dimen>
<dimen name="settings_button_margin">10dp</dimen>
<dimen name="measurements_text_size">17sp</dimen>
<dimen name="measurements_upper_text_spacing">60dp</dimen>
<dimen name="measurements_lower_text_spacing">80dp</dimen>
<integer name="measurements_text_border_thickness">3</integer>
<dimen name="grid_item_chooser_text_size">15sp</dimen>
<dimen name="grid_item_checkbox_size">30dp</dimen>
<dimen name="header_text_size">21dp</dimen>
<dimen name="metric_chooser_text_size">18dp</dimen>
<dimen name="grid_item_left_padding">4dp</dimen>
<dimen name="grid_item_bottom_padding">8dp</dimen>
<dimen name="grid_item_metric_name">15sp</dimen>
<dimen name="metric_chooser_column_width">140dp</dimen>
</resources> </resources>

View file

@ -5,7 +5,7 @@
<item name="android:layout_height">wrap_content</item> <item name="android:layout_height">wrap_content</item>
<item name="android:gravity">center</item> <item name="android:gravity">center</item>
<item name="android:textStyle">bold</item> <item name="android:textStyle">bold</item>
<item name="android:textColor">@color/affectiva_orange</item> <item name="android:textColor">@color/letter_orange</item>
<item name="android:shadowColor">#000000</item> <item name="android:shadowColor">#000000</item>
<item name="android:shadowDy">2</item> <item name="android:shadowDy">2</item>
<item name="android:shadowRadius">1</item> <item name="android:shadowRadius">1</item>

View file

@ -1,36 +1,18 @@
<resources> <resources>
<string name="app_name">AffdexMe</string> <string name="app_name">AffdexMe</string>
<!--MainActivity strings--> <string name="smile">SMILE</string>
<string name="not_found">Sorry, AffdexMe requires the use of a front-facing camera, which was not found on your device.</string> <string name="brow_raise">BROW RAISE</string>
<string name="affectiva_logo_content_description">Affectiva Logo</string> <string name="brow_furrow">BROW FURROW</string>
<string name="valence">VALENCE</string>
<string name="engagement">ENGAGEMENT</string>
<string name="frown">FROWN</string>
<string name="fps">FPS:</string> <string name="fps">FPS:</string>
<string name="loading">Loading&#8230;</string>
<string name="settings_content_description">Settings</string>
<!--MetricSelectionFragment strings--> <string name="show_fps">Show FPS</string>
<string name="clear_all">Clear All</string> <string name="show_tracking">Show Tracking</string>
<string name="metric_chooser_default_message">0 metrics chosen.</string> <string name="processed_frames_per_second">Processed Frames Per Second: </string>
<string name="choose_six_message">Choose 6 emotions and expressions below to display on the main screen.</string>
<!--SettingsActivity strings--> <string name="not_found">Sorry, AffdexMe requires the use of a front-facing camera, which was not found on your device.</string>
<string name="select_metrics_title">Select Metrics</string>
<string name="select_metrics_message">Select emotions and expressions to display.</string>
<string name="settings_title">Settings</string>
<string name="settings_message">Change application settings.</string>
<!--SettingsFragment strings-->
<string name="set_fps_title">Set Target FPS</string>
<string name="set_fps_message">Set the desired processed frames per second.</string>
<string name="fps_edittext_title">Processed Frames Per Second</string>
<string name="show_tracking_title">Show Tracking Dots</string>
<string name="show_tracking_message">Display tracking dots and bounding box.</string>
<string name="show_measurements_title">Show Measurements</string>
<string name="show_measurements_message">Display roll, yaw, pitch, and interocular distance.</string>
<string name="show_fps_title">Show FPS</string>
<string name="show_fps_message">Display the actual processed frames per second.</string>
</resources> </resources>

Some files were not shown because too many files have changed in this diff Show more