com.microsoft.mimickeralarm.mimics.MimicColorCaptureFragment.java Source code

Java tutorial

Introduction

Here is the source code for com.microsoft.mimickeralarm.mimics.MimicColorCaptureFragment.java

Source

/*
 *
 * 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.support.v4.graphics.ColorUtils;
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.vision.VisionServiceRestClient;
import com.microsoft.projectoxford.vision.contract.AnalyzeResult;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.Random;

/**
 * The logic and UI implementation of the Color capture mimic game
 *
 * See MimicWithCameraFragment for details of the camera interacts with the UI
 *
 * In vision_question.xml we defined a bunch of color and their acceptable HSL value ranges
 * see https://en.wikipedia.org/wiki/HSL_and_HSV for HSL color space
 *
 * On creation a random color is selected.
 * when an image is capture it is sent to the ProjectOxford Vision API which returns the main colors
 * and accent colors. Accent colors are defined as HEX codes of RGB values which we turn to HSL and
 * compare to see if it's in range of the color we specified.
 *
 */
public class MimicColorCaptureFragment extends MimicWithCameraFragment {
    private VisionServiceRestClient mVisionServiceRestClient;
    private String mQuestionColorName;
    private float[] mQuestionColorRangeLower;
    private float[] mQuestionColorRangeUpper;

    @SuppressWarnings("deprecation")
    public MimicColorCaptureFragment() {
        CameraFacing = Camera.CameraInfo.CAMERA_FACING_BACK;
    }

    @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(), "vision");
        mVisionServiceRestClient = new VisionServiceRestClient(subscriptionKey);

        String[] questions = resources.getStringArray(R.array.vision_color_questions);
        TextView instruction = (TextView) view.findViewById(R.id.instruction_text);
        mQuestionColorName = questions[new Random().nextInt(questions.length)];
        instruction.setText(String.format(resources.getString(R.string.mimic_vision_prompt), mQuestionColorName));

        TypedArray colorCodeLower = resources.obtainTypedArray(resources
                .getIdentifier(mQuestionColorName + "_range_lower", "array", getActivity().getPackageName()));
        mQuestionColorRangeLower = new float[] { colorCodeLower.getFloat(0, 0f), colorCodeLower.getFloat(1, 0f),
                colorCodeLower.getFloat(2, 0f) };
        colorCodeLower.recycle();
        TypedArray colorCodeUpper = resources.obtainTypedArray(resources
                .getIdentifier(mQuestionColorName + "_range_upper", "array", getActivity().getPackageName()));
        mQuestionColorRangeUpper = new float[] { colorCodeUpper.getFloat(0, 0f), colorCodeUpper.getFloat(1, 0f),
                colorCodeUpper.getFloat(2, 0f) };
        colorCodeUpper.recycle();

        Logger.init(getActivity());
        Loggable playGameEvent = new Loggable.UserAction(Loggable.Key.ACTION_GAME_COLOR);
        Logger.track(playGameEvent);

        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());
            String[] features = { "Color" };
            Loggable.AppAction appAction = new Loggable.AppAction(Loggable.Key.APP_API_VISION);
            Logger.trackDurationStart(appAction);
            AnalyzeResult result = mVisionServiceRestClient.analyzeImage(inputStream, features);
            Logger.track(appAction);

            float[] accentHsl = new float[3];
            int[] accentRgb = hexStringToRgb(result.color.accentColor);
            ColorUtils.RGBToHSL(accentRgb[0], accentRgb[1], accentRgb[2], accentHsl);
            boolean colorInRange = isColorInRange(mQuestionColorRangeLower, mQuestionColorRangeUpper, accentHsl);
            Loggable.UserAction userAction = new Loggable.UserAction(Loggable.Key.ACTION_GAME_COLOR_SUCCESS);
            userAction.putProp(Loggable.Key.PROP_QUESTION, mQuestionColorName);
            userAction.putVision(result);

            //TODO: this will not work for languages other than English.
            if (colorInRange || result.color.dominantColorForeground.toLowerCase().equals(mQuestionColorName)
                    || result.color.dominantColorBackground.toLowerCase().equals(mQuestionColorName)) {
                gameResult.success = true;
            }

            for (String color : result.color.dominantColors) {
                if (color.toLowerCase().equals(mQuestionColorName)) {
                    gameResult.success = true;
                    break;
                }
            }

            if (!gameResult.success) {
                userAction.Name = Loggable.Key.ACTION_GAME_COLOR_FAIL;
            }

            Logger.track(userAction);
        } catch (Exception ex) {
            Logger.trackException(ex);
        }

        return gameResult;
    }

    @Override
    protected void gameFailure(GameResult gameResult, boolean allowRetry) {
        if (!allowRetry) {
            Loggable.UserAction userAction = new Loggable.UserAction(Loggable.Key.ACTION_GAME_COLOR_TIMEOUT);
            userAction.putProp(Loggable.Key.PROP_QUESTION, mQuestionColorName);
            Logger.track(userAction);
        }
        super.gameFailure(gameResult, allowRetry);
    }

    private int[] hexStringToRgb(String hex) {
        int color = (int) Long.parseLong(hex, 16);
        int r = (color >> 16) & 0xFF;
        int g = (color >> 8) & 0xFF;
        int b = (color) & 0xFF;
        return new int[] { r, g, b };
    }

    private boolean isColorInRange(float[] lowerHsl, float[] upperHsl, float[] queryHsl) {
        boolean result = true;
        for (int i = 0; i < 3; i++) {
            //looped around the color wheel
            if (upperHsl[i] < lowerHsl[i]) {
                result &= (queryHsl[i] >= lowerHsl[i] && queryHsl[i] <= 360)
                        || (queryHsl[i] >= 0 && queryHsl[i] <= upperHsl[i]);
            } else {
                result &= (queryHsl[i] >= lowerHsl[i] && queryHsl[i] <= upperHsl[i]);
            }
        }

        Logger.local("HSL 1: " + lowerHsl[0] + " " + lowerHsl[1] + " " + lowerHsl[2]);
        Logger.local("HSL 2: " + upperHsl[0] + " " + upperHsl[1] + " " + upperHsl[2]);
        Logger.local("question HSL 2: " + queryHsl[0] + " " + queryHsl[1] + " " + queryHsl[2]);
        Logger.local("result: " + result);

        return result;
    }
}