/*
*
* Copyright (c) Microsoft. All rights reserved.
* Licensed under the MIT license.
*
* Project Oxford: http://ProjectOxford.ai
*
* Project Oxford Mimicker Alarm Github:
* https://github.com/Microsoft/ProjectOxford-Apps-MimickerAlarm
*
* Copyright (c) Microsoft Corporation
* All rights reserved.
*
* MIT License:
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
package com.microsoft.mimickeralarm.mimics;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.hardware.Camera;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.microsoft.mimickeralarm.R;
import com.microsoft.mimickeralarm.utilities.Loggable;
import com.microsoft.mimickeralarm.utilities.Logger;
import com.microsoft.mimickeralarm.utilities.KeyUtilities;
import com.microsoft.projectoxford.emotion.EmotionServiceRestClient;
import com.microsoft.projectoxford.emotion.contract.RecognizeResult;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Random;
/**
* Implements the logic and UI of the Express yourself game
*
* on start, choose a random emotion (defined in emotion_questions.xml)
* after capturing an image from the front camera, send it to Project Oxford Emotion API which
* return a list of detected faces and list of emotions and their probabilities.
*
* The game predefined an acceptance rating for each emotion. If the returned emotion has a probability
* higher than that acceptance rating then the game passes.
*/
public class MimicExpressYourselfFragment extends MimicWithCameraFragment {
private double mEmotionAcceptance = 0.5;
private EmotionServiceRestClient mEmotionServiceRestClient;
private String mEmotion;
@SuppressWarnings("deprecation")
public MimicExpressYourselfFragment() {
CameraFacing = Camera.CameraInfo.CAMERA_FACING_FRONT;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = super.onCreateView(inflater, container, savedInstanceState);
Resources resources = getResources();
String subscriptionKey = KeyUtilities.getToken(getActivity(), "emotion");
mEmotionServiceRestClient = new EmotionServiceRestClient(subscriptionKey);
String[] emotions = resources.getStringArray(R.array.emotions);
int randomNumber = new Random().nextInt(emotions.length);
mEmotion = emotions[randomNumber];
TextView instruction = (TextView) view.findViewById(R.id.instruction_text);
int adjectiveId = resources.getIdentifier("emotion_" + mEmotion, "string", getActivity().getPackageName());
String adjective = resources.getString(adjectiveId);
instruction.setText(String.format(resources.getString(R.string.mimic_emotion_prompt), adjective));
TypedArray acceptances = resources.obtainTypedArray(R.array.emotion_acceptance);
mEmotionAcceptance = acceptances.getFloat(randomNumber, 0.5f);
acceptances.recycle();
Logger.init(getActivity());
Loggable.UserAction userAction = new Loggable.UserAction(Loggable.Key.ACTION_GAME_EMOTION);
Logger.track(userAction);
return view;
}
@Override
public GameResult verify(Bitmap bitmap) {
GameResult gameResult = new GameResult();
gameResult.question = ((TextView) getView().findViewById(R.id.instruction_text)).getText().toString();
try{
ByteArrayOutputStream output = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, output);
ByteArrayInputStream inputStream = new ByteArrayInputStream(output.toByteArray());
Loggable.AppAction appAction = new Loggable.AppAction(Loggable.Key.APP_API_EMOTION);
Logger.trackDurationStart(appAction);
List<RecognizeResult> result = mEmotionServiceRestClient.recognizeImage(inputStream);
Logger.track(appAction);
String dominantEmotion = null;
double dominantEmotionScore = 0;
for (RecognizeResult r : result) {
for (Field field : r.scores.getClass().getFields()) {
double score = (double)field.get(r.scores);
if (field.getName().equalsIgnoreCase(mEmotion)) {
if (score > mEmotionAcceptance) {
gameResult.success = true;
break;
}
}
if (score > dominantEmotionScore) {
dominantEmotion = field.getName();
dominantEmotionScore = score;
}
}
}
Loggable.UserAction userAction = new Loggable.UserAction(Loggable.Key.ACTION_GAME_EMOTION_SUCCESS);
userAction.putProp(Loggable.Key.PROP_QUESTION, mEmotion);
userAction.putEmotions(result);
if (gameResult.success)
{
Logger.track(userAction);
}
else {
userAction.Name = Loggable.Key.ACTION_GAME_EMOTION_FAIL;
Logger.track(userAction);
if (dominantEmotion != null) {
Resources resources = getResources();
int adjectiveId = resources.getIdentifier("emotion_" + dominantEmotion, "string", getActivity().getPackageName());
String adjective = resources.getString(adjectiveId);
if (dominantEmotion.equalsIgnoreCase(mEmotion)) {
gameResult.message = String.format(resources.getString(R.string.mimic_emotion_failure_not_enough), adjective);
}
else {
gameResult.message = String.format(resources.getString(R.string.mimic_emotion_failure), adjective);
}
}
}
}
catch(Exception ex) {
Logger.trackException(ex);
gameResult.success = false;
}
return gameResult;
}
@Override
protected void gameFailure(GameResult gameResult, boolean allowRetry) {
if (!allowRetry){
Loggable.UserAction userAction = new Loggable.UserAction(Loggable.Key.ACTION_GAME_EMOTION_TIMEOUT);
userAction.putProp(Loggable.Key.PROP_QUESTION, mEmotion);
Logger.track(userAction);
}
super.gameFailure(gameResult, allowRetry);
}
}