How can I detect a shake event with android? How can I detect the shake direction?
I want to change the image in an imageview when shaking occurs.
From the code point of view, you need to implement the SensorListener:
public class ShakeActivity extends Activity implements SensorListener
You will need to acquire a SensorManager:
sensorMgr = (SensorManager) getSystemService(SENSOR_SERVICE);
And register this sensor with desired flags:
sensorMgr.registerListener(this,
SensorManager.SENSOR_ACCELEROMETER,
SensorManager.SENSOR_DELAY_GAME);
In your onSensorChange() method, you determine whether it’s a shake or not:
public void onSensorChanged(int sensor, float[] values) {
if (sensor == SensorManager.SENSOR_ACCELEROMETER) {
long curTime = System.currentTimeMillis();
// only allow one update every 100ms.
if ((curTime - lastUpdate) > 100) {
long diffTime = (curTime - lastUpdate);
lastUpdate = curTime;
x = values[SensorManager.DATA_X];
y = values[SensorManager.DATA_Y];
z = values[SensorManager.DATA_Z];
float speed = Math.abs(x+y+z - last_x - last_y - last_z) / diffTime * 10000;
if (speed > SHAKE_THRESHOLD) {
Log.d("sensor", "shake detected w/ speed: " + speed);
Toast.makeText(this, "shake detected w/ speed: " + speed, Toast.LENGTH_SHORT).show();
}
last_x = x;
last_y = y;
last_z = z;
}
}
}
The shake threshold is defined as:
private static final int SHAKE_THRESHOLD = 800;
There are some other methods too, to detect shake motion. look at this link.(If that link does not work or link is dead, look at this web archive.).
Have a look at this example for android shake detect listener.
Note: SensorListener is deprecated. we can use SensorEventListener instead. Here is a quick example using SensorEventListener.
Thanks.
Google helps a lot.
/* The following code was written by Matthew Wiggins
* and is released under the APACHE 2.0 license
*
* http://www.apache.org/licenses/LICENSE-2.0
*/
package com.hlidskialf.android.hardware;
import android.hardware.SensorListener;
import android.hardware.SensorManager;
import android.content.Context;
import java.lang.UnsupportedOperationException;
public class ShakeListener implements SensorListener
{
private static final int FORCE_THRESHOLD = 350;
private static final int TIME_THRESHOLD = 100;
private static final int SHAKE_TIMEOUT = 500;
private static final int SHAKE_DURATION = 1000;
private static final int SHAKE_COUNT = 3;
private SensorManager mSensorMgr;
private float mLastX=-1.0f, mLastY=-1.0f, mLastZ=-1.0f;
private long mLastTime;
private OnShakeListener mShakeListener;
private Context mContext;
private int mShakeCount = 0;
private long mLastShake;
private long mLastForce;
public interface OnShakeListener
{
public void onShake();
}
public ShakeListener(Context context)
{
mContext = context;
resume();
}
public void setOnShakeListener(OnShakeListener listener)
{
mShakeListener = listener;
}
public void resume() {
mSensorMgr = (SensorManager)mContext.getSystemService(Context.SENSOR_SERVICE);
if (mSensorMgr == null) {
throw new UnsupportedOperationException("Sensors not supported");
}
boolean supported = mSensorMgr.registerListener(this, SensorManager.SENSOR_ACCELEROMETER, SensorManager.SENSOR_DELAY_GAME);
if (!supported) {
mSensorMgr.unregisterListener(this, SensorManager.SENSOR_ACCELEROMETER);
throw new UnsupportedOperationException("Accelerometer not supported");
}
}
public void pause() {
if (mSensorMgr != null) {
mSensorMgr.unregisterListener(this, SensorManager.SENSOR_ACCELEROMETER);
mSensorMgr = null;
}
}
public void onAccuracyChanged(int sensor, int accuracy) { }
public void onSensorChanged(int sensor, float[] values)
{
if (sensor != SensorManager.SENSOR_ACCELEROMETER) return;
long now = System.currentTimeMillis();
if ((now - mLastForce) > SHAKE_TIMEOUT) {
mShakeCount = 0;
}
if ((now - mLastTime) > TIME_THRESHOLD) {
long diff = now - mLastTime;
float speed = Math.abs(values[SensorManager.DATA_X] + values[SensorManager.DATA_Y] + values[SensorManager.DATA_Z] - mLastX - mLastY - mLastZ) / diff * 10000;
if (speed > FORCE_THRESHOLD) {
if ((++mShakeCount >= SHAKE_COUNT) && (now - mLastShake > SHAKE_DURATION)) {
mLastShake = now;
mShakeCount = 0;
if (mShakeListener != null) {
mShakeListener.onShake();
}
}
mLastForce = now;
}
mLastTime = now;
mLastX = values[SensorManager.DATA_X];
mLastY = values[SensorManager.DATA_Y];
mLastZ = values[SensorManager.DATA_Z];
}
}
}
You can also take a look on library Seismic
public class Demo extends Activity implements ShakeDetector.Listener {
#Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
ShakeDetector sd = new ShakeDetector(this);
// A non-zero delay is required for Android 12 and up (https://github.com/square/seismic/issues/24)
int sensorDelay = SensorManager.SENSOR_DELAY_GAME
sd.start(sensorManager, sensorDelay);
TextView tv = new TextView(this);
tv.setGravity(CENTER);
tv.setText("Shake me, bro!");
setContentView(tv, new LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
#Override public void hearShake() {
Toast.makeText(this, "Don't shake me, bro!", Toast.LENGTH_SHORT).show();
}
}
There are a lot of solutions to this question already, but I wanted to post one that:
Doesn't use a library depricated in API 3
Calculates the magnitude of the acceleration correctly
Correctly applies a timeout between shake events
Here is such a solution:
// variables for shake detection
private static final float SHAKE_THRESHOLD = 3.25f; // m/S**2
private static final int MIN_TIME_BETWEEN_SHAKES_MILLISECS = 1000;
private long mLastShakeTime;
private SensorManager mSensorMgr;
To initialize the timer:
// Get a sensor manager to listen for shakes
mSensorMgr = (SensorManager) getSystemService(SENSOR_SERVICE);
// Listen for shakes
Sensor accelerometer = mSensorMgr.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
if (accelerometer != null) {
mSensorMgr.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_NORMAL);
}
SensorEventListener methods to override:
#Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
long curTime = System.currentTimeMillis();
if ((curTime - mLastShakeTime) > MIN_TIME_BETWEEN_SHAKES_MILLISECS) {
float x = event.values[0];
float y = event.values[1];
float z = event.values[2];
double acceleration = Math.sqrt(Math.pow(x, 2) +
Math.pow(y, 2) +
Math.pow(z, 2)) - SensorManager.GRAVITY_EARTH;
Log.d(APP_NAME, "Acceleration is " + acceleration + "m/s^2");
if (acceleration > SHAKE_THRESHOLD) {
mLastShakeTime = curTime;
Log.d(APP_NAME, "Shake, Rattle, and Roll");
}
}
}
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// Ignore
}
When you are all done
// Stop listening for shakes
mSensorMgr.unregisterListener(this);
Since SensorListener is deprecated so use the following code:
/* put this into your activity class */
private SensorManager mSensorManager;
private float mAccel; // acceleration apart from gravity
private float mAccelCurrent; // current acceleration including gravity
private float mAccelLast; // last acceleration including gravity
private final SensorEventListener mSensorListener = new SensorEventListener() {
public void onSensorChanged(SensorEvent se) {
float x = se.values[0];
float y = se.values[1];
float z = se.values[2];
mAccelLast = mAccelCurrent;
mAccelCurrent = (float) Math.sqrt((double) (x*x + y*y + z*z));
float delta = mAccelCurrent - mAccelLast;
mAccel = mAccel * 0.9f + delta; // perform low-cut filter
}
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
};
#Override
protected void onResume() {
super.onResume();
mSensorManager.registerListener(mSensorListener, mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_NORMAL);
}
#Override
protected void onPause() {
mSensorManager.unregisterListener(mSensorListener);
super.onPause();
}
Then:
/* do this in onCreate */
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensorManager.registerListener(mSensorListener, mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_NORMAL);
mAccel = 0.00f;
mAccelCurrent = SensorManager.GRAVITY_EARTH;
mAccelLast = SensorManager.GRAVITY_EARTH;
The question with full details could be found here:
Android: I want to shake it
This is for Kotlin and use SensorEventListener
Create new class ShakeDetector
class ShakeDetector : SensorEventListener {
private var mListener: OnShakeListener? = null
private var mShakeTimestamp: Long = 0
private var mShakeCount = 0
fun setOnShakeListener(listener: OnShakeListener?) {
mListener = listener
}
interface OnShakeListener {
fun onShake(count: Int)
}
override fun onAccuracyChanged(
sensor: Sensor,
accuracy: Int
) { // ignore
}
override fun onSensorChanged(event: SensorEvent) {
if (mListener != null) {
val x = event.values[0]
val y = event.values[1]
val z = event.values[2]
val gX = x / SensorManager.GRAVITY_EARTH
val gY = y / SensorManager.GRAVITY_EARTH
val gZ = z / SensorManager.GRAVITY_EARTH
// gForce will be close to 1 when there is no movement.
val gForce: Float = sqrt(gX * gX + gY * gY + gZ * gZ)
if (gForce > SHAKE_THRESHOLD_GRAVITY) {
val now = System.currentTimeMillis()
// ignore shake events too close to each other (500ms)
if (mShakeTimestamp + SHAKE_SLOP_TIME_MS > now) {
return
}
// reset the shake count after 3 seconds of no shakes
if (mShakeTimestamp + SHAKE_COUNT_RESET_TIME_MS < now) {
mShakeCount = 0
}
mShakeTimestamp = now
mShakeCount++
mListener!!.onShake(mShakeCount)
}
}
}
companion object {
/*
* The gForce that is necessary to register as shake.
* Must be greater than 1G (one earth gravity unit).
* You can install "G-Force", by Blake La Pierre
* from the Google Play Store and run it to see how
* many G's it takes to register a shake
*/
private const val SHAKE_THRESHOLD_GRAVITY = 2.7f
private const val SHAKE_SLOP_TIME_MS = 500
private const val SHAKE_COUNT_RESET_TIME_MS = 3000
}
}
Your main Activity
class MainActivity : AppCompatActivity() {
// The following are used for the shake detection
private var mSensorManager: SensorManager? = null
private var mAccelerometer: Sensor? = null
private var mShakeDetector: ShakeDetector? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initSensor()
}
override fun onResume() {
super.onResume()
// Add the following line to register the Session Manager Listener onResume
mSensorManager!!.registerListener(
mShakeDetector,
mAccelerometer,
SensorManager.SENSOR_DELAY_UI
)
}
override fun onPause() { // Add the following line to unregister the Sensor Manager onPause
mSensorManager!!.unregisterListener(mShakeDetector)
super.onPause()
}
private fun initSensor() {
// ShakeDetector initialization
// ShakeDetector initialization
mSensorManager = getSystemService(SENSOR_SERVICE) as SensorManager
mAccelerometer = mSensorManager!!.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
mShakeDetector = ShakeDetector()
mShakeDetector!!.setOnShakeListener(object : OnShakeListener {
override fun onShake(count: Int) { /*
* The following method, "handleShakeEvent(count):" is a stub //
* method you would use to setup whatever you want done once the
* device has been shook.
*/
Toast.makeText(this#MainActivity, count.toString(), Toast.LENGTH_SHORT).show()
}
})
}
}
Finally add this code to Manifests to make sure the phone has an accelerometer
<uses-feature android:name="android.hardware.sensor.accelerometer" android:required="true" />
You can use Seismic:
See the code here:
https://github.com/square/seismic/blob/master/library/src/main/java/com/squareup/seismic/ShakeDetector.java
Do the following:
private float xAccel, yAccel, zAccel;
private float xPreviousAccel, yPreviousAccel, zPreviousAccel;
private boolean firstUpdate = true;
private final float shakeThreshold = 1.5f;
private boolean shakeInitiated = false;
SensorEventListener mySensorEventListener;
SensorManager mySensorManager;
Put this in onCreate method.
mySensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mySensorManager.registerListener(mySensorEventListener,
mySensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
SensorManager.SENSOR_DELAY_NORMAL);
And now the main part.
private boolean isAccelerationChanged() {
float deltaX = Math.abs(xPreviousAccel - xAccel);
float deltaY = Math.abs(yPreviousAccel - yAccel);
float deltaZ = Math.abs(zPreviousAccel - zAccel);
return (deltaX > shakeThreshold && deltaY > shakeThreshold)
|| (deltaX > shakeThreshold && deltaZ > shakeThreshold)
|| (deltaY > shakeThreshold && deltaZ > shakeThreshold);
}
private void updateAccelParameters(float xNewAccel, float yNewAccel, float zNewAccel) {
if (firstUpdate) {
xPreviousAccel = xNewAccel;
yPreviousAccel = yNewAccel;
zPreviousAccel = zNewAccel;
firstUpdate = false;
}else{
xPreviousAccel = xAccel;
yPreviousAccel = yAccel;
zPreviousAccel = zAccel;
}
xAccel = xNewAccel;
yAccel = yNewAccel;
zAccel = zNewAccel;
}
private void executeShakeAction() {
//this method is called when devices shakes
}
public void onSensorChanged(SensorEvent se) {
updateAccelParameters(se.values[0], se.values[1], se.values[2]);
if ((!shakeInitiated) && isAccelerationChanged()) {
shakeInitiated = true;
}else if ((shakeInitiated) && isAccelerationChanged()){
executeShakeAction();
}else if((shakeInitiated) && (!isAccelerationChanged())){
shakeInitiated = false;
}
}
public void onAccuracyChanged(Sensor sensor, int accuracy) {
//setting the accuracy
}
Dont forget to add this code in your MainActivity.java:
MainActivity.java
mShaker = new ShakeListener(this);
mShaker.setOnShakeListener(new ShakeListener.OnShakeListener () {
public void onShake() {
Toast.makeText(MainActivity.this, "Shake " , Toast.LENGTH_LONG).show();
}
});
#Override
protected void onResume() {
super.onResume();
mShaker.resume();
}
#Override
protected void onPause() {
super.onPause();
mShaker.pause();
}
Or I give you a link about this stuff.
Related
I have an issue with my code. I have a service that implements the SensorEventListener that i want to keep running in the background. When in foreground it works as it should but when i put my app in background, the accelerometer sensor gives me results for 1-2 minutes and then it stops working. Below is my service.
public class HealthService extends Service implements SensorEventListener {
// ===========================================================
// Constants
// ===========================================================
// ===========================================================
// Fields
// ===========================================================
private Context mContext;
private SensorManager mSensorManager;
private Sensor mSensor;
private int mSteps = 0;
private final int ACCEL_RING_SIZE = 50;
private final int VEL_RING_SIZE = 10;
private final float STEP_THRESHOLD = 20f;
private final int STEP_DELAY_NS = 250000000;
private int accelRingCounter = 0;
private float[] accelRingX = new float[ACCEL_RING_SIZE];
private float[] accelRingY = new float[ACCEL_RING_SIZE];
private float[] accelRingZ = new float[ACCEL_RING_SIZE];
private int velRingCounter = 0;
private float[] velRing = new float[VEL_RING_SIZE];
private long lastStepTimeNs = 0;
private float oldVelocityEstimate = 0;
// ===========================================================
// Contructors
// ===========================================================
// ===========================================================
// Methods for/from SuperClass/Interfaces
// ===========================================================
#Override
public void onCreate() {
super.onCreate();
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
registerStepSensor();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(
() -> {
while (true) {
sendSteps();
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
).start();
return super.onStartCommand(intent, flags, startId);
}
#Override
public void onDestroy() {
super.onDestroy();
unregisterStepSensor();
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onSensorChanged(SensorEvent sensorEvent) {
if (sensorEvent.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
updateSteps(
sensorEvent.timestamp,
sensorEvent.values[0],
sensorEvent.values[1],
sensorEvent.values[2]);
}
}
#Override
public void onAccuracyChanged(Sensor sensor, int i) {
}
// ===========================================================
// Methods
// ===========================================================
public void registerStepSensor() {
mSensorManager.registerListener(this, mSensor, SensorManager.SENSOR_DELAY_GAME);
}
public void unregisterStepSensor() {
mSensorManager.unregisterListener(this);
}
private void updateSteps(long timeNs, float x, float y, float z) {
float[] currentAccel = new float[3];
currentAccel[0] = x;
currentAccel[1] = y;
currentAccel[2] = z;
// First step is to update our guess of where the global z vector is.
accelRingCounter++;
accelRingX[accelRingCounter % ACCEL_RING_SIZE] = currentAccel[0];
accelRingY[accelRingCounter % ACCEL_RING_SIZE] = currentAccel[1];
accelRingZ[accelRingCounter % ACCEL_RING_SIZE] = currentAccel[2];
float[] worldZ = new float[3];
worldZ[0] = sum(accelRingX) / Math.min(accelRingCounter, ACCEL_RING_SIZE);
worldZ[1] = sum(accelRingY) / Math.min(accelRingCounter, ACCEL_RING_SIZE);
worldZ[2] = sum(accelRingZ) / Math.min(accelRingCounter, ACCEL_RING_SIZE);
float normalization_factor = norm(worldZ);
worldZ[0] = worldZ[0] / normalization_factor;
worldZ[1] = worldZ[1] / normalization_factor;
worldZ[2] = worldZ[2] / normalization_factor;
float currentZ = dot(worldZ, currentAccel) - normalization_factor;
velRingCounter++;
velRing[velRingCounter % VEL_RING_SIZE] = currentZ;
float velocityEstimate = sum(velRing);
if (velocityEstimate > STEP_THRESHOLD
&& oldVelocityEstimate <= STEP_THRESHOLD
&& (timeNs - lastStepTimeNs > STEP_DELAY_NS)) {
mSteps++;
HealthUtils.setStepsCounter(mContext, mSteps);
lastStepTimeNs = timeNs;
}
oldVelocityEstimate = velocityEstimate;
}
private float sum(float[] array) {
float retval = 0;
for (float v : array) {
retval += v;
}
return retval;
}
private float[] cross(float[] arrayA, float[] arrayB) {
float[] retArray = new float[3];
retArray[0] = arrayA[1] * arrayB[2] - arrayA[2] * arrayB[1];
retArray[1] = arrayA[2] * arrayB[0] - arrayA[0] * arrayB[2];
retArray[2] = arrayA[0] * arrayB[1] - arrayA[1] * arrayB[0];
return retArray;
}
private float norm(float[] array) {
float retval = 0;
for (float v : array) {
retval += v * v;
}
return (float) Math.sqrt(retval);
}
private float dot(float[] a, float[] b) {
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
}
private float[] normalize(float[] a) {
float[] retval = new float[a.length];
float norm = norm(a);
for (int i = 0; i < a.length; i++) {
retval[i] = a[i] / norm;
}
return retval;
}
private void sendSteps() {
String date = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss", Locale.getDefault()).format(new Date());
HealthManager.setPacingDetails(new PacingCalculateRequest()
.setCounter(HealthUtils.getStepsCounter(mContext))
.setDate(date),
new CallbackReceiver<JSONObject>() {
#Override
public void onSuccess(JSONObject result) {
HealthUtils.log("Health Service success");
int status = result.optInt("status", 2);
if (status == 1) {
mSteps = 0;
HealthUtils.setStepsCounter(mContext, 0);
}
}
#Override
public void onFailure(int errorCode) {
HealthUtils.log("Health Service error: " + String.valueOf(errorCode));
}
});
}}
Parts of the code is based from here: http://www.gadgetsaint.com/android/create-pedometer-step-counter-android/#.Weg2KmiCyM8
in my text to speech style app I can set the speed and pitch of the voice, and also 2 variables for ultrasound pitch settings. If I set the speed and pitch and exit the settings menu everything is fine, The problem is that if I press the finish button and the frequency analyzer runs and sets the ultrasound variables the variables get saved in the preference editor in the wrong spot, causing the speed and pitch to be set at the ultrasound values, I have been trying for 2 days to fix it but nothing works, below is the relevant code, if you need more please ask, can anyone see what I'm doing wrong?
EDIT: here is an apk showing the problem, go to settings menu (from toolbar upper right) set the speed and pitch, press finish, close app, open app, go to settings and you will see the ridiculous values set as speed and pitch
Canine Remote apk
Relevant initialized variables at start of class:
public SharedPreferences appPreferences;
boolean settingUp=false;
int result = 0;
int remote = 0;
int settingLanguage=0;
private float progress = (float) 1.0;
private float progress2 = (float) 1.0;
private static final String LSTYLE = "usa";//language
private static final String MYPITCH = "1.0";//normal use voice pitch
private static final String MYSPEED = "1.0";//normal use voice speed
private static final String ULTRADOG = "1.0";//22000 +/- user pitch khz
private static final String ULTRACAT = "1.0";//48000 +/- user pitch khz
private static final String HUMANDOGCAT = "0";//human=0, dog=1, cat=2
private static final String REMOTE = "0";//speak through device=0, speak
//through remote bluetooth speaker=1
private TextView edit;
private TextView edit2;
private Button edit3;
private Button edit4;
private Button edit5;
private Button edit6;
private TextView edit7;
private ImageButton edit8;
private Button edit9;
private CheckBox ckultra;
private CheckBox cklocal;
private CheckBox ckultra2;
private SeekBar cpitch;
private SeekBar cspeed;
AudioRecord mAudioRecord;
int freq = 11025;
int Nb;
int N;
int running=0;
double FreqMin = 0;
double FreqMax = 2300;
int muestras = 1000;
double PI2n = 2 * Math.PI/muestras;
double FreqMuestras=freq/muestras;
int indMin = (int) (FreqMin/FreqMuestras);
int indMax = (int) (FreqMax/FreqMuestras);
double newFrequency = 170;
double freakMin=170;
double freakMax=170;
double mean=170;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_canine_start);
appPreferences = PreferenceManager.getDefaultSharedPreferences(this);
This button sets the pitch in the preference editor
edit3.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Globals g = (Globals) getApplication();
SharedPreferences.Editor editor = appPreferences.edit();
float pitch = g.getData2();
if (pitch < 0.1) pitch = (float) 0.1;
editor.putFloat(MYPITCH, pitch);
editor.commit();
tts.setPitch(pitch);
}
});
This button sets the speed in the preference editor, also I've included the slider bars and check boxes codes here if you need to see them
edit4.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Globals g = (Globals) getApplication();
SharedPreferences.Editor editor2 = appPreferences.edit();
float speed=g.getData3();
if (speed < 0.1) speed = (float) 0.1;
editor2.putFloat(MYSPEED, speed);
editor2.commit();
tts.setSpeechRate(speed);
}
});
//slider bars to get pitch and speed
cpitch.setProgress(1);
edit.setText("Adjust voice Pitch: " + cpitch.getProgress());
cpitch.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
Globals g = (Globals) getApplication();
#Override
public void onProgressChanged(SeekBar cpitch, int progresValue, boolean fromUser) {
progress = (float) (progresValue * 0.1);
edit.setText("Adjust voice pitch: " + progress);
}
#Override
public void onStartTrackingTouch(SeekBar cpitch) {
}
#Override
public void onStopTrackingTouch(SeekBar cpitch) {
edit.setText("Adjust voice pitch: " + progress);
g.setData2(progress);
}
});
cspeed.setProgress(1);
edit2.setText("Adjust voice speed: " + cspeed.getProgress());
cspeed.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
Globals g = (Globals) getApplication();
#Override
public void onProgressChanged(SeekBar cspeed, int progresValue2, boolean fromUser2) {
progress2 = (float) (progresValue2 * 0.1);
edit2.setText("Adjust voice speed: " + progress2);
}
#Override
public void onStartTrackingTouch(SeekBar cspeed) {
}
#Override
public void onStopTrackingTouch(SeekBar cspeed) {
edit2.setText("Adjust voice speed: " + progress2);
g.setData3(progress2);
}
});
}
//check boxes
class clicker implements CheckBox.OnCheckedChangeListener {
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
SharedPreferences.Editor editor5 = appPreferences.edit();
if (isChecked) {
if (buttonView == ckultra) {
editor5.putInt(HUMANDOGCAT, 1).commit();
ckultra2.setChecked(false);
}
if (buttonView == ckultra2) {
editor5.putInt(HUMANDOGCAT, 2).commit();
ckultra.setChecked(false);
}
if (buttonView == cklocal) {
editor5.putInt(REMOTE, 1).commit();
}
}
if (!isChecked) {
if (buttonView == ckultra) {
editor5.putInt(HUMANDOGCAT, 0).commit();
}
if (buttonView == ckultra2) {
editor5.putInt(HUMANDOGCAT, 0).commit();
}
if (buttonView == cklocal) {
editor5.putInt(REMOTE, 0).commit();
}
}
}
}
This button is the start of the code causing the error
edit6.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
edit7.setVisibility(View.VISIBLE);
edit8.setVisibility(View.VISIBLE);
edit9.setVisibility(View.VISIBLE);
edit.setVisibility(View.GONE);
edit2.setVisibility(View.GONE);
edit3.setVisibility(View.GONE);
edit4.setVisibility(View.GONE);
edit5.setVisibility(View.GONE);
edit6.setVisibility(View.GONE);
cpitch.setVisibility(View.GONE);
cspeed.setVisibility(View.GONE);
ckultra.setVisibility(View.GONE);
ckultra2.setVisibility(View.GONE);
cklocal.setVisibility(View.GONE);
speakIT("Completed.");
Spectrometer_Start();
}
});
//analyze pitch to set ultrasound variables
public void Spectrometer_Start() {
try {
Nb = AudioRecord.getMinBufferSize(freq, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT) * 4;
N = Nb * Byte.SIZE / Short.SIZE;
mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, freq, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, Nb);
mAudioRecord.startRecording();
running=1;
analizer();
} catch (IllegalArgumentException i) {
}
}
This method saves the ultrasound settings in the wrong place in the preference editor
public void analizer() {
int a=10;
while(tts.isSpeaking())
{
a-=1;
if(a==0)
{
short[] data= new short[muestras];
try {
mAudioRecord.read(data, 0, muestras-1);
Dft(data);
} catch (Exception e) {
e.printStackTrace();
}
a=10;
}
}
mAudioRecord.stop();
mAudioRecord.release();
Globals g = (Globals) getApplication();
running=0;
mean=((freakMax-freakMin)*0.5)+freakMin;
float calcDog = (float) (22000 / mean);
float calcCat = (float) (48000 / mean);
g.setData4(calcDog);
g.setData5(calcCat);
SharedPreferences.Editor editor7 = appPreferences.edit();
editor7.putFloat(ULTRADOG, calcDog);
editor7.putFloat(ULTRACAT, calcCat);
editor7.commit();
}
public void Dft(short[] inreal) {
for (int k = indMin; k < indMax; k++)
{
float sumreal = 0;
float sumimag = 0;
float PI2kn= (float) (PI2n * k);
for (int t = 0; t < muestras; t++) {
double angle = t*PI2kn;
sumreal += inreal[t] * Math.cos(angle);
sumimag += inreal[t] * Math.sin(angle);
}
newFrequency = (Math.sqrt(sumreal * sumreal + sumimag * sumimag));
if (newFrequency < freakMin && newFrequency >= 85) {
freakMin = newFrequency;
}
if (newFrequency > freakMax && newFrequency <= 255) {
freakMax = newFrequency;
}
}
}
This button starts the speech entered, and receives the wrong values from the preference editor
edit9.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String sendComands= edit7.getText().toString();
if (!sendComands.equals("") && settingLanguage == 0){
float NPITCH = appPreferences.getFloat(MYPITCH, (float) 1.0);
float NSPEED = appPreferences.getFloat(MYSPEED, (float) 1.0);
float UDOG = appPreferences.getFloat(ULTRADOG, (float) 1.0);
float UCAT = appPreferences.getFloat(ULTRACAT, (float) 1.0);
int HDC = appPreferences.getInt(HUMANDOGCAT, 0);
switch (HDC) {
case 0:
tts.setPitch(NPITCH);
break;
case 1:
tts.setPitch(UDOG);
break;
case 2:
tts.setPitch(UCAT);
break;
}
tts.setSpeechRate(NSPEED);
speakIT(sendComands);
edit7.setHint("Enter text to send");
settingUp=false;
}
if (settingLanguage == 1) {
countryC(sendComands);
settingLanguage=0;
edit7.setHint("Enter text to send");
edit9.setText("Send");
edit7.setVisibility(View.GONE);
edit9.setVisibility(View.GONE);
}
}
});
My settings menu in the toolbar with comment so you know which objects do what
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.canine_start, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
Globals g = (Globals) getApplication();
int id = item.getItemId();
if (id == R.id.action_settings) {
edit.setVisibility(View.VISIBLE);//pitch slider text
edit2.setVisibility(View.VISIBLE);//speed slider text
edit3.setVisibility(View.VISIBLE);//button - set pitch
edit4.setVisibility(View.VISIBLE);//button - set speed
edit5.setVisibility(View.VISIBLE);//button - test speak
edit6.setVisibility(View.VISIBLE);//button - finished with settings
edit7.setVisibility(View.GONE);//edit text - text to speech
edit8.setVisibility(View.GONE);//image button - speech recognition start
edit9.setVisibility(View.GONE);//button - speak edit7 text
cpitch.setVisibility(View.VISIBLE);//slider - adjust voice pitch
cspeed.setVisibility(View.VISIBLE);//slider - adjust voice speed
ckultra.setVisibility(View.VISIBLE);//checkbox - ultrasound dog on/off
ckultra2.setVisibility(View.VISIBLE);//checkbox - ultrasound cat on/off
cklocal.setVisibility(View.VISIBLE);//checkbox - send to bluetooth/local device
settingUp=true;
return true;
}
Your are using invalid keys to stores your values in sharedPreferences:
These values are used as keys:
private static final String MYPITCH = "1.0";
private static final String MYSPEED = "1.0";
private static final String ULTRADOG = "1.0";
private static final String ULTRACAT = "1.0";
Here to store values
editor.putFloat(MYPITCH, pitch);
editor2.putFloat(MYSPEED, speed);
editor7.putFloat(ULTRADOG, calcDog);
editor7.putFloat(ULTRACAT, calcCat);
And here to get values
float NPITCH = appPreferences.getFloat(MYPITCH, (float) 1.0);
float NSPEED = appPreferences.getFloat(MYSPEED, (float) 1.0);
float UDOG = appPreferences.getFloat(ULTRADOG, (float) 1.0);
float UCAT = appPreferences.getFloat(ULTRACAT, (float) 1.0);
You should instead use unique and meaningful keys:
private static final String MYPITCH = "my_pitch";
private static final String MYSPEED = "my_speed";
private static final String ULTRADOG = "ultra_dog";
private static final String ULTRACAT = "ultra_cat";
I'm having a problem creating a shake detection service
background_service
package com.likith.shakedetector;
import android.app.Service;
import android.content.Intent;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.IBinder;
import android.widget.Toast;
public class Background_service extends Service implements SensorEventListener
{
SensorManager sensorManager;
int count=0;
private float lastX = 0;
private float lastY = 0;
private float lastZ = 0;
private static final int MIN_FORCE = 10;
private static final int MIN_DIRECTION_CHANGE = 3;
private static final int MAX_PAUSE_BETHWEEN_DIRECTION_CHANGE = 200;
private static final int MAX_TOTAL_DURATION_OF_SHAKE = 400;
private long mFirstDirectionChangeTime = 0;
private long mLastDirectionChangeTime;
private int mDirectionChangeCount = 0;
#Override
public IBinder onBind(Intent intent) {
return null;
}
public void onCreate()
{
super.onCreate();
}
public void onDestroy()
{
super.onDestroy();
sensorManager.unregisterListener(this);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
sensorManager.registerListener(this,sensorManager
.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),SensorManager.SENSOR_DELAY_UI);
return START_STICKY;
}
public void onStart(Intent intent, int startId)
{
sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
sensorManager.registerListener(this,sensorManager
.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),SensorManager.SENSOR_DELAY_UI);
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy)
{
}
#SuppressWarnings("deprecation")
private void getAccelerometer(SensorEvent event)
{
//Toast.makeText(getApplicationContext(),"Shaked",Toast.LENGTH_LONG).show();
float x = event.values[SensorManager.DATA_X];
float y = event.values[SensorManager.DATA_Y];
float z = event.values[SensorManager.DATA_Z];
float totalMovement = Math.abs(x + y + z - lastX - lastY - lastZ);
if (totalMovement <= MIN_FORCE)
{
long now = System.currentTimeMillis();
if (mFirstDirectionChangeTime == 0)
{
mFirstDirectionChangeTime = now;
mLastDirectionChangeTime = now;
}
long lastChangeWasAgo = now - mLastDirectionChangeTime;
if (lastChangeWasAgo < MAX_PAUSE_BETHWEEN_DIRECTION_CHANGE)
{
mLastDirectionChangeTime = now;
mDirectionChangeCount++;
lastX = x;
lastY = y;
lastZ = z;
if (mDirectionChangeCount >= MIN_DIRECTION_CHANGE)
{
long totalDuration = now - mFirstDirectionChangeTime;
if (totalDuration < MAX_TOTAL_DURATION_OF_SHAKE)
{
Toast.makeText(getApplicationContext(),"Shaked",Toast.LENGTH_LONG).show();
resetShakeParameters();
}
}
}
else
{
resetShakeParameters();
}
}
}
public void onSensorChanged(SensorEvent event)
{
getAccelerometer(event);
}
protected void onResume()
{
sensorManager.registerListener(this,sensorManager
.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),SensorManager.SENSOR_DELAY_UI);
}
protected void onPause()
{
sensorManager.unregisterListener(this);
}
private void resetShakeParameters()
{
mFirstDirectionChangeTime = 0;
mDirectionChangeCount = 0;
mLastDirectionChangeTime = 0;
lastX = 0;
lastY = 0;
lastZ = 0;
}
When I run this service, It prints the toast message continuously.
How do I change the code so that when I shake the device, it only prints the toast message once?
You have
float totalMovement = Math.abs(x + y + z - lastX - lastY - lastZ);
You probably should do each axis separately - as each axis changes independent of the others.
float xMovement = Math.abs(x - lastX);
float yMovement = Math.abs(y - lastY);
float zMovement = Math.abs(z - lastZ);
if ((xMovement > MIN_FORCE) ||
(yMovement > MIN_FORCE) ||
(zMovement > MIN_FORCE)) {
// motion detected
}
I was doing something similar and found it better to check for the delay (MAX_PAUSE_BETHWEEN_DIRECTION_CHANGE) first.
With Android API 18 there is a significant motion sensor, if present in the Android device may be effective for your purposes.
See http://developer.android.com/reference/android/hardware/Sensor.html#TYPE_SIGNIFICANT_MOTION
try this code
Background_service.java
package com.likith.shakedetector;
import com.likith.gesturelauncherPro.gestureDrawer;
import android.app.Service;
import android.content.Intent;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorManager;
import android.os.IBinder;
public class Background_service extends Service implements com.likith.shakedetector.shake.Listener
{
#Override
public void onCreate()
{
SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
shake sd = new shake(this);
sd.start(sensorManager);
}
#Override
public void hearShake()
{
//Toast.makeText(getApplicationContext(),"Shaked",Toast.LENGTH_LONG).show();
Intent notificationIntent = new Intent(this, gestureDrawer.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(notificationIntent);
}
public void onSensorChanged(SensorEvent event) {
// TODO Auto-generated method stub
}
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// TODO Auto-generated method stub
}
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
}
shake.java
package com.likith.shakedetector;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import java.util.ArrayList;
import java.util.List;
/**
* Detects phone shaking. If > 75% of the samples taken in the past 0.5s are
* accelerating, the device is a) shaking, or b) free falling 1.84m (h =
* 1/2*g*t^2*3/4).
*
* #author Bob Lee (bob#squareup.com)
* #author Eric Burke (eric#squareup.com)
*/
public class shake implements SensorEventListener
{
private static final int ACCELERATION_THRESHOLD = 13;
public interface Listener
{
void hearShake();
}
private final SampleQueue queue = new SampleQueue();
private final Listener listener;
private SensorManager sensorManager;
private Sensor accelerometer;
public shake(Listener listener)
{
this.listener = listener;
}
public boolean start(SensorManager sensorManager)
{
if (accelerometer != null)
{
return true;
}
accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
// If this phone has an accelerometer, listen to it.
if (accelerometer != null)
{
this.sensorManager = sensorManager;
sensorManager.registerListener(this, accelerometer,SensorManager.SENSOR_DELAY_FASTEST);
}
return accelerometer != null;
}
/**
* Stops listening. Safe to call when already stopped. Ignored on devices
* without appropriate hardware.
*/
public void stop()
{
if (accelerometer != null)
{
sensorManager.unregisterListener(this, accelerometer);
sensorManager = null;
accelerometer = null;
}
}
#Override public void onSensorChanged(SensorEvent event)
{
boolean accelerating = isAccelerating(event);
long timestamp = event.timestamp;
queue.add(timestamp, accelerating);
if (queue.isShaking())
{
queue.clear();
listener.hearShake();
}
}
/** Returns true if the device is currently accelerating. */
private boolean isAccelerating(SensorEvent event)
{
float ax = event.values[0];
float ay = event.values[1];
float az = event.values[2];
// Instead of comparing magnitude to ACCELERATION_THRESHOLD,
// compare their squares. This is equivalent and doesn't need the
// actual magnitude, which would be computed using (expesive) Math.sqrt().
final double magnitudeSquared = ax * ax + ay * ay + az * az;
return magnitudeSquared > ACCELERATION_THRESHOLD * ACCELERATION_THRESHOLD;
}
/** Queue of samples. Keeps a running average. */
static class SampleQueue
{
/** Window size in ns. Used to compute the average. */
private static final long MAX_WINDOW_SIZE = 500000000; // 0.5s
private static final long MIN_WINDOW_SIZE = MAX_WINDOW_SIZE >> 1; // 0.25s
/**
* Ensure the queue size never falls below this size, even if the device
* fails to deliver this many events during the time window. The LG Ally
* is one such device.
*/
private static final int MIN_QUEUE_SIZE = 4;
private final SamplePool pool = new SamplePool();
private Sample oldest;
private Sample newest;
private int sampleCount;
private int acceleratingCount;
/**
* Adds a sample.
*
* #param timestamp in nanoseconds of sample
* #param accelerating true if > {#link #ACCELERATION_THRESHOLD}.
*/
void add(long timestamp, boolean accelerating)
{
// Purge samples that proceed window.
purge(timestamp - MAX_WINDOW_SIZE);
// Add the sample to the queue.
Sample added = pool.acquire();
added.timestamp = timestamp;
added.accelerating = accelerating;
added.next = null;
if (newest != null)
{
newest.next = added;
}
newest = added;
if (oldest == null)
{
oldest = added;
}
// Update running average.
sampleCount++;
if (accelerating)
{
acceleratingCount++;
}
}
/** Removes all samples from this queue. */
void clear() {
while (oldest != null) {
Sample removed = oldest;
oldest = removed.next;
pool.release(removed);
}
newest = null;
sampleCount = 0;
acceleratingCount = 0;
}
/** Purges samples with timestamps older than cutoff. */
void purge(long cutoff) {
while (sampleCount >= MIN_QUEUE_SIZE
&& oldest != null && cutoff - oldest.timestamp > 0) {
// Remove sample.
Sample removed = oldest;
if (removed.accelerating) {
acceleratingCount--;
}
sampleCount--;
oldest = removed.next;
if (oldest == null) {
newest = null;
}
pool.release(removed);
}
}
/** Copies the samples into a list, with the oldest entry at index 0. */
List<Sample> asList() {
List<Sample> list = new ArrayList<Sample>();
Sample s = oldest;
while (s != null) {
list.add(s);
s = s.next;
}
return list;
}
/**
* Returns true if we have enough samples and more than 3/4 of those samples
* are accelerating.
*/
boolean isShaking() {
return newest != null
&& oldest != null
&& newest.timestamp - oldest.timestamp >= MIN_WINDOW_SIZE
&& acceleratingCount >= (sampleCount >> 1) + (sampleCount >> 2);
}
}
/** An accelerometer sample. */
static class Sample {
/** Time sample was taken. */
long timestamp;
/** If acceleration > {#link #ACCELERATION_THRESHOLD}. */
boolean accelerating;
/** Next sample in the queue or pool. */
Sample next;
}
/** Pools samples. Avoids garbage collection. */
static class SamplePool {
private Sample head;
/** Acquires a sample from the pool. */
Sample acquire() {
Sample acquired = head;
if (acquired == null) {
acquired = new Sample();
} else {
// Remove instance from pool.
head = acquired.next;
}
return acquired;
}
/** Returns a sample to the pool. */
void release(Sample sample) {
sample.next = head;
head = sample;
}
}
#Override public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
}
manifest
<service android:name="com.likith.shakedetector.Background_service" />
I'm simply trying to learn android using baby-steps. I've followed a tutorial on how to implement a "shake listener" from this tutorial
This is it:
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.util.FloatMath;
public class ShakeListener implements SensorEventListener {
/*
* The gForce that is necessary to register as shake.
* Must be greater than 1G (one earth gravity unit).
* You can install "G-Force", by Blake La Pierre
* from the Google Play Store and run it to see how
* many G's it takes to register a shake
*/
private static final float SHAKE_THRESHOLD_GRAVITY = 2.7F;
private static final int SHAKE_SLOP_TIME_MS = 500;
private static final int SHAKE_COUNT_RESET_TIME_MS = 3000;
private OnShakeListener mListener;
private long mShakeTimestamp;
private int mShakeCount;
public void setOnShakeListener(OnShakeListener listener) {
this.mListener = listener;
}
public interface OnShakeListener {
public void onShake(int count);
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// ignore
}
#Override
public void onSensorChanged(SensorEvent event) {
if (mListener != null) {
float x = event.values[0];
float y = event.values[1];
float z = event.values[2];
float gX = x / SensorManager.GRAVITY_EARTH;
float gY = y / SensorManager.GRAVITY_EARTH;
float gZ = z / SensorManager.GRAVITY_EARTH;
// gForce will be close to 1 when there is no movement.
float gForce = FloatMath.sqrt(gX * gX + gY * gY + gZ * gZ);
if (gForce > SHAKE_THRESHOLD_GRAVITY) {
final long now = System.currentTimeMillis();
// ignore shake events too close to each other (500ms)
if (mShakeTimestamp + SHAKE_SLOP_TIME_MS > now) {
return;
}
// reset the shake count after 3 seconds of no shakes
if (mShakeTimestamp + SHAKE_COUNT_RESET_TIME_MS < now) {
mShakeCount = 0;
}
mShakeTimestamp = now;
mShakeCount++;
mListener.onShake(mShakeCount);
}
}
}
}
I read jokes from a folder and I'd like to display a new one every time I shake the phone. I have no idea how to call the shake listener.
private SensorManager mSensorManager;
private Sensor mAccelerometer;
private ShakeListener mShakeListener;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
String message = intent.getStringExtra(MainActivity.EXTRA_MESSAGE);
TextView textView = new TextView(this);
textView.setTextSize(16);
String[] test = processData(readText(message));
textView.setText(test[0]); // later will use random
//________________________________________________
// THIS IS THE PART I HAVE NO IDEA HOW TO IMPLEMENT.
while (true) {
if (shake)
textView.setText(test[RandomNumber]);
}
//________________________________________________
setContentView(textView);
}
I think this is what I should use, but frankly I don't understand what's going on:
// ShakeListener initialization
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mAccelerometer = mSensorManager
.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mShakeListener = new ShakeListener();
mShakeListener.setOnShakeListener(new OnShakeListener() {
#Override
public void onShake(int count) {
/*
* The following method, "handleShakeEvent(count):" is a stub //
* method you would use to setup whatever you want done once the
* device has been shook.
*/
handleShakeEvent(count);
}
});
You don't call Listeners. They call you.
Move:
while (true) {
if (shake)
textView.setText(test[RandomNumber]); // this line
}
to:
#Override
public void onShake(int count) {
/*
* HERE
*/
handleShakeEvent(count);
}
Should achieve what you're aiming for.
For the past few days I've been playing around with sensors and canvas.
So far I've managed to control the location of a bitmap based on device's angles.
The way app works is it gets orientation data, and depending on how much the device is tilted, it moves the bitmap left or right on the screen.
I do most of my testing on my Samsung Galaxy S II GT-i9100 running android 4.2.2 (AOKP), and the app works pretty much flawlessly, apart from the app crashing when resuming it (I think I know what's causing that).
The problem I'm having is as follows:
When I try running the same code on a Sony Xperia Z (running android 4.1.2, stock from Sony) the whole app becomes choppy (the bitmap barely moves), and I think it's because the sensor data retrieval is choppy/slow. Same happens on my friend's Sony Xperia S.
I gave the app to my other friend who has a Nexus 4, he says he has no such problems.
GameView
public class GameView extends SurfaceView {
private Bitmap bmp;
private SurfaceHolder holder;
private GameLoopThread gameLoopThread;
private int x = 0;
private int xMultiplier = 0;
private Paint textPaint;
//lowPass
private float smoothVal = 0; //Main variable, check algorithm
private int smoothing = 5; //How strong the smoothing is, larger the value, more time is needed before the value reaches actual sensor value
//Sensors
private SensorManager sensorManager;
private SensorEventListener sensorEventListener;
//Rotation matrices for converting coordinate systems
private float[] rotationMatrixR = new float[9];
private float[] rotationMatrixI = new float[9];
//Arrays storing data for gravity and geomagnetic data needed to get device's angles
private float[] gravity = new float[3];
private float[] geomagnetic = new float[3];
//Array holding angles
private float[] angles = new float[3];
public GameView(Context context) {
super(context);
gameLoopThread = new GameLoopThread(this);
holder = getHolder();
textPaint = new Paint();
textPaint.setColor(Color.WHITE);
textPaint.setTextSize(20);
sensorManager = (SensorManager)getContext().getSystemService(Context.SENSOR_SERVICE);
sensorEventListener = new SensorEventListener() {
#Override
public void onSensorChanged(SensorEvent sensorEvent) {
Sensor sensor = sensorEvent.sensor;
if (sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
gravity = sensorEvent.values;
}
else if (sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
geomagnetic = sensorEvent.values;
}
SensorManager.getRotationMatrix(rotationMatrixR, rotationMatrixI, gravity, geomagnetic);
SensorManager.getOrientation(rotationMatrixR, angles);
}
#Override
public void onAccuracyChanged(Sensor sensor, int i) {
}
};
sensorManager.registerListener(sensorEventListener, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_FASTEST);
sensorManager.registerListener(sensorEventListener, sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD), SensorManager.SENSOR_DELAY_FASTEST);
holder.addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
gameLoopThread.setRunning(false);
while (retry) {
try {
gameLoopThread.join();
retry = false;
}
catch (InterruptedException e) {
//Shit hit the fan
Log.e("GameLoopThread", e.toString());
}
}
}
#Override
public void surfaceCreated(SurfaceHolder holder){
gameLoopThread.setRunning(true);
gameLoopThread.start();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
});
bmp = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
}
#Override
protected void onDraw(Canvas canvas)
{
x = (int) ((canvas.getWidth() / 100) * ((lowPass(angles[2]) * 100) + 50));
canvas.drawColor(Color.DKGRAY); //This also clears the screen
canvas.drawBitmap(bmp, x, canvas.getHeight() - bmp.getHeight() - 20, null);
canvas.drawText("Azimuth (Z): " + Float.toString(angles[0]),25,25, textPaint);
canvas.drawText("Pitch (X): " + Float.toString(angles[1]),25,45, textPaint);
canvas.drawText("Roll (Y): " + Float.toString(angles[2]),25,65, textPaint);
canvas.drawText("X: " + Integer.toString(x),25,85,textPaint);
}
public static BigDecimal roundFloat(float d, int decimalPlace) {
BigDecimal bd = new BigDecimal(Float.toString(d));
bd = bd.setScale(decimalPlace, BigDecimal.ROUND_HALF_UP);
return bd;
}
private float lowPass(float curValue) {
smoothVal += (curValue - smoothVal) / smoothing;
return smoothVal;
}
}
GameLoopThread
public class GameLoopThread extends Thread {
static final long FPS = 25;
private GameView view;
private Boolean running = false;
public GameLoopThread(GameView view){
this.view = view;
}
public void setRunning(boolean run){
running = run;
}
#Override
public void run(){
long tickPS = 1000/FPS;
long startTime;
long sleepTime;
while(running){
Canvas c = null;
startTime = System.currentTimeMillis();
try {
c = view.getHolder().lockCanvas();
synchronized (view.getHolder()){
view.onDraw(c);
}
}
catch (NullPointerException e) {
Log.e("GameLoopThread", e.toString());
}
finally {
if (c != null) {
view.getHolder().unlockCanvasAndPost(c);
}
}
sleepTime = tickPS - (System.currentTimeMillis() - startTime);
try {
if (sleepTime > 0) {
sleep(sleepTime);
}
else {
sleep(10);
}
}
catch (Exception e) {
Log.e("GameLoopThread", e.toString());
}
}
}
}