/* * Copyright (C) 2015 AChep@xda <artemchep@gmail.com> * * This program 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 2 * of the License, or (at your option) any later version. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ package com.achep.acdisplay.services.activemode.detectors; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.os.SystemClock; import android.support.annotation.NonNull; import android.util.Log; import com.achep.base.Build; import com.achep.base.tests.Check; import java.util.ArrayList; import java.util.List; public class IdleExitDetector implements SensorEventListener { @SuppressWarnings("PointlessBooleanExpression") private static final boolean DEBUG_ALGORITHM = true && Build.DEBUG; private static final String TAG = "ShakeDetector"; /** * After we detect a shake, we ignore any events for a bit of time. * We don't want two shakes to close together. */ private static final int IGNORE_EVENTS_AFTER_SHAKE = 1500; // 1.5 sec. private static final long KEEP_DATA_POINTS_FOR = 1400; private final List<DataPoint> mDataPoints = new ArrayList<>(); private final Listener mListener; private SensorManager mSensorManager; private long lastShake = 0; private float last_x = 0, last_y = 0, last_z = 0; private float ave_x = 0, ave_y = 0, ave_z = 0; public interface Listener { /** * Called on shake detected. */ void onShakeDetected(); } private static class DataPoint { public float x, y, z; public long time; public DataPoint(float x, float y, float z, long time) { this.x = x; this.y = y; this.z = z; this.time = time; } } public IdleExitDetector(@NonNull Listener listener) { mListener = listener; } public void start(@NonNull SensorManager sensorManager) { mSensorManager = sensorManager; Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); Check.getInstance().isNonNull(sensor); mSensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_GAME); } public void stop() { mSensorManager.unregisterListener(this); mSensorManager = null; } @Override public void onSensorChanged(@NonNull SensorEvent event) { Check.getInstance().isTrue(event.sensor.getType() == Sensor.TYPE_ACCELEROMETER); final long now = SystemClock.elapsedRealtime(); final long deltaTime = now - KEEP_DATA_POINTS_FOR; // If a shake in last X seconds ignore. if (lastShake != 0 && (now - lastShake) < IGNORE_EVENTS_AFTER_SHAKE) return; float x = event.values[0]; float y = event.values[1]; float z = event.values[2]; if (last_x != 0 && last_y != 0 && last_z != 0 && (last_x != x || last_y != y || last_z != z)) { DataPoint dp = new DataPoint(last_x - x, last_y - y, last_z - z, now); mDataPoints.add(dp); // Remove outdated data points. while (mDataPoints.size() > 0 && mDataPoints.get(0).time < deltaTime) { mDataPoints.remove(0); } // Calculate average threshold. final int length = mDataPoints.size(); if (length > 10) { for (DataPoint i : mDataPoints) { ave_x += Math.abs(i.x); ave_y += Math.abs(i.y); ave_z += Math.abs(i.z); } ave_x /= length; ave_y /= length; ave_z /= length; // if (DEBUG_ALGORITHM) { // Log.d(TAG, "ave_x=" + ave_x // + " ave_y=" + ave_y // + " ave_z=" + ave_z // + " length=" + length); // } final float ave = (ave_x + ave_y + ave_z) / 3f; final float aveDp = (Math.abs(dp.x) + Math.abs(dp.x) + Math.abs(dp.x)) / 3f; final float ratio = aveDp / ave; if (DEBUG_ALGORITHM) { Log.d(TAG, "ave=" + ave + " ave_dp=" + aveDp + " ave_ratio=" + ratio + " delta=" + (aveDp - ave)); } if (Math.abs(ratio) > 6 && Math.abs(aveDp - ave) > 0.5f && ave <= 0.3f) { mListener.onShakeDetected(); Log.e(TAG, "ave=" + ave + " ave_dp=" + aveDp + " ave_ratio=" + ratio + " delta=" + (aveDp - ave)); } } } last_x = x; last_y = y; last_z = z; } @Override public void onAccuracyChanged(@NonNull Sensor sensor, int accuracy) { /* unused */ } }