I am programming an app which will send an SMS message when a set time runs out. When I change the orientation of the phone or temporary leave this activity, the timer still keeps counting, but the Stop button does not work, and the Start button is visible. I don't know how to save the settings; I know I have to use onSaveInstanceState, but I don't know exactly what to put in there.
Code:
public void startThread(View view) {
stopThread = false;
sum = sec + mins;
if (sum == 0) {
Toast.makeText(Background.this, "Please enter a positive number", Toast.LENGTH_SHORT).show();
return;
}
ExampleRunnable runnable = new ExampleRunnable(sum);
new Thread(runnable).start();
btnStartThread.setEnabled(false);
}
public void stopThread(View view) {
stopThread = true;
btnStartThread.setEnabled(true);
}
class ExampleRunnable implements Runnable {
int seconds;
ExampleRunnable(int seconds) {
this.seconds = seconds;
}
#Override
public void run() {
for (; ; ) {
for (int i = 0; i < seconds; i++) {
if (stopThread) {
return;
}
if (i == sum-1) {
runOnUiThread(new Runnable() {
#Override
public void run() {
if(check_sms.isChecked() && edit_phone_number.length() > 0) {
sendSMS();
}
}
});
}
Log.d(TAG, "startThread: " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
I would persist a flag like "hasTimerStarted" and set it true/false depending if the timer has started or not and, if the timer has indeed started, I would save some other flags like "timerStartedEpoch" (the epoch number when time has started), "timerTimeoutMs" (the timeout ms).
This way, whenever you leave the activity and come back to it, you can load the flags and update the state based on them.
Related
My program has a edittext field that needs to be focused at all times but ,for some reason ,after it detectes the data is duplicated and deletes it, it should refocus but it dosent.
This code below checks if the code is duplicated on the last 5 codes scanned. if it is not duplicated it adds it to the 5 last codes added and it sends it to a database (no problem here, that part works great), if it's duplicated then it clears the field and it attemps to refocus on the edit text, but here is where the code fails.
Notes:
txtmanualbarcode is an edit Text.
setInputType(inputType.Type_NULL) hides the keyboard as it is not needed.
scanneditems is an array list of the last 5 codes.
allscanneditems is the complete list of codes.
If further code or explanation is needed please ask me, this error has been dragging me down for some days.
public void addScannedItem(final QrScannedItem it) {
boolean duplicado = false;
for (int a = 0; a < scannedItems.size(); a++) {
if (scannedItems.get(a).qr_code.compareTo(it.qr_code) == 0) {
txtmanualbarcode.setText("");
txtmanualbarcode.clearFocus();
txtmanualbarcode.requestFocus();
txtmanualbarcode.setInputType(InputType.TYPE_NULL);
duplicado = true;
}
}
if (!duplicado) {
scannedItems.add(0, it);
if (scannedItems.size() > 5) {
scannedItems.remove(5);
allScannedItems.add(0, it);
}
runOnUiThread(new Runnable() {
public void run() {
adapter.notifyDataSetChanged();
}
});
showProgressBar();
Thread thr = new Thread() {
public void run() {
try {
String state = status.compareTo("in") == 0 ? "1" : "0";
String time = getScanTime();
JSONObject obj = QrCourseServer.checkin(state, it.qr_code, time, course.id, AppCache.currentUser.token);
String msg = obj.getString("message");
it.extraMessage = msg;
if (msg.toLowerCase().compareTo("socio") == 0) {
//play socio beep
playMp3Resource(R.raw.ding);
} else {
//play no socio beep
playMp3Resource(R.raw.dingdong);
}
} catch (Exception ex) {
Log.e(TAG, "Exception: " + ex.getMessage());
it.extraMessage = ex.getMessage();
playMp3Resource(R.raw.error);
//UIHelper.msbox("Error",ex.getMessage(),ScanActivity.this);
} finally {
closeProgressBar();
runOnUiThread(new Runnable() {
public void run() {
adapter.notifyDataSetChanged();
txtmanualbarcode.setText("");
txtmanualbarcode.requestFocus();
txtmanualbarcode.setInputType(InputType.TYPE_NULL);
}
});
}
}
};
thr.start();
} else {
txtmanualbarcode.requestFocus();
}
}
My problem resides (probably) within the first for loop.
One of the options would be to use InputMethodManager to make it hide the keyboard without your input losing focus.
Using helper class defined below you can update your code and get rid of using setInputType(InputType.TYPE_NULL). Also, do not clear focus before requesting focus again. There is no use and potentially it can be the source of bugs.
for (int a = 0; a < scannedItems.size(); a++) {
if (scannedItems.get(a).qr_code.compareTo(it.qr_code) == 0) {
txtmanualbarcode.setText("");
txtmanualbarcode.requestFocus();
KeyboardHelper.hideKeyboard(txtmanualbarcode);
duplicado = true;
}
}
Code to hide keyboard without losing focus:
public class KeyboardHelper {
public static void hideKeyboard(View view) {
if (view.getContext() == null) {
Log.d(KeyboardHelper.class.getSimpleName(), "hideKeyboard failed. View detached");
return;
}
InputMethodManager mgr = getInputManagerFromContext(view.getContext());
if (mgr != null) {
mgr.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
}
private static InputMethodManager getInputManagerFromContext(Context context) {
return (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
}
}
I'm making a timer which has a button I can control either pick the count-up mode or countdown mode. The count down mode is working perfectly, start, pause, reset. The count-up timer is made by reversing the countdown timer in Android studio. the problem is I can't pause the count-up timer and resume from where it stops.
What's the proper way to do that? I know it could be done by chronometer for count-up mode, but I'm trying to make a customized timer widget that as I mentioned before, a button could be pressed to let the user decide which mode he wants. So if possible, I don't want to use another widget in my current set up.
public void startBuiltInTimer(int millisecondCountingInterval) {
if (getTimeInMillis() > 0) { //getTimeInMillis will read the time value in the current Timer widget, which is a textView
builtInTimerIsCounting = true; //set the timer is running flag
showAsCounting(true);
if (getTimerCountUpOrDown() == TIMER_COUNT_DOWN) { //countdown mode
countDownTimer = new CountDownTimer(getTimeInMillis(), millisecondCountingInterval) {
#Override
public void onTick(long millisUntilFinished) {
setTime(millisUntilFinished);
}
}
#Override
public void onFinish() {
setTime(0);
builtInTimerIsCounting = false;
showAsCounting(false);
}
}
}.start();
} else if (getTimerCountUpOrDown() == TIMER_COUNT_UP) { //count up mode
long tempTimeInMillis = getTimeInMillis() + lastTimeMillisUntilFinished; //trying to save the last time value in millis and keep the total time value as user defined.
setTime(0); //set initial value of the textView to show the count-up timer start from zero, initially.
countDownTimer = new CountDownTimer(tempTimeInMillis, millisecondCountingInterval) {
#Override
public void onTick(long millisUntilFinished) {
long temp = tempTimeInMillis - millisUntilFinished;
setTime(temp); //reverse the countdown to count-up
lastTimeMillisUntilFinished = millisUntilFinished;//keep the millisUntilFinished for next start.
}
#Override
public void onFinish() {
setTime(tempTimeInMillis);
builtInTimerIsCounting = false;
showAsCounting(false);
}
}
}.start();
}
}
}
Edit:
I end up to use SystemClock to keep tracking and record the count up timer. Here is the code.
else if (getTimerCountUpOrDown() == TIMER_COUNT_UP) {
startCountUpTimer(millisecondCountingInterval);
}
public void startCountUpTimer(int millisecInterval) {
if (!countUpTimerIsRunning) {
handler = new Handler();
myRunnable = new MyRunnable(millisecInterval); //MyRunnable can pass a variable thru to let handler have more flexibility with postDelay method. You can still use regular Runnable with hard coded time interval in millisec.
startTime = SystemClock.uptimeMillis(); //use startTime to mark the time when the count up timer start ticking
handler.postDelayed(myRunnable, millisecInterval);
countUpTimerIsRunning = true;
}
}
public class MyRunnable implements Runnable {
private int interval;
public MyRunnable(int t) {
this.interval = t;
}
#Override
public void run() {
millisecondTime = SystemClock.uptimeMillis() - startTime; //this is the time value measured by the count up timer
updateTime = timeBuff + millisecondTime;
setTime(updateTime);
if (updateTime <= countUpMode_Threshold) {
handler.postDelayed(this, interval);
} else {
setTime(countUpMode_Threshold);
handler.removeCallbacks(myRunnable);
builtInTimerIsCounting = false;
showAsCounting(false);
if (onBuiltInTimerStatusChangeListener != null) {
onBuiltInTimerStatusChangeListener.onStatusChange(OnBuiltInTimerStatusChangeListener.STATUS_FINISHED);
}
}
}
}
public void pauseCountUpTimer() {
timeBuff += millisecondTime;
handler.removeCallbacks(myRunnable);
countUpTimerIsRunning = false;
}
One way is that you should keep track of the pause time manually and start timer from that time again. Or you can try using the utility at this link It has the functionality you are looking for.
Pretty helpful
Maybe something like this:
private CountDownTimer timer;
private long COUNT_DOWN = -1;
private long COUNT_UP = 1;
public void startBuiltInTimer(boolean isDown) {
if (isdown) {
launchTimer(COUNT_DOWN);
} else {
launchTimer(COUNT_UP);
}
}
public void launchTimer(long interval) {
builtInTimerIsCounting = true; //set the timer is running flag
showAsCounting(true);
timer = new CountDownTimer(getTimeInMillis(), TimeUnit.SECONDS.toMillis(interval) {
#Override
public void onTick(long time) {
setCountdownText(time.intValue());
setTimeInMillis(time);
}
#Override
public void onFinish() {
setCountdownText(time.intValue());
builtInTimerIsCounting = false;
showAsCounting(false);
}
}.start();
public void stopTimer() {
timer.cancel();
}
...
I have a MediaPlayer and an audio file, which I need to play, also I have an ArrayList with the certain seconds of this audio file, on this seconds while audio is playing, I need to switch pages in the ViewPager. How to get a CallBack from the MediaPlayer in these certain moments of the time? or maybe you will recommend some other way how is possible to do it.
What I have right now is the method, where I use CountDownTimer, which change pages in ViewPager, but it works wrong and it doesn't take into the account fact that an audio track can be stop and then resume.
public void startPlayingLabels() {
mPlayer = new MediaPlayer();
try {
String mFileName = CameraFragment.mAudioFolder + "/" + ViewActivity.parentName + ".3gp";
mPlayer.setDataSource(mFileName);
mPlayer.prepare();
mPlayer.start();
for (int i=0; i<zeroLabelPosition.size()-1; i++) {
final int finalI = i;
new CountDownTimer(Integer.parseInt(String.valueOf(Integer.parseInt((String) zeroTime.get(finalI + 1)) - Integer.parseInt((String) zeroTime.get(finalI)))), 1000) {
public void onTick(long millisUntilFinished) {
}
public void onFinish() {
mPager.setCurrentItem(Integer.parseInt(String.valueOf(zeroLabelPosition.get(finalI))));
}
}.start();
}
} catch (IOException e1) {
e1.printStackTrace();
}
}
Android madiaplayer does not have any timeline update, thats why you can write your own, or you can use something like this.
CountDownTimer timer;
int timeStampIterator;
int[] timeStamp = new int[5]; // your own time stamps when view must be switched
void play() {
mPlayer.start();
timer = new CountDownTimer(mPlayer.getDuration() - mPlayer.getCurrentPosition(), 1000) {
#Override
public void onTick(long millisUntilFinished) {
int timeSpends = mPlayer.getDuration() - mPlayer.getCurrentPosition();
if (timeSpends == timeStamp[timeStampIterator]) {
//switchToNExtSlide();
timeStampIterator++;
}
}
#Override
public void onFinish() {
}
}.start();
}
void pause() {
mPlayer.pause();
timer.cancel();
timer = null;
}
void stop() {
mPlayer.stop();
timer.cancel();
timer = null;
timeStampIterator = 0;
}
you can also use handler for it like following
Handler handler = new Handler();
Runnable notification = new Runnable() {
public void run() {
startPlayProgressUpdater();// change hear page for view pager, because it call after every 1 second
}
};
handler.postDelayed(notification, 1000);
I am trying to simulate an sending msg progress .
The main idea is to colour the members who message sent to them, promote the status bar etc.. (gui changes ) all this with delay for each loop iteration.
The main problem is that everything is going inside the onClick listener and runing thread inside it wont help ): cause gui is changing only after delay is completed.
Any one may advice me how to do this simulation ?
send.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
for(int i=0; i<phoneNumbers.size(); i++){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
members.get(i).setBackgroundColor(Color.GREEN);
members.get(i).invalidate();
mProgressDialog.setProgress(i+1);
sentCount.setText(getString(R.string.msgSentCount) + (i+1));
if(i==12){
scroolView.animate().translationYBy(-50);}
}
}
});
Thanks you very much !
If I unserstood you right - fake progress + waiting time. First of all handlers a perferct to delay an output:
//Fake Progress
progress = new ProgressDialog(this);
open(this);
// Delay output
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
setData();
}
}, 6000);
in the function setData(), do the task you wanna run after
and progress bar for fake:
public void open(Activity activity) {
progress.setMessage("Fakeprogress, Data Mining...");
progress.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progress.setIndeterminate(false);
progress.setCancelable(false);
progress.setMax(100);
progress.setProgress(10);
progress.show();
final int totalProgressTime = 100;
final Thread t = new Thread() {
#Override
public void run() {
int jumpTime = 0;
while (jumpTime < totalProgressTime) {
try {
sleep(60);
//System.out.println("********" + jumpTime);
jumpTime += 1;
progress.setProgress(0);
progress.setProgress(jumpTime);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
progress.dismiss();
}
};
t.start();
}
Finaly got it works with recursion !!!!
public static int counter=0;
public void runMother(final Handler handler)
{
if(counter==3)return;
handler.postDelayed(new Runnable() {
#Override
public void run() {
members.get(counter).setBackgroundColor(Color.GREEN);
counter++;
runMother(handler);
}
}, 1000*counter);
I have this code below that is in my main activity. Basicly whenever I click the button, it will first check if an alarm is set, if it is false it will go into a loop which reads the RSSI on a connected device until it is above a RSSI value. My question is how do i make this loop not crash my app, which it currently does. Also for some reason the mRSSI text field never gets populated with the RSSI value. Can someone please help me out. This is the last thing in my app i need to get done.
public void onMonitorClick(final View view){
if (isBLEEnabled()) {
if (!isDeviceConnected()) {
// do nothing
} else if (isImmediateAlertOn == true) {
showMonitor();
DebugLogger.v(TAG, "app is high alert");
isImmediateAlertOn = true;
}
else {
DebugLogger.v(TAG, "app is no alert");
hideMonitor();
while(monitorStop != 1)
{
((ProximityService.ProximityBinder) getService()).getRssi();
rssilevel = ((ProximityService.ProximityBinder) getService()).getRssiValue();
if (rssilevel > -50 ) {
DebugLogger.v(TAG, "greater then -50");
monitorStop = 1;
}
mRSSI.setText("-" + String.valueOf(rssilevel) + "dB");
isImmediateAlertOn = false;
mFindMeButton.setEnabled(false);
}
}
} else {
showBLEDialog();
}
}
edit redone code
public void onMonitorClick(final View view){
if (isBLEEnabled()) {
if (!isDeviceConnected()) {
// do nothing
} else if (monitorvis == 0) {
showMonitor();
} else if (isImmediateAlertOn == true) {
showMonitor();
DebugLogger.v(TAG, "app is high alert");
isImmediateAlertOn = true;
}
else {
DebugLogger.v(TAG, "app is no alert");
hideMonitor();
monitorStop = 0;
do { run(); run2(); } while(monitorStop != 1);
}
} else {
showBLEDialog();
}
}
protected void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
((ProximityService.ProximityBinder) getService()).getRssi();
rssilevel = ((ProximityService.ProximityBinder) getService()).getRssiValue();
mRSSI.setText("-" + String.valueOf(rssilevel) + "dB");
}
});
}
protected void run2() {
runOnUiThread(new Runnable() {
#Override
public void run() {
mRSSI.setText("-" + String.valueOf(rssilevel) + "dB");
if (rssilevel < -60)
{
monitorStop = 1;
showMonitor();
((ProximityService.ProximityBinder) getService()).startImmediateAlert();
}
}
});
}
This is (IMHO) the easiest way to delay execution of a piece of code:
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
// Do what you need to do
}
}, MILISECONDS_BEFORE_EXECUTION);
Here, MILISECONDS_BEFORE_EXECUTION is a value (constant or variable) of the milliseconds you need to wait before executing the code. Documentation of Handler in Android.