2015-06-23 22:44:24 +02:00
package com.affectiva.affdexme ;
import android.app.Activity ;
2015-08-27 19:15:07 +02:00
import android.content.Context ;
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 ;
import android.util.Log ;
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-08-27 19:14:40 +02:00
import android.view.inputmethod.EditorInfo ;
2015-08-27 19:15:07 +02:00
import android.view.inputmethod.InputMethodManager ;
2015-08-27 19:14:40 +02:00
import android.widget.CheckBox ;
2015-08-27 19:15:07 +02:00
import android.widget.EditText ;
import android.widget.FrameLayout ;
2015-06-23 22:44:24 +02:00
import android.widget.LinearLayout ;
import android.widget.RelativeLayout ;
import android.widget.TextView ;
import java.io.BufferedReader ;
import java.io.ByteArrayInputStream ;
import java.io.InputStreamReader ;
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
* - Copy the SDK libs folder contents into this project ' s libs folder
* - Copy the armeabi - v7a folder ( found in the SDK libs folder ) into this project ' s jniLibs folder
* - Add your license file to the / assets / Affdex folder and uncomment the line in the startCamera ( ) method
* to type in your license file name
* - 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-27 19:15:07 +02:00
implements Detector . FaceListener , Detector . ImageListener , TextView . OnEditorActionListener , View . OnTouchListener , CameraDetector . CameraSurfaceViewListener {
2015-06-23 22:44:24 +02:00
2015-07-13 20:17:57 +02:00
private static final String LOG_TAG = " Affectiva " ;
2015-06-23 22:44:24 +02:00
//Affectiva SDK Object
private CameraDetector detector = null ;
2015-08-27 19:14:40 +02:00
//Metrics View UI Objects
2015-06-23 22:44:24 +02:00
private RelativeLayout metricViewLayout ;
private LinearLayout leftMetricsLayout ;
private LinearLayout rightMetricsLayout ;
2015-08-27 19:15:07 +02:00
private MetricView smilePct ;
private MetricView browRaisePct ;
private MetricView browFurrowPct ;
private MetricView frownPct ;
private MetricView valencePct ;
private MetricView engagementPct ;
2015-06-23 22:44:24 +02:00
private TextView fpsName ;
private TextView fpsPct ;
2015-08-27 19:15:07 +02:00
private TextView smileName ;
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 ;
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-08-27 19:14:40 +02:00
//The Shared Preferences object is used to restore/save settings when activity is created/destroyed
private final String PREFS_NAME = " AffdexMe " ;
SharedPreferences sharedPreferences ;
2015-06-23 22:44:24 +02:00
//Application settings variables
2015-08-27 19:14:40 +02:00
private int detectorProcessRate = 20 ;
2015-06-23 22:44:24 +02:00
private boolean isMenuVisible = false ;
private boolean isFPSVisible = false ;
//Frames Per Second (FPS) variables
private long firstSystemTime = 0 ;
private float numberOfFrames = 0 ;
private long timeToUpdate = 0 ;
private boolean isFrontFacingCameraDetected = true ;
@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-27 19:14:17 +02:00
/ * *
* We check to make sure the device has a front - facing camera .
* If it does not , we obscure the app with a notice informing the user they cannot
* use the app .
* /
if ( ! getPackageManager ( ) . hasSystemFeature ( PackageManager . FEATURE_CAMERA_FRONT ) ) {
isFrontFacingCameraDetected = false ;
2015-06-23 22:44:24 +02:00
TextView notFoundTextView = ( TextView ) findViewById ( R . id . not_found_textview ) ;
notFoundTextView . setVisibility ( View . VISIBLE ) ;
}
2015-08-27 19:14:40 +02:00
initializeUI ( ) ;
2015-08-27 19:14:17 +02:00
initializeCameraDetector ( ) ;
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 ) ;
2015-08-27 19:15:07 +02:00
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 ) ;
2015-06-23 22:44:24 +02:00
fpsPct = ( TextView ) findViewById ( R . id . fps_value ) ;
2015-08-27 19:15:07 +02:00
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 ) ;
2015-06-23 22:44:24 +02:00
fpsName = ( TextView ) findViewById ( R . id . fps_name ) ;
2015-08-27 19:15:07 +02:00
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 ) ;
2015-06-23 22:44:24 +02:00
cameraView = ( SurfaceView ) findViewById ( R . id . camera_preview ) ;
drawingView = ( DrawingView ) findViewById ( R . id . drawing_view ) ;
//Load Application Font and set UI Elements to use it
Typeface face = Typeface . createFromAsset ( getAssets ( ) , " fonts/Square.ttf " ) ;
2015-08-27 19:15:07 +02:00
smilePct . setTypeface ( face ) ;
browRaisePct . setTypeface ( face ) ;
browFurrowPct . setTypeface ( face ) ;
frownPct . 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 ) ;
2015-06-23 22:44:24 +02:00
fpsPct . setTypeface ( face ) ;
fpsName . setTypeface ( face ) ;
2015-08-27 19:15:07 +02:00
fpsEditTextName . setTypeface ( face ) ;
fpsCheckbox . setTypeface ( face ) ;
trackingCheckbox . 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-08-27 19:15:07 +02:00
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 ) ;
2015-06-23 22:44:24 +02:00
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-08-27 19:14:17 +02:00
detector = new CameraDetector ( this , CameraDetector . CameraType . CAMERA_FRONT , cameraView ) ;
2015-08-27 19:14:40 +02:00
2015-07-13 20:17:57 +02:00
// NOTE: uncomment the line below and replace "YourLicenseFile" with your license file, which should be stored in /assets/Affdex/
//detector.setLicensePath("YourLicenseFile");
2015-08-27 19:14:40 +02:00
2015-08-27 19:15:07 +02:00
// 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 ) ;
2015-08-27 19:14:40 +02:00
2015-08-27 19:15:07 +02:00
detector . setMaxProcessRate ( detectorProcessRate ) ;
2015-08-27 19:14:40 +02:00
2015-07-13 20:17:57 +02:00
detector . setImageListener ( this ) ;
detector . setFaceListener ( this ) ;
2015-08-27 19:14:17 +02:00
detector . setCameraDetectorDimensionsListener ( 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-27 19:14:40 +02:00
setMenuVisible ( false ) ; //always make the menu invisible by default
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-27 19:15:07 +02:00
sharedPreferences = getSharedPreferences ( PREFS_NAME , 0 ) ;
2015-06-23 22:44:24 +02:00
2015-08-27 19:15:07 +02:00
detectorProcessRate = sharedPreferences . getInt ( " rate " , detectorProcessRate ) ; //restore camera processing rate
fpsEditText . setText ( String . valueOf ( detectorProcessRate ) ) ;
2015-08-27 19:14:40 +02:00
2015-06-23 22:44:24 +02:00
if ( sharedPreferences . getBoolean ( " fps " , isFPSVisible ) ) { //restore isFPSMetricVisible
2015-08-27 19:15:07 +02:00
fpsCheckbox . setChecked ( true ) ;
2015-06-23 22:44:24 +02:00
setFPSVisible ( true ) ;
} else {
2015-08-27 19:15:07 +02:00
fpsCheckbox . setChecked ( false ) ;
2015-06-23 22:44:24 +02:00
setFPSVisible ( false ) ;
}
if ( sharedPreferences . getBoolean ( " track " , drawingView . getDrawPointsEnabled ( ) ) ) { //restore isTrackingDotsVisible
setTrackPoints ( true ) ;
2015-08-27 19:15:07 +02:00
trackingCheckbox . setChecked ( true ) ;
2015-06-23 22:44:24 +02:00
} else {
setTrackPoints ( false ) ;
2015-08-27 19:15:07 +02:00
trackingCheckbox . setChecked ( false ) ;
2015-08-27 19:14:40 +02:00
}
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-08-27 19:14:40 +02:00
* We start the camera as soon as the application has been given focus , which occurs as soon as the application has
* 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 .
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 ) {
2015-08-27 19:14:40 +02:00
2015-06-23 22:44:24 +02:00
if ( hasFocus & & isFrontFacingCameraDetected ) {
2015-07-13 20:17:57 +02:00
2015-08-27 19:14:40 +02:00
startCamera ( ) ;
if ( ! drawingView . isSurfaceDimensionsNeeded ( ) ) {
progressBarLayout . setVisibility ( View . GONE ) ;
2015-08-20 17:24:17 +02:00
}
2015-08-27 19:14:40 +02:00
resetFPSCalculations ( ) ;
}
2015-06-23 22:44:24 +02:00
}
2015-08-27 19:14:17 +02:00
void startCamera ( ) {
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-08-27 18:07:37 +02:00
2015-08-27 19:14:17 +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 ) ;
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 ) ;
2015-08-27 19:14:17 +02:00
drawingView . invalidatePoints ( ) ; //inform the drawing thread that the latest facial tracking points are now invalid
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.
}
/ * *
* This event is received every time the SDK processes a frame .
* /
@Override
public void onImageResults ( List < Face > faces , Frame image , float timeStamp ) {
2015-08-27 19:14:17 +02:00
/ * *
* If the flag indicating that we still need to know the size of the camera frames , call calculateImageDimensions ( ) .
* The flag is a boolean stored in our drawingView object , retrieved through DrawingView . isImageDimensionsNeeded ( ) .
* /
if ( drawingView . isImageDimensionsNeeded ( ) ) {
calculateImageDimensions ( image ) ;
}
2015-06-23 22:44:24 +02:00
//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 ) {
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-27 19:15:07 +02:00
smilePct . setScore ( face . getSmileScore ( ) ) ;
browRaisePct . setScore ( face . getBrowRaiseScore ( ) ) ;
browFurrowPct . setScore ( face . getBrowFurrowScore ( ) ) ;
engagementPct . setScore ( face . getEngagementScore ( ) ) ;
frownPct . setScore ( face . getLipCornerDepressorScore ( ) ) ;
float valenceScore = face . getValenceScore ( ) ;
valencePct . setScore ( valenceScore ) ;
2015-06-23 22:44:24 +02:00
/ * *
2015-08-27 19:14:40 +02:00
* If the user has selected to have facial tracking dots 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-08-27 19:15:07 +02:00
if ( drawingView . getDrawPointsEnabled ( ) ) {
drawingView . setScore ( valenceScore ) ;
2015-06-23 22:44:24 +02:00
drawingView . updatePoints ( face . getFacePoints ( ) ) ;
}
}
2015-08-27 19:14:17 +02:00
/ * *
2015-08-27 19:14:40 +02:00
* 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
* thickness of the bounding box .
2015-08-27 19:14:17 +02:00
* /
void calculateImageDimensions ( Frame image ) {
///referenceDimension will be used to determine the size of the facial tracking dots
float referenceDimension = activityLayout . getHeight ( ) ;
//get size of frames being passed by camera
int imageWidth = image . getWidth ( ) ;
int imageHeight = image . getHeight ( ) ;
/ * *
* If device is rotated vertically , reverse the width and height returned by the Frame object ,
* and switch the dimension we consider to be the reference dimension .
* /
if ( ( ROTATE . BY_90_CW = = image . getTargetRotation ( ) ) | | ( ROTATE . BY_90_CCW = = image . getTargetRotation ( ) ) ) {
int temp = imageWidth ;
imageWidth = imageHeight ;
imageHeight = temp ;
referenceDimension = activityLayout . getWidth ( ) ;
}
2015-08-27 19:15:07 +02:00
drawingView . updateImageDimensions ( imageWidth , imageHeight ) ;
drawingView . setThickness ( ( int ) ( referenceDimension / 160f ) ) ;
2015-08-27 19:14:17 +02:00
}
/ * *
* This method is called when the SDK has corrected the aspect ratio of the SurfaceView . We use this information to resize
* our mainLayout ViewGroup so the UI fits snugly around the SurfaceView . We also update our drawingView object , so the tracking dots
* are drawn in the correct coordinates .
* /
@Override
public void onSurfaceViewAspectRatioChanged ( int width , int height ) {
drawingView . updateSurfaceViewDimensions ( width , height ) ;
RelativeLayout . LayoutParams params = ( RelativeLayout . LayoutParams ) mainLayout . getLayoutParams ( ) ;
params . height = height ;
params . width = width ;
mainLayout . setLayoutParams ( params ) ;
//Now that our main layout has been resized, we can remove the progress bar that was obscuring the screen (its purpose was to obscure the resizing of the SurfaceView)
progressBarLayout . setVisibility ( View . GONE ) ;
}
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 ) ;
2015-08-27 19:15:07 +02:00
saveApplicationSettings ( ) ;
2015-08-27 19:14:17 +02:00
stopCamera ( ) ;
2015-08-27 18:07:37 +02:00
}
2015-08-20 17:24:17 +02:00
2015-08-27 19:14:17 +02:00
private void stopCamera ( ) {
performFaceDetectionStoppedTasks ( ) ;
try {
detector . stop ( ) ;
} catch ( Exception e ) {
Log . e ( LOG_TAG , e . getMessage ( ) ) ;
}
}
2015-06-23 22:44:24 +02:00
2015-08-27 19:15:07 +02:00
/ * *
* 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'
}
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 ) {
isMenuVisible = b ;
if ( b ) {
2015-08-27 19:15:07 +02:00
menuLayout . 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 {
2015-08-27 19:15:07 +02:00
InputMethodManager imm = ( InputMethodManager ) getSystemService (
Context . INPUT_METHOD_SERVICE ) ;
imm . hideSoftInputFromWindow ( fpsEditText . getWindowToken ( ) , 0 ) ;
2015-06-23 22:44:24 +02:00
//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-08-27 19:15:07 +02:00
menuLayout . 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 ) ;
}
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 ;
}
}