/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.android.talkback; import android.content.Context; import android.content.SharedPreferences; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.os.PowerManager; import com.android.talkback.controller.FullScreenReadController; import com.android.utils.SharedPreferencesUtils; import com.google.android.marvin.talkback.TalkBackService; /** * Detector for shake events used to trigger continuous reading. */ public class ShakeDetector implements SensorEventListener { private static final float MOVEMENT_WINDOW = 200; private final TalkBackService mContext; private final SharedPreferences mPrefs; private final FullScreenReadController mFullScreenReadController; private final SensorManager mSensorManager; private final Sensor mAccelerometer; private boolean mIsFeatureEnabled; private boolean mIsActive; private long mLastSensorUpdate; private float[] mLastEventValues; public ShakeDetector(FullScreenReadController fullScreenReadController, TalkBackService context) { if (fullScreenReadController == null) throw new IllegalStateException(); mContext = context; mPrefs = SharedPreferencesUtils.getSharedPreferences(context); mFullScreenReadController = fullScreenReadController; mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); } /** * Sets whether or not to enable the shake detection feature. * * @param enable {@code true} to enable, {@code false} otherwise */ public void setEnabled(boolean enable) { mIsFeatureEnabled = enable; if (enable) { final PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); //noinspection deprecation if (pm.isScreenOn()) { resumePolling(); } } else { pausePolling(); } } /** * Starts polling the accelerometer for shake detection. If the feature has * not be enabled by calling {@link #setEnabled(boolean)}, calling this * method is a no-op. */ public void resumePolling() { if (!mIsFeatureEnabled || mIsActive) { return; } mLastSensorUpdate = 0; mLastEventValues = new float[3]; mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL); mIsActive = true; } /** * Stops polling the accelerometer for shake detection. */ public void pausePolling() { if (!mIsActive) { return; } mSensorManager.unregisterListener(this); mIsActive = false; } @Override public void onSensorChanged(SensorEvent event) { final long time = System.currentTimeMillis(); final long deltaT = (time - mLastSensorUpdate); if (deltaT > MOVEMENT_WINDOW) { final float movement = Math.abs(event.values[0] + event.values[1] + event.values[2] - mLastEventValues[0] - mLastEventValues[1] - mLastEventValues[2]); final float speed = (movement / deltaT) * 10000; mLastSensorUpdate = time; mLastEventValues = event.values.clone(); final int threshold = SharedPreferencesUtils.getIntFromStringPref(mPrefs, mContext.getResources(), R.string.pref_shake_to_read_threshold_key, R.string.pref_shake_to_read_threshold_default); if ((threshold > 0) && (speed >= threshold)) { mFullScreenReadController.startReadingFromNextNode(); } } } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { // Do nothing. } }