se.toxbee.sleepfighter.challenge.shake.ShakeChallenge.java Source code

Java tutorial

Introduction

Here is the source code for se.toxbee.sleepfighter.challenge.shake.ShakeChallenge.java

Source

/*******************************************************************************
 * Copyright (c) 2013 See AUTHORS file.
 * 
 * This file is part of SleepFighter.
 * 
 * SleepFighter is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * SleepFighter is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with SleepFighter. If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/
package se.toxbee.sleepfighter.challenge.shake;

import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;

import se.toxbee.sleepfighter.R;
import se.toxbee.sleepfighter.challenge.BaseChallenge;
import se.toxbee.sleepfighter.challenge.ChallengePrototypeDefinition;
import se.toxbee.sleepfighter.challenge.ChallengeResolvedParams;
import se.toxbee.sleepfighter.model.challenge.ChallengeType;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.util.Log;
import android.widget.ProgressBar;
import android.widget.TextView;

/**
 * A challenge where the user have to shake the device to complete it.
 */
public class ShakeChallenge extends BaseChallenge {

    private static final String TAG = "ShakeChallenge";

    public static class PrototypeDefinition extends ChallengePrototypeDefinition {
        {
            setType(ChallengeType.SHAKE);
        }
    }

    private static final float GOAL = 5000;
    private static final double MIN_WORK = 100;

    private static final String KEY_PROGRESS_FLOAT = "progress";
    private ProgressBar progressBar;
    private TextView progressText;

    private SensorManager sensorManager;
    private Sensor accelerometer;

    private float progress = 0;
    private Vector3D lastVector;
    private long lastTime;

    @Override
    public void start(Activity activity, ChallengeResolvedParams params) {
        start(activity, params, null);
    }

    @Override
    public void start(Activity activity, ChallengeResolvedParams params, Bundle state) {
        super.start(activity, params);

        this.activity().setContentView(R.layout.challenge_shake);

        // Get view references
        this.progressBar = (ProgressBar) this.activity().findViewById(R.id.progressBar);
        this.progressText = (TextView) this.activity().findViewById(R.id.progressText);

        // Check if required sensor is available
        boolean hasAccelerometer = activity.getPackageManager()
                .hasSystemFeature(PackageManager.FEATURE_SENSOR_ACCELEROMETER);
        if (!hasAccelerometer) {
            // Complete right away, for now. Checking if device has required
            // hardware could perhaps be done before the challenge is started.
            Log.e(TAG, "Device lacks required sensor for ShakeChallenge");
            this.complete();
            return;
        }

        // Register to get acceleration events
        this.sensorManager = (SensorManager) activity.getSystemService(Context.SENSOR_SERVICE);
        this.accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

        // Get last progress from bundle, if it exists
        if (state != null) {
            this.progress = state.getFloat(KEY_PROGRESS_FLOAT);
        }
        updateProgress();
    }

    private SensorEventListener sensorEventListener = new SensorEventListener() {

        @Override
        public void onSensorChanged(SensorEvent event) {
            long time = System.nanoTime();
            float x = event.values[0];
            float y = event.values[1];
            float z = event.values[2];
            Vector3D vector = new Vector3D(x, y, z);
            onAccelerationChange(vector, time);
        }

        @Override
        public void onAccuracyChanged(Sensor sensor, int accuracy) {
        }
    };

    /**
     * Handle an acceleration change event.
     * 
     * @param vector
     *            the current acceleration vector
     * @param time
     *            the time the acceleration vector was acquired, in nono
     *            seconds, since some time in the past defined by the system
     */
    private void onAccelerationChange(Vector3D vector, long time) {
        // Only take vector difference into account, need a last vector
        if (lastVector != null) {
            // The difference of the vectors
            Vector3D diff = vector.subtract(lastVector);

            // Time difference in seconds
            double timeDiff = (double) (time - lastTime) / 1000000000;

            // The one dimensional "size" of the difference, there are probably
            // better things to measure
            double work = diff.getNorm();

            double workPerTime = work / timeDiff;

            // Only take effort above a set limit into account
            // Simple way to sort out small movements
            if (workPerTime > MIN_WORK) {
                // Increase the accumulator
                this.progress += work;

                updateProgress();

                checkIfCompleted();
            }
        }
        this.lastVector = vector;
        this.lastTime = time;
    }

    /**
     * Update the progress shown in the UI.
     */
    private void updateProgress() {
        int percentage = Math.min(100, Math.round(100 * (progress / GOAL)));
        this.progressBar.setProgress(percentage);
        this.progressText.setText(percentage + "%");
    }

    private void checkIfCompleted() {
        if (progress >= GOAL) {
            // Unregister when done
            this.sensorManager.unregisterListener(sensorEventListener);
            this.complete();
        }
    }

    @Override
    public Bundle savedState() {
        Bundle state = new Bundle();
        state.putFloat(KEY_PROGRESS_FLOAT, this.progress);
        return state;
    }

    @Override
    public void onPause() {
        this.sensorManager.unregisterListener(sensorEventListener);
    }

    @Override
    public void onResume() {
        this.sensorManager.registerListener(this.sensorEventListener, accelerometer,
                SensorManager.SENSOR_DELAY_GAME);
    }
}