I need to upload a bitmap to Amazon S3. I have never used S3, and the docs are proving less than helpful as I can't see anything to cover this specific requirement. Unfortunately I'm struggling to find time on this project to spend a whole day learning how it all hangs together so hoping one of you kind people can give me some pointers.
Can you point to me to a source of reference that explains how to push a file to S3, and get a URL reference in return?
More specifically:
- Where do the credentials go when using the S3 Android SDK?
- Do I need to create a bucket before uploading a file, or can they exist outside buckets?
- Which SDK method do I use to push a bitmap up to S3?
- Am I right in thinking I need the CORE and S3 libs to do what I need, and no others?
String ACCESS_KEY="****************",
SECRET_KEY="****************",
MY_BUCKET="bucket_name",
OBJECT_KEY="unique_id";
AWSCredentials credentials = new BasicAWSCredentials(ACCESS_KEY, SECRET_KEY);
AmazonS3 s3 = new AmazonS3Client(credentials);
java.security.Security.setProperty("networkaddress.cache.ttl" , "60");
s3.setRegion(Region.getRegion(Regions.AP_SOUTHEAST_1));
s3.setEndpoint("https://s3-ap-southeast-1.amazonaws.com/");
List<Bucket> buckets=s3.listBuckets();
for(Bucket bucket:buckets){
Log.e("Bucket ","Name "+bucket.getName()+" Owner "+bucket.getOwner()+ " Date " + bucket.getCreationDate());
}
Log.e("Size ", "" + s3.listBuckets().size());
TransferUtility transferUtility = new TransferUtility(s3, getApplicationContext());
UPLOADING_IMAGE=new File(Environment.getExternalStorageDirectory().getPath()+"/Screenshot.png");
TransferObserver observer = transferUtility.upload(MY_BUCKET,OBJECT_KEY,UPLOADING_IMAGE);
observer.setTransferListener(new TransferListener() {
#Override
public void onStateChanged(int id, TransferState state) {
// do something
progress.hide();
path.setText("ID "+id+"\nState "+state.name()+"\nImage ID "+OBJECT_KEY);
}
#Override
public void onProgressChanged(int id, long bytesCurrent, long bytesTotal) {
int percentage = (int) (bytesCurrent / bytesTotal * 100);
progress.setProgress(percentage);
//Display percentage transfered to user
}
#Override
public void onError(int id, Exception ex) {
// do something
Log.e("Error ",""+ex );
}
});
Take a look at the Amazon S3 API documentation to get a feel for what can and can't be done with Amazon S3. Note that there are two APIs, a simpler REST API and a more-involved SOAP API.
You can write your own code to make HTTP requests to interact with the REST API, or use a SOAP library to consume the SOAP API. All of the Amazon services have these standard API endpoints (REST, SOAP) and in theory you can write a client in any programming language!
Fortunately for Android developers, Amazon have released a (Beta) SDK that does all of this work for you. There's a Getting Started guide and Javadocs too. With this SDK you should be able to integrate S3 with your application in a matter of hours.
The Getting Started guide comes with a full sample and shows how to supply the required credentials.
Conceptually, Amazon S3 stores data in Buckets where a bucket contains Objects. Generally you'll use one bucket per application, and add as many objects as you like. S3 doesn't support or have any concept of folders, but you can put slashes (/) in your object names.
We can directly use "Amazone s3" bucket for storing any type of file on server, and we did not need to send any of file to Api server it will reduce the request time.
Gradle File :-
compile 'com.amazonaws:aws-android-sdk-core:2.2.+'
compile 'com.amazonaws:aws-android-sdk-s3:2.2.+'
compile 'com.amazonaws:aws-android-sdk-ddb:2.2.+'
Manifest File :-
<service android:name="com.amazonaws.mobileconnectors.s3.transferutility.TransferService"
android:enabled="true" />
FileUploader Function in any Class :-
private void setUPAmazon() {
//we Need Identity Pool ID like :- "us-east-1:f224****************8"
CognitoCachingCredentialsProvider credentialsProvider = new CognitoCachingCredentialsProvider(getActivity(),
"us-east-1:f224****************8", Regions.US_EAST_1);
AmazonS3 s3 = new AmazonS3Client(credentialsProvider);
final TransferUtility transferUtility = new TransferUtility(s3, getActivity());
final File file = new File(mCameraUri.getPath());
final TransferObserver observer = transferUtility.upload(GeneralValues.AMAZON_BUCKET, file.getName(), file, CannedAccessControlList.PublicRead);
observer.setTransferListener(new TransferListener() {
#Override
public void onStateChanged(int id, TransferState state) {
Log.e("onStateChanged", id + state.name());
if (state == TransferState.COMPLETED) {
String url = "https://"+GeneralValues.AMAZON_BUCKET+".s3.amazonaws.com/" + observer.getKey();
Log.e("URL :,", url);
//we just need to share this File url with Api service request.
}
}
#Override
public void onProgressChanged(int id, long bytesCurrent, long bytesTotal) {
}
#Override
public void onError(int id, Exception ex) {
Toast.makeText(getActivity(), "Unable to Upload", Toast.LENGTH_SHORT).show();
ex.printStackTrace();
}
});
}
you can use the below mentioned class to Upload data to amazon s3 buckets.
public class UploadAmazonS3{
private CognitoCachingCredentialsProvider credentialsProvider = null;
private AmazonS3Client s3Client = null;
private TransferUtility transferUtility = null;
private static UploadAmazonS3 uploadAmazonS3;
/**
* Creating single tone object by defining private.
* <P>
* At the time of creating
* </P>*/
private UploadAmazonS3(Context context, String canito_pool_id)
{
/**
* Creating the object of the getCredentialProvider object. */
credentialsProvider=getCredentialProvider(context,canito_pool_id);
/**
* Creating the object of the s3Client */
s3Client=getS3Client(context,credentialsProvider);
/**
* Creating the object of the TransferUtility of the Amazone.*/
transferUtility=getTransferUtility(context,s3Client);
}
public static UploadAmazonS3 getInstance(Context context, String canito_pool_id)
{
if(uploadAmazonS3 ==null)
{
uploadAmazonS3 =new UploadAmazonS3(context,canito_pool_id);
return uploadAmazonS3;
}else
{
return uploadAmazonS3;
}
}
/**
* <h3>Upload_data</h3>
* <P>
* Method is use to upload data in the amazone server.
*
* </P>*/
public void uploadData(final String bukkate_name, final File file, final Upload_CallBack callBack)
{
Utility.printLog("in amazon upload class uploadData "+file.getName());
if(transferUtility!=null&&file!=null)
{
TransferObserver observer=transferUtility.upload(bukkate_name,file.getName(),file);
observer.setTransferListener(new TransferListener()
{
#Override
public void onStateChanged(int id, TransferState state)
{
if(state.equals(TransferState.COMPLETED))
{
callBack.sucess(com.tarha_taxi.R.string.AMAZON_END_POINT_LINK+bukkate_name+"/"+file.getName());
}
}
#Override
public void onProgressChanged(int id, long bytesCurrent, long bytesTotal)
{
}
#Override
public void onError(int id, Exception ex)
{
callBack.error(id+":"+ex.toString());
}
});
}else
{
callBack.error("Amamzones3 is not intialize or File is empty !");
}
}
/**
* This method is used to get the CredentialProvider and we provide only context as a parameter.
* #param context Here, we are getting the context from calling Activity.*/
private CognitoCachingCredentialsProvider getCredentialProvider(Context context,String pool_id)
{
if (credentialsProvider == null)
{
credentialsProvider = new CognitoCachingCredentialsProvider(
context.getApplicationContext(),
pool_id, // Identity Pool ID
AMAZON_REGION // Region
);
}
return credentialsProvider;
}
/**
* This method is used to get the AmazonS3 Client
* and we provide only context as a parameter.
* and from here we are calling getCredentialProvider() function.
* #param context Here, we are getting the context from calling Activity.*/
private AmazonS3Client getS3Client(Context context,CognitoCachingCredentialsProvider cognitoCachingCredentialsProvider)
{
if (s3Client == null)
{
s3Client = new AmazonS3Client(cognitoCachingCredentialsProvider);
s3Client.setRegion(Region.getRegion(AMAZON_REGION));
s3Client.setEndpoint(context.getString(com.tarha_taxi.R.string.AMAZON_END_POINT_LINK));
}
return s3Client;
}
/**
* This method is used to get the Transfer Utility
* and we provide only context as a parameter.
* and from here we are, calling getS3Client() function.
* #param context Here, we are getting the context from calling Activity.*/
private TransferUtility getTransferUtility(Context context,AmazonS3Client amazonS3Client)
{
if (transferUtility == null)
{
transferUtility = new TransferUtility(amazonS3Client,context.getApplicationContext());
}
return transferUtility;
}
/**
* Interface for the sucess callback fro the Amazon uploading .
* */
public interface Upload_CallBack
{
/**
*Method for sucess .
* #param sucess it is true on sucess and false for falure.*/
void sucess(String sucess);
/**
* Method for falure.
* #param errormsg contains the error message.*/
void error(String errormsg);
}
}
use the below method to access the above calss :
private void uploadToAmazon() {
dialogL.show();
UploadAmazonS3 amazonS3 = UploadAmazonS3.getInstance(getActivity(), getString(R.string.AMAZON_POOL_ID));
amazonS3.uploadData(getString(R.string.BUCKET_NAME), Utility.renameFile(VariableConstants.TEMP_PHOTO_FILE_NAME, phone.getText().toString().substring(1) + ".jpg"), new UploadAmazonS3.Upload_CallBack() {
#Override
public void sucess(String sucess) {
if (Utility.isNetworkAvailable(getActivity())) {
dialogL.dismiss();
/**
* to set the image into image view and
* add the write the image in the file
*/
activity.user_image.setTag(setTarget(progress_bar));
Picasso.with(getActivity()).load(getString(R.string.AMAZON_IMAGE_LINK) + phone.getText().toString().substring(1) + ".jpg").
networkPolicy(NetworkPolicy.NO_CACHE).memoryPolicy(MemoryPolicy.NO_CACHE).into((Target) activity.user_image.getTag());
Utility.printLog("amazon upload success ");
new BackgroundForUpdateProfile().execute();
} else {
dialogL.dismiss();
Utility.showToast(getActivity(), getResources().getString(R.string.network_connection_fail));
}
}
#Override
public void error(String errormsg) {
dialogL.dismiss();
Utility.showToast(getActivity(), getResources().getString(R.string.network_connection_fail));
}
});
}
You can use a library called S3UploadService. First you would need to convert your Bitmap to a File. To do so take a look at this post:
Convert Bitmap to File
S3UploadService is a library that handles uploads to Amazon S3. It provides a service called S3UploadService with a static method where you provide a Context (so the static method can start the service), a File, a boolean indicating if said file should be deleted after upload completion and optionally you can set a callback (Not like an ordinary callback though. The way this works is explained in the README file).
It's an IntentService so the upload will run even if the user kills the app while uploading (because its lifecycle is not attached to the app's lifecycle).
To use this library you just have to declare the service in your manifest:
<application
...>
...
<service
android:name="com.onecode.s3.service.S3UploadService"
android:exported="false" />
</application>
Then you build an S3BucketData instance and make a call to S3UploadService.upload():
S3Credentials s3Credentials = new S3Credentials(accessKey, secretKey, sessionToken);
S3BucketData s3BucketData = new S3BucketData.Builder()
.setCredentials(s3Credentials)
.setBucket(bucket)
.setKey(key)
.setRegion(region)
.build();
S3UploadService.upload(getActivity(), s3BucketData, file, null);
To add this library you need to add the JitPack repo to your root build.gradle:
allprojects {
repositories {
...
maven { url "https://jitpack.io" }
}
}
and then add the dependency:
dependencies {
compile 'com.github.OneCodeLabs:S3UploadService:1.0.0#aar'
}
Here is a link to the repo:
https://github.com/OneCodeLabs/S3UploadService
This answer is a bit late, but I hope it helps someone
you can upload image and download image in s3 amazon. you make a simple class use this WebserviceAmazon
public class WebserviceAmazon extends AsyncTask<Void, Void, Void> {
private String mParams;
private String mResult = "x";
WebServiceInterface<String, String> mInterface;
private int mRequestType;
private String UserId;
private Context mContext;
public WebserviceAmazon(Context context,String imagePath,String AppId,int type) {
this.mContext = context;
this.mParams = imagePath;
this.mRequestType = type;
this.UserId = AppId;
}
public void result(WebServiceInterface<String, String> myInterface) {
this.mInterface = myInterface;
}
#Override
protected Void doInBackground(Void... params) {
String ACCESS_KEY ="abc..";
String SECRET_KEY = "klm...";
try {
if (mRequestType == 1) { // POST
AmazonS3Client s3Client = new AmazonS3Client(new BasicAWSCredentials(ACCESS_KEY, SECRET_KEY));
PutObjectRequest request = new PutObjectRequest("bucketName", "imageName", new File(mParams));
s3Client.putObject(request);
mResult = "success";
} if (mRequestType == 2) { // For get image data
AmazonS3Client s3Client = new AmazonS3Client(new BasicAWSCredentials(ACCESS_KEY, SECRET_KEY));
S3Object object = s3Client.getObject(new GetObjectRequest("bucketName", mParams));
S3ObjectInputStream objectContent = object.getObjectContent();
byte[] byteArray = IOUtils.toByteArray(objectContent);
Bitmap bitmap = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length);
mResult = "success";
}
} catch (Exception e) {
mResult = e.toString();
e.printStackTrace();
}
return null;
}
#Override
protected void onPreExecute() {
// TODO Auto-generated method stub
super.onPreExecute();
}
#Override
protected void onPostExecute(Void result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
mInterface.success(this.mResult);
}
public interface WebServiceInterface<E, R> {
public void success(E reslut);
public void error(R Error);
}
}
call this webservice any where in project
WebserviceAmazon amazon = new WebserviceAmazon(getActivity(), imageName, "", 2);
amazon.result(new WebserviceAmazon.WebServiceInterface<String, String>() {
#Override
public void success(String reslut) {
}
#Override
public void error(String Error) {
}
});
return totalPoints;
}
here is my code to upload image to Amazon AWS S3 bucket
Hope this is helpful for you
First let me clear some key points
you can either take image from camera or pick image from gallery ,AWS store images as a file so first thing you need to store image to local dir and get path of image then create a file from that path after this you will be able to upload that imagefile to AWS.
first you need to add configration on Oncreate
BasicAWSCredentials credentials = new BasicAWSCredentials(KEY,SECRET);
s3 = new AmazonS3Client(credentials);
s3.setRegion(Region.getRegion(Regions.US_EAST_1));
private void uploadFile() {
verifyStoragePermissions(CustomCameraActivity.this);
TransferUtility transferUtility =
TransferUtility.builder()
.context(getApplicationContext())
.awsConfiguration(AWSMobileClient.getInstance().getConfiguration())
.s3Client(s3)
.build();
TransferObserver uploadObserver= null;
File file2 = FileUtils.getFile(CustomCameraActivity.this, storageImagePath);//here i am converting path to file ,,FileUtils.getFile is custom class
uploadObserver = transferUtility.upload("your bucket name", imageNameWithoutExtension + ".jpg", file2);// imagenamewithoutExtension is actually the name that you want to store
uploadObserver.setTransferListener(new TransferListener() {
#Override
public void onStateChanged(int id, TransferState state) {
if (TransferState.COMPLETED == state) {
Toast.makeText(getApplicationContext(), "Upload Completed!", Toast.LENGTH_SHORT).show();
uploadResourcesApi(uploadResoucesURL);
//imageFile.delete();
//file2.delete();
} else if (TransferState.FAILED == state) {
//imageFile.delete();
//file2.delete();
}
}
#Override
public void onProgressChanged(int id, long bytesCurrent, long bytesTotal) {
float percentDonef = ((float) bytesCurrent / (float) bytesTotal) * 100;
int percentDone = (int) percentDonef;
//tvFileName.setText("ID:" + id + "|bytesCurrent: " + bytesCurrent + "|bytesTotal: " + bytesTotal + "|" + percentDone + "%");
}
#Override
public void onError(int id, Exception ex) {
ex.printStackTrace();
}
});
}
this is fileUtils class
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.util.Log;
import android.webkit.MimeTypeMap;
import java.io.File;
import java.io.FileFilter;
import java.text.DecimalFormat;
import java.util.Comparator;
/**
* #version 2009-07-03
* #author Peli
* #version 2013-12-11
* #author paulburke (ipaulpro)
*/
public class FileUtils {
private FileUtils() {} //private constructor to enforce Singleton pattern
/** TAG for log messages. */
static final String TAG = "FileUtils";
private static final boolean DEBUG = false; // Set to true to enable logging
public static final String MIME_TYPE_AUDIO = "audio/*";
public static final String MIME_TYPE_TEXT = "text/*";
public static final String MIME_TYPE_IMAGE = "image/*";
public static final String MIME_TYPE_VIDEO = "video/*";
public static final String MIME_TYPE_APP = "application/*";
public static final String HIDDEN_PREFIX = ".";
/**
* Gets the extension of a file name, like ".png" or ".jpg".
*
* #param uri
* #return Extension including the dot("."); "" if there is no extension;
* null if uri was null.
*/
public static String getExtension(String uri) {
if (uri == null) {
return null;
}
int dot = uri.lastIndexOf(".");
if (dot >= 0) {
return uri.substring(dot);
} else {
// No extension.
return "";
}
}
/**
* #return Whether the URI is a local one.
*/
public static boolean isLocal(String url) {
if (url != null && !url.startsWith("http://") && !url.startsWith("https://")) {
return true;
}
return false;
}
/**
* #return True if Uri is a MediaStore Uri.
* #author paulburke
*/
public static boolean isMediaUri(Uri uri) {
return "media".equalsIgnoreCase(uri.getAuthority());
}
/**
* Convert File into Uri.
*
* #param file
* #return uri
*/
public static Uri getUri(File file) {
if (file != null) {
return Uri.fromFile(file);
}
return null;
}
/**
* Returns the path only (without file name).
*
* #param file
* #return
*/
public static File getPathWithoutFilename(File file) {
if (file != null) {
if (file.isDirectory()) {
// no file to be split off. Return everything
return file;
} else {
String filename = file.getName();
String filepath = file.getAbsolutePath();
// Construct path without file name.
String pathwithoutname = filepath.substring(0,
filepath.length() - filename.length());
if (pathwithoutname.endsWith("/")) {
pathwithoutname = pathwithoutname.substring(0, pathwithoutname.length() - 1);
}
return new File(pathwithoutname);
}
}
return null;
}
/**
* #return The MIME type for the given file.
*/
public static String getMimeType(File file) {
String extension = getExtension(file.getName());
if (extension.length() > 0)
return MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension.substring(1));
return "application/octet-stream";
}
/**
* #return The MIME type for the give Uri.
*/
public static String getMimeType(Context context, Uri uri) {
File file = new File(getPath(context, uri));
return getMimeType(file);
}
/**
* #param uri The Uri to check.
* #return Whether the Uri authority is {#link LocalStorageProvider}.
* #author paulburke
*/
public static boolean isLocalStorageDocument(Uri uri) {
return LocalStorageProvider.AUTHORITY.equals(uri.getAuthority());
}
/**
* #param uri The Uri to check.
* #return Whether the Uri authority is ExternalStorageProvider.
* #author paulburke
*/
public static boolean isExternalStorageDocument(Uri uri) {
return "com.android.externalstorage.documents".equals(uri.getAuthority());
}
/**
* #param uri The Uri to check.
* #return Whether the Uri authority is DownloadsProvider.
* #author paulburke
*/
public static boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}
/**
* #param uri The Uri to check.
* #return Whether the Uri authority is MediaProvider.
* #author paulburke
*/
public static boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri.getAuthority());
}
/**
* #param uri The Uri to check.
* #return Whether the Uri authority is Google Photos.
*/
public static boolean isGooglePhotosUri(Uri uri) {
return "com.google.android.apps.photos.content".equals(uri.getAuthority());
}
/**
* Get the value of the data column for this Uri. This is useful for
* MediaStore Uris, and other file-based ContentProviders.
*
* #param context The context.
* #param uri The Uri to query.
* #param selection (Optional) Filter used in the query.
* #param selectionArgs (Optional) Selection arguments used in the query.
* #return The value of the _data column, which is typically a file path.
* #author paulburke
*/
public static String getDataColumn(Context context, Uri uri, String selection,
String[] selectionArgs) {
Cursor cursor = null;
final String column = "_data";
final String[] projection = {
column
};
try {
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
null);
if (cursor != null && cursor.moveToFirst()) {
if (DEBUG)
DatabaseUtils.dumpCursor(cursor);
final int column_index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(column_index);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
public static String getPath(final Context context, final Uri uri) {
if (DEBUG)
Log.d(TAG + " File -",
"Authority: " + uri.getAuthority() +
", Fragment: " + uri.getFragment() +
", Port: " + uri.getPort() +
", Query: " + uri.getQuery() +
", Scheme: " + uri.getScheme() +
", Host: " + uri.getHost() +
", Segments: " + uri.getPathSegments().toString()
);
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
// DocumentProvider
if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
// LocalStorageProvider
if (isLocalStorageDocument(uri)) {
// The path is the id
return DocumentsContract.getDocumentId(uri);
}
// ExternalStorageProvider
else if (isExternalStorageDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
if ("primary".equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() + "/" + split[1];
}
// TODO handle non-primary volumes
}
// DownloadsProvider
else if (isDownloadsDocument(uri)) {
final String id = DocumentsContract.getDocumentId(uri);
final Uri contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
return getDataColumn(context, contentUri, null, null);
}
// MediaProvider
else if (isMediaDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
Uri contentUri = null;
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
final String selection = "_id=?";
final String[] selectionArgs = new String[] {
split[1]
};
return getDataColumn(context, contentUri, selection, selectionArgs);
}
}
// MediaStore (and general)
else if ("content".equalsIgnoreCase(uri.getScheme())) {
// Return the remote address
if (isGooglePhotosUri(uri))
return uri.getLastPathSegment();
return getDataColumn(context, uri, null, null);
}
// File
else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
return null;
}
public static File getFile(Context context, Uri uri) {
if (uri != null) {
String path = getPath(context, uri);
if (path != null && isLocal(path)) {
return new File(path);
}
}
return null;
}
public static String getReadableFileSize(int size) {
final int BYTES_IN_KILOBYTES = 1024;
final DecimalFormat dec = new DecimalFormat("###.#");
final String KILOBYTES = " KB";
final String MEGABYTES = " MB";
final String GIGABYTES = " GB";
float fileSize = 0;
String suffix = KILOBYTES;
if (size > BYTES_IN_KILOBYTES) {
fileSize = size / BYTES_IN_KILOBYTES;
if (fileSize > BYTES_IN_KILOBYTES) {
fileSize = fileSize / BYTES_IN_KILOBYTES;
if (fileSize > BYTES_IN_KILOBYTES) {
fileSize = fileSize / BYTES_IN_KILOBYTES;
suffix = GIGABYTES;
} else {
suffix = MEGABYTES;
}
}
}
return String.valueOf(dec.format(fileSize) + suffix);
}
public static Bitmap getThumbnail(Context context, File file) {
return getThumbnail(context, getUri(file), getMimeType(file));
}
public static Bitmap getThumbnail(Context context, Uri uri) {
return getThumbnail(context, uri, getMimeType(context, uri));
}
public static Bitmap getThumbnail(Context context, Uri uri, String mimeType) {
if (DEBUG)
Log.d(TAG, "Attempting to get thumbnail");
if (!isMediaUri(uri)) {
Log.e(TAG, "You can only retrieve thumbnails for images and videos.");
return null;
}
Bitmap bm = null;
if (uri != null) {
final ContentResolver resolver = context.getContentResolver();
Cursor cursor = null;
try {
cursor = resolver.query(uri, null, null, null, null);
if (cursor.moveToFirst()) {
final int id = cursor.getInt(0);
if (DEBUG)
Log.d(TAG, "Got thumb ID: " + id);
if (mimeType.contains("video")) {
bm = MediaStore.Video.Thumbnails.getThumbnail(
resolver,
id,
MediaStore.Video.Thumbnails.MINI_KIND,
null);
}
else if (mimeType.contains(FileUtils.MIME_TYPE_IMAGE)) {
bm = MediaStore.Images.Thumbnails.getThumbnail(
resolver,
id,
MediaStore.Images.Thumbnails.MINI_KIND,
null);
}
}
} catch (Exception e) {
if (DEBUG)
Log.e(TAG, "getThumbnail", e);
} finally {
if (cursor != null)
cursor.close();
}
}
return bm;
}
public static Comparator<File> sComparator = new Comparator<File>() {
#Override
public int compare(File f1, File f2) {
// Sort alphabetically by lower case, which is much cleaner
return f1.getName().toLowerCase().compareTo(
f2.getName().toLowerCase());
}
};
public static FileFilter sFileFilter = new FileFilter() {
#Override
public boolean accept(File file) {
final String fileName = file.getName();
// Return files only (not directories) and skip hidden files
return file.isFile() && !fileName.startsWith(HIDDEN_PREFIX);
}
};
public static FileFilter sDirFilter = new FileFilter() {
#Override
public boolean accept(File file) {
final String fileName = file.getName();
// Return directories only and skip hidden directories
return file.isDirectory() && !fileName.startsWith(HIDDEN_PREFIX);
}
};
public static Intent createGetContentIntent() {
// Implicitly allow the user to select a particular kind of data
final Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
// The MIME data type filter
intent.setType("*/*");
// Only return URIs that can be opened with ContentResolver
intent.addCategory(Intent.CATEGORY_OPENABLE);
return intent;
}
}
then you will also need this class
public class LocalStorageProvider extends DocumentsProvider {
public static final String AUTHORITY = "com.ianhanniballake.localstorage.documents";
private final static String[] DEFAULT_ROOT_PROJECTION = new String[] {
Root.COLUMN_ROOT_ID,
Root.COLUMN_FLAGS, Root.COLUMN_TITLE, Root.COLUMN_DOCUMENT_ID, Root.COLUMN_ICON,
Root.COLUMN_AVAILABLE_BYTES
};
private final static String[] DEFAULT_DOCUMENT_PROJECTION = new String[] {
Document.COLUMN_DOCUMENT_ID,
Document.COLUMN_DISPLAY_NAME, Document.COLUMN_FLAGS, Document.COLUMN_MIME_TYPE,
Document.COLUMN_SIZE,
Document.COLUMN_LAST_MODIFIED
};
#Override
public Cursor queryRoots(final String[] projection) throws FileNotFoundException {
final MatrixCursor result = new MatrixCursor(projection != null ? projection
: DEFAULT_ROOT_PROJECTION);
File homeDir = Environment.getExternalStorageDirectory();
final MatrixCursor.RowBuilder row = result.newRow();
// These columns are required
row.add(Root.COLUMN_ROOT_ID, homeDir.getAbsolutePath());
row.add(Root.COLUMN_DOCUMENT_ID, homeDir.getAbsolutePath());
row.add(Root.COLUMN_TITLE, "Internal storage");
row.add(Root.COLUMN_FLAGS, Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_CREATE);
row.add(Root.COLUMN_ICON, R.drawable.ic_launcher_foreground);
// These columns are optional
row.add(Root.COLUMN_AVAILABLE_BYTES, homeDir.getFreeSpace());
// Root.COLUMN_MIME_TYPE is another optional column and useful if you
return result;
}
#Override
public String createDocument(final String parentDocumentId, final String mimeType,
final String displayName) throws FileNotFoundException {
File newFile = new File(parentDocumentId, displayName);
try {
newFile.createNewFile();
return newFile.getAbsolutePath();
} catch (IOException e) {
Log.e(LocalStorageProvider.class.getSimpleName(), "Error creating new file " + newFile);
}
return null;
}
#Override
public AssetFileDescriptor openDocumentThumbnail(final String documentId, final Point sizeHint,
final CancellationSignal signal) throws FileNotFoundException {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(documentId, options);
final int targetHeight = 2 * sizeHint.y;
final int targetWidth = 2 * sizeHint.x;
final int height = options.outHeight;
final int width = options.outWidth;
options.inSampleSize = 1;
if (height > targetHeight || width > targetWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
while ((halfHeight / options.inSampleSize) > targetHeight
|| (halfWidth / options.inSampleSize) > targetWidth) {
options.inSampleSize *= 2;
}
}
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeFile(documentId, options);
// Write out the thumbnail to a temporary file
File tempFile = null;
FileOutputStream out = null;
try {
tempFile = File.createTempFile("thumbnail", null, getContext().getCacheDir());
out = new FileOutputStream(tempFile);
bitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
} catch (IOException e) {
Log.e(LocalStorageProvider.class.getSimpleName(), "Error writing thumbnail", e);
return null;
} finally {
if (out != null)
try {
out.close();
} catch (IOException e) {
Log.e(LocalStorageProvider.class.getSimpleName(), "Error closing thumbnail", e);
}
}
// It appears the Storage Framework UI caches these results quite
// aggressively so there is little reason to
// write your own caching layer beyond what you need to return a single
// AssetFileDescriptor
return new AssetFileDescriptor(ParcelFileDescriptor.open(tempFile,
ParcelFileDescriptor.MODE_READ_ONLY), 0,
AssetFileDescriptor.UNKNOWN_LENGTH);
}
#Override
public Cursor queryChildDocuments(final String parentDocumentId, final String[] projection,
final String sortOrder) throws FileNotFoundException {
// Create a cursor with either the requested fields, or the default
// projection if "projection" is null.
final MatrixCursor result = new MatrixCursor(projection != null ? projection
: DEFAULT_DOCUMENT_PROJECTION);
final File parent = new File(parentDocumentId);
for (File file : parent.listFiles()) {
// Don't show hidden files/folders
if (!file.getName().startsWith(".")) {
// Adds the file's display name, MIME type, size, and so on.
includeFile(result, file);
}
}
return result;
}
#Override
public Cursor queryDocument(final String documentId, final String[] projection)
throws FileNotFoundException {
// Create a cursor with either the requested fields, or the default
// projection if "projection" is null.
final MatrixCursor result = new MatrixCursor(projection != null ? projection
: DEFAULT_DOCUMENT_PROJECTION);
includeFile(result, new File(documentId));
return result;
}
private void includeFile(final MatrixCursor result, final File file)
throws FileNotFoundException {
final MatrixCursor.RowBuilder row = result.newRow();
// These columns are required
row.add(Document.COLUMN_DOCUMENT_ID, file.getAbsolutePath());
row.add(Document.COLUMN_DISPLAY_NAME, file.getName());
String mimeType = getDocumentType(file.getAbsolutePath());
row.add(Document.COLUMN_MIME_TYPE, mimeType);
int flags = file.canWrite() ? Document.FLAG_SUPPORTS_DELETE | Document.FLAG_SUPPORTS_WRITE
: 0;
// We only show thumbnails for image files - expect a call to
// openDocumentThumbnail for each file that has
// this flag set
if (mimeType.startsWith("image/"))
flags |= Document.FLAG_SUPPORTS_THUMBNAIL;
row.add(Document.COLUMN_FLAGS, flags);
// COLUMN_SIZE is required, but can be null
row.add(Document.COLUMN_SIZE, file.length());
// These columns are optional
row.add(Document.COLUMN_LAST_MODIFIED, file.lastModified());
// Document.COLUMN_ICON can be a resource id identifying a custom icon.
// The system provides default icons
// based on mime type
// Document.COLUMN_SUMMARY is optional additional information about the
// file
}
#Override
public String getDocumentType(final String documentId) throws FileNotFoundException {
File file = new File(documentId);
if (file.isDirectory())
return Document.MIME_TYPE_DIR;
// From FileProvider.getType(Uri)
final int lastDot = file.getName().lastIndexOf('.');
if (lastDot >= 0) {
final String extension = file.getName().substring(lastDot + 1);
final String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
if (mime != null) {
return mime;
}
}
return "application/octet-stream";
}
#Override
public void deleteDocument(final String documentId) throws FileNotFoundException {
new File(documentId).delete();
}
#Override
public ParcelFileDescriptor openDocument(final String documentId, final String mode,
final CancellationSignal signal) throws FileNotFoundException {
File file = new File(documentId);
final boolean isWrite = (mode.indexOf('w') != -1);
if (isWrite) {
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_WRITE);
} else {
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
}
}
#Override
public boolean onCreate() {
return true;
}
}
Related
I am currently developing an application for Android and wanted to know how to detect a screenshot. I tried with FileObserver but the problem is that all events are detected ( when device goes into sleep, message, etc. ) . How to detect only screenshot ?
Thank you in advance !
How did you use FileObserver to detect screen shot creation? When using FileObserver, only monitor the file creation event in screen shot directory.
String path = Environment.getExternalStorageDirectory()
+ File.separator + Environment.DIRECTORY_PICTURES
+ File.separator + "Screenshots" + File.separator;
Log.d(TAG, path);
FileObserver fileObserver = new FileObserver(path, FileObserver.CREATE) {
#Override
public void onEvent(int event, String path) {
Log.d(TAG, event + " " + path);
}
};
fileObserver.startWatching();
Don't forget to declare corresponding permissions to access content in SD card.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
Another solution to detect the screen shot is using ContentObserver, because there will be a record inserted to the system media database after screen shot. Following is the code snippet using ContentObserver to monitor the event. By using ContentObserver, it's not necessary to declare write/read external storage permissions, but you have to do some filters on the file name to make sure it's a screen shot event.
HandlerThread handlerThread = new HandlerThread("content_observer");
handlerThread.start();
final Handler handler = new Handler(handlerThread.getLooper()) {
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
getContentResolver().registerContentObserver(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
true,
new ContentObserver(handler) {
#Override
public boolean deliverSelfNotifications() {
Log.d(TAG, "deliverSelfNotifications");
return super.deliverSelfNotifications();
}
#Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
}
#Override
public void onChange(boolean selfChange, Uri uri) {
Log.d(TAG, "onChange " + uri.toString());
if (uri.toString().matches(MediaStore.Images.Media.EXTERNAL_CONTENT_URI.toString() + "/[0-9]+")) {
Cursor cursor = null;
try {
cursor = getContentResolver().query(uri, new String[] {
MediaStore.Images.Media.DISPLAY_NAME,
MediaStore.Images.Media.DATA
}, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
final String fileName = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME));
final String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
// TODO: apply filter on the file name to ensure it's screen shot event
Log.d(TAG, "screen shot added " + fileName + " " + path);
}
} finally {
if (cursor != null) {
cursor.close();
}
}
}
super.onChange(selfChange, uri);
}
}
);
Updated
If you use second method, you have to request READ_EXTERNAL_STORAGE after version Android M, otherwise it will throw SecurityException. For more information how to request runtime permission, refer here.
I have improve the code from alijandro's comment to make it easy-to-use class and fix the problem when content observer has detect the image from camera (should be screenshot image only). Then wrap it to delegate class for convenient to use.
• ScreenshotDetectionDelegate.java
public class ScreenshotDetectionDelegate {
private WeakReference<Activity> activityWeakReference;
private ScreenshotDetectionListener listener;
public ScreenshotDetectionDelegate(Activity activityWeakReference, ScreenshotDetectionListener listener) {
this.activityWeakReference = new WeakReference<>(activityWeakReference);
this.listener = listener;
}
public void startScreenshotDetection() {
activityWeakReference.get()
.getContentResolver()
.registerContentObserver(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true, contentObserver);
}
public void stopScreenshotDetection() {
activityWeakReference.get().getContentResolver().unregisterContentObserver(contentObserver);
}
private ContentObserver contentObserver = new ContentObserver(new Handler()) {
#Override
public boolean deliverSelfNotifications() {
return super.deliverSelfNotifications();
}
#Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
}
#Override
public void onChange(boolean selfChange, Uri uri) {
super.onChange(selfChange, uri);
if (isReadExternalStoragePermissionGranted()) {
String path = getFilePathFromContentResolver(activityWeakReference.get(), uri);
if (isScreenshotPath(path)) {
onScreenCaptured(path);
}
} else {
onScreenCapturedWithDeniedPermission();
}
}
};
private void onScreenCaptured(String path) {
if (listener != null) {
listener.onScreenCaptured(path);
}
}
private void onScreenCapturedWithDeniedPermission() {
if (listener != null) {
listener.onScreenCapturedWithDeniedPermission();
}
}
private boolean isScreenshotPath(String path) {
return path != null && path.toLowerCase().contains("screenshots");
}
private String getFilePathFromContentResolver(Context context, Uri uri) {
try {
Cursor cursor = context.getContentResolver().query(uri, new String[]{
MediaStore.Images.Media.DISPLAY_NAME,
MediaStore.Images.Media.DATA
}, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
cursor.close();
return path;
}
} catch (IllegalStateException ignored) {
}
return null;
}
private boolean isReadExternalStoragePermissionGranted() {
return ContextCompat.checkSelfPermission(activityWeakReference.get(), Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
}
public interface ScreenshotDetectionListener {
void onScreenCaptured(String path);
void onScreenCapturedWithDeniedPermission();
}
}
• ScreenshotDetectionActivity.java
import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;
public abstract class ScreenshotDetectionActivity extends AppCompatActivity implements ScreenshotDetectionDelegate.ScreenshotDetectionListener {
private static final int REQUEST_CODE_READ_EXTERNAL_STORAGE_PERMISSION = 3009;
private ScreenshotDetectionDelegate screenshotDetectionDelegate = new ScreenshotDetectionDelegate(this, this);
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
checkReadExternalStoragePermission();
}
#Override
protected void onStart() {
super.onStart();
screenshotDetectionDelegate.startScreenshotDetection();
}
#Override
protected void onStop() {
super.onStop();
screenshotDetectionDelegate.stopScreenshotDetection();
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
switch (requestCode) {
case REQUEST_CODE_READ_EXTERNAL_STORAGE_PERMISSION:
if (grantResults[0] == PackageManager.PERMISSION_DENIED) {
showReadExternalStoragePermissionDeniedMessage();
}
break;
default:
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
#Override
public void onScreenCaptured(String path) {
// Do something when screen was captured
}
#Override
public void onScreenCapturedWithDeniedPermission() {
// Do something when screen was captured but read external storage permission has denied
}
private void checkReadExternalStoragePermission() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
requestReadExternalStoragePermission();
}
}
private void requestReadExternalStoragePermission() {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_CODE_READ_EXTERNAL_STORAGE_PERMISSION);
}
private void showReadExternalStoragePermissionDeniedMessage() {
Toast.makeText(this, "Read external storage permission has denied", Toast.LENGTH_SHORT).show();
}
}
• MainActivity.java
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
public class MainActivity extends ScreenshotDetectionActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
public void onScreenCaptured(String path) {
Toast.make(this, path, Toast.LENGTH_SHORT).show();
}
#Override
public void onScreenCapturedWithDeniedPermission() {
Toast.make(this, "Please grant read external storage permission for screenshot detection", Toast.LENGTH_SHORT).show();
}
}
You can create FileObserver that only monitors screenshot directory plus only trigger events for file or directory creation.
For more information click here.
I made a git project for Android screenshot detection using Content Observer.
It is working fine from API 14 to the most recent version (at the time of posting).
1.ScreenShotContentObserver .class
(original screenshot delete -> inform screenshot taken and give screenshot bitmap )
public class ScreenShotContentObserver extends ContentObserver {
private final String TAG = this.getClass().getSimpleName();
private static final String[] PROJECTION = new String[]{
MediaStore.Images.Media.DISPLAY_NAME, MediaStore.Images.Media.DATA,
MediaStore.Images.Media.DATE_ADDED, MediaStore.Images.ImageColumns._ID
};
private static final long DEFAULT_DETECT_WINDOW_SECONDS = 10;
private static final String SORT_ORDER = MediaStore.Images.Media.DATE_ADDED + " DESC";
public static final String FILE_POSTFIX = "FROM_ASS";
private static final String WATERMARK = "Scott";
private ScreenShotListener mListener;
private ContentResolver mContentResolver;
private String lastPath;
public ScreenShotContentObserver(Handler handler, ContentResolver contentResolver, ScreenShotListener listener) {
super(handler);
mContentResolver = contentResolver;
mListener = listener;
}
#Override
public boolean deliverSelfNotifications() {
Log.e(TAG, "deliverSelfNotifications");
return super.deliverSelfNotifications();
}
#Override
synchronized public void onChange(boolean selfChange) {
super.onChange(selfChange);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
//above API 16 Pass~!(duplicated call...)
return;
}
Log.e(TAG, "[Start] onChange : " + selfChange);
try {
process(MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
Log.e(TAG, "[Finish] general");
} catch (Exception e) {
Log.e(TAG, "[Finish] error : " + e.toString(), e);
}
}
#Override
synchronized public void onChange(boolean selfChange, Uri uri) {
super.onChange(selfChange, uri);
Log.e(TAG, "[Start] onChange : " + selfChange + " / uri : " + uri.toString());
if (uri.toString().startsWith(MediaStore.Images.Media.EXTERNAL_CONTENT_URI.toString())) {
try {
process(uri);
Log.e(TAG, "[Finish] general");
} catch (Exception e) {
Log.e(TAG, "[Finish] error : " + e.toString(), e);
}
} else {
Log.e(TAG, "[Finish] not EXTERNAL_CONTENT_URI ");
}
}
public void register() {
Log.d(TAG, "register");
mContentResolver.registerContentObserver(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true, this);
}
public void unregister() {
Log.d(TAG, "unregister");
mContentResolver.unregisterContentObserver(this);
}
private boolean process(Uri uri) throws Exception {
Data result = getLatestData(uri);
if (result == null) {
Log.e(TAG, "[Result] result is null!!");
return false;
}
if (lastPath != null && lastPath.equals(result.path)) {
Log.e(TAG, "[Result] duplicate!!");
return false;
}
long currentTime = System.currentTimeMillis() / 1000;
if (matchPath(result.path) && matchTime(currentTime, result.dateAdded)) {
lastPath = result.path;
Uri screenUri = Uri.parse(MediaStore.Images.Media.EXTERNAL_CONTENT_URI.toString() + "/" + result.id);
Log.e(TAG, "[Result] This is screenshot!! : " + result.fileName + " | dateAdded : " + result.dateAdded + " / " + currentTime);
Bitmap bitmap = MediaStore.Images.Media.getBitmap(mContentResolver, screenUri);
Bitmap copyBitmap = bitmap.copy(bitmap.getConfig(), true);
bitmap.recycle();
int temp = mContentResolver.delete(screenUri, null, null);
Log.e(TAG, "Delete Result : " + temp);
if (mListener != null) {
mListener.onScreenshotTaken(copyBitmap, result.fileName);
}
return true;
} else {
Log.e(TAG, "[Result] No ScreenShot : " + result.fileName);
}
return false;
}
private Data getLatestData(Uri uri) throws Exception {
Data data = null;
Cursor cursor = null;
try {
cursor = mContentResolver.query(uri, PROJECTION, null, null, SORT_ORDER);
if (cursor != null && cursor.moveToFirst()) {
long id = cursor.getLong(cursor.getColumnIndex(MediaStore.Images.ImageColumns._ID));
String fileName = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME));
String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
long dateAdded = cursor.getLong(cursor.getColumnIndex(MediaStore.Images.Media.DATE_ADDED));
if (fileName.contains(FILE_POSTFIX)) {
if (cursor.moveToNext()) {
id = cursor.getLong(cursor.getColumnIndex(MediaStore.Images.ImageColumns._ID));
fileName = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME));
path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
dateAdded = cursor.getLong(cursor.getColumnIndex(MediaStore.Images.Media.DATE_ADDED));
} else {
return null;
}
}
data = new Data();
data.id = id;
data.fileName = fileName;
data.path = path;
data.dateAdded = dateAdded;
Log.e(TAG, "[Recent File] Name : " + fileName);
}
} finally {
if (cursor != null) {
cursor.close();
}
}
return data;
}
private boolean matchPath(String path) {
return (path.toLowerCase().contains("screenshots/") && !path.contains(FILE_POSTFIX));
}
private boolean matchTime(long currentTime, long dateAdded) {
return Math.abs(currentTime - dateAdded) <= DEFAULT_DETECT_WINDOW_SECONDS;
}
class Data {
long id;
String fileName;
String path;
long dateAdded;
}
}
Util.class
public static void saveImage(Context context, Bitmap bitmap, String title) throws Exception {
OutputStream fOut = null;
title = title.replaceAll(" ", "+");
int index = title.lastIndexOf(".png");
String fileName = title.substring(0, index) + ScreenShotContentObserver.FILE_POSTFIX + ".png";
final String appDirectoryName = "Screenshots";
final File imageRoot = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), appDirectoryName);
imageRoot.mkdirs();
final File file = new File(imageRoot, fileName);
fOut = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fOut);
fOut.flush();
fOut.close();
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.TITLE, "XXXXX");
values.put(MediaStore.Images.Media.DESCRIPTION, "description here");
values.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis());
values.put(MediaStore.Images.ImageColumns.BUCKET_ID, file.hashCode());
values.put(MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME, file.getName());
values.put("_data", file.getAbsolutePath());
ContentResolver cr = context.getContentResolver();
Uri newUri = cr.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, newUri));
}
I have a photo editing android app that users can choose the output directory of the the result photos. Problem is Google made a change on sdcard write permission with the KITKAT version and devices with Android KITKAT version won't allow apps to write secondary sdcards. Now I need to check if the choosen directory by user has granted the permission and won't throw EACCES error. I am already checking canRead and canWrite but these won't help. Could you please tell me how can I check if the choosen directory won't throw EACCES. My only solution is trying to write a file in a try catch, however I am hoping there is better way to do it.
[update k3b 2016-09-19]
i tried this on my android-4.4 but without success
Uri uri = Uri.fromFile(file);
int permissionCode =
context.checkCallingOrSelfUriPermission(uri,
Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
if (permissionCode == PackageManager.PERMISSION_DENIED) {
// on my android-4.4 i always get PERMISSION_DENIED even
// if i can overwrite the file
return false;
}
try {
Process p = new ProcessBuilder("ls", "-l", "-s", dir.getCanonicalPath()).start();
String line;
ArrayList<String> lineOut = new ArrayList<>();
BufferedReader error = new BufferedReader(new InputStreamReader(p.getErrorStream()));
while ((line = error.readLine()) != null) {
Log.e(TAG, "ls error = "+line);
}
error.close();
BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
while ((line = input.readLine()) != null) {
lineOut.add(line);
}
input.close();
String[] strings = lineOut.toArray(new String[]{});
List<FilesLS.FileEntry> fileEntries = FilesLS.processNewLines(strings);
for(FilesLS.FileEntry file : fileEntries){
Log.d(TAG, file.name +" = " + file.permissions);
}
} catch (IOException e) {
e.printStackTrace();
}
And some edits to this class
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public final class FilesLS {
/**
* Entry type: File
*/
public static final int TYPE_FILE = 0;
/**
* Entry type: Directory
*/
public static final int TYPE_DIRECTORY = 1;
/**
* Entry type: Directory Link
*/
public static final int TYPE_DIRECTORY_LINK = 2;
/**
* Entry type: Block
*/
public static final int TYPE_BLOCK = 3;
/**
* Entry type: Character
*/
public static final int TYPE_CHARACTER = 4;
/**
* Entry type: Link
*/
public static final int TYPE_LINK = 5;
/**
* Entry type: Socket
*/
public static final int TYPE_SOCKET = 6;
/**
* Entry type: FIFO
*/
public static final int TYPE_FIFO = 7;
/**
* Entry type: Other
*/
public static final int TYPE_OTHER = 8;
/**
* Device side file separator.
*/
public static final String FILE_SEPARATOR = "/"; //$NON-NLS-1$
/**
* Regexp pattern to parse the result from ls.
*/
private static Pattern sLsPattern = Pattern
.compile("^([bcdlsp-][-r][-w][-xsS][-r][-w][-xsS][-r][-w][-xstST])\\s+(\\S+)\\s+ (\\S+)\\s+(\\d{4}-\\d\\d-\\d\\d)\\s+(\\d\\d:\\d\\d)\\s+(.*)$"); //$NON-NLS-1$ \s+([\d\s,]*)
public static List<FileEntry> processNewLines(String[] lines) {
List<FileEntry> listOfFiles = new ArrayList<FileEntry>();
for (String line : lines) {
// no need to handle empty lines.
if (line.length() == 0) {
continue;
}
// run the line through the regexp
Matcher m = sLsPattern.matcher(line);
if (m.matches() == false) {
continue;
}
// get the name
String name = m.group(6);
// get the rest of the groups
String permissions = m.group(1);
String owner = m.group(2);
String group = m.group(3);
// String size = m.group(4);
String date = m.group(4);
String time = m.group(5);
String info = null;
// and the type
int objectType = TYPE_OTHER;
switch (permissions.charAt(0)) {
case '-':
objectType = TYPE_FILE;
break;
case 'b':
objectType = TYPE_BLOCK;
break;
case 'c':
objectType = TYPE_CHARACTER;
break;
case 'd':
objectType = TYPE_DIRECTORY;
break;
case 'l':
objectType = TYPE_LINK;
break;
case 's':
objectType = TYPE_SOCKET;
break;
case 'p':
objectType = TYPE_FIFO;
break;
}
// now check what we may be linking to
if (objectType == TYPE_LINK) {
String[] segments = name.split("\\s->\\s"); //$NON-NLS-1$
// we should have 2 segments
if (segments.length == 2) {
// update the entry name to not contain the link
name = segments[0];
// and the link name
info = segments[1];
// now get the path to the link
String[] pathSegments = info.split(FILE_SEPARATOR);
if (pathSegments.length == 1) {
// the link is to something in the same directory,
// unless the link is ..
if ("..".equals(pathSegments[0])) { //$NON-NLS-1$
// set the type and we're done.
objectType = TYPE_DIRECTORY_LINK;
} else {
// either we found the object already
// or we'll find it later.
}
}
}
// add an arrow in front to specify it's a link.
info = "-> " + info; //$NON-NLS-1$;
}
FileEntry entry = new FileEntry();
entry.permissions = permissions;
entry.name = name;
// entry.size = size;
entry.date = date;
entry.time = time;
entry.owner = owner;
entry.group = group;
if (objectType == TYPE_LINK) {
entry.info = info;
}
listOfFiles.add(entry);
}
return listOfFiles;
}
public final static class FileEntry {
String name;
String info;
String permissions;
String size;
String date;
String time;
String owner;
String group;
int type;
}
}
Add the permission(s) you need to the array:
private static final int REQUEST_CODE_PERMISSION = 2;
String[] mPermission = {
Manifest.permission.INTERNET,
Manifest.permission.CHANGE_WIFI_STATE,
Manifest.permission.CHANGE_NETWORK_STATE,
Manifest.permission.ACCESS_WIFI_STATE
};
Add this to onCreate or where you want it to be:
try {
if (
ActivityCompat.checkSelfPermission(this, mPermission[0])
!= MockPackageManager.PERMISSION_GRANTED ||
ActivityCompat.checkSelfPermission(this, mPermission[1])
!= MockPackageManager.PERMISSION_GRANTED ||
ActivityCompat.checkSelfPermission(this, mPermission[2])
!= MockPackageManager.PERMISSION_GRANTED ||
ActivityCompat.checkSelfPermission(this, mPermission[3])
!= MockPackageManager.PERMISSION_GRANTED
) {
Log.e("TAGTAG", "DENIED");
ActivityCompat.requestPermissions(
this, mPermission, REQUEST_CODE_PERMISSION
);
// 'Will execute recursively if any of the permissions was not granted.
} else {
Log.e("TAGTAG", "GRANTED");
}
} catch (Exception e) {
e.printStackTrace();
}
Don't forget to declare the permissions in AndroidManifest.xml.
I wrote my own camera app. This app writes exif information in the jpg files. It works well but I have some problems with the exifInferface class, e.g., I get the following errors when re-read the JPG file:
Warning Invalid EXIF text encoding
Warning Invalid size (8589934590) for IFD0 tag 0x8827
Warning Bad IFD1 directory
I know that my IFD0 pointer at the exif-Information is broken. It may be that while writing the exif information the pointer is broken?
However, I've Googled and found nothing
I use this class to read an write the exif-informations:
public class ExifHelper {
private String aperature = null;
private String exposureTime = null;
private String flash = null;
private String focalLength = null;
private String gpsAltitude = null;
private String gpsAltitudeRef = null;
private String gpsDateStamp = null;
private String gpsLatitude = null;
private String gpsLatitudeRef = null;
private String gpsLongitude = null;
private String gpsLongitudeRef = null;
private String gpsProcessingMethod = null;
private String gpsTimestamp = null;
private String iso = null;
private String make = null;
private String model = null;
private String imageLength = null;
private String imageWidth = null;
private String orientation = null;
private String whiteBalance = null;
private String exifVersion = null;
private String time = null;
private ExifInterface inFile = null;
private ExifInterface outFile = null;
final static String TAG = "ExifHelper";
/**
* The file before it is compressed
*
* #param filePath
* #throws IOException
*/
public void createInFile(String filePath) throws IOException {
this.inFile = new ExifInterface(filePath);
}
/**
* The file after it has been compressed
*
* #param filePath
* #throws IOException
*/
public void createOutFile(String filePath) throws IOException {
this.outFile = new ExifInterface(filePath);
}
/**
* Reads all the EXIF data from the input file.
*/
public void readExifData() {
this.aperature = inFile.getAttribute(ExifInterface.TAG_APERTURE);
this.exposureTime = inFile.getAttribute(ExifInterface.TAG_EXPOSURE_TIME);
this.flash = inFile.getAttribute(ExifInterface.TAG_FLASH);
this.focalLength = inFile.getAttribute(ExifInterface.TAG_FOCAL_LENGTH);
this.gpsAltitude = inFile.getAttribute(ExifInterface.TAG_GPS_ALTITUDE);
this.gpsAltitudeRef = inFile.getAttribute(ExifInterface.TAG_GPS_ALTITUDE_REF);
this.gpsDateStamp = inFile.getAttribute(ExifInterface.TAG_GPS_DATESTAMP);
this.gpsLatitude = inFile.getAttribute(ExifInterface.TAG_GPS_LATITUDE);
this.gpsLatitudeRef = inFile.getAttribute(ExifInterface.TAG_GPS_LATITUDE_REF);
this.gpsLongitude = inFile.getAttribute(ExifInterface.TAG_GPS_LONGITUDE);
this.gpsLongitudeRef = inFile.getAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF);
this.gpsProcessingMethod = inFile.getAttribute(ExifInterface.TAG_GPS_PROCESSING_METHOD);
this.gpsTimestamp = inFile.getAttribute(ExifInterface.TAG_GPS_TIMESTAMP);
this.imageLength = inFile.getAttribute(ExifInterface.TAG_IMAGE_LENGTH);
this.imageWidth = inFile.getAttribute(ExifInterface.TAG_IMAGE_WIDTH);
this.iso = inFile.getAttribute(ExifInterface.TAG_ISO);
this.make = inFile.getAttribute(ExifInterface.TAG_MAKE);
this.model = inFile.getAttribute(ExifInterface.TAG_MODEL);
this.orientation = inFile.getAttribute(ExifInterface.TAG_ORIENTATION);
this.whiteBalance = inFile.getAttribute(ExifInterface.TAG_WHITE_BALANCE);
this.exifVersion = inFile.getAttribute("ExifVersion");
}
/**
* Writes the previously stored EXIF data to the output file.
* #param pictureDate
* #param orientationValues
* #param accelValues
*
* #throws IOException
*/
public void writeExifData(String pictureDate) throws IOException {
// Don't try to write to a null file
if (this.outFile == null) {
return;
}
if (this.aperature != null) {
this.outFile.setAttribute(ExifInterface.TAG_APERTURE, this.aperature);
}
if (this.exposureTime != null) {
this.outFile.setAttribute(ExifInterface.TAG_EXPOSURE_TIME, this.exposureTime);
}
if (this.flash != null) {
this.outFile.setAttribute(ExifInterface.TAG_FLASH, this.flash);
}
if (this.focalLength != null) {
this.outFile.setAttribute(ExifInterface.TAG_FOCAL_LENGTH, this.focalLength);
}
if (this.gpsAltitude != null) {
this.outFile.setAttribute(ExifInterface.TAG_GPS_ALTITUDE, this.gpsAltitude);
}
if (this.gpsAltitudeRef != null) {
this.outFile.setAttribute(ExifInterface.TAG_GPS_ALTITUDE_REF, this.gpsAltitudeRef);
}
if (this.gpsDateStamp != null) {
this.outFile.setAttribute(ExifInterface.TAG_GPS_DATESTAMP, this.gpsDateStamp);
}
if (this.gpsLatitude != null) {
this.outFile.setAttribute(ExifInterface.TAG_GPS_LATITUDE, this.gpsLatitude);
}
if (this.gpsLatitudeRef != null) {
this.outFile.setAttribute(ExifInterface.TAG_GPS_LATITUDE_REF, this.gpsLatitudeRef);
}
if (this.gpsLongitude != null) {
this.outFile.setAttribute(ExifInterface.TAG_GPS_LONGITUDE, this.gpsLongitude);
}
if (this.gpsLongitudeRef != null) {
this.outFile.setAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF, this.gpsLongitudeRef);
}
if (this.gpsProcessingMethod != null) {
this.outFile.setAttribute(ExifInterface.TAG_GPS_PROCESSING_METHOD, this.gpsProcessingMethod);
}
if (this.gpsTimestamp != null) {
this.outFile.setAttribute(ExifInterface.TAG_GPS_TIMESTAMP, this.gpsTimestamp);
}
if (this.iso != null) {
this.outFile.setAttribute(ExifInterface.TAG_ISO, this.iso);
}
if (this.make != null) {
this.outFile.setAttribute(ExifInterface.TAG_MAKE, this.make);
}
if (this.model != null) {
this.outFile.setAttribute(ExifInterface.TAG_MODEL, this.model);
}
if (this.orientation != null) {
this.outFile.setAttribute(ExifInterface.TAG_ORIENTATION, this.orientation);
}
if (this.whiteBalance != null) {
this.outFile.setAttribute(ExifInterface.TAG_WHITE_BALANCE, this.whiteBalance);
}
if (this.exifVersion != null) {
this.outFile.setAttribute("ExifVersion", this.exifVersion);
}
this.outFile.setAttribute(ExifInterface.TAG_IMAGE_LENGTH, this.imageLength);
this.outFile.setAttribute(ExifInterface.TAG_IMAGE_WIDTH, this.imageWidth);
this.outFile.setAttribute(ExifInterface.TAG_DATETIME, pictureDate);
String mString = exifRandomZahlen();
this.outFile.setAttribute("UserComment", mString);
this.outFile.saveAttributes();
}
I also write 2 images form the takepicture-method. One the original there is no problem and the second with modify exif informations. There is the problem with the exifinterface I think by writing it back to the JPG.
I used the tool DumpImage to view the exif informations. This tool is from the metaworking group (www.metadataworkinggroup.org)
So I have a big question, How I can fix this broken exif data? For example the IDF0 pointer
somebody know or have the same problem?
I get, for example, the Tag TAG_DATETIME two times in my exif information
this is the class for saveing the photo:
public class Photo extends Activity implements PictureCallback {
public interface OnPictureTakenListener {
void pictureTaken(File pictureFile, File pictureFilePatched, String exifDateString);
}
private final Context context;
private OnPictureTakenListener listener;
public Photo(Context ourContext, OnPictureTakenListener theListener) {
this.context = ourContext;
this.listener = theListener;
}
#SuppressLint("SimpleDateFormat")
#Override
public void onPictureTaken(byte[] data, Camera camera) {
Date date = new Date();
File pictureFileDir = getDir();
if (!pictureFileDir.exists() && !pictureFileDir.mkdirs())
{
Log.d(AndroidCamera.DEBUG_TAG,
"Can't create directory to save image.");
Toast.makeText(context, "Can't create directory to save image.",
Toast.LENGTH_LONG).show();
return;
}
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
SimpleDateFormat dateConverter = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss");
String exifDateString = dateFormat.format(date);
String datePicture = dateConverter.format(date);
String photoFile = "Picture_" + datePicture + ".jpg";
String photoFilePatched = "Picture_" + datePicture + "_patched.jpg";
String filename = pictureFileDir.getPath() + File.separator + photoFile;
String filenamePatched = pictureFileDir.getPath() + File.separator + photoFilePatched;
File pictureFile = new File(filename);
File pictureFilePatched = new File (filenamePatched);
try {
FileOutputStream fos = new FileOutputStream(pictureFile);
FileOutputStream fosPatched = new FileOutputStream(pictureFilePatched);
fos.write(data);
fosPatched.write(data);
fos.close();
fosPatched.close();
Toast.makeText(context, "New Image saved:" + photoFile,
Toast.LENGTH_LONG).show();
} catch (Exception error) {
Log.d(AndroidCamera.DEBUG_TAG, "File" + filename + " not saved: "
+ error.getMessage());
Toast.makeText(context, "Image could not be saved.",
Toast.LENGTH_LONG).show();
}
listener.pictureTaken(pictureFile,pictureFilePatched,exifDateString);
context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://"+ Environment.getExternalStorageDirectory())));
}
private File getDir() {
File sdDir = Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
return new File(sdDir, "Camera");
}
static final int REQUEST_IMAGE_CAPTURE = 1;
}
here in main activity i call on one from the two pictures to write the exif infortmations:
camera.takePicture(null, null, new Photo(this,
new Photo.OnPictureTakenListener() {
public void pictureTaken(final File pictureFile,final File pictureFilePatched , final String date) {
final String fileName = pictureFile.getPath();
final String fileNamePatched = pictureFilePatched.getPath();
final String dateTime = date;
// don't start picture preview immediately, but a little
// delayed...
continueWithPreview.postDelayed(new Runnable() {
#Override
public void run() {
try {
// EXIF Matadata change
ExifHelper exifHelper = new ExifHelper();
exifHelper.createInFile(fileName);
//EXIF Metadata read
exifHelper.readExifData();
exifHelper.createOutFile(fileNamePatched);
//Exif Metadata write
exifHelper.writeExifData(dateTime);
} catch (IOException e) {
e.printStackTrace();
Log.e("PictureActivity", e.getLocalizedMessage());
}
if (null != camera)
{
camera.startPreview();
Toast.makeText(AndroidCamera.this, "started!",
Toast.LENGTH_SHORT).show();
}
}
}, 2500);
I am developing android app where SQlite as a database.I want to export certain result from DB in to excel file format programatically, want to store that excel to local device path
I have come across following links
SQlite database programmatically convert into Excel file format in Android
Android - Generate CSV file from table values
android exporting to csv and sending as email attachment
So what is exact procedure to implement Export to Excel for android apps ?
Guys here is answer that I have implemented successfully
//new async task for file export to csv
private class ExportDatabaseCSVTask extends AsyncTask<String, String, Boolean> {
private final ProgressDialog dialog = new ProgressDialog(SearchResultActivity.this);
boolean memoryErr = false;
// to show Loading dialog box
#Override
protected void onPreExecute() {
this.dialog.setMessage("Exporting database...");
this.dialog.show();
}
// to write process
protected Boolean doInBackground(final String... args) {
boolean success = false;
String currentDateString = new SimpleDateFormat(Constants.SimpleDtFrmt_ddMMyyyy).format(new Date());
File dbFile = getDatabasePath("HLPL_FRETE.db");
Log.v(TAG, "Db path is: " + dbFile); // get the path of db
File exportDir = new File(Environment.getExternalStorageDirectory() + File.separator + Constants.FileNm.FILE_DIR_NM, "");
long freeBytesInternal = new File(getApplicationContext().getFilesDir().getAbsoluteFile().toString()).getFreeSpace();
long megAvailable = freeBytesInternal / 1048576;
if (megAvailable < 0.1) {
System.out.println("Please check"+megAvailable);
memoryErr = true;
}else {
exportDirStr = exportDir.toString();// to show in dialogbox
Log.v(TAG, "exportDir path::" + exportDir);
if (!exportDir.exists()) {
exportDir.mkdirs();
}
try {
List<SalesActivity> listdata = salesLst;
SalesActivity sa = null;
String lob = null;
for (int index = 0; index < listdata.size();) {
sa = listdata.get(index);
lob = sa.getLob();
break;
}
if (Constants.Common.OCEAN_LOB.equals(lob)) {
file = new File(exportDir, Constants.FileNm.FILE_OFS + currentDateString + ".csv");
} else {
file = new File(exportDir, Constants.FileNm.FILE_AFS + currentDateString + ".csv");
}
file.createNewFile();
CSVWriter csvWrite = new CSVWriter(new FileWriter(file));
// this is the Column of the table and same for Header of CSV
// file
if (Constants.Common.OCEAN_LOB.equals(lob)) {
csvWrite.writeNext(Constants.FileNm.CSV_O_HEADER);
}else{
csvWrite.writeNext(Constants.FileNm.CSV_A_HEADER);
}
String arrStr1[] = { "SR.No", "CUTSOMER NAME", "PROSPECT", "PORT OF LOAD", "PORT OF DISCHARGE" };
csvWrite.writeNext(arrStr1);
if (listdata.size() > 0) {
for (int index = 0; index < listdata.size(); index++) {
sa = listdata.get(index);
String pol;
String pod;
if (Constants.Common.OCEAN_LOB.equals(sa.getLob())) {
pol = sa.getPortOfLoadingOENm();
pod = sa.getPortOfDischargeOENm();
} else {
pol = sa.getAirportOfLoadNm();
pod = sa.getAirportOfDischargeNm();
}
int srNo = index;
String arrStr[] = { String.valueOf(srNo + 1), sa.getCustomerNm(), sa.getProspectNm(), pol, pod };
csvWrite.writeNext(arrStr);
}
success = true;
}
csvWrite.close();
} catch (IOException e) {
Log.e("SearchResultActivity", e.getMessage(), e);
return success;
}
}
return success;
}
// close dialog and give msg
protected void onPostExecute(Boolean success) {
if (this.dialog.isShowing()) {
this.dialog.dismiss();
}
if (success) {
dialogBox(Constants.Flag.FLAG_EXPRT_S);
} else {
if (memoryErr==true) {
dialogBox(Constants.Flag.FLAG_MEMORY_ERR);
} else {
dialogBox(Constants.Flag.FLAG_EXPRT_F);
}
}
}
}
this is my answer: And this works !
Excel file is the same as a .csv file.
Step 1: download this jar file https://code.google.com/p/opencsv/downloads/detail?name=opencsv-2.4.jar&can=2&q=
Step 2:
private class ExportDatabaseCSVTask extends AsyncTask<String ,String, String>{
private final ProgressDialog dialog = new ProgressDialog(MainActivity.this);
#Override
protected void onPreExecute() {
this.dialog.setMessage("Exporting database...");
this.dialog.show();
}
protected String doInBackground(final String... args){
File exportDir = new File(Environment.getExternalStorageDirectory(), "");
if (!exportDir.exists()) {
exportDir.mkdirs();
}
File file = new File(exportDir, "ExcelFile.csv");
try {
file.createNewFile();
CSVWriter csvWrite = new CSVWriter(new FileWriter(file));
//data
ArrayList<String> listdata= new ArrayList<String>();
listdata.add("Aniket");
listdata.add("Shinde");
listdata.add("pune");
listdata.add("anything#anything");
//Headers
String arrStr1[] ={"First Name", "Last Name", "Address", "Email"};
csvWrite.writeNext(arrStr1);
String arrStr[] ={listdata.get(0), listdata.get(1), listdata.get(2), listdata.get(3)};
csvWrite.writeNext(arrStr);
csvWrite.close();
return "";
}
catch (IOException e){
Log.e("MainActivity", e.getMessage(), e);
return "";
}
}
#SuppressLint("NewApi")
#Override
protected void onPostExecute(final String success) {
if (this.dialog.isShowing()){
this.dialog.dismiss();
}
if (success.isEmpty()){
Toast.makeText(MainActivity.this, "Export successful!", Toast.LENGTH_SHORT).show();
}
else {
Toast.makeText(MainActivity.this, "Export failed!", Toast.LENGTH_SHORT).show();
}
}
}
Write Async task in your .java file
Step3: Add call this task
ExportDatabaseCSVTask task=new ExportDatabaseCSVTask();
task.execute();
ExcelFile.csv file will be created in your sdcard.
ExportDatabaseCSVTask:
public class ExportDatabaseCSVTask extends AsyncTask<String, Void, Boolean> {
private final ProgressDialog dialog = new ProgressDialog(MainActivity.this);
#Override
protected void onPreExecute() {
this.dialog.setMessage("Exporting database...");
this.dialog.show();
}
protected Boolean doInBackground(final String... args) {
String currentDBPath = "/data/"+ "your Package name" +"/databases/abc.db";
File dbFile = getDatabasePath(""+currentDBPath);
System.out.println(dbFile); // displays the data base path in your logcat
File exportDir = new File(Environment.getExternalStorageDirectory(), "/your Folder Name/");
if (!exportDir.exists()) { exportDir.mkdirs(); }
File file = new File(exportDir, "myfile.csv");
try {
file.createNewFile();
CSVWriter csvWrite = new CSVWriter(new FileWriter(file));
Cursor curCSV = simpledb.rawQuery("select * from " + tablename,null);
csvWrite.writeNext(curCSV.getColumnNames());
while(curCSV.moveToNext()) {
String arrStr[]=null;
String[] mySecondStringArray = new String[curCSV.getColumnNames().length];
for(int i=0;i<curCSV.getColumnNames().length;i++)
{
mySecondStringArray[i] =curCSV.getString(i);
}
csvWrite.writeNext(mySecondStringArray);
}
csvWrite.close();
curCSV.close();
return true;
} catch (IOException e) {
Log.e("MainActivity", e.getMessage(), e);
return false;
}
}
protected void onPostExecute(final Boolean success) {
if (this.dialog.isShowing()) { this.dialog.dismiss(); }
if (success) {
Toast.makeText(MainActivity.this, "Export successful!", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(MainActivity.this, "Export failed", Toast.LENGTH_SHORT).show();
}
}
}
CSVWriter:
public class CSVWriter {
private PrintWriter pw;
private char separator;
private char quotechar;
private char escapechar;
private String lineEnd;
/** The character used for escaping quotes. */
public static final char DEFAULT_ESCAPE_CHARACTER = '"';
/** The default separator to use if none is supplied to the constructor. */
public static final char DEFAULT_SEPARATOR = ',';
/**
* The default quote character to use if none is supplied to the
* constructor.
*/
public static final char DEFAULT_QUOTE_CHARACTER = '"';
/** The quote constant to use when you wish to suppress all quoting. */
public static final char NO_QUOTE_CHARACTER = '\u0000';
/** The escape constant to use when you wish to suppress all escaping. */
public static final char NO_ESCAPE_CHARACTER = '\u0000';
/** Default line terminator uses platform encoding. */
public static final String DEFAULT_LINE_END = "\n";
/**
* Constructs CSVWriter using a comma for the separator.
*
* #param writer
* the writer to an underlying CSV source.
*/
public CSVWriter(Writer writer) {
this(writer, DEFAULT_SEPARATOR, DEFAULT_QUOTE_CHARACTER,
DEFAULT_ESCAPE_CHARACTER, DEFAULT_LINE_END);
}
/**
* Constructs CSVWriter with supplied separator, quote char, escape char and line ending.
*
* #param writer
* the writer to an underlying CSV source.
* #param separator
* the delimiter to use for separating entries
* #param quotechar
* the character to use for quoted elements
* #param escapechar
* the character to use for escaping quotechars or escapechars
* #param lineEnd
* the line feed terminator to use
*/
public CSVWriter(Writer writer, char separator, char quotechar, char escapechar, String lineEnd) {
this.pw = new PrintWriter(writer);
this.separator = separator;
this.quotechar = quotechar;
this.escapechar = escapechar;
this.lineEnd = lineEnd;
}
/**
* Writes the next line to the file.
*
* #param nextLine
* a string array with each comma-separated element as a separate
* entry.
*/
public void writeNext(String[] nextLine) {
if (nextLine == null)
return;
StringBuffer sb = new StringBuffer();
for (int i = 0; i < nextLine.length; i++) {
if (i != 0) {
sb.append(separator);
}
String nextElement = nextLine[i];
if (nextElement == null)
continue;
if (quotechar != NO_QUOTE_CHARACTER)
sb.append(quotechar);
for (int j = 0; j < nextElement.length(); j++) {
char nextChar = nextElement.charAt(j);
if (escapechar != NO_ESCAPE_CHARACTER && nextChar == quotechar) {
sb.append(escapechar).append(nextChar);
} else if (escapechar != NO_ESCAPE_CHARACTER && nextChar == escapechar) {
sb.append(escapechar).append(nextChar);
} else {
sb.append(nextChar);
}
}
if (quotechar != NO_QUOTE_CHARACTER)
sb.append(quotechar);
}
sb.append(lineEnd);
pw.write(sb.toString());
}
/**
* Flush underlying stream to writer.
*
* #throws IOException if bad things happen
*/
public void flush() throws IOException {
pw.flush();
}
/**
* Close the underlying stream writer flushing any buffered content.
*
* #throws IOException if bad things happen
*
*/
public void close() throws IOException {
pw.flush();
pw.close();
}
}
I have recently implemented the excel export function in my app. I have also included my full code on how to export filtered data to excel instead of the whole table.
You will need to create a second table for this. The second that will hold the data you require for this operation (In my second table I have removed my autoincrament ID column because I dont want it in my excel file).
You will need to clear the second table first and then add entries.
Then use the SqLiteToExcel object to export db to excel and save the file somewhere.
Then I have an email intent with the excel file attached for sharing (allows sharing with other apps other than email). here is my method:
private void ExportData() {
//CHECK IF YOU HAVE WRITE PERMISSIONS OR RETURN
int permission = ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (permission != PackageManager.PERMISSION_GRANTED) {
Toast.makeText(getContext(), "Storage permissions not granted", Toast.LENGTH_SHORT).show();
return;
}
//get database object
myDbhelper = new MyDbHelper(getContext());
SQLiteDatabase database = myDbhelper.getWritableDatabase();
//delete all entries in the second table
database.delete("Table2",null,null);
//Create a cursor of the main database with your filters and sort order applied
Cursor cursor = getActivity().getContentResolver().query(
uri,
projections,
selection,
args,
sortOrder);
//loop through cursor and add entries from first table to second table
try {
while (cursor.moveToNext()) {
final String ColumnOneIndex = cursor.getString(cursor.getColumnIndexOrThrow("COLUMN_ONE"));
final String ColumnTwoIndex = cursor.getString(cursor.getColumnIndexOrThrow("COLUMN_TWO"));
final String ColumnThreeIndex = cursor.getString(cursor.getColumnIndexOrThrow("COLUMN_THREE"));
//add entries from table one into the table two
ContentValues values = new ContentValues();
values.put("TABLE2_COLUMN_1", ColumnOneIndex);
values.put("TABLE2_COLUMN_2", ColumnTwoIndex );
values.put("TABLE2_COLUMN_3", ColumnThreeIndex);
database.insert("table2", null, values);
}
} finally {
//close cursor after looping is complete
cursor.close();
}
//create a string for where you want to save the excel file
final String savePath = Environment.getExternalStorageDirectory() + "/excelfileTemp";
File file = new File(savePath);
if (!file.exists()) {
file.mkdirs();
}
//create the sqLiteToExcel object
SQLiteToExcel sqLiteToExcel = new SQLiteToExcel(getContext(), "databasefile.db",savePath);
//use sqLiteToExcel object to create the excel file
sqLiteToExcel.exportSingleTable("table2","excelfilename.xls", new SQLiteToExcel.ExportListener() {
#Override
public void onStart() {
}
#Override
public void onCompleted(String filePath) {
//now attach the excel file created and be directed to email activity
Uri newPath = Uri.parse("file://" + savePath + "/" +"excelfilename.xls");
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());
Intent emailintent = new Intent(Intent.ACTION_SEND);
emailintent.setType("application/vnd.ms-excel");
emailintent.putExtra(Intent.EXTRA_SUBJECT, "Subject");
emailintent.putExtra(Intent.EXTRA_TEXT, "I'm email body.");
emailintent.putExtra(Intent.EXTRA_STREAM,newPath);
startActivity(Intent.createChooser(emailintent, "Send Email"));
}
#Override
public void onError(Exception e) {
System.out.println("Error msg: " + e);
Toast.makeText(getContext(), "Failed to Export data", Toast.LENGTH_SHORT).show();
}
});
}
I have this method implemented in my app and it works
The CSV format is "string, string, string /n" for each line,
the "," is the column separator and "/n" for rows.
Get the data from the database and export them like this:
public static Boolean exportToCSV(List<Data> data, File file) {
try {
final String head = "ValueX, ValueY \n";
if (!file.exists()) {
file.createNewFile();
}
FileWriter fileWriter = new FileWriter(file.getAbsoluteFile());
BufferedWriter writer = new BufferedWriter(fileWriter);
writer.write(head);
for (Item item : items) {
final String line = String.format("%s,%s\n",
item.getValueX(),
item.getValueY());
writer.write(line);
}
writer.close();
} catch (IOException e) {
return false;
}
return true;
}
How to request Android download manager to download multiple files at the same time. Also I would like to know each and every file download status.
Request the first one.
Then, request the second one.
Then, request the third one.
Continue as needed.
Whether they download "at the same time" is not your concern, nor do you have control over it. They will download when DownloadManager decides to download them, which may be simultaneously or not.
1. Register listener for download complete
IntentFilter intentFilter = new IntentFilter(
DownloadManager.ACTION_DOWNLOAD_COMPLETE);
registerReceiver(downloadReceiver, intentFilter);
2.Make request
Uri downloadUri = Uri.parse(entry.getValue());
DownloadManager.Request request = new DownloadManager.Request(
downloadUri);
request.setDestinationUri(path_to _file_store)));
downloadManager.enqueue(request);
3.Check status in listener
private BroadcastReceiver downloadReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context arg0, Intent intent) {
String action = intent.getAction();
if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)) {
long downloadId = intent.getLongExtra(
DownloadManager.EXTRA_DOWNLOAD_ID, 0);
System.out.println("download id=" + downloadId);
CheckDwnloadStatus(downloadId);
}
}
};
private void CheckDwnloadStatus(long id) {
// TODO Auto-generated method stub
DownloadManager.Query query = new DownloadManager.Query();
query.setFilterById(id);
Cursor cursor = downloadManager.query(query);
if (cursor.moveToFirst()) {
int columnIndex = cursor
.getColumnIndex(DownloadManager.COLUMN_STATUS);
int status = cursor.getInt(columnIndex);
int columnReason = cursor
.getColumnIndex(DownloadManager.COLUMN_REASON);
int reason = cursor.getInt(columnReason);
switch (status) {
case DownloadManager.STATUS_FAILED:
String failedReason = "";
switch (reason) {
case DownloadManager.ERROR_CANNOT_RESUME:
failedReason = "ERROR_CANNOT_RESUME";
break;
case DownloadManager.ERROR_DEVICE_NOT_FOUND:
failedReason = "ERROR_DEVICE_NOT_FOUND";
break;
case DownloadManager.ERROR_FILE_ALREADY_EXISTS:
failedReason = "ERROR_FILE_ALREADY_EXISTS";
break;
case DownloadManager.ERROR_FILE_ERROR:
failedReason = "ERROR_FILE_ERROR";
break;
case DownloadManager.ERROR_HTTP_DATA_ERROR:
failedReason = "ERROR_HTTP_DATA_ERROR";
break;
case DownloadManager.ERROR_INSUFFICIENT_SPACE:
failedReason = "ERROR_INSUFFICIENT_SPACE";
break;
case DownloadManager.ERROR_TOO_MANY_REDIRECTS:
failedReason = "ERROR_TOO_MANY_REDIRECTS";
break;
case DownloadManager.ERROR_UNHANDLED_HTTP_CODE:
failedReason = "ERROR_UNHANDLED_HTTP_CODE";
break;
case DownloadManager.ERROR_UNKNOWN:
failedReason = "ERROR_UNKNOWN";
break;
}
Toast.makeText(this, "FAILED: " + failedReason,
Toast.LENGTH_LONG).show();
break;
case DownloadManager.STATUS_PAUSED:
String pausedReason = "";
switch (reason) {
case DownloadManager.PAUSED_QUEUED_FOR_WIFI:
pausedReason = "PAUSED_QUEUED_FOR_WIFI";
break;
case DownloadManager.PAUSED_UNKNOWN:
pausedReason = "PAUSED_UNKNOWN";
break;
case DownloadManager.PAUSED_WAITING_FOR_NETWORK:
pausedReason = "PAUSED_WAITING_FOR_NETWORK";
break;
case DownloadManager.PAUSED_WAITING_TO_RETRY:
pausedReason = "PAUSED_WAITING_TO_RETRY";
break;
}
Toast.makeText(this, "PAUSED: " + pausedReason,
Toast.LENGTH_LONG).show();
break;
case DownloadManager.STATUS_PENDING:
Toast.makeText(this, "PENDING", Toast.LENGTH_LONG).show();
break;
case DownloadManager.STATUS_RUNNING:
Toast.makeText(this, "RUNNING", Toast.LENGTH_LONG).show();
break;
case DownloadManager.STATUS_SUCCESSFUL:
caluclateLoadingData();
// Toast.makeText(this, "SUCCESSFUL", Toast.LENGTH_LONG).show();
// GetFile();
break;
}
}
}
public final class Downloads {
/**
* #hide
*/
private Downloads() {}
/**
* The permission to access the download manager
* #hide .
*/
public static final String PERMISSION_ACCESS = "android.permission.ACCESS_DOWNLOAD_MANAGER";
/**
* The permission to access the download manager's advanced functions
* #hide
*/
public static final String PERMISSION_ACCESS_ADVANCED =
"android.permission.ACCESS_DOWNLOAD_MANAGER_ADVANCED";
/**
* The permission to directly access the download manager's cache directory
* #hide
*/
public static final String PERMISSION_CACHE = "android.permission.ACCESS_CACHE_FILESYSTEM";
/**
* The permission to send broadcasts on download completion
* #hide
*/
public static final String PERMISSION_SEND_INTENTS =
"android.permission.SEND_DOWNLOAD_COMPLETED_INTENTS";
/**
* The content:// URI for the data table in the provider
* #hide
*/
public static final Uri CONTENT_URI =
Uri.parse("content://downloads/my_downloads");
/**
* Broadcast Action: this is sent by the download manager to the app
* that had initiated a download when that download completes. The
* download's content: uri is specified in the intent's data.
* #hide
*/
public static final String ACTION_DOWNLOAD_COMPLETED =
"android.intent.action.DOWNLOAD_COMPLETED";
/**
* Broadcast Action: this is sent by the download manager to the app
* that had initiated a download when the user selects the notification
* associated with that download. The download's content: uri is specified
* in the intent's data if the click is associated with a single download,
* or Downloads.CONTENT_URI if the notification is associated with
* multiple downloads.
* Note: this is not currently sent for downloads that have completed
* successfully.
* #hide
*/
public static final String ACTION_NOTIFICATION_CLICKED =
"android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED";
/**
* The name of the column containing the URI of the data being downloaded.
* <P>Type: TEXT</P>
* <P>Owner can Init/Read</P>
* #hide
*/
public static final String COLUMN_URI = "uri";
public static final String COLUMN_APP_DATA = "entity";
public static final String COLUMN_NO_INTEGRITY = "no_integrity";
public static final String COLUMN_FILE_NAME_HINT = "hint";
public static final String _DATA = "_data";
public static final String COLUMN_MIME_TYPE = "mimetype";
public static final String COLUMN_DESTINATION = "destination";
public static final String COLUMN_VISIBILITY = "visibility";
public static final String COLUMN_CONTROL = "control";
public static final String COLUMN_STATUS = "status";
/**
* The name of the column containing the date at which some interesting
* status changed in the download. Stored as a System.currentTimeMillis()
* value.
* <P>Type: BIGINT</P>
* <P>Owner can Read</P>
* #hide
*/
public static final String COLUMN_LAST_MODIFICATION = "lastmod";
/**
* The name of the column containing the package name of the application
* that initiating the download. The download manager will send
* notifications to a component in this package when the download completes.
* <P>Type: TEXT</P>
* <P>Owner can Init/Read</P>
* #hide
*/
public static final String COLUMN_NOTIFICATION_PACKAGE = "notificationpackage";
public static final String COLUMN_NOTIFICATION_CLASS = "notificationclass";
public static final String COLUMN_NOTIFICATION_EXTRAS = "notificationextras";
public static final String COLUMN_COOKIE_DATA = "cookiedata";
public static final String COLUMN_USER_AGENT = "useragent";
public static final String COLUMN_REFERER = "referer";
public static final String COLUMN_TOTAL_BYTES = "total_bytes";
public static final String COLUMN_CURRENT_BYTES = "current_bytes";
public static final String COLUMN_OTHER_UID = "otheruid";
public static final String COLUMN_TITLE = "title";
public static final String COLUMN_DESCRIPTION = "description";
public static final String COLUMN_DELETED = "deleted";
public static final int DESTINATION_EXTERNAL = 0;
public static final int DESTINATION_CACHE_PARTITION = 1;
public static final int DESTINATION_CACHE_PARTITION_PURGEABLE = 2;
public static final int DESTINATION_CACHE_PARTITION_NOROAMING = 3;
public static final int CONTROL_RUN = 0;
public static final int CONTROL_PAUSED = 1;
/**
* Returns whether the status is informational (i.e. 1xx).
* #hide
*/
public static boolean isStatusInformational(int status) {
return (status >= 100 && status < 200);
}
public static boolean isStatusSuccess(int status) {
return (status >= 200 && status < 300);
}
public static boolean isStatusError(int status) {
return (status >= 400 && status < 600);
}
public static boolean isStatusClientError(int status) {
return (status >= 400 && status < 500);
}
public static boolean isStatusServerError(int status) {
return (status >= 500 && status < 600);
}
public static boolean isStatusCompleted(int status) {
return (status >= 200 && status < 300) || (status >= 400 && status < 600);
}
public static final int STATUS_PENDING = 190;
public static final int STATUS_RUNNING = 192;
public static final int STATUS_SUCCESS = 200;
public static final int STATUS_BAD_REQUEST = 400;
public static final int STATUS_NOT_ACCEPTABLE = 406;
public static final int STATUS_LENGTH_REQUIRED = 411;
public static final int STATUS_PRECONDITION_FAILED = 412;
public static final int STATUS_CANCELED = 490;
public static final int STATUS_UNKNOWN_ERROR = 491;
public static final int STATUS_FILE_ERROR = 492;
public static final int STATUS_UNHANDLED_REDIRECT = 493;
public static final int STATUS_UNHANDLED_HTTP_CODE = 494;
public static final int STATUS_HTTP_DATA_ERROR = 495;
public static final int STATUS_HTTP_EXCEPTION = 496;
public static final int STATUS_TOO_MANY_REDIRECTS = 497;
public static final int STATUS_INSUFFICIENT_SPACE_ERROR = 498;
public static final int STATUS_DEVICE_NOT_FOUND_ERROR = 499;
public static final int VISIBILITY_VISIBLE = 0;
public static final int VISIBILITY_VISIBLE_NOTIFY_COMPLETED = 1;
public static final int VISIBILITY_HIDDEN = 2;
public static final class Impl implements BaseColumns {
private Impl() {}
/**
* The permission to access the download manager
*/
public static final String PERMISSION_ACCESS = "android.permission.ACCESS_DOWNLOAD_MANAGER";
/**
* The permission to access the download manager's advanced functions
*/
public static final String PERMISSION_ACCESS_ADVANCED =
"android.permission.ACCESS_DOWNLOAD_MANAGER_ADVANCED";
/**
* The permission to access the all the downloads in the manager.
*/
public static final String PERMISSION_ACCESS_ALL =
"android.permission.ACCESS_ALL_DOWNLOADS";
/**
* The permission to directly access the download manager's cache
* directory
*/
public static final String PERMISSION_CACHE = "android.permission.ACCESS_CACHE_FILESYSTEM";
/**
* The permission to send broadcasts on download completion
*/
public static final String PERMISSION_SEND_INTENTS =
"android.permission.SEND_DOWNLOAD_COMPLETED_INTENTS";
/**
* The permission to download files to the cache partition that won't be automatically
* purged when space is needed.
*/
public static final String PERMISSION_CACHE_NON_PURGEABLE =
"android.permission.DOWNLOAD_CACHE_NON_PURGEABLE";
/**
* The permission to download files without any system notification being shown.
*/
public static final String PERMISSION_NO_NOTIFICATION =
"android.permission.DOWNLOAD_WITHOUT_NOTIFICATION";
/**
* The content:// URI to access downloads owned by the caller's UID.
*/
public static final Uri CONTENT_URI =
Uri.parse("content://downloads/my_downloads");
public static final Uri ALL_DOWNLOADS_CONTENT_URI =
Uri.parse("content://downloads/all_downloads");
public static final String ACTION_DOWNLOAD_COMPLETED =
"android.intent.action.DOWNLOAD_COMPLETED";
public static final String ACTION_NOTIFICATION_CLICKED =
"android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED";
public static final String COLUMN_URI = "uri";
public static final String COLUMN_APP_DATA = "entity";
public static final String COLUMN_NO_INTEGRITY = "no_integrity";
public static final String COLUMN_FILE_NAME_HINT = "hint";
public static final String _DATA = "_data";
public static final String COLUMN_MIME_TYPE = "mimetype";
public static final String COLUMN_DESTINATION = "destination";
public static final String COLUMN_VISIBILITY = "visibility";
public static final String COLUMN_CONTROL = "control";
public static final String COLUMN_STATUS = "status";
public static final String COLUMN_LAST_MODIFICATION = "lastmod";
public static final String COLUMN_NOTIFICATION_PACKAGE = "notificationpackage";
public static final String COLUMN_NOTIFICATION_CLASS = "notificationclass";
public static final String COLUMN_NOTIFICATION_EXTRAS = "notificationextras";
public static final String COLUMN_COOKIE_DATA = "cookiedata";
public static final String COLUMN_USER_AGENT = "useragent";
public static final String COLUMN_REFERER = "referer";
public static final String COLUMN_TOTAL_BYTES = "total_bytes";
public static final String COLUMN_CURRENT_BYTES = "current_bytes";
public static final String COLUMN_OTHER_UID = "otheruid";
public static final String COLUMN_TITLE = "title";
public static final String COLUMN_DESCRIPTION = "description";
public static final String COLUMN_IS_PUBLIC_API = "is_public_api";
public static final String COLUMN_ALLOW_ROAMING = "allow_roaming";
public static final String COLUMN_ALLOWED_NETWORK_TYPES = "allowed_network_types";
public static final String COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI = "is_visible_in_downloads_ui";
public static final String COLUMN_BYPASS_RECOMMENDED_SIZE_LIMIT =
"bypass_recommended_size_limit";
public static final String COLUMN_DELETED = "deleted";
public static final String COLUMN_MEDIAPROVIDER_URI = "mediaprovider_uri";
/*
* Lists the destinations that an application can specify for a download.
*/
public static final int DESTINATION_EXTERNAL = 0;
public static final int DESTINATION_CACHE_PARTITION = 1;
public static final int DESTINATION_CACHE_PARTITION_PURGEABLE = 2;
/**
* This download will be saved to the download manager's private
* partition, as with DESTINATION_CACHE_PARTITION, but the download
* will not proceed if the user is on a roaming data connection.
*/
public static final int DESTINATION_CACHE_PARTITION_NOROAMING = 3;
/**
* This download will be saved to the location given by the file URI in
* {#link #COLUMN_FILE_NAME_HINT}.
*/
public static final int DESTINATION_FILE_URI = 4;
/**
* This download is allowed to run.
*/
public static final int CONTROL_RUN = 0;
/**
* This download must pause at the first opportunity.
*/
public static final int CONTROL_PAUSED = 1;
/**
* Returns whether the status is informational (i.e. 1xx).
*/
public static boolean isStatusInformational(int status) {
return (status >= 100 && status < 200);
}
/**
* Returns whether the status is a success (i.e. 2xx).
*/
public static boolean isStatusSuccess(int status) {
return (status >= 200 && status < 300);
}
/**
* Returns whether the status is an error (i.e. 4xx or 5xx).
*/
public static boolean isStatusError(int status) {
return (status >= 400 && status < 600);
}
/**
* Returns whether the status is a client error (i.e. 4xx).
*/
public static boolean isStatusClientError(int status) {
return (status >= 400 && status < 500);
}
/**
* Returns whether the status is a server error (i.e. 5xx).
*/
public static boolean isStatusServerError(int status) {
return (status >= 500 && status < 600);
}
/**
* Returns whether the download has completed (either with success or
* error).
*/
public static boolean isStatusCompleted(int status) {
return (status >= 200 && status < 300) || (status >= 400 && status < 600);
}
/**
* This download hasn't stated yet
*/
public static final int STATUS_PENDING = 190;
/**
* This download has started
*/
public static final int STATUS_RUNNING = 192;
/**
* This download has been paused by the owning app.
*/
public static final int STATUS_PAUSED_BY_APP = 193;
/**
* This download encountered some network error and is waiting before retrying the request.
*/
public static final int STATUS_WAITING_TO_RETRY = 194;
/**
* This download is waiting for network connectivity to proceed.
*/
public static final int STATUS_WAITING_FOR_NETWORK = 195;
/**
* This download exceeded a size limit for mobile networks and is waiting for a Wi-Fi
* connection to proceed.
*/
public static final int STATUS_QUEUED_FOR_WIFI = 196;
public static final int STATUS_SUCCESS = 200;
public static final int STATUS_BAD_REQUEST = 400;
/**
* This download can't be performed because the content type cannot be
* handled.
*/
public static final int STATUS_NOT_ACCEPTABLE = 406;
public static final int STATUS_LENGTH_REQUIRED = 411;
/**
* This download was interrupted and cannot be resumed.
* This is the code for the HTTP error "Precondition Failed", and it is
* also used in situations where the client doesn't have an ETag at all.
*/
public static final int STATUS_PRECONDITION_FAILED = 412;
/**
* The lowest-valued error status that is not an actual HTTP status code.
*/
public static final int MIN_ARTIFICIAL_ERROR_STATUS = 488;
/**
* The requested destination file already exists.
*/
public static final int STATUS_FILE_ALREADY_EXISTS_ERROR = 488;
/**
* Some possibly transient error occurred, but we can't resume the download.
*/
public static final int STATUS_CANNOT_RESUME = 489;
/**
* This download was canceled
*/
public static final int STATUS_CANCELED = 490;
public static final int STATUS_UNKNOWN_ERROR = 491;
public static final int STATUS_FILE_ERROR = 492;
/**
* This download couldn't be completed because of an HTTP
* redirect response that the download manager couldn't
* handle.
*/
public static final int STATUS_UNHANDLED_REDIRECT = 493;
/**
* This download couldn't be completed because of an
* unspecified unhandled HTTP code.
*/
public static final int STATUS_UNHANDLED_HTTP_CODE = 494;
/**
* This download couldn't be completed because of an
* error receiving or processing data at the HTTP level.
*/
public static final int STATUS_HTTP_DATA_ERROR = 495;
/**
* This download couldn't be completed because of an
* HttpException while setting up the request.
*/
public static final int STATUS_HTTP_EXCEPTION = 496;
/**
* This download couldn't be completed because there were
* too many redirects.
*/
public static final int STATUS_TOO_MANY_REDIRECTS = 497;
/**
* This download couldn't be completed due to insufficient storage
* space. Typically, this is because the SD card is full.
*/
public static final int STATUS_INSUFFICIENT_SPACE_ERROR = 498;
/**
* This download couldn't be completed because no external storage
* device was found. Typically, this is because the SD card is not
* mounted.
*/
public static final int STATUS_DEVICE_NOT_FOUND_ERROR = 499;
/**
* This download is visible but only shows in the notifications
* while it's in progress.
*/
public static final int VISIBILITY_VISIBLE = 0;
/**
* This download is visible and shows in the notifications while
* in progress and after completion.
*/
public static final int VISIBILITY_VISIBLE_NOTIFY_COMPLETED = 1;
/**
* This download doesn't show in the UI or in the notifications.
*/
public static final int VISIBILITY_HIDDEN = 2;
/**
* Constants related to HTTP request headers associated with each download.
*/
public static class RequestHeaders {
public static final String HEADERS_DB_TABLE = "request_headers";
public static final String COLUMN_DOWNLOAD_ID = "download_id";
public static final String COLUMN_HEADER = "header";
public static final String COLUMN_VALUE = "value";
/**
* Path segment to add to a download URI to retrieve request headers
*/
public static final String URI_SEGMENT = "headers";
/**
* Prefix for ContentValues keys that contain HTTP header lines, to be passed to
* DownloadProvider.insert().
*/
public static final String INSERT_KEY_PREFIX = "http_header_";
}
}
}
public class DownloadManager {
private static final String TAG = "DownloadManager";
public final static String COLUMN_ID = BaseColumns._ID;
public final static String COLUMN_TITLE = "title";
public final static String COLUMN_DESCRIPTION = "description";
public final static String COLUMN_URI = "uri";
public final static String COLUMN_MEDIA_TYPE = "media_type";
public final static String COLUMN_TOTAL_SIZE_BYTES = "total_size";
public final static String COLUMN_LOCAL_URI = "local_uri";
public final static String COLUMN_STATUS = "status";
public final static String COLUMN_REASON = "reason";
public final static String COLUMN_BYTES_DOWNLOADED_SO_FAR = "bytes_so_far";
public final static String COLUMN_LAST_MODIFIED_TIMESTAMP = "last_modified_timestamp";
public static final String COLUMN_MEDIAPROVIDER_URI = "mediaprovider_uri";
public final static int STATUS_PENDING = 1 << 0;
public final static int STATUS_RUNNING = 1 << 1;
public final static int STATUS_PAUSED = 1 << 2;
public final static int STATUS_SUCCESSFUL = 1 << 3;
public final static int STATUS_FAILED = 1 << 4;
public final static int ERROR_UNKNOWN = 1000;
public final static int ERROR_FILE_ERROR = 1001;
public final static int ERROR_UNHANDLED_HTTP_CODE = 1002;
public final static int ERROR_HTTP_DATA_ERROR = 1004;
public final static int ERROR_TOO_MANY_REDIRECTS = 1005;
public final static int ERROR_INSUFFICIENT_SPACE = 1006;
public final static int ERROR_DEVICE_NOT_FOUND = 1007;
public final static int ERROR_CANNOT_RESUME = 1008;
public final static int ERROR_FILE_ALREADY_EXISTS = 1009;
public final static int PAUSED_WAITING_TO_RETRY = 1;
public final static int PAUSED_WAITING_FOR_NETWORK = 2;
public final static int PAUSED_QUEUED_FOR_WIFI = 3;
public final static int PAUSED_UNKNOWN = 4;
public final static String ACTION_DOWNLOAD_COMPLETE = "android.intent.action.DOWNLOAD_COMPLETE";
public final static String ACTION_NOTIFICATION_CLICKED = "android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED";
public final static String ACTION_VIEW_DOWNLOADS = "android.intent.action.VIEW_DOWNLOADS";
public static final String EXTRA_DOWNLOAD_ID = "extra_download_id";
// this array must contain all public columns
private static final String[] COLUMNS = new String[] { COLUMN_ID,
COLUMN_MEDIAPROVIDER_URI, COLUMN_TITLE, COLUMN_DESCRIPTION,
COLUMN_URI, COLUMN_MEDIA_TYPE, COLUMN_TOTAL_SIZE_BYTES,
COLUMN_LOCAL_URI, COLUMN_STATUS, COLUMN_REASON,
COLUMN_BYTES_DOWNLOADED_SO_FAR, COLUMN_LAST_MODIFIED_TIMESTAMP };
// columns to request from DownloadProvider
private static final String[] UNDERLYING_COLUMNS = new String[] {
Downloads.Impl._ID, Downloads.Impl.COLUMN_MEDIAPROVIDER_URI,
Downloads.COLUMN_TITLE, Downloads.COLUMN_DESCRIPTION,
Downloads.COLUMN_URI, Downloads.COLUMN_MIME_TYPE,
Downloads.COLUMN_TOTAL_BYTES, Downloads.COLUMN_STATUS,
Downloads.COLUMN_CURRENT_BYTES, Downloads.COLUMN_LAST_MODIFICATION,
Downloads.COLUMN_DESTINATION, Downloads.Impl.COLUMN_FILE_NAME_HINT,
Downloads.Impl._DATA, };
private static final Set<String> LONG_COLUMNS = new HashSet<String>(
Arrays.asList(COLUMN_ID, COLUMN_TOTAL_SIZE_BYTES, COLUMN_STATUS,
COLUMN_REASON, COLUMN_BYTES_DOWNLOADED_SO_FAR,
COLUMN_LAST_MODIFIED_TIMESTAMP));
public static class Request {
public static final int NETWORK_MOBILE = 1 << 0;
public static final int NETWORK_WIFI = 1 << 1;
private Uri mUri;
private Uri mDestinationUri;
private List<Pair<String, String>> mRequestHeaders = new ArrayList<Pair<String, String>>();
private CharSequence mTitle;
private CharSequence mDescription;
private boolean mShowNotification = true;
private String mMimeType;
private boolean mRoamingAllowed = true;
private int mAllowedNetworkTypes = ~0; // default to all network types
// allowed
private boolean mIsVisibleInDownloadsUi = true;
/**
* #param uri
* the HTTP URI to download.
*/
public Request(Uri uri) {
if (uri == null) {
throw new NullPointerException();
}
String scheme = uri.getScheme();
if (scheme == null
|| !(scheme.equals("http") || scheme.equals("https"))) {
throw new IllegalArgumentException(
"Can only download HTTP URIs: " + uri);
}
mUri = uri;
}
public Request setDestinationUri(Uri uri) {
mDestinationUri = uri;
return this;
}
public Request setDestinationInExternalFilesDir(Context context,
String dirType, String subPath) {
setDestinationFromBase(context.getExternalFilesDir(dirType),
subPath);
return this;
}
public Request setDestinationInExternalPublicDir(String dirType,
String subPath) {
setDestinationFromBase(
Environment.getExternalStoragePublicDirectory(dirType),
subPath);
return this;
}
private void setDestinationFromBase(File base, String subPath) {
if (subPath == null) {
throw new NullPointerException("subPath cannot be null");
}
mDestinationUri = Uri.withAppendedPath(Uri.fromFile(base), subPath);
}
public Request addRequestHeader(String header, String value) {
if (header == null) {
throw new NullPointerException("header cannot be null");
}
if (header.contains(":")) {
throw new IllegalArgumentException("header may not contain ':'");
}
if (value == null) {
value = "";
}
mRequestHeaders.add(Pair.create(header, value));
return this;
}
public Request setTitle(CharSequence title) {
mTitle = title;
return this;
}
public Request setDescription(CharSequence description) {
mDescription = description;
return this;
}
public Request setMimeType(String mimeType) {
mMimeType = mimeType;
return this;
}
public Request setShowRunningNotification(boolean show) {
mShowNotification = show;
return this;
}
public Request setAllowedNetworkTypes(int flags) {
mAllowedNetworkTypes = flags;
return this;
}
public Request setAllowedOverRoaming(boolean allowed) {
mRoamingAllowed = allowed;
return this;
}
public Request setVisibleInDownloadsUi(boolean isVisible) {
mIsVisibleInDownloadsUi = isVisible;
return this;
}
/**
* #return ContentValues to be passed to DownloadProvider.insert()
*/
ContentValues toContentValues(String packageName) {
ContentValues values = new ContentValues();
assert mUri != null;
values.put(Downloads.COLUMN_URI, mUri.toString());
values.put(Downloads.Impl.COLUMN_IS_PUBLIC_API, true);
values.put(Downloads.COLUMN_NOTIFICATION_PACKAGE, packageName);
if (mDestinationUri != null) {
values.put(Downloads.COLUMN_DESTINATION,
Downloads.Impl.DESTINATION_FILE_URI);
values.put(Downloads.COLUMN_FILE_NAME_HINT,
mDestinationUri.toString());
} else {
values.put(Downloads.COLUMN_DESTINATION,
Downloads.DESTINATION_CACHE_PARTITION_PURGEABLE);
}
if (!mRequestHeaders.isEmpty()) {
encodeHttpHeaders(values);
}
putIfNonNull(values, Downloads.COLUMN_TITLE, mTitle);
putIfNonNull(values, Downloads.COLUMN_DESCRIPTION, mDescription);
putIfNonNull(values, Downloads.COLUMN_MIME_TYPE, mMimeType);
values.put(Downloads.COLUMN_VISIBILITY,
mShowNotification ? Downloads.VISIBILITY_VISIBLE
: Downloads.VISIBILITY_HIDDEN);
values.put(Downloads.Impl.COLUMN_ALLOWED_NETWORK_TYPES,
mAllowedNetworkTypes);
values.put(Downloads.Impl.COLUMN_ALLOW_ROAMING, mRoamingAllowed);
values.put(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI,
mIsVisibleInDownloadsUi);
return values;
}
private void encodeHttpHeaders(ContentValues values) {
int index = 0;
for (Pair<String, String> header : mRequestHeaders) {
String headerString = header.first + ": " + header.second;
values.put(Downloads.Impl.RequestHeaders.INSERT_KEY_PREFIX
+ index, headerString);
index++;
}
}
private void putIfNonNull(ContentValues contentValues, String key,
Object value) {
if (value != null) {
contentValues.put(key, value.toString());
}
}
}
/**
* This class may be used to filter download manager queries.
*/
public static class Query {
/**
* Constant for use with {#link #orderBy}
*
* #hide
*/
public static final int ORDER_ASCENDING = 1;
/**
* Constant for use with {#link #orderBy}
*
* #hide
*/
public static final int ORDER_DESCENDING = 2;
private long[] mIds = null;
private Integer mStatusFlags = null;
private String mOrderByColumn = Downloads.COLUMN_LAST_MODIFICATION;
private int mOrderDirection = ORDER_DESCENDING;
private boolean mOnlyIncludeVisibleInDownloadsUi = false;
/**
* Include only the downloads with the given IDs.
*
* #return this object
*/
public Query setFilterById(long... ids) {
mIds = ids;
return this;
}
/**
* Include only downloads with status matching any the given status
* flags.
*
* #param flags
* any combination of the STATUS_* bit flags
* #return this object
*/
public Query setFilterByStatus(int flags) {
mStatusFlags = flags;
return this;
}
/**
* Controls whether this query includes downloads not visible in the
* system's Downloads UI.
*
* #param value
* if true, this query will only include downloads that
* should be displayed in the system's Downloads UI; if false
* (the default), this query will include both visible and
* invisible downloads.
* #return this object
* #hide
*/
public Query setOnlyIncludeVisibleInDownloadsUi(boolean value) {
mOnlyIncludeVisibleInDownloadsUi = value;
return this;
}
/**
* Change the sort order of the returned Cursor.
*
* #param column
* one of the COLUMN_* constants; currently, only
* {#link #COLUMN_LAST_MODIFIED_TIMESTAMP} and
* {#link #COLUMN_TOTAL_SIZE_BYTES} are supported.
* #param direction
* either {#link #ORDER_ASCENDING} or
* {#link #ORDER_DESCENDING}
* #return this object
* #hide
*/
public Query orderBy(String column, int direction) {
if (direction != ORDER_ASCENDING && direction != ORDER_DESCENDING) {
throw new IllegalArgumentException("Invalid direction: "
+ direction);
}
if (column.equals(COLUMN_LAST_MODIFIED_TIMESTAMP)) {
mOrderByColumn = Downloads.COLUMN_LAST_MODIFICATION;
} else if (column.equals(COLUMN_TOTAL_SIZE_BYTES)) {
mOrderByColumn = Downloads.COLUMN_TOTAL_BYTES;
} else {
throw new IllegalArgumentException("Cannot order by " + column);
}
mOrderDirection = direction;
return this;
}
/**
* Run this query using the given ContentResolver.
*
* #param projection
* the projection to pass to ContentResolver.query()
* #return the Cursor returned by ContentResolver.query()
*/
Cursor runQuery(ContentResolver resolver, String[] projection,
Uri baseUri) {
Uri uri = baseUri;
List<String> selectionParts = new ArrayList<String>();
String[] selectionArgs = null;
if (mIds != null) {
selectionParts.add(getWhereClauseForIds(mIds));
selectionArgs = getWhereArgsForIds(mIds);
}
if (mStatusFlags != null) {
List<String> parts = new ArrayList<String>();
if ((mStatusFlags & STATUS_PENDING) != 0) {
parts.add(statusClause("=", Downloads.STATUS_PENDING));
}
if ((mStatusFlags & STATUS_RUNNING) != 0) {
parts.add(statusClause("=", Downloads.STATUS_RUNNING));
}
if ((mStatusFlags & STATUS_PAUSED) != 0) {
parts.add(statusClause("=",
Downloads.Impl.STATUS_PAUSED_BY_APP));
parts.add(statusClause("=",
Downloads.Impl.STATUS_WAITING_TO_RETRY));
parts.add(statusClause("=",
Downloads.Impl.STATUS_WAITING_FOR_NETWORK));
parts.add(statusClause("=",
Downloads.Impl.STATUS_QUEUED_FOR_WIFI));
}
if ((mStatusFlags & STATUS_SUCCESSFUL) != 0) {
parts.add(statusClause("=", Downloads.STATUS_SUCCESS));
}
if ((mStatusFlags & STATUS_FAILED) != 0) {
parts.add("(" + statusClause(">=", 400) + " AND "
+ statusClause("<", 600) + ")");
}
selectionParts.add(joinStrings(" OR ", parts));
}
if (mOnlyIncludeVisibleInDownloadsUi) {
selectionParts
.add(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI
+ " != '0'");
}
// only return rows which are not marked 'deleted = 1'
selectionParts.add(Downloads.Impl.COLUMN_DELETED + " != '1'");
String selection = joinStrings(" AND ", selectionParts);
String orderDirection = (mOrderDirection == ORDER_ASCENDING ? "ASC"
: "DESC");
String orderBy = mOrderByColumn + " " + orderDirection;
return resolver.query(uri, projection, selection, selectionArgs,
orderBy);
}
private String joinStrings(String joiner, Iterable<String> parts) {
StringBuilder builder = new StringBuilder();
boolean first = true;
for (String part : parts) {
if (!first) {
builder.append(joiner);
}
builder.append(part);
first = false;
}
return builder.toString();
}
private String statusClause(String operator, int value) {
return Downloads.COLUMN_STATUS + operator + "'" + value + "'";
}
}
private ContentResolver mResolver;
private String mPackageName;
private Uri mBaseUri = Downloads.Impl.CONTENT_URI;
/**
* #hide
*/
public DownloadManager(ContentResolver resolver, String packageName) {
mResolver = resolver;
mPackageName = packageName;
}
/**
* Makes this object access the download provider through /all_downloads
* URIs rather than /my_downloads URIs, for clients that have permission to
* do so.
*
* #hide
*/
public void setAccessAllDownloads(boolean accessAllDownloads) {
if (accessAllDownloads) {
mBaseUri = Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI;
} else {
mBaseUri = Downloads.Impl.CONTENT_URI;
}
}
public long enqueue(Request request) {
ContentValues values = request.toContentValues(mPackageName);
Uri downloadUri = mResolver.insert(Downloads.CONTENT_URI, values);
long id = Long.parseLong(downloadUri.getLastPathSegment());
return id;
}
public int markRowDeleted(long... ids) {
if (ids == null || ids.length == 0) {
// called with nothing to remove!
throw new IllegalArgumentException(
"input param 'ids' can't be null");
}
ContentValues values = new ContentValues();
values.put(Downloads.Impl.COLUMN_DELETED, 1);
return mResolver.update(mBaseUri, values, getWhereClauseForIds(ids),
getWhereArgsForIds(ids));
}
public int remove(long... ids) {
if (ids == null || ids.length == 0) {
// called with nothing to remove!
throw new IllegalArgumentException(
"input param 'ids' can't be null");
}
return mResolver.delete(mBaseUri, getWhereClauseForIds(ids),
getWhereArgsForIds(ids));
}
public Cursor query(Query query) {
Cursor underlyingCursor = query.runQuery(mResolver, UNDERLYING_COLUMNS,
mBaseUri);
if (underlyingCursor == null) {
return null;
}
return new CursorTranslator(underlyingCursor, mBaseUri);
}
public ParcelFileDescriptor openDownloadedFile(long id)
throws FileNotFoundException {
return mResolver.openFileDescriptor(getDownloadUri(id), "r");
}
public void restartDownload(long... ids) {
Cursor cursor = query(new Query().setFilterById(ids));
try {
for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor
.moveToNext()) {
int status = cursor
.getInt(cursor.getColumnIndex(COLUMN_STATUS));
if (status != STATUS_SUCCESSFUL && status != STATUS_FAILED) {
throw new IllegalArgumentException(
"Cannot restart incomplete download: "
+ cursor.getLong(cursor
.getColumnIndex(COLUMN_ID)));
}
}
} finally {
cursor.close();
}
ContentValues values = new ContentValues();
values.put(Downloads.Impl.COLUMN_CURRENT_BYTES, 0);
values.put(Downloads.Impl.COLUMN_TOTAL_BYTES, -1);
values.putNull(Downloads.Impl._DATA);
values.put(Downloads.Impl.COLUMN_STATUS, Downloads.Impl.STATUS_PENDING);
mResolver.update(mBaseUri, values, getWhereClauseForIds(ids),
getWhereArgsForIds(ids));
}
/**
* Get the DownloadProvider URI for the download with the given ID.
*/
Uri getDownloadUri(long id) {
return ContentUris.withAppendedId(mBaseUri, id);
}
/**
* Get a parameterized SQL WHERE clause to select a bunch of IDs.
*/
static String getWhereClauseForIds(long[] ids) {
StringBuilder whereClause = new StringBuilder();
whereClause.append("(");
for (int i = 0; i < ids.length; i++) {
if (i > 0) {
whereClause.append("OR ");
}
whereClause.append(Downloads.Impl._ID);
whereClause.append(" = ? ");
}
whereClause.append(")");
return whereClause.toString();
}
/**
* Get the selection args for a clause returned by
* {#link #getWhereClauseForIds(long[])}.
*/
static String[] getWhereArgsForIds(long[] ids) {
String[] whereArgs = new String[ids.length];
for (int i = 0; i < ids.length; i++) {
whereArgs[i] = Long.toString(ids[i]);
}
return whereArgs;
}
/**
* This class wraps a cursor returned by DownloadProvider -- the
* "underlying cursor" -- and presents a different set of columns, those
* defined in the DownloadManager.COLUMN_* constants. Some columns
* correspond directly to underlying values while others are computed from
* underlying data.
*/
private static class CursorTranslator extends CursorWrapper {
private Uri mBaseUri;
public CursorTranslator(Cursor cursor, Uri baseUri) {
super(cursor);
mBaseUri = baseUri;
}
#Override
public int getColumnIndex(String columnName) {
return Arrays.asList(COLUMNS).indexOf(columnName);
}
#Override
public int getColumnIndexOrThrow(String columnName)
throws IllegalArgumentException {
int index = getColumnIndex(columnName);
if (index == -1) {
throw new IllegalArgumentException("No such column: "
+ columnName);
}
return index;
}
#Override
public String getColumnName(int columnIndex) {
int numColumns = COLUMNS.length;
if (columnIndex < 0 || columnIndex >= numColumns) {
throw new IllegalArgumentException("Invalid column index "
+ columnIndex + ", " + numColumns + " columns exist");
}
return COLUMNS[columnIndex];
}
#Override
public String[] getColumnNames() {
String[] returnColumns = new String[COLUMNS.length];
System.arraycopy(COLUMNS, 0, returnColumns, 0, COLUMNS.length);
return returnColumns;
}
#Override
public int getColumnCount() {
return COLUMNS.length;
}
#Override
public byte[] getBlob(int columnIndex) {
throw new UnsupportedOperationException();
}
#Override
public double getDouble(int columnIndex) {
return getLong(columnIndex);
}
private boolean isLongColumn(String column) {
return LONG_COLUMNS.contains(column);
}
#Override
public float getFloat(int columnIndex) {
return (float) getDouble(columnIndex);
}
#Override
public int getInt(int columnIndex) {
return (int) getLong(columnIndex);
}
#Override
public long getLong(int columnIndex) {
return translateLong(getColumnName(columnIndex));
}
#Override
public short getShort(int columnIndex) {
return (short) getLong(columnIndex);
}
#Override
public String getString(int columnIndex) {
return translateString(getColumnName(columnIndex));
}
private String translateString(String column) {
if (isLongColumn(column)) {
return Long.toString(translateLong(column));
}
if (column.equals(COLUMN_TITLE)) {
return getUnderlyingString(Downloads.COLUMN_TITLE);
}
if (column.equals(COLUMN_DESCRIPTION)) {
return getUnderlyingString(Downloads.COLUMN_DESCRIPTION);
}
if (column.equals(COLUMN_URI)) {
return getUnderlyingString(Downloads.COLUMN_URI);
}
if (column.equals(COLUMN_MEDIA_TYPE)) {
return getUnderlyingString(Downloads.COLUMN_MIME_TYPE);
}
if (column.equals(COLUMN_MEDIAPROVIDER_URI)) {
return getUnderlyingString(Downloads.Impl.COLUMN_MEDIAPROVIDER_URI);
}
assert column.equals(COLUMN_LOCAL_URI);
return getLocalUri();
}
private String getLocalUri() {
long destinationType = getUnderlyingLong(Downloads.Impl.COLUMN_DESTINATION);
if (destinationType == Downloads.Impl.DESTINATION_FILE_URI) {
// return client-provided file URI for external download
return getUnderlyingString(Downloads.Impl.COLUMN_FILE_NAME_HINT);
}
if (destinationType == Downloads.Impl.DESTINATION_EXTERNAL) {
// return stored destination for legacy external download
String localPath = getUnderlyingString(Downloads.Impl._DATA);
if (localPath == null) {
return null;
}
return Uri.fromFile(new File(localPath)).toString();
}
// return content URI for cache download
long downloadId = getUnderlyingLong(Downloads.Impl._ID);
return ContentUris.withAppendedId(mBaseUri, downloadId).toString();
}
private long translateLong(String column) {
if (!isLongColumn(column)) {
// mimic behavior of underlying cursor -- most likely, throw
// NumberFormatException
return Long.valueOf(translateString(column));
}
if (column.equals(COLUMN_ID)) {
return getUnderlyingLong(Downloads.Impl._ID);
}
if (column.equals(COLUMN_TOTAL_SIZE_BYTES)) {
return getUnderlyingLong(Downloads.COLUMN_TOTAL_BYTES);
}
if (column.equals(COLUMN_STATUS)) {
return translateStatus((int) getUnderlyingLong(Downloads.COLUMN_STATUS));
}
if (column.equals(COLUMN_REASON)) {
return getReason((int) getUnderlyingLong(Downloads.COLUMN_STATUS));
}
if (column.equals(COLUMN_BYTES_DOWNLOADED_SO_FAR)) {
return getUnderlyingLong(Downloads.COLUMN_CURRENT_BYTES);
}
assert column.equals(COLUMN_LAST_MODIFIED_TIMESTAMP);
return getUnderlyingLong(Downloads.COLUMN_LAST_MODIFICATION);
}
private long getReason(int status) {
switch (translateStatus(status)) {
case STATUS_FAILED:
return getErrorCode(status);
case STATUS_PAUSED:
return getPausedReason(status);
default:
return 0; // arbitrary value when status is not an error
}
}
private long getPausedReason(int status) {
switch (status) {
case Downloads.Impl.STATUS_WAITING_TO_RETRY:
return PAUSED_WAITING_TO_RETRY;
case Downloads.Impl.STATUS_WAITING_FOR_NETWORK:
return PAUSED_WAITING_FOR_NETWORK;
case Downloads.Impl.STATUS_QUEUED_FOR_WIFI:
return PAUSED_QUEUED_FOR_WIFI;
default:
return PAUSED_UNKNOWN;
}
}
private long getErrorCode(int status) {
if ((400 <= status && status < Downloads.Impl.MIN_ARTIFICIAL_ERROR_STATUS)
|| (500 <= status && status < 600)) {
// HTTP status code
return status;
}
switch (status) {
case Downloads.STATUS_FILE_ERROR:
return ERROR_FILE_ERROR;
case Downloads.STATUS_UNHANDLED_HTTP_CODE:
case Downloads.STATUS_UNHANDLED_REDIRECT:
return ERROR_UNHANDLED_HTTP_CODE;
case Downloads.STATUS_HTTP_DATA_ERROR:
return ERROR_HTTP_DATA_ERROR;
case Downloads.STATUS_TOO_MANY_REDIRECTS:
return ERROR_TOO_MANY_REDIRECTS;
case Downloads.STATUS_INSUFFICIENT_SPACE_ERROR:
return ERROR_INSUFFICIENT_SPACE;
case Downloads.STATUS_DEVICE_NOT_FOUND_ERROR:
return ERROR_DEVICE_NOT_FOUND;
case Downloads.Impl.STATUS_CANNOT_RESUME:
return ERROR_CANNOT_RESUME;
case Downloads.Impl.STATUS_FILE_ALREADY_EXISTS_ERROR:
return ERROR_FILE_ALREADY_EXISTS;
default:
return ERROR_UNKNOWN;
}
}
private long getUnderlyingLong(String column) {
return super.getLong(super.getColumnIndex(column));
}
private String getUnderlyingString(String column) {
return super.getString(super.getColumnIndex(column));
}
private int translateStatus(int status) {
switch (status) {
case Downloads.STATUS_PENDING:
return STATUS_PENDING;
case Downloads.STATUS_RUNNING:
return STATUS_RUNNING;
case Downloads.Impl.STATUS_PAUSED_BY_APP:
case Downloads.Impl.STATUS_WAITING_TO_RETRY:
case Downloads.Impl.STATUS_WAITING_FOR_NETWORK:
case Downloads.Impl.STATUS_QUEUED_FOR_WIFI:
return STATUS_PAUSED;
case Downloads.STATUS_SUCCESS:
return STATUS_SUCCESSFUL;
default:
assert Downloads.isStatusError(status);
return STATUS_FAILED;
}
}
}
}