Solved - Java ShakeDetector triggers multiple times - How to get last output? - java

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.

Related

How to change magic 8 ball answer when phone is flipped?

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;
}

Faster WiFi scanning alternative than WifiManager startScan()?

TL;DR version
I need to scan and get the BSSID / M.A.C address and Signal Level of the nearby Access Points several times a second. WifiManager.startScan() scans about 3.5 seconds which is a bit slow. (better than 6 seconds for others but still too slow for me).
I'm looking for a way to get results every 200ms or maybe even less.
The more detailed version.
I'm trying to build a location finding app that uses WiFi signals instead of the GPS. So far so good with the exception that i need to get a lot of data in order to have accurate and consistent results.
I need to get the BSSID and the Signal Level from every Access Point I can find and than store this data for later use.
I've tried using the WifiManager and the BroadcastReceiver to scan and get the scan results. The problem with them is that they are really slow. It takes at least 3 seconds for a single scan. In 3 seconds signal level value will change , leading to inaccurate results. I also need to have several thousand scans which will take an awful loooot of time. So far I haven't found an alternative.
My phone is currently running Android 8.0.0
Perhaps if I optimise my code the scan will perform faster?
onCreate method:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sensor);
btnMainActivity = (Button) findViewById(R.id.button_ToMainActivity);
btnGetWifiInfo = (Button) findViewById(R.id.button_GetWifiInfo);
textWifiInfo = findViewById(R.id.textView_wifiInfo);
textWifiNr = findViewById(R.id.textView_wifiNr);
mWifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
mWifiReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context c, Intent intent) {
getScanResultInfo();
timeDifference = SystemClock.elapsedRealtime() - startTime;
textWifiInfo.setText("Seconds elapsed: "+Double.toString(timeDifference /1000.0));
nrOfScans++;
if (nrOfScans < 10){
mWifiManager.startScan();
}
}
};
getWifiInfo();
toMainActivity();
}
public void getScanResultInfo(){
int level;
List<ScanResult> wifiScanList = mWifiManager.getScanResults();
textWifiNr.setText("Nr of detected APs: "+ wifiScanList.size());
for (ScanResult scanResult : wifiScanList) {
level = WifiManager.calculateSignalLevel(scanResult.level, 5);
Log.d("WIFI","Level is " + level + " out of 5 " + scanResult.level + " on " + scanResult.BSSID + " ");
}
}
And when I press the button the scan starts.
public void getWifiInfo(){
btnGetWifiInfo.setOnClickListener(
new View.OnClickListener() {
#Override
public void onClick(View v) {
finePermission = false;
startTime = SystemClock.elapsedRealtime();
nrOfScans = 0;
checkPermissions();
if ( finePermission == true){
((WifiManager) getApplicationContext().getSystemService(WIFI_SERVICE)).startScan();
mWifiManager.startScan();
}
else {
Log.d("WIFI"," Missing Permissions: "+finePermission);
}
}
}
);
}
Thank you in advance for your time!
You can skip passive channel scanning by using the hidden API, startscanActive.
startScanActive is not an exposed API in android reference; so use it at your own risk. If you must use it, refer this - https://github.com/mozilla/MozStumbler/issues/40
Also, note that if the API indeed scans only active channels as the name suggests, you will not be able to get APs present on passive channels in your scan results
Bad news from official Android side
https://android.googlesource.com/platform/frameworks/base/+/478ee74428e04522d6ac0bf10c30ddaaf62028a4
and
https://android.googlesource.com/platform/frameworks/opt/net/wifi/+/4f11976612567e57eefb0b58c7aef1059f52b45c
announce (or propose?) to significantly limit scanResult requests per time from v9 Pie on.
In concrete values, your app should be limited to max 4 scanResult requests in 2 minutes.
This will be a disaster/nightmare for all existing WiFi-Tool apps.
Official reason: safe battery.
Let's unite our voices and let them know we want Android as a Swiss Army knife and not let it become another white brick.

CountDownTimer running even when user switches app

I'm building an Android game and I need to display a countdown. In order for the users to not cheat I can't stop this countdown if user changes app, because they would have more time to think the answer.
What I need to do is to let the CountDownTimer continue working even if my app is not currently on front (onPause has been called).
I wonder if the code I have (which seems to work) will do the trick, or if under some circumstances it would fail.
Right now the countdown continues and when it gets 0, the result screen is displayed over the current app (which, of couse, is not what I want).
I think for this second question the solution would be to store a variable or something and check that before launching results screen.
So, to sum up, what I want is the countdown to continue even if the user changes the current app, but when the countdown gets to 0, don't show the result screen until user comes back to the game.
My code is as follows:
new CountDownTimer(10000, 1000) {
public void onTick(long millisUntilFinished) {
tvTime.setText(String.valueOf(millisUntilFinished / 1000));
}
public void onFinish() {
startActivity(new Intent(GameActivity.this, ResultActivity.class));
}
}.start();
Thank you.
Just to give you an idea of what you should do. Probably should use SystemClock.elapsedTime().
public class CountdownTimer {
private final long startTime = currentTime();
private final long totalTime;
private boolean isFinished = false;
public CountdownTimer(long millis) {
totalTime = millis;
}
public long timeLeft() {
return totalTime - (currentTime() - startTime);
}
public boolean isFinished() {
return isFinished || (isFinished = timeLeft() < 0);
}
protected long currentTime() {
return System.currentTimeMillis();
}
}
If the user closes your app then you shouldn't reopen it on them. Let them open the app themselves and find out that their time is up, or display a notification.
Storing it in a variable and displaying it as long as it's not paused seems like the simplest solution. What I'd do is in the onPause method set some global variable to true, and something like
if(!thatGlobalVariable)
{
displayVariable()
}
You know what I mean, it's your idea relayed back at you.
Sorry this wasn't much help, and you were probably looking for some other more extravagant solution. I'd do this in the meantime until you get an answer that is more suitable.
Also, I think it's pretty cool if the timeout message pops up when minimized. So the person might be looking up something in google then all of a sudden "Time up, you failed". People like games that torment them into playing more.
Along with all the other game state you save in the SavedInstance you could save the current time. When user resumes your app you can compare the new time against the saved value to determine whether the time is up. Then the message can be displayed.

Handler.postDelayed function doesn't create time delays ? - Android

Below I have a Runnable "updater" ...and an OnClick function that uses Handler.PostDelayed function to run the runnable after a delay...
After a little editing, cutting of useless parts here are the functions:
(passtog = Toggle Button)
final Runnable updater = new Runnable() {
#Override
public void run() {
if (passTog.isChecked()) {
now = System.currentTimeMillis();
time = now - init;
if (time > 5000) {
Toast.makeText(getApplicationContext(), "WAKE UP !",
Toast.LENGTH_SHORT).show();
}
handler.postDelayed(this, 25);
}
}
};
passTog.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
init = System.currentTimeMillis();
flag = true;
handler.postDelayed(updater,
(new Random().nextInt(4000) + 3000));
}
});
}
Explaination
Basically, The user toggles the Toggle button. Now it's on: The runnable can run completely (Everything is in the if block).
If the user doesn't press the button again, and switches it off The app sends a Toast "Wake Up!" ..It runs and checks every 25 millisecs to update the time...
Pretty straightforward... Yet I'm having a problem.
Before the program actually gets to the runnable, I absolutely NEED there to be a minimum time delay of 3 seconds + Some Random value ... So it varies between 3 sec - 7 sec. It SHOULD vary between 3-7 , but it doesn't.
When I run it: The problem
I notice that the first time, it works great... I get atleast a 3 sec delay + a random value= Perfect
The second time, that is after the switch goes on ->off-> on : Now It acts like it doesn't see the +3000 ...and just the ~randInt(4000) function... So it may give 0 sec or it may give 4 sec delay...
In all my experience, I've never really come across this.. I've rewritten the entire code, My other apps use this function in exactly the same sytax and seem to do pretty great.. Why is this creating a problem ? Could the Toast's time possibly be causing a problem..
How to solve this ?
(I'm open to other methods, preferably quick to implement. I want a minimum 3 sec delay which I'm not getting for some reason... I need the UI to be responsive though So no thread sleeping.)
You probably should call Handler.removeCallbacksAndMessages(null) when the switch goes off.

Create an incrementing timer in seconds in 00:00 format?

I want to create an incrementing second timer like a stopwatch.
So I want to be able to display the seconds and minutes incrementing in the format 00:01...
Google only brings up 24 hour clock examples, I was wondering could anyone get me started with an example or tutorial of what I want to do?
Edit:
Here is what I have using the Chronometer in Android so far
In onCreate()
secondsT = 0;
elapsedTimeBeforePause = 0;
stopWatch.start();
startTime = SystemClock.elapsedRealtime();
stopWatch.setBase(elapsedTimeBeforePause);
stopWatch.setOnChronometerTickListener(new OnChronometerTickListener(){
#Override
public void onChronometerTick(Chronometer arg0) {
//countUp is a long declared earlier
secondsT = (SystemClock.elapsedRealtime() - arg0.getBase()) / 1000;
String asText = (secondsT / 60) + ":" + (secondsT % 60);
//textGoesHere is a TextView
((TextView)findViewById(R.id.time)).setText(asText);
}
});
In onDestroy()
#Override
public void onDestroy() {
inCall = false;
elapsedTimeBeforePause = SystemClock.elapsedRealtime() - stopWatch.getBase();
super.onDestroy();
}
The above compiles and runs but the TextView never increments, it always stays at 0, can anyone see why?
I'm assuming you aren't aware of the Android Chronometer - it already has a basic stopwatch function. You need to work with its peculiarities a bit, but it's not hard to get it to do what you want once you understand how it works.
There are a few ways that time is calculated on the phone, but the two main ones are:
The "real time", such as right now according to my computer clock, it is 11:23am in England. However, this can change if my computer contacts a time server and is told it has the wrong time, or if I were travelling with a laptop and crossed a timezone boundary. Using this would wreak havoc with your stopwatch as the measured time could change at any time.
The "elapsed time since boot", which is the number of milliseconds since the phone was switched on. This number doesn't bear any relation to the real time it is, but it will behave in a perfectly predictable manner. This is what the Android Chronometer uses.
The Chronometer is essentially a 'count up' timer, comparing the current SystemClock.elapsedRealtime() against the elapsedRealtime() that was set fot its base time. The difference between the two, divided by 1000, is the number of seconds since the timer was started. However, if you stop the timer and then start it again, you will get a counter-intuitive result - the timer will show the elapsed time as if it had never stopped. This is because you need to adjust its base time to take into consideration the time it was stopped. This is simple to do:
// When you're stopping the stopwatch, use this
// This is the number of milliseconds the timer was running for
elapsedTimeBeforePause = SystemClock.elapsedRealtime() - timer.getBase();
// When you're starting it again:
timer.setBase(SystemClock.elapsedRealtime() - elapsedTimeBeforePause);
Edit: Here is the full code for a basic stopwatch, which displays your time in a TextView rather than the Chronometer widget declared in your XML file.
public class TestProject extends Activity {
TextView textGoesHere;
long startTime;
long countUp;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Chronometer stopWatch = (Chronometer) findViewById(R.id.chrono);
startTime = SystemClock.elapsedRealtime();
textGoesHere = (TextView) findViewById(R.id.textGoesHere);
stopWatch.setOnChronometerTickListener(new OnChronometerTickListener(){
#Override
public void onChronometerTick(Chronometer arg0) {
countUp = (SystemClock.elapsedRealtime() - arg0.getBase()) / 1000;
String asText = (countUp / 60) + ":" + (countUp % 60);
textGoesHere.setText(asText);
}
});
stopWatch.start();
}
}
In your main.xml you need to have this
<Chronometer android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/chrono"
android:visibility="gone"/>
There's undoubtedly a way to get the Chronometer to work without declaring it in the XML file, but the constructor Chronometer stopwatch = new Chronometer(this); didn't work properly.
The above code displays the elapsed time in a very basic way. For example, if only 5 seconds have gone by, it will show 0:5 rather than the 0:05 you probably want. Fixing that is not hard to do, but I'll leave that for you to work out! :)
How about this one Digital Clock.
But basically you should be able to find many java implementations by googling for keywords like java, stopwatch, digital clock, ...
You could take a look at Stopwatch Applet and modify as needed (Oops no source with this one)

Categories