Receive download manager download complete intent and compare id - android - android

I am using download manager in my activity to download a file. I want to receive the ACTION_DOWNLOAD_COMPLETE intent using a broadcast receiver and match the id of the download request with the id in the intent set by download manager to check if it is the same download.
There are two options:
1) receive the intent in the activity by making a intent filter and registering a broadcast receiver. Problem here is that the broadcast receiver exists within the activity and gets destroyed with it. So if the download doesn't get completed before the activity is destroyed there is no way to receive the intent.
this.downloadId = manager.enqueue(request);
downloadID is compared with the EXTRA_DOWNLOAD_ID of the intent in onReceive method to check if it is the same download. downloadId variable is accessible inside the onReceive method.
2) receive the broadcast via manifest. Problem here is that the onReceive method does not have access to the downloadId variable set inside the activity. So I cannot check if the download is the one I need.
Am I doing it wrong? Basically I want to know if the download was completed successfully or not even if the activity/service where the download started is destroyed.
I hope my question is clear enough.

surely your first option is not good because you must do some work as soon as your download is completed so i suggest you use the second approach and store the downloadid in sharedpreferance or in a file and in onRecieve method check that by stored value. you can also use database but if you want to store just one variable (downloadid) in it, it is not a good idea because you must create table and ...
so let's see some code:
in your activity after saving your downloadid to a variable put it in a SharedPreferences file like:
SharedPreferences settings = getSharedPreferences("DownloadIDS", 0);
SharedPreferences.Editor editor = settings.edit();
editor.putLong("savedDownloadIds", your download id);
editor.commit();
and in onReceive:
#Override
public void onReceive(Context context, Intent intent) {
SharedPreferences downloadids = context.getSharedPreferences("DownloadIDS", 0);
long savedDownloadIds = downloadids.getLong("savedDownloadIds", 0);
Bundle extras = intent.getExtras();
DownloadManager.Query q = new DownloadManager.Query();
Long downloaded_id = extras.getLong(DownloadManager.EXTRA_DOWNLOAD_ID);
if(savedDownloadIds == downloaded_id ){ // so it is my file that has been completed
q.setFilterById(downloaded_id);
DownloadManager manager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
Cursor c = manager.query(q);
if (c.moveToFirst()) {
int status = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS));
if (status == DownloadManager.STATUS_SUCCESSFUL) {
// do any thing here
}
}
c.close();
}
}

//Fetching the download id received with the broadcast
long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);

Related

How to find name of file being downloaded using Download Manager

DownloadManager is a background service. I would like to get a list of files that are currently being downloaded using DownloadManager.
Say download process is launched in activity A and I open activity B. In activity B I would like to know which url/file is being downloaded. How to achieve this? If multiple files are being downloaded, then how can I get the list?
You can do something like below in your broadcast receiver to identify the files that ate downloaded. Replace YOUR_DM with your DownloadManager instance.
receiver_complete = new BroadcastReceiver(){
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE){
Bundle extras = intent.getExtras();
DownloadManager.Query q = new DownloadManager.Query();
q.setFilterById(extras.getLong(DownloadManager.EXTRA_DOWNLOAD_ID));
Cursor c = YOUR_DM.query(q);
if (c.moveToFirst()) {
int status = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS));
if (status == DownloadManager.STATUS_SUCCESSFUL) {
// process download
title = c.getString(c.getColumnIndex(DownloadManager.COLUMN_TITLE));
// get other required data by changing the constant passed to getColumnIndex
}
}
}
}
};

stand alone broadcast receiver for Download Manager

I have a BroadcastReceiver which listens for a download complete action so it opens a file when download completes.
My issue with this is that the user may download a file and then minimize the screen which then unregisters the BroadcastReceiver, so the file isn't opened automatically when download completes.
How can I set up a stand alone BroadcastReceiver so that the image is opened even when the app is stopped/destroyed. I can't figure out how to make it listen to a download complete action.
private BroadcastReceiver downloadReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
long referenceId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
if (downloadReference == referenceId) {
DownloadManager.Query query = new DownloadManager.Query();
query.setFilterById(referenceId);
Cursor cursor = downloadManager.query(query);
if (cursor.moveToFirst()) {
int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
if (status == DownloadManager.STATUS_SUCCESSFUL) {
Intent intentpic = new Intent();
intentpic.setAction(android.content.Intent.ACTION_VIEW);
intentpic.setDataAndType(Uri.fromFile(getActivity().getExternalFilesDir("/update/asd.jpg")), "image/*");
startActivity(intentpic);
if(receiverRegistered) {
getActivity().unregisterReceiver(downloadReceiver);
}
}
}
cursor.close();
Log.i(TAG, "Downloading of data just finished");
}
}
};
You shouldn't start activities when your app is in background. It is okay that your receiver is unregistered when user leaves the app.
Instead, store the information that the image is downloaded and then check it when the app is resumed. I don't know your use case, but, for example you can store this information to SharedPreferences and check it when the app is resumed. Then you can decide whether to start a new activity or not.

Send data to BroadcastReceiver that is broadcasted in DownloadManager? [duplicate]

I am trying to pass an object to a BroadcastReceiver which will do something when a download is finished. How do I access the Intent object in the BroadcastReceiver's onReceive method from my activity? Right now I have this in my activity:
DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
long id = manager.enqueue(request);
and I have this in my BroadcastReceiver that does stuff when the download is complete:
DownloadManager mgr = (DownloadManager)context.getSystemService(Context.DOWNLOAD_SERVICE);
It all works fine, my BroadcastReceiver does what I want when the download completes. But now I want to pass an object from my activity to the BroadcastReceiver. Usually, I would just create an Intent and add the object to the Intent. But, I haven't created an Intent in my code because the BroadcastReceiver responds to the download using the Context.DOWNLOAD_SERVICE.
In my BroadcastReceiver, I want to get data from the Intent object in the onReceive() method here:
#Override
public void onReceive(Context context, Intent intent)
{
intent.getParcelableExtra("object");
}
How do I pass data into this Intent object from my activity? How do I access it? I tried using getIntent().putExtra("object", object) but it seems to be a different Intent than the one used in the BroadcastReceiver's onReceive method because I get a nullPointerException
Edit: here is my relevant code in AndroidManifest.xml
<receiver
android:name="com.android.devon.appfrenzy.DownloadReceiver"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
</intent-filter>
</receiver>
But now I want to pass an object from my activity to the BroadcastReceiver.
That's not possible. The BroadcastReceiver does not exist, except when receiving the broadcast. Your entire process is perhaps gone by the time the download is complete.
You are welcome to store something in a persistent location (SharedPreferences, database, file) and read that in from onReceive(). That's the only way to pass data to an object that does not exist in a process that may not yet exist.
Before download is executed, save the value in SharedPreference
editor.putInt(MainActivity.CERIS_LAST_DW_ID_KATALOG, m_intIdKatalog);
editor.commit();
Then in onReceive get the value from Shared Preference
#Override
public void onReceive(Context arg0, Intent arg1) {
// TODO Auto-generated method stub
SharedPreferences mCeris;
mCeris = arg0.getSharedPreferences(MainActivity.CERIS_PREFERENCES,
Context.MODE_PRIVATE);
int m_intIdKatalog = mCeris.getInt(MainActivity.CERIS_LAST_DW_ID_KATALOG, 0);
}
I was struggling with this because using local storage doesn't work if you're trying to keep track of multiple queued downloads. Luckily the brainiancs at Google pass the Download ID through as an extra, so you can use:
long downloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0)
Then use
public class DownloadReceiver extends BroadcastReceiver
{
#Override
public void onReceive(final Context context, final Intent intent)
{
long downloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0)
DownloadManager manager = (DownloadManager) context.getSystemService(DOWNLOAD_SERVICE);
DownloadManager.Query query = new DownloadManager.Query();
query.setFilterById(downloadId);
Cursor cursor = manager.query(query);
if (cursor.moveToFirst()) {
int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
int reason = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_REASON));
}
cursor.close();
}
}
while calling send broadcast send data as
Intent i = new Intent();
i.putExtra("string_example", "here is a broadcasted string");
i.putExtra("int_example", 100);
sendBroadcast(i);
and in side onReceive() get data from Intent as
#Override
public void onReceive(Context context, Intent intent) {
Log.d(getString(R.string.app_name), "broadcast string: " + intent.getStringExtra("string_example"));
Log.d(getString(R.string.app_name), "extra!: " + intent.getIntExtra("int_example",0));
}
Modify as ur need...

BroadcastReceiver onReceive() getting called twice

In my app, I am using DownloadManager, for downloading PDF's, which notifies the application via a BroadcastReceiver once the download is completed. My problem is the onReceive() method of BroadcastReceiver is getting called twice. The code is as follows:
In my list adapter, a for loop is run for downloading the selected pdf's. The downloading code is written in another class as follows:
public static void downloadCheat(final SherlockFragmentActivity activity, final String cheatName, String pathOnServer){
Request request = new Request(
Uri.parse(ApplicationConstants.CHEAT_DOWNLOAD_SERVER_URL
+ "/" + pathOnServer + cheatName + ".pdf"));
if(Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) {
request.setShowRunningNotification(true);
}
else {
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
}
final DownloadManager dm = (DownloadManager) activity
.getSystemService(Context.DOWNLOAD_SERVICE);
final long enqueue = dm.enqueue(request);
BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
long i = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
System.out.println(i);
if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)) {
Query query = new Query();
query.setFilterById(enqueue);
Cursor c = dm.query(query);
if (c.moveToFirst()) {
int columnIndex = c.getColumnIndex(DownloadManager.COLUMN_STATUS);
if (DownloadManager.STATUS_SUCCESSFUL == c.getInt(columnIndex)) {
}
}
//create custom notification
}
}
};
activity.registerReceiver(receiver, new IntentFilter(
DownloadManager.ACTION_DOWNLOAD_COMPLETE));
}
I am trying to add notifications for each pdf download. This works perfectly with download managers own internal notification for HoneyComb and above versions but for GingerBread it does not work and hence I have to push my own custom notification. So I need to determine the exact time when the pdf is downloaded completely. As of now I am able to push my own custom notification but the notifications come twice for every pdf download (As onReceive() is getting twice for each pdf). Can anyone please explain why onReceive() is called twice(for every pdf). Is there any workaround for this? Also could someone please recommend how the broadcast receiver can be un-registered in my case here?The above code is not a part of Activity, so I am not sure how to unregister the receiver.
Thanks for stopping by and reading the post.
You normally register receivers onResume() and unregister in onPause(). Are you doing so?
I think I may have originally misunderstood what you were trying to do. You should be able to call unregisterReceiver from onReceive. Does this do what you want?
You said you are downloading two pdfs. I only see one Download Request in your method. So I assume what you did is to call that method twice. If that is true, you actually registered two receiver to receive the ACTION_DOWNLOAD_COMPLETE event.
You only need to register once in onCreate or onStart or some methods else. For notification purpose, you can use intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1) to get the download id, the id is unique for each download. You can use this id to get the information about the downloaded file and make your file-specific notification.
This looks like the same bug that is described here:
https://code.google.com/p/android/issues/detail?id=18462

How to get the sender of an Intent?

Is there a way for an Activity to find out who (i.e. class name) has sent an Intent? I'm looking for a generic way for my Activity to respond to a received intent by sending one back to the sender, whoever that may be.
There may be another way, but the only solution I know of is having Activity A invoke Activity B via startActivityForResult(). Then Activity B can use getCallingActivity() to retrieve Activity A's identity.
Is it an external app you receive the intent from? You could use the getReferrer() method of the activity class
A simple example: I opened google map app to share some location with my app by using the share option of google maps. Then my app opens and this method call in the Activity:
this.getReferrer().getHost()
will return:
com.google.android.apps.maps
see documentation here: https://developer.android.com/reference/android/app/Activity.html#getReferrer()
Note that this requires API 22. For older Android versions see answer from ajwillliams
A technique I use is to require the application sending the relevant Intent to add a PendingIntent as a Parcelable extra; the PendingIntent can be of any type (service, broadcast, etc.). The only thing my service does is call PendingIntent.getCreatorUid() and getCreatorPackage(); this information is populated when the PendingIntent is created and cannot be forged by the app so I can get the info about an Intent's sender.
Only caveat is that solution only works from Jellybean and later which is my case.
Hope this helps,
This isn't incredibly direct but you can get a list of the recent tasks from ActivityManager. So the caller would essentially be the task before yours and you can fetch info on that task.
Example usage:
ActivityManager am = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
List<ActivityManager.RecentTaskInfo> recentTasks = am.getRecentTasks(10000,ActivityManager.RECENT_WITH_EXCLUDED);
The above will return a list of all the tasks from most recent (yours) to the limit specified. See docs here for the type of info you can get from a RecentTaskInfo object.
Generally you don't need to know this. If the calling activity uses startActivityForResult(Intent, int), the callee can use setResult(int, Intent) to specify an Intent to send back to the caller. The caller will receive this Intent in its onActivityResult(int, int, Intent) method.
Based on your question, since you want to send an intent back to the sender startActivityForResult is a better choice than what I am going to suggest. But I needed to start activity B when a notification is clicked by the user and execute some code in activity B only if the sender activity is activity A. This is how I did it quite simply.
Inside Activity A:
String senderName = this.getClass().getSimpleName();
Intent clickIntent = new Intent(ActivityA.this, ActivityB.class);
clickIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
clickIntent.putExtra("SENDER_CLASS_NAME", senderName);
//I use PendingIntent to start Activity B but you can use what you like such as this.startActivity(clickIntent);
PendingIntent.getActivity(ActivityA.this, NOTIFICATION_ID, clickIntent, PendingIntent.FLAG_ONE_SHOT);
Inside Activity B:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
Bundle bundle = getIntent().getExtras();
if (bundle != null) {
if(bundle.containsKey("SENDER_CLASS_NAME")){
String senderName = bundle.getString("SENDER_CLASS_NAME");
//Execute some code
Log.d("GCM", "Notifications clicked");
}
}
}
}
In my case, neither the accepted here and another most voted answer works perfectly.
Activity.getCallerActivity() works only for the sender which starts your activity by startActivityForResult, meaning that if the sender is also in your app and you have full control, it works, but not every external app starts others in that way.
Another most voted answer provides the solution for external app, but it too has issue. First I would prefer getAuthority() instead of getHost(), secondly, if the sender is a browser kind of app, like Chrome, both host and authority will give you the browsing web page's address host, such as www.google.com, instead of the app itself. So it depends on how you define 'sender', if you need to find out which web page starts you, the authority/host is good enough, but if you need to find out which app starts you, I am afraid authority/host can be trusted only when getScheme() gives you android-app instead of http.
Use UsageStatsManager and the old RecentTaskInfo to get the intent sender for OnCreate or onNewIntent:
public static String getTopMostThirdPartyPackage(Context context) {
String thisPak = null, tmp, top = null;
try {
thisPak = context.getPackageName();
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
UsageStatsManager man = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
long now = System.currentTimeMillis();
UsageEvents uEvts = man.queryEvents(now - 5000,now); // query in 5 sec
UsageEvents.Event e = new UsageEvents.Event();
while (uEvts.getNextEvent(e)){
tmp = e.getPackageName();
if (!thisPak.equals(tmp)) {
top = tmp;
break;
}
}
} else {
ActivityManager man = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RecentTaskInfo> tasks = man.getRecentTasks(3, 0);
for(ActivityManager.RecentTaskInfo info:tasks) {
tmp = info.baseIntent.getComponent().getPackageName();
if (!thisPak.equals(tmp)) {
top = tmp;
break;
}
}
}
} catch (Exception e) {
}
return top;
}
permissions :
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
<uses-permission android:name="android.permission.GET_TASKS" />
intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
startActivity(intent);

Categories

Resources