2015-06-23 22:44:24 +02:00
package com.affectiva.affdexme ;
import android.app.Activity ;
2015-07-20 21:53:09 +02:00
import android.content.Intent ;
2015-06-23 22:44:24 +02:00
import android.content.SharedPreferences ;
import android.content.pm.PackageManager ;
import android.graphics.Typeface ;
import android.os.Bundle ;
import android.os.SystemClock ;
2015-07-20 21:53:09 +02:00
import android.preference.PreferenceManager ;
2015-06-23 22:44:24 +02:00
import android.util.Log ;
2015-10-02 03:12:00 +02:00
import android.util.DisplayMetrics ;
2015-06-23 22:44:24 +02:00
import android.view.KeyEvent ;
import android.view.MotionEvent ;
import android.view.SurfaceView ;
import android.view.View ;
import android.view.ViewGroup ;
import android.view.WindowManager ;
2015-07-20 21:53:09 +02:00
import android.widget.ImageButton ;
2015-06-23 22:44:24 +02:00
import android.widget.LinearLayout ;
2015-08-20 17:24:17 +02:00
import android.widget.ProgressBar ;
2015-06-23 22:44:24 +02:00
import android.widget.RelativeLayout ;
import android.widget.TextView ;
2015-08-20 17:24:17 +02:00
import android.widget.Toast ;
2015-06-23 22:44:24 +02:00
import java.io.BufferedReader ;
import java.io.ByteArrayInputStream ;
import java.io.InputStreamReader ;
2015-08-20 17:24:17 +02:00
import java.lang.reflect.Method ;
2015-06-23 22:44:24 +02:00
import java.util.List ;
import com.affectiva.android.affdex.sdk.Frame ;
import com.affectiva.android.affdex.sdk.Frame.ROTATE ;
import com.affectiva.android.affdex.sdk.detector.CameraDetector ;
import com.affectiva.android.affdex.sdk.detector.Detector ;
import com.affectiva.android.affdex.sdk.detector.Face ;
/ *
* AffdexMe is an app that demonstrates the use of the Affectiva Android SDK . It uses the
* front - facing camera on your Android device to view , process and analyze live video of your face .
* Start the app and you will see your own face on the screen and metrics describing your
* expressions .
*
* Tapping the screen will bring up a menu with options to display the Processed Frames Per Second metric ,
* display facial tracking points , and control the rate at which frames are processed by the SDK .
*
* Most of the methods in this file control the application ' s UI . Therefore , if you are just interested in learning how the Affectiva SDK works ,
2015-07-13 20:17:57 +02:00
* you will find the calls relevant to the use of the SDK in the initializeCameraDetector ( ) , startCamera ( ) , stopCamera ( ) ,
* and onImageResults ( ) methods .
2015-06-23 22:44:24 +02:00
*
* This class implements the Detector . ImageListener interface , allowing it to receive the onImageResults ( ) event .
* This class implements the Detector . FaceListener interface , allowing it to receive the onFaceDetectionStarted ( ) and
* onFaceDetectionStopped ( ) events .
2015-07-13 20:17:57 +02:00
* This class implements the CameraDetector . CameraSurfaceViewListener interface , allowing it to receive
* onSurfaceViewAspectRatioChanged ( ) events .
2015-06-23 22:44:24 +02:00
*
* In order to use this project , you will need to :
* - Obtain the SDK from Affectiva ( visit http : //www.affdex.com/mobile-sdk)
* - Copy the SDK assets folder contents into this project ' s assets folder
2015-09-24 02:08:24 +02:00
* - Copy the contents of the SDK ' s libs folder into this project ' s libs folder under AffdexMe / app / lib
* - Copy the armeabi - v7a folder ( found in the SDK libs folder ) into this project ' s jniLibs folder under AffdexMe / app / src / main / jniLibs
* - Add your license file to the / assets / Affdex folder and rename to license . txt .
* ( Note : if you name the license file something else you will need to update the licensePath in the initializeCameraDetector ( ) method in MainActivity )
2015-06-23 22:44:24 +02:00
* - Build the project
* - Run the app on an Android device with a front - facing camera
*
* Copyright ( c ) 2014 Affectiva . All rights reserved .
* /
public class MainActivity extends Activity
2015-08-28 21:10:43 +02:00
implements Detector . FaceListener , Detector . ImageListener , View . OnTouchListener , CameraDetector . CameraEventListener {
2015-06-23 22:44:24 +02:00
2015-07-13 20:17:57 +02:00
private static final String LOG_TAG = " Affectiva " ;
2015-08-20 17:24:17 +02:00
public static final int NUM_METRICS_DISPLAYED = 6 ;
2015-06-23 22:44:24 +02:00
//Affectiva SDK Object
private CameraDetector detector = null ;
2015-08-20 17:24:17 +02:00
//MetricsManager View UI Objects
2015-06-23 22:44:24 +02:00
private RelativeLayout metricViewLayout ;
private LinearLayout leftMetricsLayout ;
private LinearLayout rightMetricsLayout ;
2015-08-20 17:24:17 +02:00
private MetricDisplay [ ] metricDisplays ;
private TextView [ ] metricNames ;
2015-06-23 22:44:24 +02:00
private TextView fpsName ;
private TextView fpsPct ;
2015-08-20 17:24:17 +02:00
private TextView pleaseWaitTextView ;
private ProgressBar progressBar ;
2015-06-23 22:44:24 +02:00
//Other UI objects
private ViewGroup activityLayout ; //top-most ViewGroup in which all content resides
private RelativeLayout mainLayout ; //layout, to be resized, containing all UI elements
private RelativeLayout progressBarLayout ; //layout used to show progress circle while camera is starting
private SurfaceView cameraView ; //SurfaceView used to display camera images
private DrawingView drawingView ; //SurfaceView containing its own thread, used to draw facial tracking dots
2015-07-20 21:53:09 +02:00
private ImageButton settingsButton ;
2015-08-27 18:07:37 +02:00
private ImageButton cameraButton ;
2015-06-23 22:44:24 +02:00
//Application settings variables
2015-08-20 17:24:17 +02:00
private int detectorProcessRate ;
2015-06-23 22:44:24 +02:00
private boolean isMenuVisible = false ;
private boolean isFPSVisible = false ;
2015-08-20 17:24:17 +02:00
private boolean isMenuShowingForFirstTime = true ;
2015-06-23 22:44:24 +02:00
//Frames Per Second (FPS) variables
private long firstSystemTime = 0 ;
private float numberOfFrames = 0 ;
private long timeToUpdate = 0 ;
2015-08-27 18:07:37 +02:00
//Camera-related variables
2015-06-23 22:44:24 +02:00
private boolean isFrontFacingCameraDetected = true ;
2015-08-27 18:07:37 +02:00
private boolean isBackFacingCameraDetected = true ;
int cameraPreviewWidth = 0 ;
int cameraPreviewHeight = 0 ;
CameraDetector . CameraType cameraType ;
2015-08-28 21:10:43 +02:00
boolean mirrorPoints = false ;
2015-06-23 22:44:24 +02:00
@Override
protected void onCreate ( Bundle savedInstanceState ) {
super . onCreate ( savedInstanceState ) ;
getWindow ( ) . addFlags ( WindowManager . LayoutParams . FLAG_FULLSCREEN ) ; //To maximize UI space, we declare our app to be full-screen
setContentView ( R . layout . activity_main ) ;
2015-08-20 17:24:17 +02:00
initializeUI ( ) ;
2015-08-27 18:07:37 +02:00
determineCameraAvailability ( ) ;
initializeCameraDetector ( ) ;
}
/ * *
* We check to make sure the device has a front - facing camera .
* If it does not , we obscure the app with a notice informing the user they cannot
* use the app .
* /
void determineCameraAvailability ( ) {
PackageManager manager = getPackageManager ( ) ;
isFrontFacingCameraDetected = manager . hasSystemFeature ( PackageManager . FEATURE_CAMERA_FRONT ) ;
isBackFacingCameraDetected = manager . hasSystemFeature ( PackageManager . FEATURE_CAMERA ) ;
if ( ! isFrontFacingCameraDetected & & ! isBackFacingCameraDetected ) {
2015-08-20 17:24:17 +02:00
progressBar . setVisibility ( View . INVISIBLE ) ;
pleaseWaitTextView . setVisibility ( View . INVISIBLE ) ;
2015-06-23 22:44:24 +02:00
TextView notFoundTextView = ( TextView ) findViewById ( R . id . not_found_textview ) ;
notFoundTextView . setVisibility ( View . VISIBLE ) ;
}
2015-08-27 18:07:37 +02:00
//TODO: change this to be taken from settings
2015-08-28 21:10:43 +02:00
if ( isBackFacingCameraDetected ) {
2015-08-27 18:07:37 +02:00
cameraType = CameraDetector . CameraType . CAMERA_BACK ;
2015-08-28 21:10:43 +02:00
mirrorPoints = false ;
}
if ( isFrontFacingCameraDetected ) {
2015-08-27 18:07:37 +02:00
cameraType = CameraDetector . CameraType . CAMERA_FRONT ;
2015-08-28 21:10:43 +02:00
mirrorPoints = true ;
}
2015-06-23 22:44:24 +02:00
}
void initializeUI ( ) {
//Get handles to UI objects
activityLayout = ( ViewGroup ) findViewById ( android . R . id . content ) ;
progressBarLayout = ( RelativeLayout ) findViewById ( R . id . progress_bar_cover ) ;
metricViewLayout = ( RelativeLayout ) findViewById ( R . id . metric_view_group ) ;
leftMetricsLayout = ( LinearLayout ) findViewById ( R . id . left_metrics ) ;
rightMetricsLayout = ( LinearLayout ) findViewById ( R . id . right_metrics ) ;
mainLayout = ( RelativeLayout ) findViewById ( R . id . main_layout ) ;
fpsPct = ( TextView ) findViewById ( R . id . fps_value ) ;
fpsName = ( TextView ) findViewById ( R . id . fps_name ) ;
cameraView = ( SurfaceView ) findViewById ( R . id . camera_preview ) ;
drawingView = ( DrawingView ) findViewById ( R . id . drawing_view ) ;
2015-07-20 21:53:09 +02:00
settingsButton = ( ImageButton ) findViewById ( R . id . settings_button ) ;
2015-08-27 18:07:37 +02:00
cameraButton = ( ImageButton ) findViewById ( R . id . camera_button ) ;
2015-08-20 17:24:17 +02:00
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 ) ;
2015-06-23 22:44:24 +02:00
//Load Application Font and set UI Elements to use it
Typeface face = Typeface . createFromAsset ( getAssets ( ) , " fonts/Square.ttf " ) ;
2015-08-20 17:24:17 +02:00
for ( TextView textView : metricNames ) {
textView . setTypeface ( face ) ;
}
for ( MetricDisplay metricDisplay : metricDisplays ) {
metricDisplay . setTypeface ( face ) ;
}
2015-06-23 22:44:24 +02:00
fpsPct . setTypeface ( face ) ;
fpsName . setTypeface ( face ) ;
2015-07-20 21:53:09 +02:00
drawingView . setTypeface ( face ) ;
2015-08-20 17:24:17 +02:00
pleaseWaitTextView . setTypeface ( face ) ;
2015-06-23 22:44:24 +02:00
//Hide left and right metrics by default (will be made visible when face detection starts)
leftMetricsLayout . setAlpha ( 0 ) ;
rightMetricsLayout . setAlpha ( 0 ) ;
/ * *
* This app uses two SurfaceView objects : one to display the camera image and the other to draw facial tracking dots .
* Since we want the tracking dots to appear over the camera image , we use SurfaceView . setZOrderMediaOverlay ( ) to indicate that
* cameraView represents our ' media ' , and drawingView represents our ' overlay ' , so that Android will render them in the
* correct order .
* /
drawingView . setZOrderMediaOverlay ( true ) ;
cameraView . setZOrderMediaOverlay ( false ) ;
//Attach event listeners to the menu and edit box
activityLayout . setOnTouchListener ( this ) ;
2015-07-13 20:17:57 +02:00
/ *
2015-06-23 22:44:24 +02:00
* This app sets the View . SYSTEM_UI_FLAG_HIDE_NAVIGATION flag . Unfortunately , this flag causes
* Android to steal the first touch event after the navigation bar has been hidden , a touch event
* which should be used to make our menu visible again . Therefore , we attach a listener to be notified
* when the navigation bar has been made visible again , which corresponds to the touch event that Android
* steals . If the menu bar was not visible , we make it visible .
* /
activityLayout . setOnSystemUiVisibilityChangeListener ( new View . OnSystemUiVisibilityChangeListener ( ) {
@Override
public void onSystemUiVisibilityChange ( int uiCode ) {
if ( ( uiCode = = 0 ) & & ( isMenuVisible = = false ) ) {
setMenuVisible ( true ) ;
}
}
} ) ;
}
2015-07-13 20:17:57 +02:00
void initializeCameraDetector ( ) {
/ * Put the SDK in camera mode by using this constructor . The SDK will be in control of
* the camera . If a SurfaceView is passed in as the last argument to the constructor ,
* that view will be painted with what the camera sees .
* /
2015-10-02 03:20:00 +02:00
2015-08-27 19:14:17 +02:00
detector = new CameraDetector ( this , CameraDetector . CameraType . CAMERA_FRONT , cameraView ) ;
2015-09-24 02:08:24 +02:00
// update the license path here if you name your file something else
2015-12-18 21:51:00 +01:00
// detector.setLicensePath("license.txt");
detector . setLicensePath ( " Affectiva.license " ) ;
2015-07-13 20:17:57 +02:00
detector . setImageListener ( this ) ;
detector . setFaceListener ( this ) ;
2015-08-27 18:07:37 +02:00
detector . setOnCameraEventListener ( this ) ;
2015-07-13 20:17:57 +02:00
}
/ *
* We use onResume ( ) to restore application settings using the SharedPreferences object
2015-06-23 22:44:24 +02:00
* /
@Override
public void onResume ( ) {
super . onResume ( ) ;
restoreApplicationSettings ( ) ;
2015-08-20 17:24:17 +02:00
setMenuVisible ( true ) ;
isMenuShowingForFirstTime = true ;
2015-06-23 22:44:24 +02:00
}
2015-07-13 20:17:57 +02:00
/ *
2015-06-23 22:44:24 +02:00
* We use the Shared Preferences object to restore application settings .
2015-07-13 20:17:57 +02:00
* /
2015-06-23 22:44:24 +02:00
public void restoreApplicationSettings ( ) {
2015-08-20 17:24:17 +02:00
SharedPreferences sharedPreferences = PreferenceManager . getDefaultSharedPreferences ( this ) ;
2015-07-20 21:53:09 +02:00
2015-08-20 17:24:17 +02:00
//restore camera processing rate
detectorProcessRate = PreferencesUtils . getFrameProcessingRate ( sharedPreferences ) ;
2015-07-20 21:53:09 +02:00
detector . setMaxProcessRate ( detectorProcessRate ) ;
2015-06-23 22:44:24 +02:00
if ( sharedPreferences . getBoolean ( " fps " , isFPSVisible ) ) { //restore isFPSMetricVisible
setFPSVisible ( true ) ;
} else {
setFPSVisible ( false ) ;
}
if ( sharedPreferences . getBoolean ( " track " , drawingView . getDrawPointsEnabled ( ) ) ) { //restore isTrackingDotsVisible
setTrackPoints ( true ) ;
} else {
setTrackPoints ( false ) ;
2015-07-20 21:53:09 +02:00
}
if ( sharedPreferences . getBoolean ( " measurements " , drawingView . getDrawMeasurementsEnabled ( ) ) ) { //restore show measurements
setShowMeasurements ( true ) ;
} else {
setShowMeasurements ( false ) ;
}
2015-08-20 17:24:17 +02:00
//populate metric displays
for ( int n = 0 ; n < NUM_METRICS_DISPLAYED ; n + + ) {
activateMetric ( n , PreferencesUtils . getMetricFromPrefs ( sharedPreferences , n ) ) ;
2015-07-20 21:53:09 +02:00
}
}
2015-08-20 17:24:17 +02:00
/ * *
* 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 ) ) ;
2015-07-20 21:53:09 +02:00
2015-08-20 17:24:17 +02:00
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 ) ;
2015-07-20 21:53:09 +02:00
2015-08-20 17:24:17 +02:00
if ( metric . getType ( ) = = MetricsManager . MetricType . Emotion ) {
getFaceScoreMethod = Face . Emotions . class . getMethod ( " get " + MetricsManager . getCamelCase ( metric ) , null ) ;
2015-07-20 21:53:09 +02:00
2015-08-20 17:24:17 +02:00
//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 ( ) ) ) ;
2015-06-23 22:44:24 +02:00
}
2015-08-20 17:24:17 +02:00
metricDisplays [ index ] . setMetricToDisplay ( metric , getFaceScoreMethod ) ;
2015-06-23 22:44:24 +02:00
}
/ * *
* Reset the variables used to calculate processed frames per second .
* * * /
public void resetFPSCalculations ( ) {
firstSystemTime = SystemClock . elapsedRealtime ( ) ;
timeToUpdate = firstSystemTime + 1000L ;
numberOfFrames = 0 ;
}
/ * *
2015-09-10 19:30:10 +02:00
* We want to start the camera as late as possible , so it does not freeze the application before it has been visually resumed .
* We thus post a runnable that will take care of starting the camera .
2015-06-23 22:44:24 +02:00
* We also reset variables used to calculate the Processed Frames Per Second .
* /
@Override
public void onWindowFocusChanged ( boolean hasFocus ) {
if ( hasFocus & & isFrontFacingCameraDetected ) {
2015-09-10 19:30:10 +02:00
cameraView . post ( new Runnable ( ) {
@Override
public void run ( ) {
mainWindowResumedTasks ( ) ;
}
} ) ;
}
}
2015-07-13 20:17:57 +02:00
2015-08-20 17:24:17 +02:00
void mainWindowResumedTasks ( ) {
2015-08-27 18:07:37 +02:00
startDetector ( ) ;
if ( ! drawingView . isDimensionsNeeded ( ) ) {
2015-08-20 17:24:17 +02:00
progressBarLayout . setVisibility ( View . GONE ) ;
2015-06-23 22:44:24 +02:00
}
2015-08-20 17:24:17 +02:00
resetFPSCalculations ( ) ;
cameraView . postDelayed ( new Runnable ( ) {
@Override
public void run ( ) {
if ( isMenuShowingForFirstTime ) {
setMenuVisible ( false ) ;
}
}
2015-08-27 18:07:37 +02:00
} , 5000 ) ;
2015-06-23 22:44:24 +02:00
}
2015-08-27 18:07:37 +02:00
void startDetector ( ) {
if ( ! isBackFacingCameraDetected & & ! isFrontFacingCameraDetected )
return ; //without any cameras detected, we cannot proceed
2015-08-20 17:24:17 +02:00
2015-08-27 18:07:37 +02:00
detector . setDetectValence ( true ) ; //this app will always detect valence
2015-07-13 20:17:57 +02:00
if ( ! detector . isRunning ( ) ) {
2015-06-23 22:44:24 +02:00
try {
detector . start ( ) ;
} catch ( Exception e ) {
Log . e ( LOG_TAG , e . getMessage ( ) ) ;
}
}
2015-07-13 20:17:57 +02:00
}
2015-06-23 22:44:24 +02:00
2015-08-27 18:07:37 +02:00
2015-06-23 22:44:24 +02:00
@Override
public void onFaceDetectionStarted ( ) {
leftMetricsLayout . animate ( ) . alpha ( 1 ) ; //make left and right metrics appear
rightMetricsLayout . animate ( ) . alpha ( 1 ) ;
2015-07-20 21:53:09 +02:00
2015-06-23 22:44:24 +02:00
resetFPSCalculations ( ) ; //Since the FPS may be different whether a face is being tracked or not, reset variables.
}
@Override
public void onFaceDetectionStopped ( ) {
performFaceDetectionStoppedTasks ( ) ;
}
void performFaceDetectionStoppedTasks ( ) {
leftMetricsLayout . animate ( ) . alpha ( 0 ) ; //make left and right metrics disappear
rightMetricsLayout . animate ( ) . alpha ( 0 ) ;
resetFPSCalculations ( ) ; //Since the FPS may be different whether a face is being tracked or not, reset variables.
}
/ * *
* This event is received every time the SDK processes a frame .
* /
@Override
public void onImageResults ( List < Face > faces , Frame image , float timeStamp ) {
//If the faces object is null, we received an unprocessed frame
if ( faces = = null ) {
return ;
}
//At this point, we know the frame received was processed, so we perform our processed frames per second calculations
performFPSCalculations ( ) ;
//If faces.size() is 0, we received a frame in which no face was detected
if ( faces . size ( ) = = 0 ) {
2015-08-28 21:10:43 +02:00
drawingView . updatePoints ( null , mirrorPoints ) ; //the drawingView takes null points to mean it doesn't have to draw anything
2015-06-23 22:44:24 +02:00
return ;
}
//The SDK currently detects one face at a time, so we recover it using .get(0).
//'0' indicates we are recovering the first face.
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.
2015-08-20 17:24:17 +02:00
for ( MetricDisplay metricDisplay : metricDisplays ) {
updateMetricScore ( metricDisplay , face ) ;
}
2015-06-23 22:44:24 +02:00
/ * *
2015-08-20 17:24:17 +02:00
* If the user has selected to have facial tracking dots or measurements drawn , we use face . getFacePoints ( ) to send those points
2015-06-23 22:44:24 +02:00
* to our drawing thread and also inform the thread what the valence score was , as that will determine the color
* of the bounding box .
2015-10-02 03:12:00 +02:00
* /
2015-07-20 21:53:09 +02:00
if ( drawingView . getDrawPointsEnabled ( ) | | drawingView . getDrawMeasurementsEnabled ( ) ) {
2015-08-20 17:24:17 +02:00
drawingView . setMetrics ( face . measurements . orientation . getRoll ( ) , face . measurements . orientation . getYaw ( ) , face . measurements . orientation . getPitch ( ) , face . measurements . getInterocularDistance ( ) , face . emotions . getValence ( ) ) ;
2015-08-28 21:10:43 +02:00
drawingView . updatePoints ( face . getFacePoints ( ) , mirrorPoints ) ;
2015-06-23 22:44:24 +02:00
}
}
2015-08-20 17:24:17 +02:00
/ * *
* Use the method that we saved in activateMetric ( ) to get the metric score and display it
* /
void updateMetricScore ( MetricDisplay metricDisplay , Face face ) {
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 ( ) ) ) ;
2015-07-20 21:53:09 +02:00
}
2015-08-20 17:24:17 +02:00
metricDisplay . setScore ( score ) ;
2015-07-20 21:53:09 +02:00
}
2015-06-23 22:44:24 +02:00
/ * *
* FPS measurement simply uses SystemClock to measure how many frames were processed since
* the FPS variables were last reset .
* The constants 1000L and 1000f appear because . elapsedRealtime ( ) measures time in milliseconds .
* Note that if 20 frames per second are processed , this method could run for 1 . 5 years without being reset
* before numberOfFrames overflows .
* /
void performFPSCalculations ( ) {
numberOfFrames + = 1 ;
long currentTime = SystemClock . elapsedRealtime ( ) ;
if ( currentTime > timeToUpdate ) {
float framesPerSecond = ( numberOfFrames / ( float ) ( currentTime - firstSystemTime ) ) * 1000f ;
fpsPct . setText ( String . format ( " %.1f " , framesPerSecond ) ) ;
timeToUpdate = currentTime + 1000L ;
}
}
/ * *
* Although we start the camera in onWindowFocusChanged ( ) , we stop it in onPause ( ) , and set detector to be null so that when onWindowFocusChanged ( )
* is called it restarts the camera . We also set the Progress Bar to be visible , so the camera ( which may need resizing when the app
* is resumed ) is obscured .
* /
@Override
public void onPause ( ) {
super . onPause ( ) ;
progressBarLayout . setVisibility ( View . VISIBLE ) ;
performFaceDetectionStoppedTasks ( ) ;
2015-08-20 17:24:17 +02:00
2015-08-27 18:07:37 +02:00
stopDetector ( ) ;
}
2015-08-20 17:24:17 +02:00
2015-08-27 18:07:37 +02:00
void stopDetector ( ) {
if ( detector . isRunning ( ) ) {
try {
detector . stop ( ) ;
} catch ( Exception e ) {
Log . e ( LOG_TAG , e . getMessage ( ) ) ;
}
2015-06-23 22:44:24 +02:00
}
2015-08-27 18:07:37 +02:00
detector . setDetectAllEmotions ( false ) ;
detector . setDetectAllExpressions ( false ) ;
2015-06-23 22:44:24 +02:00
}
2015-08-27 18:07:37 +02:00
2015-06-23 22:44:24 +02:00
/ * *
* When the user taps the screen , hide the menu if it is visible and show it if it is hidden .
* * * /
void setMenuVisible ( boolean b ) {
2015-08-20 17:24:17 +02:00
isMenuShowingForFirstTime = false ;
2015-06-23 22:44:24 +02:00
isMenuVisible = b ;
if ( b ) {
2015-07-20 21:53:09 +02:00
settingsButton . setVisibility ( View . VISIBLE ) ;
2015-08-27 18:07:37 +02:00
cameraButton . setVisibility ( View . VISIBLE ) ;
2015-06-23 22:44:24 +02:00
//We display the navigation bar again
getWindow ( ) . getDecorView ( ) . setSystemUiVisibility (
View . SYSTEM_UI_FLAG_LAYOUT_STABLE
| View . SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View . SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN ) ;
}
else {
//We hide the navigation bar
getWindow ( ) . getDecorView ( ) . setSystemUiVisibility (
View . SYSTEM_UI_FLAG_LAYOUT_STABLE
| View . SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View . SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View . SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View . SYSTEM_UI_FLAG_FULLSCREEN
| View . SYSTEM_UI_FLAG_IMMERSIVE ) ;
2015-07-20 21:53:09 +02:00
settingsButton . setVisibility ( View . INVISIBLE ) ;
2015-08-27 18:07:37 +02:00
cameraButton . setVisibility ( View . INVISIBLE ) ;
2015-06-23 22:44:24 +02:00
}
}
/ * *
* If a user has a phone with a physical menu button , they may expect it to toggle
* the menu , so we add that functionality .
* /
@Override
public boolean onKeyDown ( int keyCode , KeyEvent event ) {
if ( keyCode = = KeyEvent . KEYCODE_MENU ) {
setMenuVisible ( ! isMenuVisible ) ;
return true ;
}
return super . onKeyDown ( keyCode , event ) ;
}
//If the user selects to have facial tracking dots drawn, inform our drawing thread.
void setTrackPoints ( boolean b ) {
drawingView . setDrawPointsEnabled ( b ) ;
}
2015-07-20 21:53:09 +02:00
void setShowMeasurements ( boolean b ) {
drawingView . setDrawMeasurementsEnabled ( b ) ;
}
2015-06-23 22:44:24 +02:00
void setFPSVisible ( boolean b ) {
isFPSVisible = b ;
if ( b ) {
fpsName . setVisibility ( View . VISIBLE ) ;
fpsPct . setVisibility ( View . VISIBLE ) ;
} else {
fpsName . setVisibility ( View . INVISIBLE ) ;
fpsPct . setVisibility ( View . INVISIBLE ) ;
}
}
@Override
public boolean onTouch ( View v , MotionEvent event ) {
if ( event . getAction ( ) = = MotionEvent . ACTION_DOWN ) {
setMenuVisible ( ! isMenuVisible ) ;
}
return false ;
}
2015-07-20 21:53:09 +02:00
public void settings_button_click ( View view ) {
2015-08-28 21:10:43 +02:00
startActivity ( new Intent ( this , SettingsActivity . class ) ) ;
2015-07-20 21:53:09 +02:00
}
2015-08-27 18:07:37 +02:00
2015-10-02 03:12:00 +02:00
/ * onCameraStarted is a feature of SDK 2 . 02 , commenting out for 2 . 01
2015-08-27 18:07:37 +02:00
@Override
public void onCameraStarted ( boolean b , Throwable throwable ) {
if ( throwable ! = null ) {
Toast . makeText ( this , " Failed to start camera. " , Toast . LENGTH_LONG ) . show ( ) ;
}
2015-08-28 21:29:40 +02:00
} * /
2015-08-27 18:07:37 +02:00
@Override
public void onCameraSizeSelected ( int cameraWidth , int cameraHeight , ROTATE rotation ) {
if ( rotation = = ROTATE . BY_90_CCW | | rotation = = ROTATE . BY_90_CW ) {
cameraPreviewWidth = cameraHeight ;
cameraPreviewHeight = cameraWidth ;
} else {
cameraPreviewWidth = cameraWidth ;
cameraPreviewHeight = cameraHeight ;
}
2015-10-02 03:12:00 +02:00
drawingView . setThickness ( ( int ) ( cameraPreviewWidth / 100f ) ) ;
2015-08-27 18:07:37 +02:00
mainLayout . post ( new Runnable ( ) {
@Override
public void run ( ) {
2015-10-02 03:12:00 +02:00
//Get the screen width and height, and calculate the new app width/height based on the surfaceview aspect ratio.
DisplayMetrics displaymetrics = new DisplayMetrics ( ) ;
getWindowManager ( ) . getDefaultDisplay ( ) . getMetrics ( displaymetrics ) ;
int layoutWidth = displaymetrics . widthPixels ;
int layoutHeight = displaymetrics . heightPixels ;
2015-08-27 18:07:37 +02:00
if ( cameraPreviewWidth = = 0 | | cameraPreviewHeight = = 0 | | layoutWidth = = 0 | | layoutHeight = = 0 )
return ;
float layoutAspectRatio = ( float ) layoutWidth / layoutHeight ;
float cameraPreviewAspectRatio = ( float ) cameraPreviewWidth / cameraPreviewHeight ;
int newWidth ;
int newHeight ;
if ( cameraPreviewAspectRatio > layoutAspectRatio ) {
newWidth = layoutWidth ;
newHeight = ( int ) ( layoutWidth / cameraPreviewAspectRatio ) ;
} else {
newWidth = ( int ) ( layoutHeight * cameraPreviewAspectRatio ) ;
newHeight = layoutHeight ;
}
drawingView . updateViewDimensions ( newWidth , newHeight , cameraPreviewWidth , cameraPreviewHeight ) ;
ViewGroup . LayoutParams params = mainLayout . getLayoutParams ( ) ;
params . height = newHeight ;
params . width = newWidth ;
mainLayout . setLayoutParams ( params ) ;
//Now that our main layout has been resized, we can remove the progress bar that was obscuring the screen (its purpose was to obscure the resizing of the SurfaceView)
progressBarLayout . setVisibility ( View . GONE ) ;
}
} ) ;
}
public void camera_button_click ( View view ) {
if ( cameraType = = CameraDetector . CameraType . CAMERA_FRONT ) {
if ( isBackFacingCameraDetected ) {
cameraType = CameraDetector . CameraType . CAMERA_BACK ;
2015-08-28 21:10:43 +02:00
mirrorPoints = false ;
2015-08-27 18:07:37 +02:00
} else {
Toast . makeText ( this , " No back-facing camera found " , Toast . LENGTH_LONG ) . show ( ) ;
}
} else if ( cameraType = = CameraDetector . CameraType . CAMERA_BACK ) {
if ( isFrontFacingCameraDetected ) {
cameraType = CameraDetector . CameraType . CAMERA_FRONT ;
2015-08-28 21:10:43 +02:00
mirrorPoints = true ;
2015-08-27 18:07:37 +02:00
} else {
Toast . makeText ( this , " No front-facing camera found " , Toast . LENGTH_LONG ) . show ( ) ;
}
}
performFaceDetectionStoppedTasks ( ) ;
try {
detector . setCameraType ( cameraType ) ;
} catch ( Exception e ) {
Log . e ( LOG_TAG , e . getMessage ( ) ) ;
}
}
2015-06-23 22:44:24 +02:00
}