PlaceHolder reason for Download Failure using Download Manager in Android - android

I am using Download Manager to download files of types(.mov, .pdf, .png). When I call download manager, download fails giving reason as Place holder. What does place holder mean and how do I fix this issue? Please help!
My code is as follows:
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(downloadUrlFile))
.setDestinationInExternalFilesDir(context,
(Environment.DIRECTORY_DOWNLOADS), downloadFileName).setNotificationVisibility(visibility);
mEnqueue = downloadManager.enqueue(request);
mDownloadReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "DOWNLOAD_STATUS"+intent.getAction());
String action = intent.getAction();
if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)) {
Query query = new Query();
query.setFilterById(mEnqueue);
Cursor c = downloadManager.query(query);
if (c.moveToFirst()) {
int columnIndex = c .getColumnIndex(DownloadManager.COLUMN_STATUS);
Log.d("getColumnIndex()", "Reason: " + c
.getColumnIndex(DownloadManager.COLUMN_STATUS)+DownloadManager.STATUS_SUCCESSFUL);
if (DownloadManager.STATUS_SUCCESSFUL == c.getInt(columnIndex)) {
Log.d(TAG, "DOWNLOAD_STATUS_SUCCESSFUL");
} else if (DownloadManager.STATUS_FAILED == c.getInt(columnIndex)) {
Log.d("handleData()", "Reason: " + c.getString(c.getColumnIndex(DownloadManager.COLUMN_REASON)));
Log.d(TAG, "DOWNLOAD_STATUS_FAILED");
}
}
}
}
};

The problem is that the column "COLUMN_REASON" is an int and not a String (despite some examples I have seen that imply it is a string).
Hence you should be using:
Log.d("handleData()", "Reason: " + c.getInt(c.getColumnIndex(DownloadManager.COLUMN_REASON)));
The value can be found:
Here
and
here

Related

Download Speed is slow in Android

I have a list of PDFs that I need to download all PDFs in one click. I have implemented a foreground service to do the task. In this service I am enqueuing the list of PDFs in the Android DownloadManager's request to handle the downloads.
For the enqueue of request, I am inserting 6 PDF's in request for the first time and further when any one download completed I am inserting next PDF in the request list.
In this implementation the whole process is taking around 1-2 mins for the 91 PDFs(roughly size of 40MB in total). Is there any way, so we can speed up the download process.
This is the function to start Download queue
private void startDownloadQueue()
{
for(int i=0;i<6;i++){
if(i<hashMap.size()) {
listIndex++;
initiateDownload(documentList.get(i), positionList.get(i), hashMap.size());
}
}
}
private void initiateDownload(DocumentJsonModel.Document doc, int pos, int listSize)
{
if (doc != null && isDownloading) {
String position = String.valueOf(pos);
String docId = doc.id;
String docFile = doc.file;
String docName = doc.name;
String docDescription = doc.description;
boolean isJustView = false;
isFromSearch = false;
long id = downloadFile(doc, docId, docFile, docName, docDescription, position, isJustView, listSize);
if (id != 0) {
setProgressUpdate(id, Integer.parseInt(position), Integer.parseInt(docId), isJustView, doc, listSize);
} else if (isJustView) {
Intent intent1 = new Intent(INTENT_FILTER_ACTION_OPEN_DOC);
intent1.putExtra("docID",docId);
intent1.putExtra("position",pos);
intent1.putExtra("fromDownloadAll", true);
intent1.putExtra("docId", docId);
sendBroadcast(intent1);
stopForeground(true);
stopSelf();
} else if (isFromSearch) {
Intent intent1 = new Intent(INTENT_FILTER_ACTION_OPEN_DOC_FROM_SEARCH);
intent1.putExtra("fromDownloadAll", true);
intent1.putExtra("position", pos);
intent1.putExtra("docId", docId);
sendBroadcast(intent1);
stopForeground(true);
stopSelf();
}
}
}
Method to enqueue PDFs in download manager request queue -
public long downloadFile(DocumentJsonModel.Document doc,String docId, String docFile, String docName, String description, String position, boolean isJustView, int listSize) {
long downloadReference = 0;
String root = "/twoway/saved_documents/";
String BASE_DIR = getExternalFilesDir(null).getPath();
File dir = new File(BASE_DIR + root);
File file = new File(dir, docId + "_" + docFile);
if(!isCancelledButtonPressed) {
if (!file.exists()) {
String fileUrl = DOCUMENT_URL + docFile;
fileUrl = fileUrl.replaceAll(" ", "%20");
Uri fileUri = Uri.parse(fileUrl);
//Create request for android download manager
DownloadManager downloadManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
DownloadManager.Request request = new DownloadManager.Request(fileUri);
//set title for download
request.setTitle(docName);
request.setVisibleInDownloadsUi(false);
//request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION);
//Setting description of request
request.setDescription(description);
request.setDestinationInExternalFilesDir(this, null, "/twoway/saved_documents/"
+ docId + "_" + docFile);
//Enqueue download and save into referenceId
downloadReference = downloadManager.enqueue(request);
}
}}

Android 11 - Mediastore playlist remove multiple tracks from Playlist

On Android 11, when trying to remove multiple tracks from a Playlist, all the tracks are removed instead of just the selected ones. This used to work fine till Android 10.
However, if only 1 track is removed, no other tracks are removed. Is this a bug or an expected behavior going forward from Android 11? Is this a side-effect of scoped storage?
Tested on : Pixel 3, Android 11 Official release build
public void removeTracksFromPlaylist(long playlistId, String[] ids) {
Log.d(TAG, "removeTracksFromPlaylist() called with: playlistId = [" + playlistId + "], ids = [" + Arrays.toString(ids) + "]");
Uri uri = MediaStore.Audio.Playlists.Members.getContentUri(MediaStore.VOLUME_EXTERNAL, playlistId);
StringBuilder stringBuilder = new StringBuilder();
for (int index = 0; index < ids.length; index++) {
if (index > 0) {
stringBuilder.append(",");
}
stringBuilder.append("?");
}
String selection = MediaStore.Audio.Playlists.Members._ID + " IN (" + stringBuilder.toString() + ")";
contentResolver.delete(uri, selection, ids); // Returns count of all files removed from playlist
}
Edit:
Some information from source : (https://android.googlesource.com/platform/packages/providers/MediaProvider/+blame/refs/tags/android-11.0.0_r3/src/com/android/providers/media/MediaProvider.java#5610)
From the source, I see that if the count of playlist items are more than 1, the whole playlist is cleared.
try (Cursor c = qb.query(helper,
new String[] { Playlists.Members.PLAY_ORDER }, queryArgs, null)) {
if ((c.getCount() == 1) && c.moveToFirst()) { // This condition fails if more than 1 item to delete
return c.getInt(0) - 1;
} else {
return -1;
}
}
The index is used here which clears the playlist:(https://android.googlesource.com/platform/packages/providers/MediaProvider/+blame/refs/tags/android-11.0.0_r3/src/com/android/providers/media/MediaProvider.java#5566)
private int removePlaylistMembers(#NonNull Uri playlistUri, #NonNull Bundle queryArgs)
throws FallbackException {
final int index = resolvePlaylistIndex(playlistUri, queryArgs);
try {
final File playlistFile = queryForDataFile(playlistUri, null);
final Playlist playlist = new Playlist();
playlist.read(playlistFile);
final int count;
if (index == -1) {
count = playlist.asList().size();
playlist.clear(); //Clears whole playlist
} else {
count = 1;
playlist.remove(index);
}
playlist.write(playlistFile);
resolvePlaylistMembers(playlistUri);
return count;
} catch (IOException e) {
throw new FallbackException("Failed to update playlist", e,
android.os.Build.VERSION_CODES.R);
}
}

Manually created SMS refuses to appear [duplicate]

I have been working on an SMS application. Everything was smooth until yesterday, when I updated my Nexus 4 to Android 4.4, KitKat. Features such as marking an SMS as read/unread, and deleting all messages in a thread have stopped working. Why is this happening? It works on other Samsung devices (not running KitKat).
This is my code to mark a message as read or unread:
public static void markRead(final Context context, final Uri uri,
final int read) {
Log.d(TAG, "markRead(" + uri + "," + read + ")");
if (uri == null) {
return;
}
String[] sel = Message.SELECTION_UNREAD;
if (read == 0) {
sel = Message.SELECTION_READ;
}
final ContentResolver cr = context.getContentResolver();
final ContentValues cv = new ContentValues();
cv.put(Message.PROJECTION[Message.INDEX_READ], read);
try {
cr.update(uri, cv, Message.SELECTION_READ_UNREAD, sel);
} catch (IllegalArgumentException e) {
Log.e(TAG, "failed update", e);
Toast.makeText(context, e.getMessage(), Toast.LENGTH_LONG).show();
}
}
For deleting all messages in a thread, I use:
public static void deleteMessages(final Context context, final Uri uri,
final int title, final int message, final Activity activity) {
Log.i(TAG, "deleteMessages(..," + uri + " ,..)");
final Builder builder = new Builder(context);
builder.setTitle(title);
builder.setMessage(message);
builder.setNegativeButton(android.R.string.no, null);
builder.setPositiveButton(android.R.string.yes,
new DialogInterface.OnClickListener() {
#Override
public void onClick(final DialogInterface dialog,
final int which) {
final int ret = context.getContentResolver().delete(
uri, null, null);
Log.d(TAG, "deleted: " + ret);
if (activity != null && !activity.isFinishing()) {
activity.finish();
}
if (ret > 0) {
Conversation.flushCache();
Message.flushCache();
SmsReceiver.updateNewMessageNotification(context,
null);
// adapter.notifyDataSetChanged();
}
try {
testFromFragment(context);
} catch (Exception e) {
e.printStackTrace();
}
}
});
builder.show();
}
With Android 4.4, several things have changed with regard to SMS. Among them is the fact that only the app that is registered as the default SMS app has write access to the provider.
Check here for a short blurb on changes to SMS.
Check this link for a more in depth look. This one explains what criteria your app needs to meet to be the default messaging app.
And here's the official fun stuff.
So, if your app is not the default messaging app, that would be why the specified functionalities have stopped working.
A possible workaround for the default Provider restriction can be found in the answer here.

Sony Xperia T (ST26) calendar account issue

After factory reset of the device.
I'm trying to retrieve the calendars display names(by the code below), it returns that there is no calendars.
but when opening the device Calendar application at least one time, the default phone calendar will be retrieved correctly.
Is there any way to retrieve the calendars (especially the default ) without opening the device Calendar application?
Thanks in advance.
Here is the code for retrieving calendars exist on the device:
private Uri getCalendarUri() {
return Uri.parse(Integer.parseInt(Build.VERSION.SDK) > 7 ? "content://com.android.calendar/calendars" : "content://calendar/calendars");
}
private String[] getCalendars(Context context) {
String[] res = null;
ContentResolver contentResolver = context.getContentResolver();
Cursor cursor = null;
try {
cursor = contentResolver.query( getCalendarUri(),
Integer.parseInt(Build.VERSION.SDK) > 13 ? new String[]{"_id", "calendar_displayName"} : new String[]{"_id", "displayName"}, null, null, "_id ASC");
if (cursor.getCount() > 0) {
res = new String[cursor.getCount()];
int i = 0;
while (cursor.moveToNext()) {
res[i] = cursor.getString(0) + ": " + cursor.getString(1);
i++;
}
}
}
catch (Exception e) {
e.printStackTrace();
}
finally {
if (cursor != null)
cursor.close();
}
return res;
}
I solved the issue.
using this code in my activity:
private static boolean calendar_opened = false;
private void openCalendar() {
String[] calendars = getCalendars(this);
if (!calendar_opened && calendars != null && calendars.length <= 0) {
new Timer().schedule(new TimerTask() {
#Override
public void run() {
runOnUiThread(new Runnable() {
public void run() {
try {
//bring back my activity to foreground
final Intent tmpIntent = (Intent) MainScreen.this.getIntent().clone();
tmpIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
tmpIntent.setClass(MyExams.getInstance(), MainScreen.class);
PendingIntent.getActivity(MyExams.getInstance(), 0, tmpIntent, PendingIntent.FLAG_UPDATE_CURRENT).send();
}
catch (Exception e) {
}
}
});
}
}, 100 );//time is your dissection
Intent i = new Intent();
i.setClassName("com.android.calendar", "com.android.calendar.LaunchActivity");
i.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
startActivity(i);
calendar_opened = true;
}
}
//After my activity is on foreground I killed the calendar using this code, even there's no need because of FLAG_ACTIVITY_NO_HISTORY:
ActivityManager activityManager = (ActivityManager) MainScreen.this.getSystemService(Context.ACTIVITY_SERVICE);
activityManager.killBackgroundProcesses("com.android.calendar");
I think the device calendar application must be installing a calendar when you open it which may not be available before you open it after a factory reset.
I think you don't want the user to have to open the calendar application. If you don't mind the calendar application being opened in background, you could consider opening it through a Service an then closing it soon so that the user won't notice and the device calendar would be available.
android-codes-examples.blogspot.in/2011/11/… Check this link out, is it useful?

Stop saving photos using Android native camera

I am using native Android camera and save file to my application data folder (/mnt/sdcard/Android/data/com.company.app/files/Pictures/). At the same time anther copy of photo is saved to DCIM folder.
This is my code:
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
String formattedImageName = getDateString() + ".jpg";
File image_file = new File(this.getExternalFilesDir(Environment.DIRECTORY_PICTURES), formattedImageName);
Uri imageUri = Uri.fromFile(image_file);
intent.putExtra(MediaStore.EXTRA_OUTPUT,imageUri);
startActivityForResult(intent, REQUEST_FROM_CAMERA);
How can I prevent saving additional copy of image to DCIM folder?
Many Thanks
You can use the following :
First we get the last saved image by checking which was the last modified image. Then check if last modified time is in the last few seconds. You may also have to check the exact location of where camera stores the image.
private boolean deleteLastFromDCIM() {
boolean success = false;
try {
File[] images = new File(Environment.getExternalStorageDirectory()
+ File.separator + "DCIM/Camera").listFiles();
File latestSavedImage = images[0];
for (int i = 1; i < images.length; ++i) {
if (images[i].lastModified() > latestSavedImage.lastModified()) {
latestSavedImage = images[i];
}
}
// OR JUST Use success = latestSavedImage.delete();
success = new File(Environment.getExternalStorageDirectory()
+ File.separator + "DCIM/Camera/"
+ latestSavedImage.getAbsoluteFile()).delete();
return success;
} catch (Exception e) {
e.printStackTrace();
return success;
}
}
Unfortunately, some smart phones save images in another folder such as DCIM/100MEDIA. So can't rely to these solution. I prefer use this way:
String[] projection = new String[] {
MediaStore.Images.ImageColumns._ID,
MediaStore.Images.ImageColumns.DATA,
MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME,
MediaStore.Images.ImageColumns.DATE_TAKEN,
MediaStore.Images.ImageColumns.MIME_TYPE};
final Cursor cursor = managedQuery(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,projection, null, null,
MediaStore.Images.ImageColumns.DATE_TAKEN + " DESC");
if(cursor != null){
cursor.moveToFirst();
// you will find the last taken picture here and can delete that
}
I tried to find out if a second copy exists and delete the copy. I used the above code to find the last taken picture.
Notice: Don't use cursor.close(); after using managedQuery, Leave the cursor for the Android system to manage and don't call that. You can see managedQuery()
Notice2: The managedQuery method is deprecated and it should be avoided, implement CursorLoaders instead.
check this code..
private void FillPhotoList() {
// initialize the list!
GalleryList.clear();
String[] projection = { MediaStore.Images.ImageColumns.DISPLAY_NAME };
for(int i=0;i<projection.length;i++)
Log.i("InfoLog","projection "+projection[0].toString());
// intialize the Uri and the Cursor, and the current expected size.
Cursor c = null;
Uri u = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
Log.i("InfoLog","FillPhoto Uri u "+u.toString());
// Query the Uri to get the data path. Only if the Uri is valid.
if (u != null)
{
c = managedQuery(u, projection, null, null, null);
}
// If we found the cursor and found a record in it (we also have the id).
if ((c != null) && (c.moveToFirst()))
{
do
{
// Loop each and add to the list.
GalleryList.add(c.getString(0)); // adding all the images sotred in the mobile phone(Internal and SD card)
}
while (c.moveToNext());
}
Log.i(INFOLOG,"gallery size "+ GalleryList.size());
}
and this is where the method is doing all magic
/** Method will check all the photo is the gallery and delete last captured and move it to the required folder.
*/
public void movingCapturedImageFromDCIMtoMerchandising()
{
// This is ##### ridiculous. Some versions of Android save
// to the MediaStore as well. Not sure why! We don't know what
// name Android will give either, so we get to search for this
// manually and remove it.
String[] projection = { MediaStore.Images.ImageColumns.SIZE,
MediaStore.Images.ImageColumns.DISPLAY_NAME,
MediaStore.Images.ImageColumns.DATA,
BaseColumns._ID,};
// intialize the Uri and the Cursor, and the current expected size.
for(int i=0;i<projection.length;i++)
Log.i("InfoLog","on activityresult projection "+projection[i]);
//+" "+projection[1]+" "+projection[2]+" "+projection[3] this will be needed if u remove the for loop
Cursor c = null;
Uri u = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
Log.i("InfoLog","on activityresult Uri u "+u.toString());
if (CurrentFile != null)
{
// Query the Uri to get the data path. Only if the Uri is valid,
// and we had a valid size to be searching for.
if ((u != null) && (CurrentFile.length() > 0))
{
//****u is the place from data will come and projection is the specified data what we want
c = managedQuery(u, projection, null, null, null);
}
// If we found the cursor and found a record in it (we also have the size).
if ((c != null) && (c.moveToFirst()))
{
do
{
// Check each area in the gallery we built before.
boolean bFound = false;
for (String sGallery : GalleryList)
{
if (sGallery.equalsIgnoreCase(c.getString(1)))
{
bFound = true;
Log.i("InfoLog","c.getString(1) "+c.getString(1));
break;
}
}
// To here we looped the full gallery.
if (!bFound) //the file which is newly created and it has to be deleted from the gallery
{
// This is the NEW image. If the size is bigger, copy it.
// Then delete it!
File f = new File(c.getString(2));
// Ensure it's there, check size, and delete!
if ((f.exists()) && (CurrentFile.length() < c.getLong(0)) && (CurrentFile.delete()))
{
// Finally we can stop the copy.
try
{
CurrentFile.createNewFile();
FileChannel source = null;
FileChannel destination = null;
try
{
source = new FileInputStream(f).getChannel();
destination = new FileOutputStream(CurrentFile).getChannel();
destination.transferFrom(source, 0, source.size());
}
finally
{
if (source != null)
{
source.close();
}
if (destination != null)
{
destination.close();
}
}
}
catch (IOException e)
{
// Could not copy the file over.
ToastMaker.makeToast(this, "Error Occured", 0);
}
}
//****deleting the file which is in the gallery
Log.i(INFOLOG,"imagePreORNext1 "+imagePreORNext);
Handler handler = new Handler();
//handler.postDelayed(runnable,300);
Log.i(INFOLOG,"imagePreORNext2 "+imagePreORNext);
ContentResolver cr = getContentResolver();
cr.delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, BaseColumns._ID + "=" + c.getString(3), null);
break;
}
}
while (c.moveToNext());
}
}
}
A nice solution by Parth. But it's good for Samsungs that keep images in DCIM/Camera. Some phones - Sony Ericssons, HTCs keep them in folders like DCIM/100MEDIA, DCIM/100ANDRO so I have slightly modified the code:
private boolean deleteLastFromDCIM() {
boolean success = false;
try {
//Samsungs:
File folder = new File(Environment.getExternalStorageDirectory() + File.separator + "DCIM/Camera");
if(!folder.exists()){ //other phones:
File[] subfolders = new File(Environment.getExternalStorageDirectory() + File.separator + "DCIM").listFiles();
for(File subfolder : subfolders){
if(subfolder.getAbsolutePath().contains("100")){
folder = subfolder;
break;
}
}
if(!folder.exists())
return false;
}
File[] images = folder.listFiles();
File latestSavedImage = images[0];
for (int i = 1; i < images.length; ++i) {
if (images[i].lastModified() > latestSavedImage.lastModified()) {
latestSavedImage = images[i];
}
}
success = latestSavedImage.delete();
return success;
} catch (Exception e) {
e.printStackTrace();
return success;
}
}
I am encountering a similar problem with the Moto Z Force (7.1.1). I have the MediaStore.EXTRA_OUTPUT defined on the intent, but a duplicate file is still created in the camera directory.
I need to test on other devices, but here's an approach I took regarding this issue. Rather than trying to find the specific camera directory, I'm using the MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME location.
Here's my code snippet:
private void removeCameraDuplicate() {
String[] proj = {
MediaStore.Images.ImageColumns.DATA,
MediaStore.Images.ImageColumns._ID };
String selection = MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME + " = ? ";
String[] selectionArgs = new String[] { "Camera" };
Cursor cursor = mActivity.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, proj, selection, selectionArgs, MediaStore.Images.ImageColumns.DATE_TAKEN + " desc");
if (cursor != null) {
int idxPath = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
if (cursor.getCount() > 0 && idxPath > -1 && cursor.moveToFirst()) {
File original = new File(mMediaPath);
File cameraDupe = new File(cursor.getString(idxPath));
if (original.exists() && cameraDupe.exists()) {
LogUtils.LOGE("***> camera", "original " + original.length());
LogUtils.LOGE("***> camera", "original " + original.lastModified());
LogUtils.LOGE("***> camera", "duplicate " + cameraDupe.length());
LogUtils.LOGE("***> camera", "duplicate " + cameraDupe.lastModified());
if (original.length() == cameraDupe.length() && original.lastModified() == cameraDupe.lastModified()) {
if (cameraDupe.delete()) {
LogUtils.LOGE("***> camera", "duplicate deleted");
}
}
}
}
cursor.close();
}
}

Categories

Resources