I need to create a magic 8 ball app that will change the answer when the phone is flipped. Hence when the z value of android accelerometer is less than 9 I need the answer to change once.
My problem is that since the accelerometer values keep changing when the phone is held at a stable position within the values of the if statement the loop will just keep executing.
public class MainActivity extends Activity implements SensorEventListener {
private float xvalue, yvalue, zvalue;
private SensorManager sensorManager;
private Sensor accelerometer;
private float z;
TextView answer;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_NORMAL);
}
#Override
public void onSensorChanged(SensorEvent sensorEvent) {
zvalue = sensorEvent.values[2];
if(zvalue > 9 & zvalue < 9)
printAnswer();
}
#Override
public void onAccuracyChanged(Sensor sensor, int i) {}
/** Called when the user taps the Send button */
public void sendMessage(View view) {
printAnswer();
}
public void printAnswer(){
answer = (TextView) findViewById(R.id.answer);
final String[] answers = {"It is certain", "It is decidedly so", "Without a doubt", "Yes definitely", "You may rely on it", "As I see it yes", "Most likely", "Outlook good", "Yes",
"Signs point to yes", "Reply hazy try again", "Ask again later", "Better not tell you now", "Cannot predict now", "Concentrate and ask again",
"Don't count on it", "My reply is no", "My sources say no", "Outlook not so good", "Very doubtful"};
Random r = new Random();
int randomNum = r.nextInt(answers.length);
answer.setText(answers[randomNum]);
}
}
There are a bunch of different ways to make this happen. Both of the ways I'll mention here are in the category of "debouncing" which is the process of smoothing over rough input.
You could keep a prevZ variable that always gets set at the end of onSensorChanged. This will help you know how z changes over time. If prevZ is less than your threshold and current z is above it, you've just identified a flip! If both Zs are below your threshold or above your threshold then do nothing. In this case it might be better to have 2 thresholds: a higher threshold to initialize a flip and a lower threshold for when to indicate that the flip is over. Otherwise weird scenarios might jump back and forth around your threshold and you'll still be in this situation.
Probably a better approach is to keep a timestamp of the last time you noticed a flip. When a new flip occurs, check how long it's been since the last flip. If you're within 1 second of the last flip, do nothing. More than a second? Flip again and update the timestamp for last flip. You can play around with the 1 second gap until you find a delay that's long enough that you don't display a fortune too often, but short enough that people don't feel gypped that their flip wasn't recognized (e.g. 10 seconds is probably too long).
First, you need to compare the z value from the last event and the current event and only call printAnswer() when crossing the threshold. Second, you're using a bitwise operator (&) you should be using a logical operator (&&).
long lastTime = System.currentTimeMillis();
#Override
public void onSensorChanged(SensorEvent sensorEvent) {
float newZ = sensorEvent.values[2];
long newTime = System.currentTimeMillis();
// Only print the answer when the device is flipped down
// and there wasn't an answer for at least 1 second.
long timeDiff = (newTime - lastTime) / 1000.0; // seconds
if (zvalue > 9 && newZ < 9 && timeDiff > 1)
printAnswer();
zvalue = newZ;
lastTime = newTime;
}
Related
So I wrote an android app for dice rolling, with adjustable dice (D4-100), amounts and bonuses.
It works all fine when I press the roll button, but I also wanted it to react to shaking my phone.
The problem is, when i shake it once, it displays the result, but if i shake for too long, the shown results get visibly overwritten - I don't want the user to just keep on shaking until the result is accepted!.
Is there some way to gather all ShakeEvents and only trigger the last one that occured?
Here's what's inside onCreate related to those ShakeEvents:
SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
ShakeDetector shakeDetector = new ShakeDetector(this);
shakeDetector.start(sensorManager);
and here's the "hearShake()" method (from Square, Inc.'s seismic):
#Override
public void hearShake() {
Toast.makeText(this, "Rolling...", Toast.LENGTH_SHORT).show();
Vibrator v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
v.vibrate(VibrationEffect.createOneShot(250,VibrationEffect.DEFAULT_AMPLITUDE));
} else {
v.vibrate(250);
}
rollButton.performClick();
}
Solution:
in rollButton.performClick(); I added long lastShake = System.currentTimeMillis();
The content of hearShake() is wrapped inside if (separateShake()):
public boolean separateShake(){
return ((System.currentTimeMillis() - lastShake) > 3000) ? true : false;
}
Now rollButton.performClick() only gets triggered if there's at least a 3 second delay between the shakes, which is fine for me!
One solution would be to record the time at which the shake is recorded and ignore any additional shakes that occur within, say, the next 3 seconds. That way they'd have to do quite a long shake for it to count as multiple.
I'm writing a lap timing app but have run into a GPS update frequency problem. At speeds greater than 75km/h (21m/s) my code stops working. My question is how can I request updates at a faster rate? I need it work at speeds up to 300km/h (83m/s) and would like the app to get updates every couple of meters traveled which would mean it would need an update every 0.025 seconds # 300km/h. Below is my code, I tried an alternate code to get time stamp but got the same result, I believe it's a GPS update frequency problem not a code problem. I wanted updates every couple of meters # 300km/h in case the phone passes through the proximity radius on a tangent.
int prox = 30; // Proximity Switch To Finish Line = 30 meters
int speedGov = 0; // Speed In Kmh
public void OnProviderDisabled(string provider)
{
}
public void OnProviderEnabled(string provider)
{
}
public void OnStatusChanged(string provider, Availability status, Bundle extras)
{
}
protected override void OnResume()
{
this.InitializeLocationManager();
base.OnResume();
_locationManager.RequestLocationUpdates(_locationProvider, 0, 0, this);
}
void InitializeLocationManager()
{
_locationManager = (LocationManager)GetSystemService(LocationService);
Criteria criteriaForLocationService = new Criteria
{
Accuracy = Accuracy.Fine
};
IList<string> acceptableLocationProviders = _locationManager.GetProviders(criteriaForLocationService, true);
if (acceptableLocationProviders.Any())
{
_locationProvider = acceptableLocationProviders.First();
}
else
{
_locationProvider = String.Empty;
}
}
public void OnLocationChanged(Location location)
{
_currentLocation = location;
if (_currentLocation == null)
{
}
else
{
d2fl = Convert.ToInt32(_currentLocation.DistanceTo(fl));
speedGov = Convert.ToInt32(_currentLocation.Speed * 3.6);
}
}
int A = 0; // 1st Distance to Finish Line
int B = 1000000; // 2nd Distance to Finish Line
// Get Time Stamp
while (true)
{
A = d2fl;
if (A > B && d2fl < prox && speedGov > 2) // Travelling away from Finish Line & Within 30m proximity to Finish Line & Going faster than 2km/h
{
// Time stamp for when phone first starts travelling away from Finish Line
string hours = DateTime.Now.ToString("HH");
string minutes = DateTime.Now.ToString("mm");
string seconds = DateTime.Now.ToString("ss");
string milliseconds = DateTime.Now.ToString("fff");
lapFinishTimeStamp = (Convert.ToDecimal(hours) * 3600) + (Convert.ToDecimal(minutes) * 60) + Convert.ToDecimal(seconds) + (Convert.ToDecimal(milliseconds) / 1000);
A = 0;
B = 1000000;
break;
}
B = A;
}
// Alternate Get Time Stamp - worked the same as above "Get Time Stamp"
while (true)
{
int A = d2fl;
Thread.Sleep(5);
int B = d2fl;
if (A < B && d2fl < prox && speedGov > 2)
{
string hours = DateTime.Now.ToString("HH");
string minutes = DateTime.Now.ToString("mm");
string seconds = DateTime.Now.ToString("ss");
string milliseconds = DateTime.Now.ToString("fff");
lapFinishTimeStamp = (Convert.ToDecimal(hours) * 3600) + (Convert.ToDecimal(minutes) * 60) + Convert.ToDecimal(seconds) + (Convert.ToDecimal(milliseconds) / 1000);
A = 0;
B = 0;
break;
}
A = 0;
B = 0;
}
Have read some other anwsers on this forum but are a few years old. This app will need to work on Galaxy S4 onwards.
Plus I'm a little confused about the GPS frequency's, from what I've read the GPS frequency operates at quite a high rate (hardware is around 1.6 GHz) but the phones operating systems seems to cull the data to a lower frequency, is this intentional?
Don't confuse the the radio frequency value (1.1-1.6GHz) from how frequently you will get location updates (1Hz).
Have you seen the device list in: Get GPS position on a precise time interval ? Even though its a few years old, I doubt any on device GPS will report any faster (probably due to battery/noise/use case design). Even if the on board device was reporting at 10Hz or 20Hz that is only 100ms or 50ms which is still slower than your requirement of 25ms. Remember if the CPU is talking to the GPS and calculating location - it is eating battery which is the limiting factor on mobile devices.
If you want consistent sub-second GPS value updates you'll need to use an external device.
Consumer devices update gps positions with a rate of 1hz, so one location per second.
A higher rate of e.g 10 / s would not make much sense.
Positions would not get better, more likely they get worse, however this a theoretical discussion, since consumer GPS chips usually will not provide a higher rate than 1 or 2 hz.
So just change your application design.
Especially at high speeds it is save to interpolate between two locations.
Keep in mind that position have an circular error of at least 3-5m.
So your lap timing app, migght addionally output an timingAccuracy value.
5m error at 100km/h result in an timing accuracy of 0.18s.
You can get the estimated positional error with location.getHoricontalAccuracy() (or similar names)
I'm getting weird results trying to use the method I usually use to get the duration:
float start = System.nanoTime();
//Do stuff
float duration = System.nanoTime() - start;
float durationInSeconds = duration / 1000000000.0f;
So I tried that in this flashlight app I'm making to create a strobe:
public void onClick(DialogInterface dialog, int id) {
isFlashOn = true;
isStrobeActive = true;
// Perform actual strobe
strobeHandler.post(new Runnable() {
float currDuration = 0;
final float deltaTime = 1/60f;
final float deltaTimeInMil = deltaTime * 1000;
#Override
public void run() {
float strobePerSecond = 1 / currentStrobeValue;
if (!isStrobeLightOn) {
if (currDuration > strobePerSecond) {
params = camera.getParameters();
params.setFlashMode(Parameters.FLASH_MODE_TORCH);
camera.setParameters(params);
currDuration -= strobePerSecond;
isStrobeLightOn = true;
}
} else {
params = camera.getParameters();
params.setFlashMode(Parameters.FLASH_MODE_OFF);
camera.setParameters(params);
isStrobeLightOn = false;
}
currDuration += deltaTime;
strobeHandler.postDelayed(this, (long) deltaTimeInMil);
}
});
}
The logs, however, return either 0.0 or 3.052E-5. I'm not exactly sure what I'm doing wrong, but I thing I need some help to figure it out. As always thanks.
Edit: I'm assuming that the start and end times are so similar, when they are subtracted and rounded, they are 0.
I'm guessing because float is not accurate enough if numbers get too big. Why not use long like you're supposed to?
long start = System.nanoTime();
//Do stuff
long duration = System.nanoTime() - start;
double durationInSeconds = duration / 1000000000.0d;
Take a look at the byte code. Maybe the compiler is just stripping away the call. If that's the case just create a public volatile field and assign it inside the method or as a return value.
It is call dead-code elimination. It is not the only optimisation that might be performed.
Dissasemble the class file and take a look just in case. You can do that with javap.
Take a look at [1] and [2] for micro benchmarking advice.
[1] http://www.oracle.com/technetwork/java/hotspotfaq-138619.html#benchmarking_simple
[2] http://shipilev.net/talks/devoxx-Nov2013-benchmarking.pdf
Three parts to the answer.
You should never subtract floating point numbers that are very close in value. You will get severe loss of precision, to the point of being useless. In this case, subtract the integers and convert to double only for displaying the result.
From what I can see of the code, it looks instantaneous -- in the microsecond range or below -- and the time function does not provide that level of resolution. You cannot get useful results this way.
Benchmark timing is subtle, complicated and confusing (even when it's behaving correctly!). Please read the reference provided and plan carefully. What are you trying to measure? How can you do it? What will you do with the results.
Reference: How do I write a correct micro-benchmark in Java?
I have set up an onTouchListener which allows the user to click textView2 exactly 10 times, as shown below. My goal is to measure the time between touch 1 and touch 2, and store it as a variable, say time1. However, I'm not quite sure how to do this. One idea I had was setting up a variable, i, that measures the number of times the TouchListener was clicked. I was thinking of potentially measuring the time that i contained a particular value (for example, if i was equal to 1 for 1 second, this means the time between touch 1 and touch 2 was 1 second). However I'm not sure how to implement this, and I'm not even sure if this is the correct method. Does anyone know how to solve this problem?
.java file
public class MainActivity extends Activity {
int i;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView textView2 = (TextView)findViewById(R.id.textView2);
i=0;
textView2.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN){
i++;
if (i==10) textView2.setOnTouchListener(null);
}
return false;
}
});
}
In your class
private long pressTime = -1l;
private long releaseTime = 1l;
private long duration = -1l;
Then in your onTouch method
if(event.getAction() == MotionEvent.ACTION_DOWN){
pressTime = System.currentTimeMillis();
if(releaseTime != -1l) duration = pressTime - releaseTime;
}
else if(event.getAction() == MotionEvent.ACTION_UP){
releaseTime = System.currentTimeMillis();
duration = System.currentTimeMillis() - pressTime;
}
Now you have your duration between touch events:
Duration when you press down is the time between the last time you released and the current press (if you have previously pressed down and released the button).
Duration when you release is the time between the last time you pressed down and the current release time.
-Edit-
If you need to know the difference in time of all events you can just do something like
private long lastEvent = -1l;
private long duration = -1l;
Then in onTouch event
if(lastEvent != -1l) duration = System.currentTimeMillis() - lastEvent;
lastEvent = System.currentTimeMillis();
You can also create a list of durations
private List<Long> durations = new ArrayList<Long>();
and in onTouch instead of duration = ... do
durations.add(System.currentTimeMillis() - lastEvent);
This could be useful for checking all durations between all sequential events. For example, if you want to know the time between pressing down, dragging, stopping dragging, starting dragging, and then lifting up you could check your list after you lift up for every time in question instead of having to constantly check a single duration.
You may want to keep a record of events in a List. The objects stored in this list would keep the timestamp of the touch event, and since UI events are dispatched by a single thread and the clock is monothonic, you are guaranteed that event at N + 1 has a later (at most equal) timestamp than event at index N.
I'm not sure about how you clean this list, however. It depends on how and why you read events, which in turn depends on what you want to do with the delay between two subsequent touch.
For example, if you just wanted to display the time since last touch, a simple code like this could be enough:
public class MyActivity {
private int times = 0;
private long lastTimestamp;
private void onTouchEvent(Event evt) {
if (times > 0) {
long delay = evt.getTimestamp() - lastTimestamp;
// do something with the delay
}
lastTimestamp = evt.getTimestamp();
times++;
}
}
Can I use light sensor to measure period between flashes of light? Is there any class that I can use or should I code it by myself? Can anyone provide me with something to start with?
public void onSensorChanged(SensorEvent event) {
difference = System.nanoTime() - startTime;
if (event.values[0] >= 50) {
newdifference = System.nanoTime() - (startTime + difference);
if (newdifference >= 2450 && newdifference < 2550) {
dotordash = ".";
}
else if (newdifference > 7450 && newdifference < 7550) {
dotordash = "-";
}
code += dotordash;
}
show.setText(" " + code);
}
What you do with nanoTime() is basically totally random regarding to get a dot or a hash. There're two things you're interested in:
If the light gets bright enough to count as "flash is on" or "flash is off". This is in the value of the SensorEvent and you already check that with event.values[0] >= 50. To put it in one line of code:
boolean isFlashOn = (event.values[0] >= 50)
Second is to count the time between changes of isFlashOn. You get the actual time of a event only from the SensorEvent itself as it might take some time, till the event is delivered to you. So never try to compare with the current system time.
Saying all that there's still a chance, that the light sensor delivers noise, which would be measure of darkness during a flash. If this is the case, you would need some kind of noise filtering which can get tricky, especially with something with very short durations like a flash. See Signal noise for a discussion about noise and noise filtering.