Android- executing commands while countdown timer is running - android

I am trying to write an app that shows the user a string on textview, and replace the text due to user input. This should happen on time intervals of 60 seconds.
I tried to use a countdown timer, which sets a boolean var to false on it's onFinish method. While this boolean is true, I am trying to change the strings in the textview (further work will be to change the text due to the user actions, but I'm still trying to get this work for simple actions).
For now, the app seems to be stucked and nothing happens, but if I am removing the while loop, there is a single text on the screen (as it should be).
Is there a problem using a while loop for that purpose? Is there a different way to work along with the timer?
(p.s I know there are some problems in the code, but this is just for isolating the problem. Thanks!)
EDIT:
I will try to clarify my intentions:
I want to give the user tasks, which he should perform in a given time. I want to display the time remaining on the screen, along with the task to perform. When the user finishes the task, if there is still time on the clock, he will press a button and another task will appear. The goal is to finish as many tasks as possible in the given time.
My code:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_start_game);
Bundle b = getIntent().getExtras();
game = b.getParcelable("game_record");
figuresList = new ArrayList<String>(game.getFiguresList());
clockView = (TextView) findViewById(R.id.clockView);
figureText = (TextView) findViewById(R.id.figureText);
timer = new CountDownTimer(10000, 1000) {
public void onTick(long millisUntilFinished) {
clockView.setText(Long.toString(millisUntilFinished / 1000));
}
public void onFinish() {
clockView.setText("done!");
isTurn = false;
}
};
playRound();
}
private void playRound() {
figuresIterator = figuresListUsage.iterator();
isTurn = true;
String nextFigure = figuresIterator.next();
timer.start();
while (isTurn == true) {
figureText.setText(nextFigure);
nextFigure = figuresIterator.next();
}
}

It's a little difficult to understand your games logic but the main problem i see with your code is that you are entering the loop in a lifecycle event handler. Take a look at this lifecycle description. You are stopping it in onCreate and there is still work to be done before the activity will finish its lifecycle handling.
I suggest you try making play round event bound or use a diffrent thread for it. there are alot of threading APIs for android and as i dont know the nature of your game rounds i cant recommend any.

Related

Android: Progress bar not showing intermediate states

There are other similar SO questions but none really helped.
I just want to implement a progress bar, that on button click, starts from 0, moves gradually and stops at a point (which is read from sharedPreferences).
However things are working but that progress bar is not gradually updating , instead it appears straight at the end point.
A simplified code I'm pasting here:
public class MainActivity extends ActionBarActivity {
int primaryProgress = 0;
int finalPrimaryProgress = 60;
ProgressBar progressBar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
progressBar = (ProgressBar) findViewById(R.id.progress_bar);
progressBar.setMax(100);
progressBar.setProgress(0);
}
public void click(View view) {
while (primaryProgress < finalPrimaryProgress)
{
progressBar.setProgress(primaryProgress+=1);
Thread.sleep(100); //This is inside Try-catch in actual code.
}
}
}
I know the problem is with Threads, but I am not too friendly with threads so unable to understand the problem.
I'm assuming your "click" method is happening on the UI thread (otherwise, you'd probably get an exception when you try to alter the view on a background thread).
Think about what you're doing here - the moment you click a button, you begin a loop. On each iteration of the loop, you change the value of the progress bar, and then make the (main) thread sleep for 100ms, then repeat.
Problem is, you're never letting the runtime react to what change you made. Once you change a value of a view (for instance, change the text of a TextView), the Android runtime needs to have time to react to your change, adjusting the view's size, position and then redraw. Your code basically blocks anything from happening before you change the value again, because you're telling the main thread to sleep, and then you immediately change the value again.
The expected result here would be seeing the progress bar drawn in its final state once you stopped changing its value without letting the runtime actually do anything with it.
As an alternative to your current click method, try doing something with postDelayed. This will allow the runtime to perform a full measure-layout-draw cycle between every iteration:
private void increaseProgressBar()
{
if (progressBar.getProgress() < progressBar.getMax())
progressBar.postDelayed(new Runnable() {
#Override
public void run() {
progressBar.setProgress(progressBar.getProgress() + 1); // increase the value of the progress bar.
increaseProgressBar(); // call the function again, but after a delay.
}
}, 100);
}
public void click(View v)
{
increaseProgressBar();
}
Note: Keep in mind that currently, clicking the button multiple times will cause erratic behaviour, since your basically adding additional runnables to this sequence, causing the progress bar to grow faster. You could defend against such a case fairly easily with a simple flag.

Android game countdown timers

I am working on a game (my first) for Android. I want a function like a lot of games do where a "consumable" regenerates over time. I want to display the countdown timer for a +1 on the consumable. When it reaches 0, i want the timer to reset and start over, while the consumable increases by 1. If the consumable is at it's pre-defined max, I want the timer to stop until the consumable is used, then systematically start again.
public class MainActivity extends Activity {
int currentConsumable = 2;
int maxConsumable = 5;
boolean isConsumableMaxed = false;
long timeToAddConsumable; //unknown calculation for 10 minutes
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
TextView countDown = (TextView) findViewById(R.id.countDownDisplay);
if (currentConsumable==maxConsumable) {
isConsumableMaxed = true;
//some function to stop the timer
} else { isConsumableMaxed = false; }
//function to begin countdown and setText to countDown's TextView
....
}
}
What I am looking for assistance with is how to calculate the time I need (10 minutes - but i'd prefer the equation so I can change it without asking for the specific time again).
I would also like to know how to get a countdown to begin and display it on countDown, as well as how to cancel it when isConsumableMaxed == true.
Note - the countdown should continue when the game is closed.
Thanks in advance for your assistance.

Click Listener in Android, How to check if no Button is clicked for particular time period?

I am having 10 different buttons in my application for different task to perform. I want to develop one service which continuously check (listens) and if user is not clicking any button for particular time let say for 5sec than i wish to perform some other task. How can I check that user has not clicked any button? If anyone having any idea please kindly let me know.
You could simply set a Timer to the desired length. When a button is clicked, just reset the timer. Start the timer in onResume so it starts even if the user is coming back from a phone call or other activity. You should probably stop the timer in onPause of the activity too.
In each of your click listeners save off the time the last button was clicked:
private long lastClickTimestamp;
private Handler handler = new Handler();
public void onCreate( Bundle saved ) {
BackgroundJob job = new BackgroundJob();
handler.postDelayed( job, SECONDS_TO_WAIT * 1000 );
button1.setClickListener( new OnClickListener() {
public void onClick( View view ) {
lastClickTimestamp = System.currentTimeInMillis();
// do the listener logic for button 1 here.
}
});
button2.setClickListner( new OnClickListener() {
public void onClick( View view ) {
lastClickTimestamp = System.currentTimeInMillis();
// do the listener logic for button 2 here.
}
});
// repeat that for all 10 buttons.
}
Now the smarter developer would create a reusable base class that handled setting the timestamp once, then reuse that base class in each of the 10 buttons. But, that's left up to you. Then the background job would look like:
public class BackgroundJob implements Runnable {
private boolean done = false;
// meanwhile in job:
public void run() {
if( lastClickTimestamp > 0 && System.currentTimeInMillis() - lastClickTimestamp > SECONDS_TO_WAIT * 1000 ) {
// let's do that job!
}
if( !done ) {
// reschedule us to continue working
handler.postDelayed( this, SECONDS_TO_WAIT * 1000 );
}
}
}
If you have to use a service you can send a notification to the service saying a button was clicked, then the service can keep track of the time when that occurred. I wouldn't use a service for this because playing an animation or sound doesn't need to survive if the app is put into the background or killed. Services are meant for things like playing music when someone is doing something else, chat applications, or things that need to run in the background when the user isn't interacting with the application. What you're describing could be done as I've shown because when the user gets a phone call or text message they'll leave your application, and the animation or sound you're playing probably should stop too. Pretty easy to do with the Handler option I showed. More difficult, but doable, with a service.
On each button click, update some Calendar object to a new Calendar instance and then you can check what the time is of that Calendar and see if it's more than 5 minutes ago.
//this is a field
Calendar calendar;
public void onClick(View v) {
calendar = Calendar.getInstance();
//etc
switch(v.getId()) {
}
}

Android Chronometer, retain time state (and keep counting in background)

I have a timer that counts up from the time a user encounters that activity
I am currently using a Chronometer set during onCreate (initially started only when certain conditions are met). But I need the chronometer to keep counting upward until the app and all its views are closed (I have an "Exit" function to do that).
The problem is that the Chronometer gets reset to zero on every time I look at another tab and come back to its activity. (This has to do with the oncreate, but I dont know the way around it)
I didn't find an intuitive way to save the chronometer's state or countup in the background on its own (or to perhaps keep track of the time on my own and update the chronometer visually at a different point in time)
One idea I had was to start the Chronometer with a service and let the service keep counting , while having a textview in the existing activity update using the chronometer's current time tally as a string
any insight on a known approach to this problem be appreciated!
This is further complicated because this is an activity in a tabhost, and tabhosts call both onPause and onResume every time you load a view, so this breaks lifecycle functions.
There are a number of ways to persist the time. The easiest one I have found is to store the time in the Intent that was used to create the original activity via getIntent().putExtra("START_TIME", floatvalue). You may retrieve the value with getIntent().getFloatExtra("START_TIME", 0f). Doing it this way has a number of benefits:
It doesn't break the Activity LifeCycle and does not require a Context.
It can be passed easily between other Activities and Applicaitons.
It persists among Pauses and Stops.
It doesn't require special listeners.
It doesn't create any new objects (the Intent is the one used to create the Activity the first time).
This solution is great for persisting in a Tabbed Activity, or across Dialogs, etc. It has some limitations if leaving the Application to a more memory intensive one, but only if your Activity is destroyed (due to memory).
Because of my Tabhost, the lifecycle functions could not be relied on.
What I did was make the chronometer a static global in a central class, and added a ontabchangedlistener within my tabhost that checked to see if the tab being changed to was the tab with the chronometer. If this was true then it stores the Long value of the chronometer's current time.
tabHost.setOnTabChangedListener(new OnTabChangeListener(){
#Override
public void onTabChanged(String arg0) {
// TODO Auto-generated method stub
if(arg0.contentEquals("homeGroup"))
{
//store time in centralhelper.java
//stopWatch is of type Chronometer
//stopWatchLastTime is of type Long and is initially set to zero. Chronometer uses milliseconds to determine time, will never be zero after set
CentralHelper.stopWatchLastTime = CentralHelper.stopWatch.getBase();
}
}
});
When my homeGroup view loads, the onResume() function is called, there is a condition here to retrieve the time for the chronometer to resume counting from. Despite the fact that a tabhost will call both onPause() and onResume() in EVERY load outside of normal lifecycle functions, they still get called before onCreate()
public void onResume(){
super.onResume();
//update Chronometer with time stored in tabchangelistener
if(CentralHelper.stopWatchLastTime!=0)
CentralHelper.stopWatch.setBase(CentralHelper.stopWatchLastTime);
}
this allowed me to do a similar check in onCreate()
if(CentralHelper.stopWatchLastTime!=0)
{
CentralHelper.stopWatch.start(); //this is where it resumes counting from the base set in onResume()
}
else
{
CentralHelper.stopWatch.start();
CentralHelper.stopWatch.setBase(SystemClock.elapsedRealtime());
}
When you switch to a different activity the previous one is paused (onPause, asand so on, in attached image) when you came back to the activity it is resumed, but occasionaly when dalvik runs out of memory your Activity object can be deleted when ton showing.
If you keep your application data in the Activity instance you might loose it accidentally, please read this Activity Lifecycle http://developer.android.com/reference/android/app/Activity.html
This approach is tested and it works really well.
Try this:
Take a boolean volatile variable which will control your thread(start/stop). Take three text views, hour, min and sec text views, and remove chronometer completely. Update your UI using a Handler Write the following code.
public void timeUpdate()
{
timerThread = new Thread(new Runnable() {
#Override
public void run() {
while(continueThread){
Date newDate = new Date();
if(((newDate.getTime()) - date.getTime()) > 1000){
secondCounter = secondCounter+1;
mHandlerUpdateSec.post(mUpdateSec);
System.out.println("Inside the Theread ..."+secondCounter);
if(secondCounter > 59){
minuteCounter = minuteCounter + 1;
mHandlerUpdateMinute.post(mUpdateMinute);
secondCounter = 0;
if(minuteCounter > 59){
hourCounter = hourCounter + 1;
mHandlerUpdateHour.post(mUpdateHour);
minuteCounter = 0;
}
}
}
try{
timerThread.sleep(1000);
}catch (Exception e) {
// TODO: handle exception
}
}
}
});
timerThread.start();
}
The continueThread is a boolean volatile variable. Setting it to false will stop the thread. The timerThread is an instance of thread. There are three counters, hour, min and sec counters which will give you the latest time values. The handlers are updated as follows.
final Handler mHandlerUpdateSec = new Handler();
final Runnable mUpdateSec = new Runnable() {
public void run() {
String temp = "" + secondCounter;
System.out.println("Temp second counter length: " + temp.length());
if(temp.length() == 1)
secTextView.setText("0" + secondCounter);
else
secTextView.setText("" + secondCounter);
}
};
final Handler mHandlerUpdateMinute = new Handler();
final Runnable mUpdateMinute= new Runnable() {
public void run() {
String temp = "" + minuteCounter;
System.out.println("Temp second counter length: " + temp.length());
if(temp.length() == 1)
minTextView.setText("0" + minuteCounter);
else
minTextView.setText("" + minuteCounter);
}
};
final Handler mHandlerUpdateHour = new Handler();
final Runnable mUpdateHour = new Runnable() {
public void run() {
String temp = "" + hourCounter;
System.out.println("Temp second counter length: " + temp.length());
if(temp.length() == 1)
hourTextView.setText("0" + hourCounter);
else
hourTextView.setText("" + hourCounter);
}
};
Now, whenever you want to start the timer, set continueThread to true and call timeUpdate(). To stop it, just do continueThread = false. To start the thread again, set continueThread to true and call timeUpdate() again. Make sure you update the counters accordingly while you start/stop the timer.
You could save the start time in a sharedpreferences (or file, etc.) and establish your count-up from that (rather than starting at 0) in onResume().
Your UI may need some changes to handle the fact that you will have to reset the start time, since it could theoretically count forever.

Implementing a while loop in android

I can't understand the implementation of a while loop in android.
Whenever I implement a while loop inside the onCreate() bundle, (code shown below)
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
TextView=(TextView)findViewById(R.id.TextView);
while (testByte == 0)
updateAuto();
}
nothing boots up, and the program enters a "hanging" state after a while and I can't understand why. Testbyte is as follows:
byte testByte == 0;
and updateAuto() is supposed to update the code per 1 second and display inside the textView portion. I've been using setText inside updateAuto() as shown below and everything works fine, but once i implement the while loop all i see is a black screen and then an option to force close after a few seconds due to it "not responding".
TextView.setText(updateWords);
I've changed it to a button format (meaning i have to click on the button to update itself for now), but i want it to update itself instead of manually clicking it.
Am i implementing the while loop in a wrong way?
I've also tried calling the while loop in a seperate function but it still gives me the black screen of nothingness.
I've been reading something about a Handler service... what does it do? Can the Handler service update my TextView in a safer or memory efficient way?
Many thanks if anyone would give some pointers on what i should do on this.
Brace yourself. And try to follow closely, this will be invaluable as a dev.
While loops really should only be implemented in a separate Thread. A separate thread is like a second process running in your app. The reason why it force closed is because you ran the loop in the UI thread, making the UI unable to do anything except for going through that loop. You have to place that loop into the second Thread so the UI Thread can be free to run. When threading, you can't update the GUI unless you are in the UI Thread. Here is how it would be done in this case.
First, you create a Runnable, which will contain the code that loops in it's run method. In that Runnable, you will have to make a second Runnable that posts to the UI thread. For example:
TextView myTextView = (TextView) findViewById(R.id.myTextView); //grab your tv
Runnable myRunnable = new Runnable() {
#Override
public void run() {
while (testByte == 0) {
Thread.sleep(1000); // Waits for 1 second (1000 milliseconds)
String updateWords = updateAuto(); // make updateAuto() return a string
myTextView.post(new Runnable() {
#Override
public void run() {
myTextView.setText(updateWords);
});
}
}
};
Next just create your thread using the Runnable and start it.
Thread myThread = new Thread(myRunnable);
myThread.start();
You should now see your app looping with no force closes.
You can create a new Thread for a while loop.
This code will create a new thread to wait for a boolean value to change its state.
private volatile boolean isClickable = false;
new Thread() {
#Override
public void run() {
super.run();
while (!isClickable) {
// boolean is still false, thread is still running
}
// do your stuff here after the loop is finished
}
}.start();

Categories

Resources