From 301ba88795ab560e7ebc7577ccf2f5d277a4fc1d Mon Sep 17 00:00:00 2001 From: acasallas Date: Thu, 27 Aug 2015 12:07:37 -0400 Subject: [PATCH] Made changes for AffdexMe to work with SDK v2.01 --- AffdexMe/app/src/main/AndroidManifest.xml | 11 + .../com/affectiva/affdexme/DrawingView.java | 100 +++----- .../com/affectiva/affdexme/MainActivity.java | 229 +++++++++++------- .../errorreporting/CustomApplication.java | 44 ++++ .../errorreporting/ErrorReporter.java | 66 +++++ .../drawable-hdpi/ic_more_vert_black_48dp.png | Bin 0 -> 205 bytes .../drawable-hdpi/ic_more_vert_white_48dp.png | Bin 0 -> 216 bytes .../ic_switch_camera_black_48dp.png | Bin 0 -> 365 bytes .../ic_switch_camera_white_48dp.png | Bin 0 -> 379 bytes ...ssed.png => settings_button_pressed_1.png} | Bin .../drawable-mdpi/ic_more_vert_black_48dp.png | Bin 0 -> 155 bytes .../drawable-mdpi/ic_more_vert_white_48dp.png | Bin 0 -> 158 bytes .../ic_switch_camera_black_48dp.png | Bin 0 -> 248 bytes .../ic_switch_camera_white_48dp.png | Bin 0 -> 259 bytes ...ssed.png => settings_button_pressed_1.png} | Bin .../src/main/res/drawable-nodpi/disgust.jpg | Bin 5775 -> 7722 bytes .../ic_more_vert_black_48dp.png | Bin 0 -> 272 bytes .../ic_more_vert_white_48dp.png | Bin 0 -> 305 bytes .../ic_switch_camera_black_48dp.png | Bin 0 -> 451 bytes .../ic_switch_camera_white_48dp.png | Bin 0 -> 472 bytes ...ssed.png => settings_button_pressed_1.png} | Bin .../ic_more_vert_black_48dp.png | Bin 0 -> 411 bytes .../ic_more_vert_white_48dp.png | Bin 0 -> 472 bytes .../ic_switch_camera_black_48dp.png | Bin 0 -> 674 bytes .../ic_switch_camera_white_48dp.png | Bin 0 -> 713 bytes ...ssed.png => settings_button_pressed_1.png} | Bin .../drawable/camera_button_not_pressed.xml | 8 + .../res/drawable/camera_button_pressed.xml | 8 + .../res/drawable/camera_button_selector.xml | 5 + .../drawable/settings_button_not_pressed.xml | 8 + .../res/drawable/settings_button_pressed.xml | 8 + ...ssed.png => settings_button_pressed_1.png} | Bin .../res/drawable/settings_button_selector.xml | 2 +- .../main/res/drawable/transluscent_circle.xml | 10 + .../app/src/main/res/layout/activity_main.xml | 71 +++--- .../src/main/res/layout/error_reporter.xml | 23 ++ AffdexMe/app/src/main/res/raw/disgust.mp4 | Bin 15453 -> 19557 bytes 37 files changed, 410 insertions(+), 183 deletions(-) create mode 100644 AffdexMe/app/src/main/java/com/affectiva/errorreporting/CustomApplication.java create mode 100644 AffdexMe/app/src/main/java/com/affectiva/errorreporting/ErrorReporter.java create mode 100644 AffdexMe/app/src/main/res/drawable-hdpi/ic_more_vert_black_48dp.png create mode 100644 AffdexMe/app/src/main/res/drawable-hdpi/ic_more_vert_white_48dp.png create mode 100644 AffdexMe/app/src/main/res/drawable-hdpi/ic_switch_camera_black_48dp.png create mode 100644 AffdexMe/app/src/main/res/drawable-hdpi/ic_switch_camera_white_48dp.png rename AffdexMe/app/src/main/res/drawable-hdpi/{settings_button_pressed.png => settings_button_pressed_1.png} (100%) create mode 100644 AffdexMe/app/src/main/res/drawable-mdpi/ic_more_vert_black_48dp.png create mode 100644 AffdexMe/app/src/main/res/drawable-mdpi/ic_more_vert_white_48dp.png create mode 100644 AffdexMe/app/src/main/res/drawable-mdpi/ic_switch_camera_black_48dp.png create mode 100644 AffdexMe/app/src/main/res/drawable-mdpi/ic_switch_camera_white_48dp.png rename AffdexMe/app/src/main/res/drawable-mdpi/{settings_button_pressed.png => settings_button_pressed_1.png} (100%) create mode 100644 AffdexMe/app/src/main/res/drawable-xhdpi/ic_more_vert_black_48dp.png create mode 100644 AffdexMe/app/src/main/res/drawable-xhdpi/ic_more_vert_white_48dp.png create mode 100644 AffdexMe/app/src/main/res/drawable-xhdpi/ic_switch_camera_black_48dp.png create mode 100644 AffdexMe/app/src/main/res/drawable-xhdpi/ic_switch_camera_white_48dp.png rename AffdexMe/app/src/main/res/drawable-xhdpi/{settings_button_pressed.png => settings_button_pressed_1.png} (100%) create mode 100644 AffdexMe/app/src/main/res/drawable-xxhdpi/ic_more_vert_black_48dp.png create mode 100644 AffdexMe/app/src/main/res/drawable-xxhdpi/ic_more_vert_white_48dp.png create mode 100644 AffdexMe/app/src/main/res/drawable-xxhdpi/ic_switch_camera_black_48dp.png create mode 100644 AffdexMe/app/src/main/res/drawable-xxhdpi/ic_switch_camera_white_48dp.png rename AffdexMe/app/src/main/res/drawable-xxhdpi/{settings_button_pressed.png => settings_button_pressed_1.png} (100%) create mode 100644 AffdexMe/app/src/main/res/drawable/camera_button_not_pressed.xml create mode 100644 AffdexMe/app/src/main/res/drawable/camera_button_pressed.xml create mode 100644 AffdexMe/app/src/main/res/drawable/camera_button_selector.xml create mode 100644 AffdexMe/app/src/main/res/drawable/settings_button_not_pressed.xml create mode 100644 AffdexMe/app/src/main/res/drawable/settings_button_pressed.xml rename AffdexMe/app/src/main/res/drawable/{settings_button_pressed.png => settings_button_pressed_1.png} (100%) create mode 100644 AffdexMe/app/src/main/res/drawable/transluscent_circle.xml create mode 100644 AffdexMe/app/src/main/res/layout/error_reporter.xml diff --git a/AffdexMe/app/src/main/AndroidManifest.xml b/AffdexMe/app/src/main/AndroidManifest.xml index 5d6165a..fc7cb0e 100644 --- a/AffdexMe/app/src/main/AndroidManifest.xml +++ b/AffdexMe/app/src/main/AndroidManifest.xml @@ -14,6 +14,7 @@ + + + + + + + diff --git a/AffdexMe/app/src/main/java/com/affectiva/affdexme/DrawingView.java b/AffdexMe/app/src/main/java/com/affectiva/affdexme/DrawingView.java index 1fc8f50..7ac5175 100644 --- a/AffdexMe/app/src/main/java/com/affectiva/affdexme/DrawingView.java +++ b/AffdexMe/app/src/main/java/com/affectiva/affdexme/DrawingView.java @@ -25,13 +25,17 @@ import com.affectiva.android.affdex.sdk.detector.Face; */ public class DrawingView extends SurfaceView implements SurfaceHolder.Callback { + class PointFArraySharer { + PointF[] nextPointsToDraw = null; + } + //Inner Thread class class DrawingThread extends Thread{ private SurfaceHolder mSurfaceHolder; private Paint circlePaint; private Paint boxPaint; - private boolean stopFlag = false; //boolean to indicate when thread has been told to stop - private PointF[] nextPointsToDraw = null; //holds a reference to the most recent set of points returned by CameraDetector, passed in by main thread + private volatile boolean stopFlag = false; //boolean to indicate when thread has been told to stop + private final PointFArraySharer sharer; private DrawingViewConfig config; private final long drawPeriod = 33; //draw at 30 fps @@ -52,6 +56,7 @@ public class DrawingView extends SurfaceView implements SurfaceHolder.Callback { boxPaint.setStyle(Paint.Style.STROKE); config = con; + sharer = new PointFArraySharer(); setThickness(config.drawThickness); } @@ -84,7 +89,9 @@ public class DrawingView extends SurfaceView implements SurfaceHolder.Callback { //Updates thread with latest points returned by the onImageResults() event. public void updatePoints(PointF[] pointList) { - nextPointsToDraw = pointList; + synchronized (sharer) { + sharer.nextPointsToDraw = pointList; + } } void setThickness(int thickness) { @@ -93,7 +100,9 @@ public class DrawingView extends SurfaceView implements SurfaceHolder.Callback { //Inform thread face detection has stopped, so array of points is no longer valid. public void invalidatePoints() { - nextPointsToDraw = null; + synchronized (sharer) { + sharer.nextPointsToDraw = null; + } } @Override @@ -101,7 +110,6 @@ public class DrawingView extends SurfaceView implements SurfaceHolder.Callback { android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); while(!stopFlag) { - long startTime = SystemClock.elapsedRealtime(); //get time at the start of thread loop /** * We use SurfaceHolder.lockCanvas() to get the canvas that draws to the SurfaceView. @@ -114,9 +122,7 @@ public class DrawingView extends SurfaceView implements SurfaceHolder.Callback { if (c!= null) { synchronized (mSurfaceHolder) { c.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); //clear previous dots - if ((nextPointsToDraw != null) ) { - draw(c); - } + draw(c); } } } @@ -126,23 +132,18 @@ public class DrawingView extends SurfaceView implements SurfaceHolder.Callback { } } - //send thread to sleep so we don't draw faster than the requested 'drawPeriod'. - long sleepTime = drawPeriod - (SystemClock.elapsedRealtime() - startTime); - try { - if(sleepTime>0){ - this.sleep(sleepTime); - } - } catch (InterruptedException ex) { - Log.e(LOG_TAG,ex.getMessage()); - } } config = null; //nullify object to avoid memory leak } void draw(Canvas c) { - //Save our own reference to the list of points, in case the previous reference is overwritten by the main thread. - PointF[] points = nextPointsToDraw; + PointF[] points; + synchronized (sharer) { + if (sharer.nextPointsToDraw == null) + return; + points = sharer.nextPointsToDraw; + } //Coordinates around which to draw bounding box. float leftBx = config.surfaceViewWidth; @@ -218,10 +219,9 @@ public class DrawingView extends SurfaceView implements SurfaceHolder.Callback { private int surfaceViewHeight = 0; private float screenToImageRatio = 0; private int drawThickness = 0; - private boolean isImageDimensionsNeeded = true; - private boolean isSurfaceViewDimensionsNeeded = true; private boolean isDrawPointsEnabled = true; //by default, have the drawing thread draw tracking dots private boolean isDrawMeasurementsEnabled = false; + private boolean isDimensionsNeeded = true; private Paint textPaint; private int textSize; @@ -235,32 +235,16 @@ public class DrawingView extends SurfaceView implements SurfaceHolder.Callback { this.upperTextSpacing = upperTextSpacing; } - public void updateImageDimensions(int w, int h) { - - if ( (w <= 0) || (h <= 0)) { - throw new IllegalArgumentException("Image Dimensions must be positive."); + public void updateViewDimensions(int surfaceViewWidth, int surfaceViewHeight, int imageWidth, int imageHeight) { + if (surfaceViewWidth <= 0 || surfaceViewHeight <= 0 || imageWidth <= 0 || imageHeight <= 0) { + throw new IllegalArgumentException("All dimensions submitted to updateViewDimensions() must be positive"); } - - imageWidth = w; - imageHeight = h; - if (!isSurfaceViewDimensionsNeeded) { - screenToImageRatio = (float)surfaceViewWidth / (float)imageWidth; - } - isImageDimensionsNeeded = false; - } - - public void updateSurfaceViewDimensions(int w, int h) { - - if ( (w <= 0) || (h <= 0)) { - throw new IllegalArgumentException("SurfaceView Dimensions must be positive."); - } - - surfaceViewWidth = w; - surfaceViewHeight = h; - if (!isImageDimensionsNeeded) { - screenToImageRatio = (float)surfaceViewWidth / (float)imageWidth; - } - isSurfaceViewDimensionsNeeded = false; + this.imageWidth = imageWidth; + this.imageHeight = imageHeight; + this.surfaceViewWidth = surfaceViewWidth; + this.surfaceViewHeight = surfaceViewHeight; + screenToImageRatio = (float)surfaceViewWidth / imageWidth; + isDimensionsNeeded = false; } public void setDrawThickness(int t) { @@ -365,29 +349,17 @@ public class DrawingView extends SurfaceView implements SurfaceHolder.Callback { } } - public boolean isImageDimensionsNeeded() { - return drawingViewConfig.isImageDimensionsNeeded; + public boolean isDimensionsNeeded() { + return drawingViewConfig.isDimensionsNeeded; } - public boolean isSurfaceDimensionsNeeded() { - return drawingViewConfig.isSurfaceViewDimensionsNeeded; + public void invalidateDimensions() { + drawingViewConfig.isDimensionsNeeded = true; } - public void invalidateImageDimensions() { - drawingViewConfig.isImageDimensionsNeeded = true; - } - - public void updateImageDimensions(int w, int h) { + public void updateViewDimensions(int surfaceViewWidth, int surfaceViewHeight, int imageWidth, int imageHeight) { try { - drawingViewConfig.updateImageDimensions(w, h); - } catch (Exception e) { - Log.e(LOG_TAG,e.getMessage()); - } - } - - public void updateSurfaceViewDimensions(int w, int h) { - try { - drawingViewConfig.updateSurfaceViewDimensions(w, h); + drawingViewConfig.updateViewDimensions(surfaceViewWidth,surfaceViewHeight,imageWidth,imageHeight); } catch (Exception e) { Log.e(LOG_TAG,e.getMessage()); } diff --git a/AffdexMe/app/src/main/java/com/affectiva/affdexme/MainActivity.java b/AffdexMe/app/src/main/java/com/affectiva/affdexme/MainActivity.java index 7449d88..7178f62 100644 --- a/AffdexMe/app/src/main/java/com/affectiva/affdexme/MainActivity.java +++ b/AffdexMe/app/src/main/java/com/affectiva/affdexme/MainActivity.java @@ -67,13 +67,15 @@ import com.affectiva.android.affdex.sdk.detector.Face; */ public class MainActivity extends Activity - implements Detector.FaceListener, Detector.ImageListener, View.OnTouchListener, CameraDetector.CameraSurfaceViewListener { + implements Detector.FaceListener, Detector.ImageListener, View.OnTouchListener, CameraDetector.OnCameraEventListener { private static final String LOG_TAG = "Affectiva"; public static final int NUM_METRICS_DISPLAYED = 6; //Affectiva SDK Object private CameraDetector detector = null; + //TODO: License file in byte form. Should NOT be included in released sample code. + private byte[] licenseBytes = {123,34,116,111,107,101,110,34,58,32,34,102,98,51,51,101,102,57,98,102,98,49,57,53,100,97,99,55,97,53,99,48,98,50,56,54,54,51,51,48,56,52,100,102,56,48,57,55,52,101,99,99,57,98,51,54,97,97,101,51,57,99,97,51,98,97,53,54,57,50,49,102,56,49,53,34,44,34,108,105,99,101,110,115,111,114,34,58,32,34,65,102,102,101,99,116,105,118,97,32,73,110,99,46,34,44,34,101,120,112,105,114,101,115,34,58,32,34,50,48,57,57,45,48,49,45,48,49,34,44,34,100,101,118,101,108,111,112,101,114,73,100,34,58,32,34,65,102,102,101,99,116,105,118,97,45,105,110,116,101,114,110,97,108,34,44,34,115,111,102,116,119,97,114,101,34,58,32,34,65,102,102,100,101,120,32,83,68,75,34,125}; //MetricsManager View UI Objects private RelativeLayout metricViewLayout; @@ -93,6 +95,7 @@ public class MainActivity extends Activity private SurfaceView cameraView; //SurfaceView used to display camera images private DrawingView drawingView; //SurfaceView containing its own thread, used to draw facial tracking dots private ImageButton settingsButton; + private ImageButton cameraButton; //Application settings variables private int detectorProcessRate; @@ -105,7 +108,12 @@ public class MainActivity extends Activity private float numberOfFrames = 0; private long timeToUpdate = 0; + //Camera-related variables private boolean isFrontFacingCameraDetected = true; + private boolean isBackFacingCameraDetected = true; + int cameraPreviewWidth = 0; + int cameraPreviewHeight = 0; + CameraDetector.CameraType cameraType; @Override protected void onCreate(Bundle savedInstanceState) { @@ -115,20 +123,33 @@ public class MainActivity extends Activity initializeUI(); - /** - * We check to make sure the device has a front-facing camera. - * If it does not, we obscure the app with a notice informing the user they cannot - * use the app. - */ - if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT)) { - isFrontFacingCameraDetected = false; + determineCameraAvailability(); + + initializeCameraDetector(); + } + + /** + * We check to make sure the device has a front-facing camera. + * If it does not, we obscure the app with a notice informing the user they cannot + * use the app. + */ + void determineCameraAvailability() { + PackageManager manager = getPackageManager(); + isFrontFacingCameraDetected = manager.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT); + isBackFacingCameraDetected = manager.hasSystemFeature(PackageManager.FEATURE_CAMERA); + + if (!isFrontFacingCameraDetected && !isBackFacingCameraDetected) { progressBar.setVisibility(View.INVISIBLE); pleaseWaitTextView.setVisibility(View.INVISIBLE); TextView notFoundTextView = (TextView) findViewById(R.id.not_found_textview); notFoundTextView.setVisibility(View.VISIBLE); } - initializeCameraDetector(); + //TODO: change this to be taken from settings + if (isBackFacingCameraDetected) + cameraType = CameraDetector.CameraType.CAMERA_BACK; + if (isFrontFacingCameraDetected) + cameraType = CameraDetector.CameraType.CAMERA_FRONT; } void initializeUI() { @@ -145,6 +166,7 @@ public class MainActivity extends Activity cameraView = (SurfaceView) findViewById(R.id.camera_preview); drawingView = (DrawingView) findViewById(R.id.drawing_view); settingsButton = (ImageButton) findViewById(R.id.settings_button); + cameraButton = (ImageButton) findViewById(R.id.camera_button); progressBar = (ProgressBar) findViewById(R.id.progress_bar); pleaseWaitTextView = (TextView) findViewById(R.id.please_wait_textview); @@ -216,12 +238,14 @@ public class MainActivity extends Activity * the camera. If a SurfaceView is passed in as the last argument to the constructor, * that view will be painted with what the camera sees. */ - detector = new CameraDetector(this, CameraDetector.CameraType.CAMERA_FRONT, cameraView); + detector = new CameraDetector(this, cameraType, cameraView); + //TODO: this method SHOULD NOT be included in sample code release (customer should enter their own license file). + detector.setLicenseStream(new BufferedReader(new InputStreamReader(new ByteArrayInputStream(licenseBytes)))); // NOTE: uncomment the line below and replace "YourLicenseFile" with your license file, which should be stored in /assets/Affdex/ //detector.setLicensePath("YourLicenseFile"); detector.setImageListener(this); detector.setFaceListener(this); - detector.setCameraDetectorDimensionsListener(this); + detector.setOnCameraEventListener(this); } /* @@ -329,8 +353,10 @@ public class MainActivity extends Activity } void mainWindowResumedTasks() { - startCamera(); - if (!drawingView.isSurfaceDimensionsNeeded()) { + + startDetector(); + + if (!drawingView.isDimensionsNeeded()) { progressBarLayout.setVisibility(View.GONE); } resetFPSCalculations(); @@ -341,14 +367,14 @@ public class MainActivity extends Activity setMenuVisible(false); } } - },5000); + }, 5000); } - void startCamera() { - - //this app will always detect valence (it will also always detect measurements, but measurement don't need to be enabled) - detector.setDetectValence(true); + void startDetector() { + if (!isBackFacingCameraDetected && !isFrontFacingCameraDetected) + return; //without any cameras detected, we cannot proceed + detector.setDetectValence(true); //this app will always detect valence if (!detector.isRunning()) { try { detector.start(); @@ -356,9 +382,10 @@ public class MainActivity extends Activity Log.e(LOG_TAG, e.getMessage()); } } - } + + @Override public void onFaceDetectionStarted() { leftMetricsLayout.animate().alpha(1); //make left and right metrics appear @@ -375,8 +402,7 @@ public class MainActivity extends Activity void performFaceDetectionStoppedTasks() { leftMetricsLayout.animate().alpha(0); //make left and right metrics disappear rightMetricsLayout.animate().alpha(0); - - drawingView.invalidatePoints(); //inform the drawing thread that the latest facial tracking points are now invalid + drawingView.updatePoints(null); resetFPSCalculations(); //Since the FPS may be different whether a face is being tracked or not, reset variables. } @@ -385,14 +411,6 @@ public class MainActivity extends Activity */ @Override public void onImageResults(List faces, Frame image, float timeStamp) { - /** - * If the flag indicating that we still need to know the size of the camera frames, call calculateImageDimensions(). - * The flag is a boolean stored in our drawingView object, retrieved through DrawingView.isImageDimensionsNeeded(). - */ - if (drawingView.isImageDimensionsNeeded() ) { - calculateImageDimensions(image); - } - //If the faces object is null, we received an unprocessed frame if (faces == null) { return; @@ -403,6 +421,7 @@ public class MainActivity extends Activity //If faces.size() is 0, we received a frame in which no face was detected if (faces.size() == 0) { + drawingView.updatePoints(null); //the drawingView takes null points to mean it doesn't have to draw anything return; } @@ -448,54 +467,6 @@ public class MainActivity extends Activity metricDisplay.setScore(score); } - /** - * In this method, we inform our drawingView of the size of the incoming camera images. - * We also set the thickness (which controls the size of the dots and bounding box) based on a reference thickness, which - * should be the same whether the device is landscape or portrait. - */ - void calculateImageDimensions(Frame image){ - ///referenceDimension will be used to determine the size of the facial tracking dots - float referenceDimension = activityLayout.getHeight(); - - //get size of frames being passed by camera - int imageWidth = image.getWidth(); - int imageHeight = image.getHeight(); - - /** - * If device is rotated vertically, reverse the width and height returned by the Frame object, - * and switch the dimension we consider to be the reference dimension. - */ - if ((ROTATE.BY_90_CW == image.getTargetRotation()) || (ROTATE.BY_90_CCW == image.getTargetRotation())) { - int temp = imageWidth; - imageWidth = imageHeight; - imageHeight = temp; - - referenceDimension = activityLayout.getWidth(); - } - - drawingView.updateImageDimensions(imageWidth, imageHeight); - drawingView.setThickness((int) (referenceDimension / 160f)); - } - - - /** - * This method is called when the SDK has corrected the aspect ratio of the SurfaceView. We use this information to resize - * our mainLayout ViewGroup so the UI fits snugly around the SurfaceView. We also update our drawingView object, so the tracking dots - * are drawn in the correct coordinates. - */ - @Override - public void onSurfaceViewAspectRatioChanged(int width, int height) { - drawingView.updateSurfaceViewDimensions(width,height); - - RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) mainLayout.getLayoutParams(); - params.height = height; - params.width = width; - mainLayout.setLayoutParams(params); - - //Now that our main layout has been resized, we can remove the progress bar that was obscuring the screen (its purpose was to obscure the resizing of the SurfaceView) - progressBarLayout.setVisibility(View.GONE); - } - /** * FPS measurement simply uses SystemClock to measure how many frames were processed since * the FPS variables were last reset. @@ -522,23 +493,27 @@ public class MainActivity extends Activity public void onPause() { super.onPause(); progressBarLayout.setVisibility(View.VISIBLE); - stopCamera(); + + performFaceDetectionStoppedTasks(); + + stopDetector(); } - private void stopCamera() { - performFaceDetectionStoppedTasks(); + void stopDetector() { + if (detector.isRunning()) { + try { + detector.stop(); + } catch (Exception e) { + Log.e(LOG_TAG,e.getMessage()); + } + } detector.setDetectAllEmotions(false); detector.setDetectAllExpressions(false); - - try { - detector.stop(); - } catch (Exception e) { - Log.e(LOG_TAG, e.getMessage()); - } } + /** * When the user taps the screen, hide the menu if it is visible and show it if it is hidden. * **/ @@ -547,6 +522,7 @@ public class MainActivity extends Activity isMenuVisible = b; if (b) { settingsButton.setVisibility(View.VISIBLE); + cameraButton.setVisibility(View.VISIBLE); //We display the navigation bar again getWindow().getDecorView().setSystemUiVisibility( @@ -567,6 +543,7 @@ public class MainActivity extends Activity settingsButton.setVisibility(View.INVISIBLE); + cameraButton.setVisibility(View.INVISIBLE); } } @@ -614,6 +591,86 @@ public class MainActivity extends Activity public void settings_button_click(View view) { startActivity(new Intent(this,SettingsActivity.class)); } + + @Override + public void onCameraStarted(boolean b, Throwable throwable) { + if (throwable != null) { + Toast.makeText(this,"Failed to start camera.",Toast.LENGTH_LONG).show(); + } + } + + @Override + public void onCameraSizeSelected(int cameraWidth, int cameraHeight, ROTATE rotation) { + if (rotation == ROTATE.BY_90_CCW || rotation == ROTATE.BY_90_CW) { + cameraPreviewWidth = cameraHeight; + cameraPreviewHeight = cameraWidth; + } else { + cameraPreviewWidth = cameraWidth; + cameraPreviewHeight = cameraHeight; + } + drawingView.setThickness((int)(cameraWidth/100f)); + + mainLayout.post(new Runnable() { + @Override + public void run() { + int layoutWidth = mainLayout.getWidth(); + int layoutHeight = mainLayout.getHeight(); + + if (cameraPreviewWidth == 0 || cameraPreviewHeight == 0 || layoutWidth == 0 || layoutHeight == 0) + return; + + float layoutAspectRatio = (float)layoutWidth/layoutHeight; + float cameraPreviewAspectRatio = (float)cameraPreviewWidth/cameraPreviewHeight; + + int newWidth; + int newHeight; + + if (cameraPreviewAspectRatio > layoutAspectRatio) { + newWidth = layoutWidth; + newHeight =(int) (layoutWidth / cameraPreviewAspectRatio); + } else { + newWidth = (int) (layoutHeight * cameraPreviewAspectRatio); + newHeight = layoutHeight; + } + + drawingView.updateViewDimensions(newWidth,newHeight,cameraPreviewWidth,cameraPreviewHeight); + + ViewGroup.LayoutParams params = mainLayout.getLayoutParams(); + params.height = newHeight; + params.width = newWidth; + mainLayout.setLayoutParams(params); + + //Now that our main layout has been resized, we can remove the progress bar that was obscuring the screen (its purpose was to obscure the resizing of the SurfaceView) + progressBarLayout.setVisibility(View.GONE); + } + }); + + } + + + public void camera_button_click(View view) { + if (cameraType == CameraDetector.CameraType.CAMERA_FRONT) { + if (isBackFacingCameraDetected) { + cameraType = CameraDetector.CameraType.CAMERA_BACK; + } else { + Toast.makeText(this,"No back-facing camera found",Toast.LENGTH_LONG).show(); + } + } else if (cameraType == CameraDetector.CameraType.CAMERA_BACK) { + if (isFrontFacingCameraDetected) { + cameraType = CameraDetector.CameraType.CAMERA_FRONT; + } else { + Toast.makeText(this,"No front-facing camera found",Toast.LENGTH_LONG).show(); + } + } + + performFaceDetectionStoppedTasks(); + + try { + detector.setCameraType(cameraType); + } catch (Exception e) { + Log.e(LOG_TAG,e.getMessage()); + } + } } diff --git a/AffdexMe/app/src/main/java/com/affectiva/errorreporting/CustomApplication.java b/AffdexMe/app/src/main/java/com/affectiva/errorreporting/CustomApplication.java new file mode 100644 index 0000000..d1ae05d --- /dev/null +++ b/AffdexMe/app/src/main/java/com/affectiva/errorreporting/CustomApplication.java @@ -0,0 +1,44 @@ +package com.affectiva.errorreporting; + +import android.app.Application; +import android.content.Intent; + +/** + * Created by Alan on 8/21/2015. + */ +public class CustomApplication extends Application { + + static volatile boolean wasErrorActivityStarted = false; + static final boolean enableCustomErrorMessage = false; + Thread.UncaughtExceptionHandler exceptionHandler; + + @Override + public void onCreate () + { + super.onCreate(); + exceptionHandler = Thread.getDefaultUncaughtExceptionHandler(); + // Setup handler for uncaught exceptions. + Thread.setDefaultUncaughtExceptionHandler (new Thread.UncaughtExceptionHandler() + { + @Override + public void uncaughtException (Thread thread, Throwable e) + { + handleUncaughtException (thread, e); + } + }); + } + + public void handleUncaughtException (Thread thread, Throwable e) + { + if (!wasErrorActivityStarted && enableCustomErrorMessage) { + Intent intent = new Intent(); + intent.setAction("com.affectiva.REPORT_ERROR"); // see step 5. + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // required when starting from Application + intent.putExtra("affdexme_error", e); + startActivity(intent); + wasErrorActivityStarted = true; + } + + exceptionHandler.uncaughtException(thread,e); + } +} diff --git a/AffdexMe/app/src/main/java/com/affectiva/errorreporting/ErrorReporter.java b/AffdexMe/app/src/main/java/com/affectiva/errorreporting/ErrorReporter.java new file mode 100644 index 0000000..87e39ec --- /dev/null +++ b/AffdexMe/app/src/main/java/com/affectiva/errorreporting/ErrorReporter.java @@ -0,0 +1,66 @@ +package com.affectiva.errorreporting; + +import android.app.Activity; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Bundle; +import android.view.View; +import android.view.Window; +import android.widget.Button; + +import com.affectiva.affdexme.R; + +import java.text.DateFormat; +import java.util.Date; + +/** + * Created by Alan on 8/21/2015. + */ +public class ErrorReporter extends Activity implements View.OnClickListener { + + String errorMessage; + Button sendButton; + + + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_NO_TITLE); // make a dialog without a titlebar + setContentView(R.layout.error_reporter); + + Throwable error = (Throwable) getIntent().getSerializableExtra("affdexme_error"); + if (error != null) { + + StringBuilder builder = new StringBuilder(); + builder.append("AffdexMe Error Report:"); + builder.append(DateFormat.getDateTimeInstance().format(new Date())); + builder.append("\n"); + builder.append(error.getMessage()); + builder.append(("\n")); + + StackTraceElement[] stackTraceElements = error.getStackTrace(); + for (StackTraceElement element : stackTraceElements) { + builder.append("\n"); + builder.append(element.toString()); + } + errorMessage = builder.toString(); + } else { + errorMessage = "Failed to catch error."; + } + + sendButton = (Button) findViewById(R.id.send_button); + sendButton.setOnClickListener(this); + } + + @Override + public void onClick(View v) { + Intent intent = new Intent (Intent.ACTION_SEND); + intent.setType("plain/text"); + intent.putExtra(Intent.EXTRA_EMAIL, new String[]{"sdk@affectiva.com"}); + intent.putExtra(Intent.EXTRA_SUBJECT, "AffdexMe Crash Report"); + intent.putExtra(Intent.EXTRA_TEXT, errorMessage); // do this so some email clients don't complain about empty body. + startActivity(intent); + finish(); + } +} diff --git a/AffdexMe/app/src/main/res/drawable-hdpi/ic_more_vert_black_48dp.png b/AffdexMe/app/src/main/res/drawable-hdpi/ic_more_vert_black_48dp.png new file mode 100644 index 0000000000000000000000000000000000000000..94d5ab98c6d3b3548536f04a2d635fb5c4a05b88 GIT binary patch literal 205 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY0wn)GsXhawrg*wIhEy=Vy=KqV>>$DV@#Ph3 zmm-(IJxlAmHdKggydBZHA?LrN(f;zraK2yFla}<&ocsFTF&FO*>g+$WJLfsbvhR#o zqU5?6`INL2k&sND<*?!p=%CFlpb5%Rrp)D#!8^w7WtAYxjJ8qaZaq8!Lha3*C xpO9i0)g!jJUB|8aV$u`{=V%64OvRJ&URzP%pP$_?yn*gx@O1TaS?83{1OO~yQ~m$| literal 0 HcmV?d00001 diff --git a/AffdexMe/app/src/main/res/drawable-hdpi/ic_more_vert_white_48dp.png b/AffdexMe/app/src/main/res/drawable-hdpi/ic_more_vert_white_48dp.png new file mode 100644 index 0000000000000000000000000000000000000000..d32281307232fefc363e19d9d2728af62b440a8d GIT binary patch literal 216 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY0wn)GsXhaw=6kw0hGg7(d(E5ckb#KX!!teA z8+IJIbSd_mp~lgrEob_e9cxnR1kZ18I^5b~uj)DJpG>-%nby>)6J_QWKRvd(S?`Ri~_DWys(>8rgyQv zG%aCaz>Js9>y=L)eUVgRxJx3oN6$@sv5NTO?u&4dNh*K$SbOB%sZae;9uIUXgQu&X J%Q~loCIECSTxI|O literal 0 HcmV?d00001 diff --git a/AffdexMe/app/src/main/res/drawable-hdpi/ic_switch_camera_black_48dp.png b/AffdexMe/app/src/main/res/drawable-hdpi/ic_switch_camera_black_48dp.png new file mode 100644 index 0000000000000000000000000000000000000000..1f6be43c30dd19b3c9f6c15a12f297b6799503b5 GIT binary patch literal 365 zcmV-z0h0cSP)yfVcy33%Y(QC3XsdF`#%{!6Bc@{UMz9Yj5#@ zlgy1YSrkPJOxS`#Jz~FCxFKWLyko!I8&_oNl2_~xdtu-o zB}SGUu*^HMVNS=1dY-X@+tK4kayU7+CE+$=6*j|dSp~Gl?N9|Y;bxTlx`^gtqY00000 LNkvXXu0mjf8}g!V literal 0 HcmV?d00001 diff --git a/AffdexMe/app/src/main/res/drawable-hdpi/ic_switch_camera_white_48dp.png b/AffdexMe/app/src/main/res/drawable-hdpi/ic_switch_camera_white_48dp.png new file mode 100644 index 0000000000000000000000000000000000000000..bee95a1d4d77dbb0824cf10f831d6321b343224c GIT binary patch literal 379 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY0wn)GsXoKN!06=Z;uuoF`1Yp17qg+r@sDea z$~VkryxznvkgpKu$ZxP^w%n%r1KZSROx+Qz(ZhOly}HNm?n!%e?tPDaz&tlIZuW(w zdCxbxWO|;M@9@uN@z=w_ycd-3uc-YPAXRc+-O~5YRF=D+9P{4anZgwN`B;3_(*OJ# zMjIY^y!6Y7yb#ND_D0}l=FPPM$^=S_g|6%VwoI2{_d>%srJ7&|&vk-TbVh<5S;l;%8hhyk$Kc9U$ho zN!;a@as7tMKN^!>mP|UmC!qU^(pC>$p{$8fOT;)oR_iUC=UG0_`?VR+LIzJ)KbLh* G2~7aOZ8w1c literal 0 HcmV?d00001 diff --git a/AffdexMe/app/src/main/res/drawable-mdpi/ic_switch_camera_black_48dp.png b/AffdexMe/app/src/main/res/drawable-mdpi/ic_switch_camera_black_48dp.png new file mode 100644 index 0000000000000000000000000000000000000000..3dfc224b1825f9ce641fd9ba08d4f5789c1bac5b GIT binary patch literal 248 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}b0D{hlt4Ar*{or+Kexb`W5AZF10= zrKo9w_MUQ%@3qSI?A(hc=?GnlWdC2*%@Mm*W(HH~t%m|EnpO#2%>Q33kpE{;^tiY0 z$D^)BZvT=)?CEKfCIl~bi}srI@2&QgldJS6F76X6SrN<3D|_W8hm4kju=cTro;8gT z%tlh|0^SY@Mhu(G6b!T(HmND-I8I}0S$g6hW3bb=`jg+zm;7&jX0NrUJ@!w5(uKU* wk6N+JH@8i{Z>|2qTf~HqU;X!8JB}v(Y0a}5)?Db{0`v-lr>mdKI;Vst09*uK0ssI2 literal 0 HcmV?d00001 diff --git a/AffdexMe/app/src/main/res/drawable-mdpi/ic_switch_camera_white_48dp.png b/AffdexMe/app/src/main/res/drawable-mdpi/ic_switch_camera_white_48dp.png new file mode 100644 index 0000000000000000000000000000000000000000..9a5c9661b431cebb89ee596531069f3b2b24cecd GIT binary patch literal 259 zcmV+e0sQ`nP)8wgtTvtEBQMB>ya~5qGqic zu|}}wm-Qa}c!6*3Y<1(y1AKDD>cYpXK%!;D+e}Yd0ulpJ4Wt}siLA?kk;tYTh^_#q z0@OHh0=jV~s25OV6A;t`C}IMF{ufZh1l9=?P~?qK8?b-gAdUxIgdYEp{jcfqA6ooX z5q;T}Kb!*0doNx+kv01+zkgKA>wCw9w~2-d$$|JOKmpDIJOMAh!O8Df!tnqA002ov JPDHLkV1hMCYCZq} literal 0 HcmV?d00001 diff --git a/AffdexMe/app/src/main/res/drawable-mdpi/settings_button_pressed.png b/AffdexMe/app/src/main/res/drawable-mdpi/settings_button_pressed_1.png similarity index 100% rename from AffdexMe/app/src/main/res/drawable-mdpi/settings_button_pressed.png rename to AffdexMe/app/src/main/res/drawable-mdpi/settings_button_pressed_1.png diff --git a/AffdexMe/app/src/main/res/drawable-nodpi/disgust.jpg b/AffdexMe/app/src/main/res/drawable-nodpi/disgust.jpg index 36044595237eecfa05e70354cb92763c9fe6c784..4a6d097cd7d98ac92b16b63d4e93ef6da45b1f6f 100644 GIT binary patch delta 7244 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 I=akR{05Df}#Q*>R literal 0 HcmV?d00001 diff --git a/AffdexMe/app/src/main/res/drawable-xhdpi/ic_more_vert_white_48dp.png b/AffdexMe/app/src/main/res/drawable-xhdpi/ic_more_vert_white_48dp.png new file mode 100644 index 0000000000000000000000000000000000000000..2f2cb3d004f6e828917b5f6b1decc7fe5d99b445 GIT binary patch literal 305 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD0wg^q?%xcget5b#hE&{od&7{g$w9>R;>#mP zB_2gJIy{WxDHIfl;54zZ?(Dj(@bmNJ>M(=pX9D&J05vf@IA0RFIB;_s@2aW4t^P!s z?~T5q@wxa<`?f89Li_gpTlKB*dwsOb9#+$?m6!M>7BklVVk|aEPh1DJ$>V{EJl=XH?V({#-awY-(QL|0?dORlc*XtSl(} z&d3n4k$a}x{MZxU-fiZ-$+m%8aD#C}>V?${Gwe!OwoRt{8X v))3p#FqiR-(={Oe2E;4(u$C!;+}~cs6m#}#(>L)Q#vrbztDnm{r-UW|mL!4d literal 0 HcmV?d00001 diff --git a/AffdexMe/app/src/main/res/drawable-xhdpi/ic_switch_camera_black_48dp.png b/AffdexMe/app/src/main/res/drawable-xhdpi/ic_switch_camera_black_48dp.png new file mode 100644 index 0000000000000000000000000000000000000000..e88bbea85208e8de130ad71e229ca365b2d57a27 GIT binary patch literal 451 zcmV;!0X+VRP)?Mm zKJVOwPg0<5N`aJ8N~ty8b88ay9`C(|cO>a8fA9hO%^MQ*nqT<9{p1zFRs6t5>^mz0 zd%-t+Y* zn*(4>nN1Barp~4W7;|I-gc3|&;y{Awk1YODLKk!Km(#kT3A?4#O?|*)6+PI}qs0Of zKmY**5I_I{{-+n7&Zz+pw6;$T*w9)$ zNh=t+bevdw@mT?j1+z@j=Us=_J>{$sOPQ#=X={$4Gl$>}rnAZ_3NL29Ok36RFI@dc z)5VhMw_XK%X)fK7nzcQm+c4@(^fL1gYTOS@_s_WOAD7Co=Lvi9?{nQ54#&;2ex0%s z`Y=cT%D0*6fep)R7P+d=b77GCJTa{HL16U(UR!lNm-2@vrsYXJm?gVY>P2KPYs}|) zJ#Qm>nctkaRmSt6Z4Ylrs)PSCv(y8fXJk(`aL=@!&UjP!!Fv_~2L?tKfdeyVr88L^ zyTWsxsUSz}zy}_N<7v!1{cILttOhnh4X>J4(2!`5KIH?9F$Pap KKbLh*2~7asbiNV* literal 0 HcmV?d00001 diff --git a/AffdexMe/app/src/main/res/drawable-xhdpi/settings_button_pressed.png b/AffdexMe/app/src/main/res/drawable-xhdpi/settings_button_pressed_1.png similarity index 100% rename from AffdexMe/app/src/main/res/drawable-xhdpi/settings_button_pressed.png rename to AffdexMe/app/src/main/res/drawable-xhdpi/settings_button_pressed_1.png diff --git a/AffdexMe/app/src/main/res/drawable-xxhdpi/ic_more_vert_black_48dp.png b/AffdexMe/app/src/main/res/drawable-xxhdpi/ic_more_vert_black_48dp.png new file mode 100644 index 0000000000000000000000000000000000000000..cf0f8f360e44f733d4317562002c8b171b0040dd GIT binary patch literal 411 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q1xWh(YZ)^zFeZ7rIEGX(zP)v@SEx|p*u(7t z;$gFu8Y5aRIxLWP3X^UK;A>=J;S*3-X7_VRRWq#mG4J{72WR>I+x}q!ngRrWjO90- zFl&#=-+tcg^PXpY-+xY1dwbZl^0dK|+jrR;c{t|JWsh36B++l~v!CT?$MSaT z0uL#yHrZdXb6Vv|*~4cxoz$Ct`|YN4U;f*^0Xlj?;1!K55z{Uy*WN{Lfr}#=fW(Tx zDGww)fRv5{kU)|b>{SA40W00e09KDI&2ruWY6?hn+7h@yU#?>qkqE z*@6118gh%aNPMwV+9n=QG-m<(id_LR`*yulyO74_6RFh&K2Sro}E$p0nVLs;}$ThM(uS7_ll8Oak%lD{m&zRCf<9+8WoVVPCUaw z>3Xl1en5irI#EZ%fF~|}<#HkCWrK@)rt+uhf2j}o1 zl6l|Syi=ZAt~_q5Z0zr8`<|0~>nXF}GDU8uYQ$we&O5&5(k#Q>KAx?)r}UDeHXPJk zd13B`gLBlD95PSjlU~AA)-Ty>xmM`{r*hh-Ss^FmjG9B2U1gbNv}|K;kZZL^^6yaA zTZ=ayUd1GP;YMU=qd60}NOUuQ(hGI3mEz>MZ2UEyCl=qZwe} zVmJjT*xI;><4O1#`Ah$1edj+K$5grU;n7vwt@k8Ob5zLRS#|l|aXUMUFBgl&8g2GG z_$baFP|0%Md5#?OIjsroCyi$y`vu`G{aH4PH!{bE{<4oRyM18sDkk5IoRF*B+uhGR z2sLdin{kp$J@koX+Dys2z)-X*zM*4xUaff7;lhLQ>q?_Goat8j|MMc>=`CrkobP`e pd(Wh&#djn55CEpA($@Z^Qp#6tl;ZwZL`YxYn||a?)7)7N?>@EP z`1{OH*J1llSNXqB!gw8&{a&uNJbbA!@6%kn-^Y3HtpTZ0<6{qX6s+Zd{J zA@ttlRpl|7{4Z2Bs{^Gj6bF6J+3b+aXOVbr(xu%`ru5sS%J63#5!BAgJ}S6a!a?}l z%B{v~v5m{F+}px^p+&4}gWiRX;;0ur533yVGnZa}&CnZp(M#Lyp#%GtD~}5n2uHoL zkje1#K6|Kf+LkM>76Hke9u5zIm`P|t1B;3RBaHdv$f<0WqMiuJIOz$=fsH)tIZteS z;m~%S=}Ck>kYacek?+tpo$pD+dLYH~B;q}g;(8Jxq;S}qrAS9|!o#JEmeWK|e3;ZA z$U{*V^7_g z<3cNcUQ}$ZxSpPC{FK@Lhxbd}nT2PjB!2iBDFqKj6@^};P<3O0gz|!{Mw9=H_Fql> UmdKI;Vst0C5a0=Kufz literal 0 HcmV?d00001 diff --git a/AffdexMe/app/src/main/res/drawable-xxhdpi/settings_button_pressed.png b/AffdexMe/app/src/main/res/drawable-xxhdpi/settings_button_pressed_1.png similarity index 100% rename from AffdexMe/app/src/main/res/drawable-xxhdpi/settings_button_pressed.png rename to AffdexMe/app/src/main/res/drawable-xxhdpi/settings_button_pressed_1.png diff --git a/AffdexMe/app/src/main/res/drawable/camera_button_not_pressed.xml b/AffdexMe/app/src/main/res/drawable/camera_button_not_pressed.xml new file mode 100644 index 0000000..ad2ebaa --- /dev/null +++ b/AffdexMe/app/src/main/res/drawable/camera_button_not_pressed.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/AffdexMe/app/src/main/res/drawable/camera_button_pressed.xml b/AffdexMe/app/src/main/res/drawable/camera_button_pressed.xml new file mode 100644 index 0000000..883b6aa --- /dev/null +++ b/AffdexMe/app/src/main/res/drawable/camera_button_pressed.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/AffdexMe/app/src/main/res/drawable/camera_button_selector.xml b/AffdexMe/app/src/main/res/drawable/camera_button_selector.xml new file mode 100644 index 0000000..669fc9e --- /dev/null +++ b/AffdexMe/app/src/main/res/drawable/camera_button_selector.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/AffdexMe/app/src/main/res/drawable/settings_button_not_pressed.xml b/AffdexMe/app/src/main/res/drawable/settings_button_not_pressed.xml new file mode 100644 index 0000000..739dcec --- /dev/null +++ b/AffdexMe/app/src/main/res/drawable/settings_button_not_pressed.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/AffdexMe/app/src/main/res/drawable/settings_button_pressed.xml b/AffdexMe/app/src/main/res/drawable/settings_button_pressed.xml new file mode 100644 index 0000000..a1922a5 --- /dev/null +++ b/AffdexMe/app/src/main/res/drawable/settings_button_pressed.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/AffdexMe/app/src/main/res/drawable/settings_button_pressed.png b/AffdexMe/app/src/main/res/drawable/settings_button_pressed_1.png similarity index 100% rename from AffdexMe/app/src/main/res/drawable/settings_button_pressed.png rename to AffdexMe/app/src/main/res/drawable/settings_button_pressed_1.png diff --git a/AffdexMe/app/src/main/res/drawable/settings_button_selector.xml b/AffdexMe/app/src/main/res/drawable/settings_button_selector.xml index f6f77f6..f30d2af 100644 --- a/AffdexMe/app/src/main/res/drawable/settings_button_selector.xml +++ b/AffdexMe/app/src/main/res/drawable/settings_button_selector.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/AffdexMe/app/src/main/res/drawable/transluscent_circle.xml b/AffdexMe/app/src/main/res/drawable/transluscent_circle.xml new file mode 100644 index 0000000..11a1121 --- /dev/null +++ b/AffdexMe/app/src/main/res/drawable/transluscent_circle.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/AffdexMe/app/src/main/res/layout/activity_main.xml b/AffdexMe/app/src/main/res/layout/activity_main.xml index 45b3524..8378a3a 100644 --- a/AffdexMe/app/src/main/res/layout/activity_main.xml +++ b/AffdexMe/app/src/main/res/layout/activity_main.xml @@ -1,7 +1,9 @@ - - - - - - + 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"/> + + + + + +