“Good programmers write solid code, while great programmers reuse the code of good programmers”
— W. Jason Gilmore, the author of “Beginning PHP and MySQL”
In Android Platform, it supports some ways to download files from URLs via HttpURLConnection or HttpClient, or DownloadProvider.When you write an android programm with Eclipse, try to import:
import android.provider.Downloads
The Eclipse gives a red error prompting and complains:
The import android.provider.Downloads cannot be resolved
Oops…
The download manager is not part of the public SDK, and there are no relational API description in Android developers Reference.Comment in sources frameworks/base/core/java/android/provider/Downloads.java, says: “For 1.0 the download manager can’t deal with abuse from untrusted apps, so this API is hidden.” Refer to the document description from: packages/providers/DownloadProvider/docs/index.html. we Know that Browser / Gmail / Market / Updater depend on the download manager. Yes, show some screenshots in the market:
We maybe can’t compile with Eclipse, but we can write an Android.mk makefile, use mmm to do it.
DownloadProvider is very easy to use.First, declare permission in AndroidManifest.xml:
<uses-permission
android:name="android.permission.INTERNET"
>
</uses-permission>
<uses-permission
android:name="android.permission.ACCESS_DOWNLOAD_MANAGER"
>
</uses-permission>
And then just need to fill some field values:
ContentValues values = new ContentValues();
String url = "https://blog.lytsing.org/wp-content/uploads/2010/06/android_downloadprovider_market.jpg";
values.put(Downloads.URI, url);
values.put(Downloads.MIMETYPE, "image/jpeg");
values.put(Downloads.FILENAME_HINT, getFullFilename("android_downloadprovider_market.jpg"));
values.put(Downloads.TITLE, "screenshot");
values.put(Downloads.DESCRIPTION, "screenshot file for DownloadProvider Demo");
values.put(Downloads.VISIBILITY, Downloads.VISIBILITY_VISIBLE);
values.put(Downloads.NOTIFICATION_CLASS, "org.lytsting.android.downloads.DownloadReceiver");
values.put(Downloads.NOTIFICATION_PACKAGE, "org.lytsting.android.downloads");
getContentResolver().insert(Downloads.CONTENT_URI, values);
private String getFullFilename(String filename) {
return Environment.getExternalStorageDirectory().toString() + "/download/" + filename);
}
Notes
Downloads.FILENAME_HINT, for a demo, here, I put the file into SD Card, the download directory, without checking any exception.
Downloads.NOTIFICATION_CLASS, you want to write a DownloadReceiver class, which extends BroadcastReceiver, and handle the message Downloads.DOWNLOAD_COMPLETED_ACTION or Downloads.NOTIFICATION_CLICKED_ACTION. like this:
public class DownloadReceiver extends BroadcastReceiver {
static final String TAG = "DownloadReceiver";
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, intent.getAction());
if (intent.getAction().equals(Downloads.NOTIFICATION_CLICKED_ACTION)) {
Intent activityIntent = new Intent(Intent.ACTION_VIEW);
activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
activityIntent.setClass(context, Downloads.class); // Downloads Activity
try {
context.startActivity(activityIntent);
} catch (ActivityNotFoundException ex) {
Log.d(TAG, "no activity for Downloads.NOTIFICATION_CLICKED_ACTION" + ex);
}
} else if (intent.getAction().equals(Downloads.DOWNLOAD_COMPLETED_ACTION)) {
// balabala
}
}
}
By here, we need to modify the AndroidManifest.xml, add:
<receiver
android:name=".DownloadReceiver"
android:permission="android.permission.SEND_DOWNLOAD_COMPLETED_INTENTS"
android:exported="true" >
<intent-filter>
<action
android:name="android.intent.action.DOWNLOAD_COMPLETED">
</action>
</intent-filter>
<intent-filter>
<action
android:name="android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED">
</action>
<category
android:name="android.intent.category.DEFAULT">
</category>
<data
android:scheme="content">
</data>
</intent-filter>
</receiver>
Maybe, you don’t like show the the Download state in status bar, just change Downloads.VISIBILITY_VISIBLE to Downloads.VISIBILITY_HIDDEN
and even, maybe you would like to show the download progress in the Activity, little case.
Cursor c = getContentResolver().query(
Downloads.CONTENT_URI, new String[] {
Downloads._ID,
Downloads.CURRENT_BYTES,
Downloads.TOTAL_BYTES,
Downloads.STATUS },
" " + Downloads.MIMETYPE + " = 'image/jpeg'", null,
Downloads._ID);
if (c == null) {
return;
}
// Columns match projection in query above
final int idColumn = 0;
final int currentBytesColumn = 1;
final int totalBytesColumn = 2;
final int statusColumn = 3;
c.moveToFirst();
long totalBytes = c.getLong(totalBytesColumn);
long currentBytes = c.getLong(currentBytesColumn);
c.close();
Add a ProgressBar and a Handler to display and refresh the progress, I suppose you should know how to do. You also can write a class extends ContentObserver to observer the download status.
The last step, delete the history data:
getContentResolver().delete(Downloads.CONTENT_URI,
"(" + Downloads.TITLE + " = 'screenshot')", null);
This code snippet should be write into a function in really code.
Read the code packages/apps/Browser for a more complete example.
Update:Since Android 2.2, the Downloads’s api has changed, they put some variables input a sub class which named Impl, so you should check it in the source code.
21 replies on “How to Use Android Downloads Provider”
We maybe can’t compile with Eclipse, but we can write an Android.mk makefile, use mmm to do it.
How do?could you tell me more.thank you.
hi kingmove:
First, you want to compile all the android code, and then put your project to packages/apps/ , create Android.mk file in the root of your project, can refer to the Browser’s Android.mk, or others.
. build/envsetup.sh
mmm packages/apps/your_project
hi deli ,
I can compile my code successfully with the method you mentioned and I push the apk file under the /system/app , but when I tried to run it on the device, it told me the error ” 09-03 08:53:37.696: ERROR/AndroidRuntime(3304): The import android.provider.Downloads cannot be resolved” .
Why?
solved , sorry
Hi,
I am trying to do the same thing as you but I am new to the android and don’t know how write a make file. It would be great help if u can share ur mk file.
Thanks in advance.
$ cat Android.mk
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := user
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_SRC_FILES += \
src/com/google/android/googleapps/IGoogleLoginService.aidl \
src/com/android/vending/licensing/ILicenseResultListener.aidl\
src/com/android/vending/licensing/ILicensingService.aidl
LOCAL_PACKAGE_NAME := Vending
LOCAL_CERTIFICATE := platform
include $(BUILD_PACKAGE)
include $(BUILD_MULTI_PREBUILT)
Well, I agree with what you wrote, but not with all of it. Regardless, it’s all great material. Thanks!
Great post, I’ve been looking for something like that!!
Thank You,
Marsha
Thanks to your nice blog. I appreciate your writing.
Excellent post, I will be checking back regularly to look for updates. 🙂
FYI, your RSS feed is broken in my Feedburner, please fix it! thanks
[…] LinkedIn […]
Hi there. This is a very informative post. I am fairly new to developing on this platform, but I was very surprised to read this:
“The download manager is not part of the public SDK, and there are no relational API description in Android developers Reference.”
Maybe it’s my ignorance. However, it looks like you have provided a solid solution. I’ve bookmarked this page because I believe it will directly relate to some things I will be doing next month. Oh, and thanks for the update at the end too. 🙂
Darla
hi, If your apps have the same sign key with your ROM, or you can get the root user privileges, you can use the downloads Provider. If not, you can use the exists Downloads Provider code , just change the package name, the URI for your own.
Thank you for a very useful post.
I have a standalone android application (eclipse project) which cannot be added to “packages/apps” for compilation and therefore I couldn’t import “android.provider.Downloads” class in my activity class.
Instead, I have taken the string definitions from Downloads class in my activity.
The call to getContentResolver().insert(Uri.parse(“content://downloads/my_downloads”), values) is throwing following exception:
10-27 21:31:01.595: ERROR/DatabaseUtils(2872): Writing exception to parcel
10-27 21:31:01.595: ERROR/DatabaseUtils(2872): java.lang.SecurityException: Invalid value for is_public_api: null
10-27 21:31:01.595: ERROR/DatabaseUtils(2872): at com.android.providers.downloads.DownloadProvider.enforceAllowedValues(DownloadProvider.java:686)
10-27 21:31:18.830: ERROR/AndroidRuntime(8025): FATAL EXCEPTION: main
10-27 21:31:18.830: ERROR/AndroidRuntime(8025): java.lang.SecurityException: Invalid value for is_public_api: null
10-27 21:31:18.830: ERROR/AndroidRuntime(8025): at android.os.Parcel.readException(Parcel.java:1322)
1) Is there a way I could still import Downloads class in my eclipse project?
2) Any idea about the above exception?
Thanks
I want to know the idea about the above exception?
hi , just pull your app into /system/app not /data/app.
[…] 很久以前写过一篇《How to Use Android Downloads Provider》,到了Android 2.2(froyo),Android 提供了更优雅的接口: […]
I know this web site presents quality dependent articles
and additional stuff, is there any other website which gives such stuff in
quality?
My website :: nyconvergence.Com
[…] 本文转载自 http://blog.lytsing.org/archives/135.html […]
从头到尾看了本文,然后一看开头,发现作者是中国人,哈