I'd like to know if there's some way to set a timeout for an Intent started via startActivityForResult, so when the time is passed some actions can be performed with the activity of the mentioned intent (in my case finishing it).
There doesn't seem to be any direct way to set a timeout directly to the Intent, but this doesn't look too much to worry about, as I guess I could create a CountDownTimer that in onFinish() would call the code to finish the intent.
Problem is I don't see a way to finish that ActivityForResult.
Is there any way to do this?
Well, I finally got to solve the problem, indeed it wasn't very difficult.
For my particular case of INTENT_PICK the following code is valid to stop the activity after 2 minutes:
final int RQS_PICKCONTACT = 1;
[...]
Intent intentPickContact = new Intent(Intent.ACTION_PICK, uriContact);
startActivityForResult(intentPickContact, RQS_PICKCONTACT);
mcd = new CountDownTimer(120000, 10000) {
public void onTick(long millisUntilFinished) {
}
public void onFinish() {
try
{
finishActivity(RQS_PICKCONTACT);
}
catch (Exception ex)
{
}
}
}.start();
In my case I wanted to limit the time a user was doing a task so I created my own timer class.
public class Timer {
private static final String TAG = "Timer" ;
int timeout;
Context mContext;
boolean compleatedTask;
String timeoutKey;
CountDownTimer countDown;
public Timer(Context mContext, int timeout, String timeoutKey){
this.timeout = timeout;
this.mContext = mContext;
this.timeoutKey = timeoutKey;
}
public void startTimer() {
this.countDown = new CountDownTimer(this.timeout, 1000) {
public void onTick(long millisUntilFinished) {
Log.i(TAG, "OnTick, context: " + mContext + ", milisUntilFinished: " + millisUntilFinished + ", compleatedTask: " + compleatedTask);
if(compleatedTask)
cancel();
}
public void onFinish() {
Log.i(TAG, "OnFinish, context: " + mContext + ", compleatedTask: " + compleatedTask);
try
{
if(!compleatedTask){
Intent intent = new Intent(mContext, UnsuccessfullPosTaskActivity.class);
intent.putExtra("error", StateMachine.databaseAccess.getDictionaryTranslation(timeoutKey, StateMachine.language));
mContext.startActivity(intent);
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
};
countDown.start();
}
public void setCompleatedTask(boolean compleatedTask){
this.compleatedTask = compleatedTask;
if(compleatedTask)
countDown.onFinish();
}
}
Then in your activity
int timeout = 1000;
Timer timer = new Timer(Activity.this, timeout);
timer.startTimer();
//do stuff
if(conditionToStop)
timer.setCompleatedTask(true);
And if you are using recycle views and you want to stop the counter when they click an option just send the timer object to your custom recycle view adapter.
Related
I have an App that Monitors room noise levels, I initially got the Code from Github, in the original code, the programmer was monitoring noise levels from Main Activity and displaying the results in textviews, but I want to monitor using a service, I have implemented everything and its working but the textviews seem to be lagging behind, lets say I make a bit of noise and the noise level reach 5, it sticks at 5 even when there is no noise in the room, but in the original app, it was so sensitive that it would go back to 0 or another value depending on the noise levels, I do not know where I have gone wrong but below is my code:
Main Activity
public class StartingPoint extends Activity {
private String volumeBars;
private String volumeLevel;
private TextView volumeBarView;
private TextView volumeLevelView;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Toast.makeText(getBaseContext(), "Loading...", Toast.LENGTH_LONG).show();
setContentView(R.layout.activity_starting_point);
//starting Service
startService(new Intent(this, VolumeListerner.class));
volumeBarView = (TextView) findViewById(R.id.volumeBars);
volumeLevelView = (TextView) findViewById(R.id.volumeLevel);
}
#Override
public void onResume() {
LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, new IntentFilter("UI_UPDATER"));
super.onResume();
// Sound based code
}
#Override
public void onPause() {
super.onPause();
}
public void updateTextView() {
volumeBarView.setText(volumeBars);
volumeLevelView.setText(volumeLevel);
return;
}
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
volumeBars = intent.getStringExtra("VolumeBars");
volumeLevel = intent.getStringExtra("volumeLevel");
Log.d("receiver", "Got message: " + volumeBars + " : " + volumeLevel);
updateTextView();
}
};
Service:
public class VolumeListerner extends Service {
private static String volumeVisual = "";
private static int volumeToSend;
private Handler handler;
private SoundMeter mSensor;
/** interface for clients that bind */
IBinder mBinder;
/** indicates whether onRebind should be used */
boolean mAllowRebind;
/** The service is starting, due to a call to startService() */
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
soundLevelCheck();
return super.onStartCommand(intent, flags, startId);
}
private void soundLevelCheck()
{
mSensor = new SoundMeter();
try {
mSensor.start();
Toast.makeText(getBaseContext(), "Sound sensor initiated.", Toast.LENGTH_SHORT).show();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
handler = new Handler();
final Runnable r = new Runnable() {
public void run() {
// Get the volume from 0 to 255 in 'int'
double volume = 10 * mSensor.getTheAmplitude() / 32768;
volumeToSend = (int) volume;
volumeVisual = "";
for( int i=0; i<volumeToSend; i++){
volumeVisual += "|";
updateUI();
}
handler.postDelayed(this, 250); // amount of delay between every cycle of volume level detection + sending the data out
}
};
// Is this line necessary? --- YES IT IS, or else the loop never runs
// this tells Java to run "r"
handler.postDelayed(r, 250);
}
private void updateUI()
{
Intent intent = new Intent( "UI_UPDATER" );
intent.putExtra("VolumeBars", "Volume Bars: " + String.valueOf(volumeVisual));
intent.putExtra("volumeLevel","Volume Levels: " + String.valueOf(volumeToSend));
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
I recommmand you to use an enhanced event bus with emphasis on Android support. you have a choice between :
1- Otto
2- Event Bus
I have an Android activity that is launching a child activity, which runs Sygic navigation on a surfaceview.
Launching the child activity works great, the navigation starts up. However, when they exit out of the Sygic application, I want to close the child activity and return back to showing the parent activity. As you can see, in the Finish() method I've tried calling both this.finish(); and getParent().finish(); however neither are working. All it does is show a black screen, which I'm guessing is the surface view. Any ideas how I can get it to successfully close the child activity and show the parent activity again?
Here is the code to launch the child activity:
NavigationActivity.SygicModule = this;
Log("Creating intent for navigation activity");
Intent intent = new Intent(getActivity(), NavigationActivity.class);
Log("Starting navigation activity");
getActivity().startActivity(intent);
Here is the xml for the child navigation activity view:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/layout">
<SurfaceView android:id="#+id/surfaceView" android:layout_height="match_parent" android:layout_width="match_parent"></SurfaceView>
</LinearLayout>
And here is the code for the child navigation activity:
package ti.sygic;
public class NavigationActivity extends SygicDriveActivity {
private static final String LCAT = "SygicModule";
private static SError error = new SError();
private static Activity currentActivity = null;
public static SygicModule SygicModule;
private Handler mHandler = new Handler();
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
currentActivity = this;
super.onCreate(savedInstanceState);
try {
setContentView(TiRHelper.getApplicationResource("layout.sygicmain"));
final ApiCallback apicallback = new ApiCallback() {
public void onRunDrive() {
try {
final SurfaceView surface = (SurfaceView) findViewById(TiRHelper.getApplicationResource("id.surfaceView"));
mHandler.post(new Runnable() {
public void run() {
runDrive(surface, getPackageName()); // starts the drive app
}
});
} catch (ResourceNotFoundException e) {
// TODO Auto-generated catch block
Log("Failed to find id.surfaceView!");
e.printStackTrace();
} // surface
}
public void onInitApi() // gets called after runDrive();
{
// code to make sure that gps is enabled before initializing
Log("Checking that gps is enabled");
String provider = "com.android.settings.widget.SettingsAppWidgetProvider";
Criteria criteria = new Criteria();
criteria.setAccuracy(Criteria.NO_REQUIREMENT);
criteria.setPowerRequirement(Criteria.NO_REQUIREMENT);
criteria.setCostAllowed(false);
ContentResolver contentResolver = getContentResolver();
if (!Settings.Secure.isLocationProviderEnabled(contentResolver, LocationManager.GPS_PROVIDER)) {
Log("Gps is not enabled, showing gps settings");
final Intent poke = new Intent();
poke.setClassName("com.android.settings", provider);
poke.addCategory(Intent.CATEGORY_ALTERNATIVE);
poke.setData(Uri.parse("3"));
sendBroadcast(poke);
}
int status = ApplicationAPI.InitApi(getPackageName(), true, msgHandler); // api initialization
Log("Status = " + Integer.toString(status));
if (status != 1) {
Log("InitApi failed! " + status);
}
else {
//start checking every second if application is running. if it isn't then finish this and parent activities.
//note: we wouldn't have to do this if APP_EXIT event actually fired. :(
CheckIfRunning();
}
}
};
ApplicationAPI.startDrive(apicallback);
} catch (ResourceNotFoundException e1) {
Log("Failed to find resource!");
e1.printStackTrace();
}
}
public static void navigateTo(Float latitude, Float longitude) {
try
{
Log("navigateTo: " + latitude + " / " + longitude);
Integer lat = (int)(latitude * 100000);
Integer lon = (int)(longitude * 100000);
//start navigation.
Log("Starting navigation for " + lat + " / " + lon);
SWayPoint wayPoint = new SWayPoint();
wayPoint.SetLocation(lat, lon);
ApplicationAPI.StartNavigation(error, wayPoint, 0, true, true, 0);//NavigationParams.NpMessageAvoidTollRoadsUnable
Log("Start navigation result: " + error.nCode + " " + error.GetDescription());
//if waiting for gps, call in a bit.
if(error.nCode == -6)
{
Thread.sleep(5000);
navigateTo(latitude, longitude);
}
}
catch(Exception exc)
{
Log(exc.getMessage());
}
}
final ApplicationHandler msgHandler = new ApplicationHandler() {
#Override
public void onApplicationEvent(int nEvent, String strData) {
Log("Event No. " + Integer.toString(nEvent) + " detected."); // event handling
switch (nEvent) {
case ApplicationEvents.EVENT_APP_EXIT:
Log("In EVENT_APP_EXIT event");
Finish();
break;
case ApplicationEvents.EVENT_WAIPOINT_VISITED:
Log("Waypoint visited.");
break;
case ApplicationEvents.EVENT_ROUTE_COMPUTED:
Log("Route computed.");
break;
case ApplicationEvents.EVENT_ROUTE_FINISH:
Log("Route finished.");
break;
case ApplicationEvents.EVENT_POI_WARNING:
Log("Poi warning!.");
break;
case ApplicationEvents.EVENT_CHANGE_LANGUAGE:
Log("Language changed.");
break;
case ApplicationEvents.EVENT_EXIT_MENU:
Log("Menu exited.");
break;
case ApplicationEvents.EVENT_MAIN_MENU:
Log("Entering main menu.");
break;
case ApplicationEvents.EVENT_BORDER_CROSSING:
Log("Crossing border.");
}
}
};
private void CheckIfRunning()
{
new Thread(new Runnable() {
#Override
public void run() {
try
{
int numTimesNotRunning = 0;
while(true)
{
boolean isRunning = ApplicationAPI.IsApplicationRunning(error, 0) == 1;
if(isRunning)
{
Log("App is running, will check again in 2 seconds");
numTimesNotRunning = 0;
Thread.sleep(2000);
}
else {
if(numTimesNotRunning < 3)
{
numTimesNotRunning++;
Log("App not running, num times " + numTimesNotRunning);
Thread.sleep(2000);
}
else
break;
}
}
Log("App is not running, calling sygicmodule finish!");
Finish();
}
catch(Exception exc)
{
Log(exc.getMessage());
}
}
}).start();
}
private void Finish()
{
Log("In NavigationActivity Finish");
runOnUiThread(new Runnable() {
public void run() {
try
{
//launch parent activity.
Log("Starting parent activity");
Intent intent = new Intent(currentActivity, SygicModule.getActivity().getClass());
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
currentActivity.startActivity(intent);
//finish current activity.
Log("Finishing activity");
//setContentView(null);
currentActivity.finish();
}
catch(Exception exc)
{
Log(exc.getMessage());
}
}
});
}
private static void Log(String message) {
//org.appcelerator.kroll.common.Log.i(LCAT, message);
if(message != null && message.length() > 0)
android.util.Log.i(LCAT, message);
}
}
You will have to reference the parent activity instead of just calling finish();
i.e. YourActivityClassName.this.finish();
Would cause your activity to finish, Make sure to add this call on the DriveEvent.EVENT_APP_EXIT and your activity should close moments after Sygic has exited
I'm trying to download multiple files using IntentService. The IntentService donwloads them okey as expected one at a time, the only problem is that when the Internet is down the intent service will not stop the donwload rather it will get stuck on the current thread. If I manage to stop the current thread it will continue running the other threads stored in its queue even though the internet connection is down.
It was suggested in another post that I use LinkedBlockingQueue and create my own Worker thread that constantly checks this queue for new threads. Now I know there are some increased overheads and thus performance issues when creating and destroying threads but that's not a concern in my case.
At this point, All I want to do is understand how IntentService works which as of yet I don't (and I have looked at the code) and then come up with my own implementation for it using LinkedBlockingQueue controlled by a Worker thread. Has anyone done this before ? Could provide a working example, if you feel uncomfortable providing the source code, pseudo code is fine by me. Thanks!
UPDATE: I eventually implemented my own Intent Service using a thread that has a looper which checks the queue which in turn stores the intents passed from the startService(intent).
public class MyIntentService extends Service {
private BlockingQueue<Download> queue = new LinkedBlockingQueue<Download>();
public MyIntentService(){
super();
}
#Override
public void onCreate() {
super.onCreate();
new Thread(queueController).start();
Log.e("onCreate","onCreate is running again");
}
boolean killed = false;
Runnable queueController = new Runnable() {
public void run() {
while (true) {
try {
Download d =queue.take();
if (killed) {
break;
}
else {
d.downloadFile();
Log.e("QueueInfo","queue size: " + queue.size());
}
}
catch (InterruptedException e) {
break;
}
}
Log.e("queueController", "queueController has finished processing");
Log.e("QueueInfo","queue size: " + queue.toString());
}
};
class Download {
String name;
//Download files process
void downloadFile() {
//Download code here
}
Log.e("Download","Download being processed is: " + name);
}
public void setName(String n){
name = n;
}
public String getName(){
return name;
}
}
public void killService(){
killed = true;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Download d = new Download();
d.setName(intent.getStringExtra("VIDEOS"));
queue.add(d);
return START_NOT_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
Log.e("stopSelf","stopSelf has been just called to stop the Service");
stopSelf();
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
I'm not so sure about the START_NOT_STICKY in the onStartCommand() method. If it's the right flag to return or not. Any clarification on that would be appreciated!
UPDATE: I eventually implemented my own Intent Service using a thread that has a looper which checks the queue which in turn stores the intents passed from the startService(intent).
public class MyIntentService extends Service {
private BlockingQueue<Download> queue = new LinkedBlockingQueue<Download>();
public MyIntentService(){
super();
}
#Override
public void onCreate() {
super.onCreate();
new Thread(queueController).start();
Log.e("onCreate","onCreate is running again");
}
boolean killed = false;
Runnable queueController = new Runnable() {
public void run() {
while (true) {
try {
Download d =queue.take();
if (killed) {
break;
}
else {
d.downloadFile();
Log.e("QueueInfo","queue size: " + queue.size());
}
}
catch (InterruptedException e) {
break;
}
}
Log.e("queueController", "queueController has finished processing");
Log.e("QueueInfo","queue size: " + queue.toString());
}
};
class Download {
String name;
//Download files process
void downloadFile() {
//Download code here
}
Log.e("Download","Download being processed is: " + name);
}
public void setName(String n){
name = n;
}
I'm trying to change images on buttons and turn them back to the original image, and to do it one after the other in 4 different images.
I have tried the following code, but it didn't work, the result causes only to one of the images to blink for a milisecond:
ArrayList<Integer> scenario = new ArrayList<Integer>();
...
void delayedPlay(){
// each button should be posted in 1 second spacing
int count = 1;
for (final int btnid : scenario){
// turn off
final Runnable r2 = new Runnable(){
public void run(){
imagebuttons[btnid].setImageBitmap(imagesTurnedOff.get(btnid));
}
};
// turn on and call turn off
Runnable r1 = new Runnable(){
public void run(){
imagebuttons[btnid].setImageBitmap(imagesTurnedOn.get(btnid));
imagebuttons[btnid].postDelayed(r2, 1000);
}
};
// post the above delayed
imagebuttons[btnid].postDelayed(r1, 1000 * count++);
}
}
Can anyone help me, and suggest why it doesn't working for me?
It worked for me. Are you sure that imagesTurnedOn/imagesTurnedOff are returning the correct values?
This solution leaves a lot to be desired in terms of timing -- it will be quite uneven. Perhaps something like this would work better (using an AsyncTask)
public void deplayedPlay2() {
if (mTaskHandler == null) {
mTaskHandler = new AsyncTask<Void, Void, Void>() {
#Override
public Void doInBackground(Void... params) {
long now = System.currentTimeMillis();
try {
for (final int btnid : mScenario) {
Log.d(TAG,
"ON: " + btnid + " (" + (System.currentTimeMillis() - now) + ")");
mButtons[btnid].post(new Runnable() {
public void run() {
mButtons[btnid]
.setBackgroundDrawable(GoodbyeAndroidActivity.this
.getResources()
.getDrawable(
R.drawable.on_icon));
}
});
Thread.sleep(1000);
Log.d(TAG,
"OFF: " + btnid + " (" + (System.currentTimeMillis() - now) + ")");
mButtons[btnid].post(new Runnable() {
public void run() {
mButtons[btnid]
.setBackgroundDrawable(GoodbyeAndroidActivity.this
.getResources()
.getDrawable(
R.drawable.off_icon));
}
});
}
} catch (InterruptedException ex) {
Log.d(TAG, "Interrupted.");
}
return null;
}
#Override
public void onPostExecute(Void param) {
Log.d(TAG, "Done!");
mTaskHandler = null;
}
};
mTaskHandler.execute();
}
}
Don't forget to handle this in onPause():
public void onPause() {
super.onPause();
if (mTaskHandler != null) {
mTaskHandler.cancel(true);
// May want to reset buttons too?
}
}
I am using IntentService to download 200 large JPGs from a list. While this is loading, the user can skip through the not-loaded JPGs and load JPG #156 for example, but after it is loaded, it should continue loading the rest. So it's like a Lazy Loader... but it continues when it's idle.
I previously used onHandleIntent and put a loop from #1 to #200... which obviously doesn't work when I try to send another IntentService call for JPG #156. So the call to #156 only happens after onHandleIntent is done with #200.
I then changed it so onHandleIntent reorders request #156 to be at the top of the list, then requests the top of the list (and downloads the JPG), then removes it from the list. It then calls the IntentService again, which sounds rather risky from a recursive/stack overflow kinda way. It works sometimes and I can see file #156 being put first... sometimes.
Is there a better way to do this? A way I could think of would be to run it all through a database.
EDIT: This is what I have come up with:
code
public class PBQDownloader extends IntentService {
int currentWeight = 0;
PriorityBlockingQueue<WeightedAsset> pbQueue = new PriorityBlockingQueue<WeightedAsset>(100, new CompareWeightedAsset());
public PBQDownloader() {
super("PBQDownloader");
}
public PBQDownloader(String name) {
super(name);
}
#Override
protected void onHandleIntent(Intent intent) {
String downloadUrl = "-NULL-";
Bundle extras = intent.getExtras();
if (extras!=null) {
downloadUrl = extras.getString("url");
Log.d("onHandleIntent 1.1", "asked to download: " + downloadUrl);
} else {
Log.d("onHandleIntent 1.2", "no URL sent so let's start queueing everything");
int MAX = 10;
for (int i = 1; i <= MAX; i++) {
// should read URLs from list
WeightedAsset waToAdd = new WeightedAsset("url: " + i, MAX - i);
if (pbQueue.contains(waToAdd)) {
Log.d("onStartCommand 1", downloadUrl + " already exists, so we are removing it and adding it back with a new priority");
pbQueue.remove(waToAdd);
}
pbQueue.put(waToAdd);
}
currentWeight = MAX + 1;
}
while (!pbQueue.isEmpty()) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
}
WeightedAsset waToProcess = pbQueue.poll();
Log.d("onHandleIntent 2 DOWNLOADED", waToProcess.url);
}
Log.d("onHandleIntent 99", "finished all IntentService calls");
}
#Override
public int onStartCommand(Intent intent, int a, int b) {
super.onStartCommand(intent, a, b);
currentWeight++;
String downloadUrl = "-NULL-";
Bundle extras = intent.getExtras();
if (extras!=null) downloadUrl = extras.getString("url");
Log.d("onStartCommand 0", "download: " + downloadUrl + " with current weight: " + currentWeight);
WeightedAsset waToAdd = new WeightedAsset(downloadUrl, currentWeight);
if (pbQueue.contains(waToAdd)) {
Log.d("onStartCommand 1", downloadUrl + " already exists, so we are removing it and adding it back with a new priority");
pbQueue.remove(waToAdd);
}
pbQueue.put(waToAdd);
return 0;
}
private class CompareWeightedAsset implements Comparator<WeightedAsset> {
#Override
public int compare(WeightedAsset a, WeightedAsset b) {
if (a.weight < b.weight) return 1;
if (a.weight > b.weight) return -1;
return 0;
}
}
private class WeightedAsset {
String url;
int weight;
public WeightedAsset(String u, int w) {
url = u;
weight = w;
}
}
}
code
Then I have this Activity:
code
public class HelloPBQ extends Activity {
int sCount = 10;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button tv01 = (Button) findViewById(R.id.tv01);
Button tv02 = (Button) findViewById(R.id.tv02);
Button tv03 = (Button) findViewById(R.id.tv03);
tv01.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
doPBQ();
}
});
tv02.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
doInitPBQ();
}
});
tv03.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
sCount = 0;
}
});
}
private void doInitPBQ() {
Intent intent = new Intent(getApplicationContext(), PBQDownloader.class);
//intent.putExtra("url", "url: " + sCount);
startService(intent);
}
private void doPBQ() {
sCount++;
Intent intent = new Intent(getApplicationContext(), PBQDownloader.class);
intent.putExtra("url", "url: " + sCount);
startService(intent);
}
}
code
Now the messy bit is that I have to keep an ever-increasing counter that runs the risk of going out of int bounds (WeightedAsset.weight) - is there a way to programmatically add to the queue and have it automatically be the head of the queue? I tried to replace WeightedAsset with a String, but it didn't poll() as I wanted, as a FIFO instead of a LIFO stack.
Here's how I'd try it first:
Step #1: Have the IntentService hold onto a PriorityBlockingQueue.
Step #2: Have onHandleIntent() iterate over the PriorityBlockingQueue, downloading each file in turn as it gets popped off the queue.
Step #3: Have onStartCommand() see if the command is the "kick off all downloads" command (in which case, chain to the superclass). If, instead, it's the "prioritize this download" command, re-prioritize that entry in the PriorityBlockingQueue, so it'll be picked up next by onHandleIntent() when the current download is finishing.