I'm trying to develop an app that detects a fall and so far the accuracy of a fall is in an acceptable range but the problem is the "OnSensorChanged" gets invoked way too frequently causing the toast to appear on my screen constantly.
I think the problem is that during a free fall, the "OnSensorChanged" keeps on getting called for the duration of the fall (since the values correspond to a fall) which is whats causing the toast spam on my screen.
A solution i tried is to unregister the listener after a fall so that it only appears once but the problem is that it wont detect a fall again unless i restart the app (because obviously the listener stopped working).
Is there any solution to this issue?
Thanks
private void checkFall(SensorEvent xyzValues) {
float[] values = xyzValues.values;
double rootSquare = Math.sqrt(Math.pow(values[0],2)+Math.pow(values[1],2)+Math.pow(values[2],2));
if (rootSquare < 1.5){
Toast toast = Toast.makeText(this, "The Phone Fell!", Toast.LENGTH_LONG);
toast.show();
sensorManager.unregisterListener(this);
sensorManager = null;
}
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
checkFall(event);
}
}
You can make use of a member boolean variable, mFallen that keeps track of when a fall has just occurred. You might also want to define some threshold value to indicate when the phone is back to a normal "standing" state. Try the following:
if (rootSquare < 1.5 && !mFallen){
Toast toast = Toast.makeText(this, "The Phone Fell!", Toast.LENGTH_LONG);
toast.show();
mFallen = true;
} else if (rootSquare > STANDING_THRESHOLD) {
mFallen = false;
}
Better Standing Detection Algorithm
Once the state of mFallen has changed from false to true, the magnitude of the device's acceleration vector (as you've calculated and stored in rootSquare) should be very close to 0. This is because when the device is in free-fall (falling all by itself), its acceleration will be that of gravity from the reference frame of a standing person. But in theory, the accelerometer would only be measuring values relative to free-fall, and therefore it would be exactly 0.
Since the acceleration of gravity is approximately 9.81 m/s^2, to detect when the device is not moving again, you'd need to check when the magnitude of acceleration is close to that. This is because while still, the sensor will be detecting the force that is preventing it from falling (canceling the acceleration due to gravity) which are the forces of whatever is keeping it still. The overall magnitude of the accelerations due to these forces must sum up exactly to the magnitude of the acceleration due to gravity in this case.
You could define a constant GRAVITY to be 9.81, and GRAVITY_THRESHOLD to be a tolerance value, which you can try setting to 0.1 and then refining later on. Try replacing your code with the following:
if (rootSquare < 1.5 && !mFallen){
Toast toast = Toast.makeText(this, "The Phone Fell!", Toast.LENGTH_LONG);
toast.show();
mFallen = true;
} else if (Math.abs(rootSquare - GRAVITY) < GRAVITY_THRESHOLD) {
mFallen = false;
}
Just to emphasize, the standing state here is defined as only when the device has stopped moving, so it won't be able to detect when the phone has reached a certain height with respect to the ground.
Please see https://developer.android.com/reference/android/hardware/SensorEvent.html#values, as it may help you understand the problem better.
Related
Above GIF is current look of my music player.
The center layout is WaveView and front of that is a simple musical note vector-drawable.
I first tried to set Waveview progress according to SeekBar's progress without any song playing to test it. And it worked perfectly fine (Seekbars current progress/100 which is 0.01 to 1.00).
Now what I want to do is set WaveView's Water Level (height of water) according to song progress.
When song will play, the water level will be set from 0.0f to 1.0f and revealing the White Music note icon.
Note that the WaveView's water level can only be set from 0.0f to 1.0f.
I tried multiple ways but could not solve this problem.
Here is my code which should return 0.01, 0.02 and so on ... :
this.runOnUiThread(new Runnable() {
#Override
public void run() {
if (mainMediaPlayer != null) {
currentDurationPosition = mainMediaPlayer.getCurrentPosition()/1000;
songSeekBar.setProgress(currentDurationPosition+1);
topSongTitle.setText(songsList.get(currentSongPosition).getName());
songCircleProgress.setWaterLevelRatio(currentDurationPosition/mainMediaPLayer.getDuration());
} else {
Toast.makeText(MainActivity.this, "No song is playing now", Toast.LENGTH_SHORT).show();
}
handler.postDelayed(this, 1000);
}
});
After rereading your code, I think I see two issues here:
first, your staticFloat initialisation:
private static float staticFloat = 1/100;
By doing this, you are actually doing an integer division, then converting it to a float. 1/100 in integer division equals to 0, so your float will always be 0.0f.
To have the 0.01 value, you need to do
private static float staticFloat = 1/100f; // or 1f/100
This way you are dividing an int by a float, or the opposite, and it will have a floating result.
Secondly, your getWaterPosition function. You are returning 0 when progress is 0. In every other case, you are returning, well... 0 too.
Why? You are returning on each case
returnPosition = minusPosition - (minusPosition - staticFloat);
which is equivalent to:
returnPosition = minusPosition - minusPosition + staticFloat;
and again equivalent to:
returnPosition = staticFloat;
and as we've seen before, this would always be 0, or even 0.01f when you will fix it.
I guess you made some errors when coming with that formula. But won't it work by doing only something like return progress / 100f; ?
You can also add in your xml a max value to your seekbar, so it's never above 100 and you'll have your return between 0.0f and 1.0f
<SeekBar
// initialisation
android:max="100"/>
My audio isn't playing...I've been stuck on this for around 7 hours (ALL DAY!), and have cannot figure out the issue. I am trying to have the volume mute when user clicks mute and unmute when user clicks it again.
I want to be able to have the user unmute the app to the same volume that the user was originally on. userVolumeOnStart.userVolume is where the users original volume is stored in. I use this value in the unMute, to set their volume to what it used to be.
For example:
If your volume was 7 and you used my mute button:
Open app->Mute (Volume=0)->Unmute(Volume = 7 again) etc.
Here is the listener to the mute button:
public void mute(View view) {
mutebutton = (ImageButton) findViewById(R.id.mutebutton);
if ((variableForMute.x % 2) != 0) { //If it's odd UNMUTE
Log.v(TAG, "Use this volume to unmute " +userVolumeOnStart.userVolume +"");
Toast.makeText(Main_Menu.this, "UNMUTED", Toast.LENGTH_SHORT).show();
mAudioManager.setStreamVolume(AudioManager.STREAM_RING, userVolumeOnStart.userVolume, 0);
variableForMute.x++;
} else { //If its even MUTE
userVolumeOnStart.userVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_RING);
Log.v(TAG, "Right Before Mute " +userVolumeOnStart.userVolume +"");
mAudioManager.setStreamVolume(AudioManager.STREAM_RING, 0, 0);
Toast.makeText(Main_Menu.this, "MUTED", Toast.LENGTH_SHORT).show();
variableForMute.x++;
}
}
userVolumeOnStart.userVolume is a variable that I defined in a seperate class, to use it globally. Here is where the audio actually runs. I can assure you the issue is not here (as I have not messed with the code in this class today) , but I will still provide the code anyway:
cdt = new CountDownTimer(20000, 1000) {
#Override
public void onTick(long millisUntilFinished) {
Log.v(TAG, millisUntilFinished + "left");
millisUntilFinishedRounded = ((500 + millisUntilFinished) / 1000) * 1000;
if (millisUntilFinishedRounded == 10000) { //TEN SECONDS
ten = MediaPlayer.create(TwentySeconds.this, R.raw.tenmoreseconds);
ten.start();
}
if (millisUntilFinishedRounded == 3000) {
three = MediaPlayer.create(TwentySeconds.this, R.raw.threetwoone);
three.start();
}
}
This is a service that runs in the background when the app starts.
Here is my log after changing ringer volume on my phone while app is running and pressing the mute button. It shows that userVolumeOnStart.userVolume is doing what it's supposed to be doing:
I am desperate now, so I would really appreciate any feedback (negative or positive!)
Thanks so much,
{Rich}
By the way, my question is not a duplicate of The audio volume is zero?. I have changed the audio stream since that question, and have figured out the problem there. There, there was an issue with AudioManager.STREAM_MUSIC where userVolumeOnStart.userVolume would always be 0. I have changed STRAM_MUSIC to STREAM_RING. That is not the issue now, as userVolumeOnStart.userVolume successfully changes based on user volume. The problem now is that the Media Players are not effectively working for an unknown reason.
I found the answer after 2 days!
MediaPlayer mp = MediaPlayer.create(this, R.raw.MYAUDIO);
I shouldn't break it up into declaration and instantiation:
Mp.create will call the static method which is an instance that won't get stored. When I call start() I am starting the original MediaPlayer, not the instance! So basically, I am satrting something that I never even stored, and that's why the audio wasn't playing!
For a project I need to detect the inclination (pitch) of an android device. For that, I use the acceleration sensor and the magnetic field sensor and everything works fine. The device is able to move forward and backward hanging on a pendulum. If I have the angle of the pendulum/device, I can calculate the acceleration. The problem is: All that works only stationary. If the device is in a car which is moving, it doesnn't work any more. When the car is braking, the pendulum moves forward and the inclination changes, but the device cannot detect that because the force is still acting "downward" along the y-axis.
Is it any possible to detect the inclination angle in this case?
Not sure if I've explained my problem clearly. What I need is the angle of a moving pendulum, detected by device sensors.
<< EDIT >>
What I need is the correctly measrued angle of the pendulum even when it is within a moving car (please take a look at my pdf lying in my Dropbox):
https://dl.dropboxusercontent.com/u/5655235/sensors.pdf
As you can see on the image, the measured angle is not correct if the box is accelerating.
Here is my code:
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER)
acc = event.values;
if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD)
mag = event.values;
if (acc != null && mag != null) {
float R[] = new float[9];
float I[] = new float[9];
boolean success = SensorManager.getRotationMatrix(R, I, acc, mag);
if (success) {
float orientation[] = new float[3];
SensorManager.getOrientation(R, orientation);
phiRad = orientation[1];
phi = Math.toDegrees(phiRad);
}
}
I hope that I understood your problem correctly!! So you want to detect the inclination of the device when it is in a moving car...
First of all, when you say that:
When the car is braking, the pendulum moves forward and the inclination changes, but the device cannot detect that because the force is still acting "downward" along the y-axis.
Are you sure that the device is kept vertically straight?
So, when the car is moving the pendulum will obtain an acceleration of the car in forward direction. Here, forward doesn't mean that it is directed towards x-axis of the device, but the x-axis of the car/surface. As per my understanding, you are trying to check the pendulum's movement towards x-axis, but you should check for the position of device. Hope it helps, if I understood your problem correctly.
I have a MapActivity which has a touch event.
In the touch event:
I get the current geocodes
Put an overlay image on that location.
and set it as the center of the mapview.
This seems to work fine and as expected for a while. But then the application force closes. I think the touch event is being fired like crazy even when nothing's being touched. I know that because I had put a toast set up that shows the longitude and lattitude of the touched position. and it keeps showing up even when nothing's touched.
I cant seem to accurately position the problem, let alone fixing it. Here's the touch event for my mapActivity
private void LocationStuffs(double latitude, double longitude){
itemizedOverlay.clear();
itemizedOverlay.addOverlayItem((int)latitude, (int)longitude, "place");
mapView.invalidate();
mapView.getOverlays().add(itemizedOverlay);
MapController mc = mapView.getController();
mc.setCenter(new GeoPoint((int)latitude, (int)longitude));
mc.setZoom(20);
}
public boolean onTouchEvent(MotionEvent event, MapView mapView){
//---when user lifts his finger---
if (event.getAction() == 1) {
GeoPoint p = mapView.getProjection().fromPixels(
(int) event.getX(),
(int) event.getY());
Toast.makeText(getBaseContext(),
p.getLatitudeE6() / 1E6 + "," +
p.getLongitudeE6() /1E6 ,
Toast.LENGTH_SHORT).show();
latitude = p.getLatitudeE6() / 1E6;
longitude = p.getLongitudeE6() / 1E6;
LocationStuffs(latitude*1E6, longitude*1E6);
}
return false;
}
Change your return statement to return true:
return true;
Basically, what that does is that it tells the system that you have handled the touchevent. Let me know if that solves it.
I believe you have few errors in your code:
1- You need a "return true" statement before you close the "if" statement in the "onTouchEvent" method. This tells the system that the event has been handled and it should be consumed. That's probably the reason for multiple touchs.
2- the "mapView.invalidate();" should be moved to the end of your "LocationStuffs" method, as you want to have you screen redrawn after you have add the new location and not before.
3- You are adding repeatedly the same itemizedOverlay to the mapview. After calling your "LocationStuffs" method 10 times, you will have the same overlay added 10 times to the overlays list. You should add the Itemizedoverlay only once, outside this method (on the onCreate or onResume for example)
good luck
I have been steaming over this one a few days now. I try to have a selector for a big range of numbers... within int-limits. You ought to hold down a button and the value should change slow and the longer you press the more rapid it changes. You all know that kind of button, don't you?! :D
It has to be precise, yet fast to enter the number. I use a OnTouchListener. The event allows to .getDownTime().
I tried a lot, but always only got it to skip numbers (not really but too fast to use) or not repeatingly increase the value while holding down the button. If run without changes it is pretty fast, but it would be nice, if it starts to make 10 100 or 1000-jumps if it is held down long enough.
I prepared a little SSCCE looking like this:
public class ValueChangerSSCCE extends Activity implements OnTouchListener {
Button plusButton;
TextView valueView;
Button minusButton;
int value;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LinearLayout layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL);
plusButton = new Button(this);
plusButton.setText("+");
plusButton.setTextSize(120f);
plusButton.setOnTouchListener(this);
layout.addView(plusButton);
valueView = new TextView(this);
valueView.setGravity(Gravity.CENTER_HORIZONTAL);
valueView.setTextColor(Color.WHITE);
valueView.setText(value + "");
valueView.setTextSize(120f);
layout.addView(valueView);
minusButton = new Button(this);
minusButton.setText("-");
minusButton.setTextSize(120f);
minusButton.setOnTouchListener(this);
layout.addView(minusButton);
setContentView(layout);
}
/*
* Called when a button is pressed
*/
#Override
public boolean onTouch(View v, MotionEvent event) {
if (v == plusButton) {
value++;
} else if (v == minusButton) {
value--;
}
valueView.setText(value + "");
return false;
}
}
For devices with low resolution one might have to decrease the text-sizes.
Thanks in advance!
Have a look at the NumberPicker source code. This is a widget from the SDK that does exactly what you are describing, except without the acceleration. Using post calls to a Handler as is done here is the best way to handle the timing events. The only addition you would need to make to this class is to vary the mSpeed value in the post callbacks (and perhaps the increment) based on the down time.
HTH.
The way I might try and tackle this is as follows:
Pressing + or - will start a Thread. The thread updates the number at a set interval by an offset value, if the number has changed more than 20 times then decrease the interval so the number updates faster, if the number is still changing a lot then the offset value would be made larger to skip numbers. Remember all this is happening in a thread.
When the user lifts their finger the thread is killed and the offset, interval is reset.
Use the value of getDownTime() to compute how big the counter increment should be.
First, design the response curve you would like. (That is, determine a function that defines how the value should change as a function of touch time and, perhaps, starting value. It does not — should not, from the way you describe it — be a straight line function.)
Then just make your increment follow the curve. It will probably help to store the button value and down time at the last event.