Add sound and proper tick for scenario
This commit is contained in:
parent
1dcb08a932
commit
1503b65439
5 changed files with 261 additions and 65 deletions
|
@ -2,9 +2,12 @@ package com.rubenvandeven.emotionhero;
|
||||||
|
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
|
import android.annotation.TargetApi;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.graphics.Bitmap;
|
import android.media.AudioAttributes;
|
||||||
import android.graphics.Canvas;
|
import android.media.AudioManager;
|
||||||
|
import android.media.SoundPool;
|
||||||
|
import android.os.Build;
|
||||||
import android.support.v4.app.ActivityCompat;
|
import android.support.v4.app.ActivityCompat;
|
||||||
import android.support.v7.app.ActionBar;
|
import android.support.v7.app.ActionBar;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
@ -25,6 +28,7 @@ import com.affectiva.android.affdex.sdk.detector.CameraDetector;
|
||||||
import com.affectiva.android.affdex.sdk.detector.Detector;
|
import com.affectiva.android.affdex.sdk.detector.Detector;
|
||||||
import com.affectiva.android.affdex.sdk.detector.Face;
|
import com.affectiva.android.affdex.sdk.detector.Face;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -123,6 +127,12 @@ public class GamingActivity extends AppCompatActivity implements Detector.ImageL
|
||||||
|
|
||||||
TextView paramText;
|
TextView paramText;
|
||||||
|
|
||||||
|
public SoundPool sound;
|
||||||
|
public HashMap<Integer, Integer> soundIds = new HashMap<>();
|
||||||
|
|
||||||
|
final static int SOUND_SCORE = 1;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
@ -182,6 +192,7 @@ public class GamingActivity extends AppCompatActivity implements Detector.ImageL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setMeasuredDimension(width,height);
|
setMeasuredDimension(width,height);
|
||||||
|
// setMeasuredDimension(1,1); // this DOES increase performance....
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
@ -191,16 +202,15 @@ public class GamingActivity extends AppCompatActivity implements Detector.ImageL
|
||||||
videoLayout.addView(cameraPreview,0);
|
videoLayout.addView(cameraPreview,0);
|
||||||
|
|
||||||
|
|
||||||
currentScenario = new ScenarioAnger();
|
currentScenario = new ScenarioAnger(this);
|
||||||
|
|
||||||
scenarioView = new ScenarioView(this, currentScenario);
|
scenarioView = new ScenarioView(this, currentScenario);
|
||||||
RelativeLayout.LayoutParams scenarioViewParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
|
RelativeLayout.LayoutParams scenarioViewParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
|
||||||
videoLayout.addView(scenarioView, 1, scenarioViewParams);
|
videoLayout.addView(scenarioView, 1, scenarioViewParams);
|
||||||
// new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT)
|
|
||||||
// videoLayout.addView(scenarioView, 200, 100);
|
|
||||||
|
|
||||||
|
createSoundPool(); // instantiate SoundPool in sound
|
||||||
|
soundIds.put(SOUND_SCORE, sound.load(this, R.raw.score2, 1));
|
||||||
|
|
||||||
// startDetector();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -254,14 +264,16 @@ public class GamingActivity extends AppCompatActivity implements Detector.ImageL
|
||||||
if(detector == null)
|
if(detector == null)
|
||||||
{
|
{
|
||||||
// SurfaceView surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
|
// SurfaceView surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
|
||||||
detector = new CameraDetector(this, CameraDetector.CameraType.CAMERA_FRONT, cameraPreview);
|
|
||||||
|
detector = new CameraDetector(this, CameraDetector.CameraType.CAMERA_FRONT, cameraPreview, 1, Detector.FaceDetectorMode.LARGE_FACES);
|
||||||
detector.setLicensePath("emotionhero_dev.license");
|
detector.setLicensePath("emotionhero_dev.license");
|
||||||
|
|
||||||
detector.setMaxProcessRate(10);
|
detector.setMaxProcessRate(10);
|
||||||
detector.setDetectAllEmotions(true);
|
detector.setDetectAllEmotions(true);
|
||||||
detector.setDetectAllAppearances(false);
|
detector.setDetectAllAppearances(false);
|
||||||
detector.setDetectAllEmojis(false);
|
detector.setDetectAllEmojis(false);
|
||||||
detector.setDetectAllExpressions(false);
|
detector.setDetectAllExpressions(false);
|
||||||
detector.setMaxProcessRate(12);
|
detector.setMaxProcessRate(20);
|
||||||
|
|
||||||
detector.setImageListener(this);
|
detector.setImageListener(this);
|
||||||
detector.setOnCameraEventListener(this);
|
detector.setOnCameraEventListener(this);
|
||||||
|
@ -269,7 +281,7 @@ public class GamingActivity extends AppCompatActivity implements Detector.ImageL
|
||||||
}
|
}
|
||||||
|
|
||||||
detector.start();
|
detector.start();
|
||||||
mContentView.setText("STARTING...");
|
setText("STARTING...");
|
||||||
Log.d(LOG_TAG, Boolean.toString(detector.isRunning()));
|
Log.d(LOG_TAG, Boolean.toString(detector.isRunning()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -321,10 +333,12 @@ public class GamingActivity extends AppCompatActivity implements Detector.ImageL
|
||||||
* Detector callback gives the faces found so we can match their scores to the given scenario.
|
* Detector callback gives the faces found so we can match their scores to the given scenario.
|
||||||
*/
|
*/
|
||||||
public void onImageResults(List<Face> list, Frame frame, float timestamp) {
|
public void onImageResults(List<Face> list, Frame frame, float timestamp) {
|
||||||
|
// frame.getOriginalBitmapFrame()
|
||||||
// Log.e(LOG_TAG, "RESULT! faces: " + Integer.toString(list.size()) + " t: " + Float.toString(timestamp) + "s" );
|
// Log.e(LOG_TAG, "RESULT! faces: " + Integer.toString(list.size()) + " t: " + Float.toString(timestamp) + "s" );
|
||||||
if(!currentScenario.isWithinTime(timestamp))
|
// if(!currentScenario.isWithinTime(timestamp))
|
||||||
|
if(currentScenario.isFinished())
|
||||||
{
|
{
|
||||||
mContentView.setText(String.format("LEVEL ENDED\nScore: %.2f", currentScenario.getTotalScore()));
|
setText(String.format("LEVEL ENDED\nScore: %.2f", currentScenario.getTotalScore()));
|
||||||
stopDetector();
|
stopDetector();
|
||||||
restartButton.setVisibility(View.VISIBLE);
|
restartButton.setVisibility(View.VISIBLE);
|
||||||
return;
|
return;
|
||||||
|
@ -332,12 +346,12 @@ public class GamingActivity extends AppCompatActivity implements Detector.ImageL
|
||||||
if (list == null)
|
if (list == null)
|
||||||
return;
|
return;
|
||||||
if (list.size() == 0) {
|
if (list.size() == 0) {
|
||||||
mContentView.setText("NO FACE FOUND");
|
// mContentView.setText("NO FACE FOUND"); // this happens in onFaceDetectionStopped
|
||||||
} else {
|
} else {
|
||||||
|
hideText(); // hide textView as we want as few elements as possible.
|
||||||
Face face = list.get(0);
|
Face face = list.get(0);
|
||||||
currentScenario.validateFaceOnTime(face, timestamp);
|
currentScenario.setCurrentAttributeScoresForFace(face);
|
||||||
scenarioView.setCurrentAttributeScoresForFace(face);
|
scenarioView.setCurrentAttributeScoresForFace(face);
|
||||||
mContentView.setText(String.format("SCORE \n%.2f",currentScenario.getTotalScore()));
|
|
||||||
|
|
||||||
// String paramString = "";
|
// String paramString = "";
|
||||||
// paramString += "Anger " + String.format("%02.2f", face.emotions.getAnger()) + "%\n";
|
// paramString += "Anger " + String.format("%02.2f", face.emotions.getAnger()) + "%\n";
|
||||||
|
@ -394,14 +408,53 @@ public class GamingActivity extends AppCompatActivity implements Detector.ImageL
|
||||||
@Override
|
@Override
|
||||||
public void onFaceDetectionStarted()
|
public void onFaceDetectionStarted()
|
||||||
{
|
{
|
||||||
mContentView.setText("START!");
|
setText("START!");
|
||||||
currentScenario.start();
|
currentScenario.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFaceDetectionStopped()
|
public void onFaceDetectionStopped()
|
||||||
{
|
{
|
||||||
mContentView.setText("No face found...");
|
setText("No face found...");
|
||||||
// paramText.setText("No face found");
|
currentScenario.pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setText(String text)
|
||||||
|
{
|
||||||
|
mContentView.setVisibility(View.VISIBLE);
|
||||||
|
mContentView.setText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void hideText()
|
||||||
|
{
|
||||||
|
mContentView.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// http://stackoverflow.com/a/27552576
|
||||||
|
protected void createSoundPool() {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
createNewSoundPool();
|
||||||
|
} else {
|
||||||
|
createOldSoundPool();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// http://stackoverflow.com/a/27552576
|
||||||
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||||
|
protected void createNewSoundPool(){
|
||||||
|
AudioAttributes attributes = new AudioAttributes.Builder()
|
||||||
|
.setUsage(AudioAttributes.USAGE_GAME)
|
||||||
|
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
|
||||||
|
.build();
|
||||||
|
sound = new SoundPool.Builder()
|
||||||
|
.setAudioAttributes(attributes)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
// http://stackoverflow.com/a/27552576
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
protected void createOldSoundPool(){
|
||||||
|
sound = new SoundPool(5, AudioManager.STREAM_MUSIC,0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,12 @@
|
||||||
package com.rubenvandeven.emotionhero;
|
package com.rubenvandeven.emotionhero;
|
||||||
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.graphics.Canvas;
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.graphics.Paint;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.affectiva.android.affdex.sdk.detector.Face;
|
import com.affectiva.android.affdex.sdk.detector.Face;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Timer;
|
||||||
|
import java.util.TimerTask;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by ruben on 16/08/16.
|
* Created by ruben on 16/08/16.
|
||||||
|
@ -18,10 +14,36 @@ import java.util.Map;
|
||||||
|
|
||||||
abstract public class Scenario {
|
abstract public class Scenario {
|
||||||
|
|
||||||
|
static int DESIRED_FPS = 25;
|
||||||
|
|
||||||
float duration = 0;
|
float duration = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
long startTime = 0;
|
long startTime = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The timer that provides the tick
|
||||||
|
*/
|
||||||
|
Timer timer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increment on each tick
|
||||||
|
*/
|
||||||
|
float runningTime = -1;
|
||||||
|
|
||||||
|
|
||||||
|
boolean isRunning = false;
|
||||||
|
|
||||||
|
private GamingActivity _activity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The scorres in this moment, as to draw them on the screen.
|
||||||
|
* Indexes are Emotion ordinals
|
||||||
|
*/
|
||||||
|
private Map<Emotion, Float> currentAttributeScores = new HashMap<>();
|
||||||
|
|
||||||
ArrayList<Target> targets = new ArrayList<>();
|
ArrayList<Target> targets = new ArrayList<>();
|
||||||
|
|
||||||
abstract void createScenario();
|
abstract void createScenario();
|
||||||
|
@ -30,6 +52,7 @@ abstract public class Scenario {
|
||||||
public Emotion emotion;
|
public Emotion emotion;
|
||||||
public float value;
|
public float value;
|
||||||
public float timestamp;
|
public float timestamp;
|
||||||
|
public Score score;
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayList<Score> scores = new ArrayList<>();
|
ArrayList<Score> scores = new ArrayList<>();
|
||||||
|
@ -40,20 +63,68 @@ abstract public class Scenario {
|
||||||
* Extra bonus given
|
* Extra bonus given
|
||||||
*/
|
*/
|
||||||
public boolean isSpotOn = false;
|
public boolean isSpotOn = false;
|
||||||
/**
|
|
||||||
* The target the score is awarded for
|
|
||||||
*/
|
|
||||||
public Target target;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*/
|
*/
|
||||||
public Scenario()
|
public Scenario(GamingActivity activity)
|
||||||
{
|
{
|
||||||
createScenario();
|
createScenario();
|
||||||
|
timer = new Timer("ScenarioTimer");
|
||||||
|
TimerTask tickTask;
|
||||||
|
tickTask = new TimerTask() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
// if (System.currentTimeMillis() - scheduledExecutionTime() >=
|
||||||
|
// MAX_TARDINESS)
|
||||||
|
// return; // Too late; skip this execution.
|
||||||
|
tick();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
timer.schedule(tickTask, 0, 1000/DESIRED_FPS);
|
||||||
|
_activity = activity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To be called on each timer tick
|
||||||
|
*/
|
||||||
|
public void tick()
|
||||||
|
{
|
||||||
|
if(!isRunning)
|
||||||
|
return;
|
||||||
|
|
||||||
|
runningTime += 1.0f/DESIRED_FPS;
|
||||||
|
|
||||||
|
if(isFinished()) {
|
||||||
|
stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = targets.size() - 1; i >= 0; i--) {
|
||||||
|
Target target = targets.get(i);
|
||||||
|
// skip targets that are already scored
|
||||||
|
if(target.score != null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(target.timestamp <= runningTime) {
|
||||||
|
float scored_value = currentAttributeScores.get(target.emotion);
|
||||||
|
float required_value = target.value;
|
||||||
|
Score score = new Score();
|
||||||
|
score.value = Math.round(100 - Math.abs(scored_value-required_value));
|
||||||
|
scores.add(score);
|
||||||
|
|
||||||
|
//
|
||||||
|
_activity.sound.play(_activity.soundIds.get(_activity.SOUND_SCORE), 1, 1,
|
||||||
|
1,0, score.value / 200f + 0.5f ); // play back the sound slower
|
||||||
|
// depending on score value
|
||||||
|
target.score = score;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a target on given timestamp
|
* Add a target on given timestamp
|
||||||
* @param emotion
|
* @param emotion
|
||||||
|
@ -63,9 +134,9 @@ abstract public class Scenario {
|
||||||
public void setTarget(Emotion emotion, float value, float timestamp)
|
public void setTarget(Emotion emotion, float value, float timestamp)
|
||||||
{
|
{
|
||||||
// Log.e(GamingActivity.LOG_TAG, "Set target:" + Float.toString(timestamp) + " " + Float.toString(duration));
|
// Log.e(GamingActivity.LOG_TAG, "Set target:" + Float.toString(timestamp) + " " + Float.toString(duration));
|
||||||
if(timestamp > duration)
|
if((timestamp + 1) > duration)
|
||||||
{
|
{
|
||||||
duration = timestamp;
|
duration = timestamp + 1; // always a bit onger than last target, as it otherwise the game does not finish pretty
|
||||||
}
|
}
|
||||||
Target target = new Target();
|
Target target = new Target();
|
||||||
target.timestamp = timestamp;
|
target.timestamp = timestamp;
|
||||||
|
@ -91,6 +162,11 @@ abstract public class Scenario {
|
||||||
setTarget(emotion, value, timestamp);
|
setTarget(emotion, value, timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use @see tick()
|
||||||
|
* @param face
|
||||||
|
* @param timestamp
|
||||||
|
*/
|
||||||
public void validateFaceOnTime(Face face, float timestamp)
|
public void validateFaceOnTime(Face face, float timestamp)
|
||||||
{
|
{
|
||||||
// TODO: interpolation of time
|
// TODO: interpolation of time
|
||||||
|
@ -101,7 +177,7 @@ abstract public class Scenario {
|
||||||
float required_value = target.value;
|
float required_value = target.value;
|
||||||
Score score = new Score();
|
Score score = new Score();
|
||||||
score.value = 100 - Math.abs(scored_value-required_value);
|
score.value = 100 - Math.abs(scored_value-required_value);
|
||||||
score.target = target;
|
target.score = score;
|
||||||
scores.add(score);
|
scores.add(score);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -135,21 +211,46 @@ abstract public class Scenario {
|
||||||
* Get the time within the scenario (so since start() has been called)
|
* Get the time within the scenario (so since start() has been called)
|
||||||
*/
|
*/
|
||||||
public float getTime() {
|
public float getTime() {
|
||||||
|
return runningTime;
|
||||||
// if not started, don't move the labels, if started, move them by diff_y
|
// if not started, don't move the labels, if started, move them by diff_y
|
||||||
if(startTime == 0) {
|
// if(startTime == 0) {
|
||||||
return 0;
|
// return 0;
|
||||||
} else {
|
// } else {
|
||||||
float diff_t = ((System.currentTimeMillis() - startTime)) / (float) 1000;
|
// float diff_t = ((System.currentTimeMillis() - startTime)) / (float) 1000;
|
||||||
if(diff_t > duration) { // never larger than scenario duration
|
// if(diff_t > duration) { // never larger than scenario duration
|
||||||
return duration;
|
// return duration;
|
||||||
}
|
// }
|
||||||
return diff_t;
|
// return diff_t;
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start()
|
public void start()
|
||||||
{
|
{
|
||||||
startTime = System.currentTimeMillis();
|
startTime = System.currentTimeMillis();
|
||||||
|
isRunning = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pause() {
|
||||||
|
isRunning = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop()
|
||||||
|
{
|
||||||
|
isRunning = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: create AttributeScoreCollection class, with this method.
|
||||||
|
public void setCurrentAttributeScoresForFace(Face face)
|
||||||
|
{
|
||||||
|
for(Emotion emotion: Emotion.values()) {
|
||||||
|
currentAttributeScores.put(emotion, emotion.getValueFromFace(face));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFinished()
|
||||||
|
{
|
||||||
|
return runningTime > duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: create a 'tick' that checks all current values with requirements and increases the timer etc
|
// TODO: create a 'tick' that checks all current values with requirements and increases the timer etc
|
||||||
|
|
|
@ -7,6 +7,11 @@ import android.util.Log;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class ScenarioAnger extends Scenario {
|
public class ScenarioAnger extends Scenario {
|
||||||
|
|
||||||
|
public ScenarioAnger(GamingActivity activity) {
|
||||||
|
super(activity);
|
||||||
|
}
|
||||||
|
|
||||||
void createScenario()
|
void createScenario()
|
||||||
{
|
{
|
||||||
Log.d(GamingActivity.LOG_TAG, "CREATE SCENARIO: anger");
|
Log.d(GamingActivity.LOG_TAG, "CREATE SCENARIO: anger");
|
||||||
|
|
|
@ -6,8 +6,7 @@ import android.graphics.Color;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.graphics.Path;
|
import android.graphics.Path;
|
||||||
import android.graphics.PixelFormat;
|
import android.graphics.PixelFormat;
|
||||||
import android.util.AttributeSet;
|
import android.graphics.Typeface;
|
||||||
import android.util.Log;
|
|
||||||
import android.view.SurfaceHolder;
|
import android.view.SurfaceHolder;
|
||||||
import android.view.SurfaceView;
|
import android.view.SurfaceView;
|
||||||
|
|
||||||
|
@ -32,6 +31,14 @@ public class ScenarioView extends SurfaceView implements SurfaceHolder.Callback
|
||||||
*/
|
*/
|
||||||
private Map<Emotion, Float> currentAttributeScores = new HashMap<>();
|
private Map<Emotion, Float> currentAttributeScores = new HashMap<>();
|
||||||
|
|
||||||
|
//avoid object instantiation on each onDraw
|
||||||
|
private Map<Emotion, Paint> emoPaints = new HashMap<>();
|
||||||
|
private Map<Emotion, Paint> emoOutlinePaints = new HashMap<>();
|
||||||
|
private Map<Emotion, Paint> emoScoredPaints = new HashMap<>();
|
||||||
|
private Paint mainPaint = new Paint();
|
||||||
|
private Paint attrScorePaint = new Paint();
|
||||||
|
private Paint linePaint = new Paint();
|
||||||
|
|
||||||
|
|
||||||
// see: http://blog.danielnadeau.io/2012/01/android-canvas-beginners-tutorial.html
|
// see: http://blog.danielnadeau.io/2012/01/android-canvas-beginners-tutorial.html
|
||||||
class PanelThread extends Thread {
|
class PanelThread extends Thread {
|
||||||
|
@ -58,6 +65,7 @@ public class ScenarioView extends SurfaceView implements SurfaceHolder.Callback
|
||||||
c = null; //set to false and loop ends, stopping thread
|
c = null; //set to false and loop ends, stopping thread
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// c = _surfaceHolder.lockCanvas(null);
|
||||||
c = _surfaceHolder.lockCanvas(null);
|
c = _surfaceHolder.lockCanvas(null);
|
||||||
synchronized (_surfaceHolder) {
|
synchronized (_surfaceHolder) {
|
||||||
//Insert methods to modify positions of items in onDraw()
|
//Insert methods to modify positions of items in onDraw()
|
||||||
|
@ -76,6 +84,43 @@ public class ScenarioView extends SurfaceView implements SurfaceHolder.Callback
|
||||||
super(context);
|
super(context);
|
||||||
getHolder().addCallback(this);
|
getHolder().addCallback(this);
|
||||||
_scenario = s;
|
_scenario = s;
|
||||||
|
|
||||||
|
//setup paints for drawing
|
||||||
|
mainPaint.setColor(Color.GRAY);
|
||||||
|
mainPaint.setTextSize(40);
|
||||||
|
mainPaint.setTypeface(Typeface.SANS_SERIF);
|
||||||
|
|
||||||
|
linePaint.setColor(Color.GRAY);
|
||||||
|
linePaint.setStrokeWidth(5);
|
||||||
|
attrScorePaint.setColor(Color.DKGRAY);
|
||||||
|
|
||||||
|
for(Emotion emotion: Emotion.values()) {
|
||||||
|
Paint emoPaint = new Paint();
|
||||||
|
emoPaint.setTextSize(20);
|
||||||
|
emoPaint.setColor(emotion.getColor());
|
||||||
|
emoPaints.put(emotion, emoPaint);
|
||||||
|
|
||||||
|
Paint emoPaintOutline = new Paint();
|
||||||
|
emoPaintOutline.setColor(emotion.getColor());
|
||||||
|
emoPaintOutline.setStyle(Paint.Style.STROKE);
|
||||||
|
emoPaintOutline.setStrokeWidth(2);
|
||||||
|
emoOutlinePaints.put(emotion, emoPaintOutline);
|
||||||
|
|
||||||
|
Paint emoScoredPaint = new Paint();
|
||||||
|
|
||||||
|
float darkenFactor = 0.4f;
|
||||||
|
int red = (int) ((Color.red(emotion.getColor()) * darkenFactor));
|
||||||
|
int green = (int) ((Color.green(emotion.getColor()) * darkenFactor));
|
||||||
|
int blue = (int) ((Color.blue(emotion.getColor()) * darkenFactor));
|
||||||
|
int emoLightenedColor = Color.rgb(red, green, blue);
|
||||||
|
|
||||||
|
emoScoredPaint.setColor(emoLightenedColor);
|
||||||
|
emoScoredPaint.setTextSize(20);
|
||||||
|
emoScoredPaint.setStrokeWidth(2);
|
||||||
|
emoScoredPaint.setStyle(Paint.Style.FILL_AND_STROKE);
|
||||||
|
emoScoredPaints.put(emotion, emoScoredPaint);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -96,15 +141,6 @@ public class ScenarioView extends SurfaceView implements SurfaceHolder.Callback
|
||||||
float step_y = (canvas.getWidth() * used_width) / Emotion.values().length;
|
float step_y = (canvas.getWidth() * used_width) / Emotion.values().length;
|
||||||
float max_ball_radius = step_y * (float) 0.8 / 2;
|
float max_ball_radius = step_y * (float) 0.8 / 2;
|
||||||
|
|
||||||
|
|
||||||
// bottom at 80%;
|
|
||||||
Paint mainPaint = new Paint();
|
|
||||||
mainPaint.setColor(Color.GRAY);
|
|
||||||
|
|
||||||
|
|
||||||
Paint linePaint = new Paint();
|
|
||||||
linePaint.setColor(Color.GRAY);
|
|
||||||
linePaint.setStrokeWidth(5);
|
|
||||||
// canvas.drawLine(0, bottomline_height, width, bottomline_height, linePaint);
|
// canvas.drawLine(0, bottomline_height, width, bottomline_height, linePaint);
|
||||||
|
|
||||||
|
|
||||||
|
@ -117,22 +153,16 @@ public class ScenarioView extends SurfaceView implements SurfaceHolder.Callback
|
||||||
if(value < 5) {
|
if(value < 5) {
|
||||||
value = 5;
|
value = 5;
|
||||||
}
|
}
|
||||||
Paint emoPaint = new Paint();
|
|
||||||
Paint emoPaintOutline = new Paint();
|
|
||||||
emoPaint.setTextSize(20);
|
|
||||||
emoPaint.setColor(emotion.getColor());
|
|
||||||
emoPaintOutline.setColor(emotion.getColor());
|
|
||||||
emoPaintOutline.setStyle(Paint.Style.STROKE);
|
|
||||||
emoPaintOutline.setStrokeWidth(2);
|
|
||||||
|
|
||||||
float cx = padding_left + (step_y * emotion.ordinal() + step_y / 2);
|
float cx = padding_left + (step_y * emotion.ordinal() + step_y / 2);
|
||||||
float cy = bottomline_height;
|
float cy = bottomline_height;
|
||||||
|
|
||||||
|
|
||||||
canvas.drawCircle(cx, cy, max_ball_radius, mainPaint);
|
canvas.drawCircle(cx, cy, max_ball_radius, mainPaint);
|
||||||
canvas.drawCircle(cx, cy, max_ball_radius, emoPaintOutline);
|
canvas.drawCircle(cx, cy, max_ball_radius, emoOutlinePaints.get(emotion));
|
||||||
|
|
||||||
canvas.drawCircle(cx, cy, max_ball_radius * value/100, emoPaint);
|
// canvas.drawCircle(cx, cy, max_ball_radius * value/100, emoPaints.get(emotion.ordinal()));
|
||||||
|
canvas.drawCircle(cx, cy, max_ball_radius * value/100, attrScorePaint);
|
||||||
|
|
||||||
Path emoNamePath = new Path();
|
Path emoNamePath = new Path();
|
||||||
emoNamePath.moveTo(cx, cy + max_ball_radius * 1.5f);
|
emoNamePath.moveTo(cx, cy + max_ball_radius * 1.5f);
|
||||||
|
@ -141,7 +171,7 @@ public class ScenarioView extends SurfaceView implements SurfaceHolder.Callback
|
||||||
emoNamePath.rLineTo(1000,1000);
|
emoNamePath.rLineTo(1000,1000);
|
||||||
|
|
||||||
// canvas.drawText(emotion.toString(), cx, cy + max_ball_radius * (float) 1.3, emoPaint);
|
// canvas.drawText(emotion.toString(), cx, cy + max_ball_radius * (float) 1.3, emoPaint);
|
||||||
canvas.drawTextOnPath(emotion.toString(), emoNamePath, 0, 0, emoPaint);
|
canvas.drawTextOnPath(emotion.toString(), emoNamePath, 0, 0, emoPaints.get(emotion));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw targets:
|
// Draw targets:
|
||||||
|
@ -150,21 +180,28 @@ public class ScenarioView extends SurfaceView implements SurfaceHolder.Callback
|
||||||
float diff_y = sec_height * _scenario.getTime();
|
float diff_y = sec_height * _scenario.getTime();
|
||||||
|
|
||||||
for(Scenario.Target target: _scenario.getTargets()) {
|
for(Scenario.Target target: _scenario.getTargets()) {
|
||||||
Paint emoPaint = new Paint();
|
Paint targetPaint = (target.score == null) ? emoPaints.get(target.emotion) : emoScoredPaints.get(target.emotion);
|
||||||
emoPaint.setColor(target.emotion.getColor());
|
|
||||||
|
|
||||||
float cy = bottomline_height - (target.timestamp * sec_height) + diff_y;
|
float cy = bottomline_height - (target.timestamp * sec_height) + diff_y;
|
||||||
|
|
||||||
float cx = padding_left + (step_y * target.emotion.ordinal() + step_y / 2);
|
float cx = padding_left + (step_y * target.emotion.ordinal() + step_y / 2);
|
||||||
|
|
||||||
canvas.drawCircle(cx, cy, max_ball_radius * target.value/100, emoPaint);
|
canvas.drawCircle(cx, cy, max_ball_radius * target.value/100, targetPaint);
|
||||||
|
|
||||||
|
if(target.score != null) {
|
||||||
|
canvas.drawText(Float.toString(target.score.value), cx, cy, targetPaint);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// String target_text = target.emotion.toString() + " " + target.value + "%";
|
// String target_text = target.emotion.toString() + " " + target.value + "%";
|
||||||
// canvas.drawText(target_text, cx, y_pos + diff_y , emoPaint);
|
// canvas.drawText(target_text, cx, y_pos + diff_y , emoPaint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
canvas.drawText("Total: " + Float.toString(_scenario.getTotalScore()), 50, 50, mainPaint);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: create AttributeScoreCollection class, with this method.
|
||||||
public void setCurrentAttributeScoresForFace(Face face)
|
public void setCurrentAttributeScoresForFace(Face face)
|
||||||
{
|
{
|
||||||
for(Emotion emotion: Emotion.values()) {
|
for(Emotion emotion: Emotion.values()) {
|
||||||
|
|
BIN
app/src/main/res/raw/score2.mp3
Normal file
BIN
app/src/main/res/raw/score2.mp3
Normal file
Binary file not shown.
Loading…
Reference in a new issue