Android, Glide Image Loading Library: NullPointerException, ProviderInfo - java

Just getting into the Glide image loading library for Android. Currently working with some code from here:
https://github.com/bumptech/glide/issues/459
My full project is here is you want to look at it:
https://github.com/mhurwicz/glide02
I'm getting the following exception when I run the app in the emulator in Android Studio:
java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.XmlResourceParser android.content.pm.ProviderInfo.loadXmlMetaData(android.content.pm.PackageManager, java.lang.String)' on a null object reference
This is the key statement in MainActivity:
new ShareTask(this).execute("http://thelink");
(thelink is actually goo.gl/gEgYUd -- couldn't leave that in above because stackoverflow doesn't allow URL shorteners. )
Here is my code for the ShareTask class
class ShareTask extends AsyncTask<String, Void, File> {
private final Context context;
public ShareTask(Context context) {
this.context = context;
}
#Override protected File doInBackground(String... params) {
String url = params[0]; // should be easy to extend to share multiple images at once
try {
return Glide
.with(context)
.load(url)
.downloadOnly(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
.get() // needs to be called on background thread
;
} catch (Exception ex) {
Log.w("SHARE", "Sharing " + url + " failed", ex);
return null;
}
}
#Override protected void onPostExecute(File result) {
if (result == null) { return; }
Uri uri = FileProvider.getUriForFile(context, context.getPackageName(), result);
share(uri); // startActivity probably needs UI thread
}
private void share(Uri result) {
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("image/jpeg");
intent.putExtra(Intent.EXTRA_SUBJECT, "Shared image");
intent.putExtra(Intent.EXTRA_TEXT, "Look what I found!");
intent.putExtra(Intent.EXTRA_STREAM, result);
context.startActivity(Intent.createChooser(intent, "Share image"));
}
}
Using debug, it seems I may be running into trouble at the get() statement. For one thing, the width and the height are very large negative numbers. (See the code highlighted in green below.) Then the get() statement returns null. (See the code highlighted in red below.)
Thanks in advance for any help you can provide!

The NPE is coming from FileProvider.getUriForFile because you're passing in the wrong authority. You declared android:authorities="com.example.fileprovider" in the manifest, but you're using the package name at the call. This fails to resolve the info in FileProvider.parsePathStrategy. Match those two strings up and you'll be good to go.
The easiest fix is to use android:authorities="${applicationId}", this leads to 0 hardcoded strings, so you can keep using context.getPackageName().
Regarding your concerns during debug:
Target.SIZE_ORIGINAL is declared to be MIN_VALUE, hence the large number
it's not returning null, IDEA is just confused about where it is in the method, that return null; shouldn't be executed if it fails in the FileProvider code.
doGet(null): null is the timeout here, it's guarded properly in code
I've run the app and weirdly I got a log line saying
W/SHARE: Sharing http://... failed
but not a stack trace, which is weird, because ex cannot be null in a catch!

Related

Demo of MediaPlayer.setDataSource()?

I am learning about mediaplayer in android.
I wanted some simple and easy to understand code example of MediaPlayer.setDataSource().
Well, for more deep understanding of MediaPlayer that's better to read official documentation https://developer.android.com/reference/android/media/MediaPlayer#setDataSource(android.content.res.AssetFileDescriptor). But for basic comprehension here is the code example.
MediaPlayer mp = new MediaPlayer();
// Here you may set which stream to use either MEDIA or ALARM etc.
mp.setAudioStreamType(AudioManager.STREAM_ALARM);
try {
if (isAnyActiveSongExist){
// Here you may set dataSource as path of the file
mp.setDataSource(firstPrioritySongEntityPath);
}
else{
// Here you may set dataSource using Uri
mp.setDataSource(context, Settings.System.DEFAULT_RINGTONE_URI);
}
mp.prepare();
} catch (IOException e) {
e.printStackTrace();
}
mp.start();
setDataSource() defines which file should be used by your MediaPlayer for playing.
First of all, code never been simple and easy until you'll not go through it. Check this link click here, I think you'll have your answer from here
About setDataSource(String) call
After seeing your comment, it looks like you exactly want setDataSource(string) to be used for your purpose. I don't understand why. But, what I assume is, for some reason you are trying to avoid using "context". If that is not the case then the above two solutions should work perfectly for you or if you are trying to avoid context, I'm afraid that is not possible with the function with signature setDataSource(String) call. The reason is as below,
MediaPlayer setDataSource() function has these below options out of which you are only interested in setDataSource(String),
public void setDataSource(String path)
throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
setDataSource(path, null, null);
}
and if you check setDataSource(String path, String[] keys, String[] values) code, you will see the below condition filtering the path based on its scheme, particularly if it is "file" scheme it calls setDataSource(FileDescriptor) or if scheme is non "file", it calls native JNI media function.
{
final Uri uri = Uri.parse(path);
final String scheme = uri.getScheme();
if ("file".equals(scheme)) {
path = uri.getPath();
} else if (scheme != null) {
// handle non-file sources
nativeSetDataSource(
MediaHTTPService.createHttpServiceBinderIfNecessary(path),
path,
keys,
values);
return;
}
final File file = new File(path);
if (file.exists()) {
FileInputStream is = new FileInputStream(file);
FileDescriptor fd = is.getFD();
setDataSource(fd);
is.close();
} else {
throw new IOException("setDataSource failed.");
}
}
In the above code, your resource file URI scheme will not be null (android.resource://) and setDataSource(String) will try to use native JNI function nativeSetDataSource() thinking that your path is http/https/rtsp and obviously that call will fail as well without throwing any exception. Thats why your call to setDataSource(String) escapes without an exception and gets to prepare() call with the following exception.
Prepare failed.: status=0x1
So setDataSource(String) override cannot handle your resource file. You need to choose another override for that.
On the other side, check setDataSource(Context context, Uri uri, Map headers) which is used by setDataSource(Context context, Uri uri), it uses AssetFileDescriptor, ContentResolver from your context and openAssetFileDescriptor to open the URI which gets success as openAssetFileDescriptor() can open your resource file and finally the resultant fd is used to call setDataSource(FileDescriptor) override.
AssetFileDescriptor fd = null;
try {
ContentResolver resolver = context.getContentResolver();
fd = resolver.openAssetFileDescriptor(uri, "r");
// :
// :
// :
if (fd.getDeclaredLength() < 0) {
setDataSource(fd.getFileDescriptor());
} else {
setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getDeclaredLength());
}
To conclude, you cannot use setDataSource(String) override as is to use your resource mp3 file. Instead, if you want use string to play your resource file you can use either MediaPlayer.create() static function with getIdentifier() as given above or setDataSource(context,uri) as given in Update#1.
Refer to the complete source code for more understanding here: Android MediaPlayer

Android AsyncTask sending message to a Handler on a dead thread

I am trying to make a program that should work something like a catalog - I have a JSON array that I loop through and parse the JSON objects into a string containing the image link, and two strings for descriptions for each object. Now, when downloading the images from the internet with the link, I run into a problem at the same image every time, image number 93. I checked the link, and it's working, just the same as the others.
This happens:
W/MessageQueue: Handler (android.os.Handler) {f95f6fe} sending message to a Handler on a dead thread
java.lang.IllegalStateException: Handler (android.os.Handler) {f95f6fe} sending message to a Handler on a dead thread
at android.os.MessageQueue.enqueueMessage(MessageQueue.java:543)
at android.os.Handler.enqueueMessage(Handler.java:643)
at android.os.Handler.sendMessageAtTime(Handler.java:612)
at android.os.Handler.sendMessageDelayed(Handler.java:582)
at android.os.Handler.post(Handler.java:338)
at android.os.ResultReceiver$MyResultReceiver.send(ResultReceiver.java:57)
at com.android.internal.os.IResultReceiver$Stub.onTransact(IResultReceiver.java:58)
at android.os.Binder.execTransact(Binder.java:565)
This is how my AsyncTask looks:
private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
ImageView bmImage;
public DownloadImageTask(ImageView bmImage) {
this.bmImage = bmImage;
}
protected Bitmap doInBackground(String... urls) {
String urldisplay = urls[0];
Bitmap mIcon11 = null;
try {
InputStream in = new java.net.URL(urldisplay).openStream();
mIcon11 = BitmapFactory.decodeStream(in);
} catch (Exception e) {
e.printStackTrace();
}
return mIcon11;
}
protected void onPostExecute(Bitmap result) {
bmImage.setImageBitmap(result);
}
}
This is how I call the AsyncTask:
new DownloadImageTask(imgProductPicture).execute(pictureLink);
layoutTest.addView(NewImgView);
In my code I make sure that no more than 5 of these are called at a time, and they all load without issue until reaching the object indexed 92. The object itself is fine, but the app shuts off at this point always. Does anyone have an idea why? I've tried downloading less images at a time (one by one) and it still fails at the same point. I'd appreciate any help.
AsyncTask uses handler of the main thread, to callback onPostExecute(). If the main thread is dead when to callback, system throws the exception. To avoid this, you have to keep the main thread alive until all the work completes.
I solved the problem by creating a new handler/runnable every time I called the DownloadImageTask. Thanks to all who have tried to help.

How can I delete a pre-existing image from storage before re-downloading using DownloadManager?

I am writing code for an Android app using Eclipse that is supposed to download an image from a URL (which is generated by the app, elsewhere in the code, using GPS information), then attach the newly downloaded image to an e-mail to be sent. I am able to, in general, accomplish this without much issue.
My problem is this: I only want one image downloaded by the app to be present in the device's external storage at any given time. Deleting the image after the email intent does not work, because because the app doesn't always call onStop or onDestroy when switching to another app to send the email. Time-sensitive deleting of the image will not work either, because I cannot assume that the user will send only one email from the app per hour. I want to give the user the freedom of sending as many of these emails (with one newly downloaded image, each) as they wish.
My current method (which works MOST of the time) is this: in the downloadFile method, simply check for the file's existence (I call it sensorMap.png), then delete it if it exists, before downloading a new one. This SHOULD ensure that there may be only one sensorMap.png image in external storage at any given time (EDIT: it does do this), and that when it comes time to attach the image to the email intent, there will be exactly one image ready to go. Instead, I see that sometimes a second sensorMap image is sometimes being downloaded into storage (i.e. "sensorMap-1.png"), OR the image cannot be attached to the email due to a "File size: 0 bytes" error, OR the image cannot be attached due to a "File does not exist" error. I am unsure what the difference between the latter two problems is. EDIT: Upon manually examining the contents of the directory I created, it seems that, as intended, I end up with only one image titled "sensorMap.png" at a time; it remains in the directory after the app closes, as expected. However, I still occasionally get the "File size: 0 bytes" message or the "File does not exist." message with no attached image, even though I see that the image DOES exist upon looking in directory afterwards. Other times, everything works just fine. It's rather bewildering.
In addition, there is an issue of the button which sends the email becoming unresponsive occasionally. Most of the time, it prompts the user to select an email client, as intended, but occasionally the button will LOOK as if clicked, but do nothing. When this happens, the logcat does not sense that the button was even clicked (I inserted a println statement to test it).
I am unsure of why my delete-before-download is not working flawlessly; the basic idea, at least, appears to be logically sound. Here is the code pertaining to my issue:
Code used to download file (in MainCountActivity.java):
//Function to download image given URL. Will use to attach image file to email.
public void downloadFile(String uRl) {
//delete existing file first so that only one sensorMap image exists in memory
//at any given time.
File file = new File(Environment.getExternalStorageDirectory()+"/SensorLocationImages");
File checkFile = new File(Environment.getExternalStorageDirectory()+"/SensorLocationImages/sensorMap.png");
if(checkFile.exists())
{
//debugging:
System.out.println("About to delete file!");
//deleteFiles(Environment.getExternalStorageDirectory()+"/SensorLocationImages");
checkFile.delete();
}
DownloadManager mgr = (DownloadManager) getActivity().getSystemService(Context.DOWNLOAD_SERVICE);
Uri downloadUri = Uri.parse(uRl);
DownloadManager.Request request = new DownloadManager.Request(
downloadUri);
request.setAllowedNetworkTypes(
DownloadManager.Request.NETWORK_WIFI
| DownloadManager.Request.NETWORK_MOBILE)
.setAllowedOverRoaming(false).setTitle("Sensor Location Map")
.setDescription("Pinpointed is the location from which the log file was sent.")
.setDestinationInExternalPublicDir("/SensorLocationImages", "sensorMap.png");
mgr.enqueue(request);
}
public Activity getActivity() //I wasn't sure if this would work, but it did. Or at least appears to.
{ return this; }
Method to send email (in MainCountActivity.java):
public void sendEmail(String toAddress, String ccAddress, String bccAddress, String subject, String body, String attachmentMimeType) throws Exception{
try {
Intent emailIntent = new Intent(Intent.ACTION_SEND_MULTIPLE);
emailIntent.setType(attachmentMimeType); //new
String sToAddress[] = { toAddress };
String sCCAddress[] = { ccAddress};
String sBCCAddress[] = { bccAddress };
emailIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
emailIntent.putExtra(Intent.EXTRA_EMAIL, sToAddress);
emailIntent.putExtra(android.content.Intent.EXTRA_CC, sCCAddress);
emailIntent.putExtra(android.content.Intent.EXTRA_BCC, sBCCAddress);
emailIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
emailIntent.putExtra(Intent.EXTRA_TEXT, body);
//get URI of logfile
File tempFile = new File(Environment.getExternalStorageDirectory () + MainCountActivity.dirPath);
Uri uri = Uri.fromFile(tempFile);
//create URI arraylist and add first URI
ArrayList<Uri> uris = new ArrayList<Uri>();
uris.add(uri);
//get URI of map image and add to arraylist
//make sure it is there to attach
File file = new File(Environment.getExternalStorageDirectory()+"/SensorLocationImages");
do {
downloadFile(getMapLink());
//createDirectoryAndSaveFile(getBitmapFromURL(getMapLink()), "sensorMap.png");
} while (!file.exists());
uris.add(Uri.fromFile(new File(Environment
.getExternalStorageDirectory()
+ "/SensorLocationImages/sensorMap.png")));
//+ "/sdcard/SensorLocationImages/sensorMap.png")));
emailIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);
startActivity(emailIntent);
}
catch(Exception ex) {
ex.printStackTrace();
throw ex;
}
}
OnClick method, for my occasional button issue (In MaincountActivity.java):
public void onClick(View v){
switch(v.getId())
{
case R.id.textView1:
{
break;
}
case R.id.Reset:
{
//allowCounting will let the program know when to let it to count or not, depending if Start or Stop button are pressed.
logCount=0;
mCounter.setText("Total: 0");
mToggle.setChecked(false);
break;
}
/* case R.id.toggleButton:
{
break;
}*/
case R.id.SendEmail:
{
//for debugging purposes:
System.out.println("Email button being clicked!");
LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER))
{
Toast.makeText(this, "GPS is enabled in your device", Toast.LENGTH_SHORT).show();
try {
sendEmail("","","","Sensor Log Info",getEmailBody(),"multipart/mixed");
} catch (Exception e) {
e.printStackTrace();
}
}
else
{
showGPSAlertForEmail();
}
break;
}
}
Basically, I really want to know why my delete-then-download method has not worked every time. Logcat errors have provided no insight. Thank you for your time.

Unable to create link with DBX chooser error android

I am attempting to integrate the Dropbox chooser drop-in api into my application. I am running into an abnormal issue. In my app when I launch the dbx chooser, anytime that I select a file the application fails with the following error code:
Sorry, an error has occurred. Please try again later.
Here is the portion of my code that implements the Dropbox API. This portion of the code is where the dropbox api is initially invoked.
public void StartDropboxApplication() {
// create the chooser
DbxChooser chooser = new DbxChooser(APP_KEY);
DbxChooser.ResultType result;
// determine which mode to be in // TODO REMOVE ALL BUT FILE CONTENT TODO SIMPLIFY by making this a setting
switch(( (RadioGroup) ParentActivity.findViewById(R.id.link_type)).getCheckedRadioButtonId() ) {
case R.id.link_type_content:
result = DbxChooser.ResultType.DIRECT_LINK;
break;
default:
throw new RuntimeException("Radio Group Related error.");
}
// launch the new activity
chooser.forResultType(result).launch(ParentActivity, 0);
}
Here is the position where the code should then pick it up although it never does.
protected void onActivityResult( int request, int result, Intent data ) {
Log.i(fileName, "result: " + result);
// check to see if the camera took a picture
if (request == 1) {
// check to see if the picture was successfully taken
if (result == Activity.RESULT_OK) {
onPicture();
} else {
Log.i(fileName, "Camera App cancelled.");
}
} else if (request == 0) {
if ( result == Activity.RESULT_OK ) {
onDropbox(data);
} else {
Log.i(fileName, "dropbox related issue.");
}
}
}
Thank you for any help or suggestions that you are able to provide.
I was able to solve my own issues and get this working. On the off chance that someone else has a similar problem I will detail the solution. The first issue was I was that my APP_KEY was incorrect.
The next issue was that I was attempting to read from a direct link instead of a content link. The direct link provides the application with a link to the file on the Dropbox server whereas the content link provides the application with a cached version of the file. If the file is not present on the device, the SDK downloads a copy for you.

Simple way to add contact photo to ImageView?

I'm having problems getting and setting a contact's image as a view's background, surprisingly there are few examples on how to do it. I'm trying to build something similar to the People app which displays big contact photos.
This is what I'm doing right now:
Uri uri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, Long.valueOf(id));
InputStream input = ContactsContract.Contacts.openContactPhotoInputStream(context.getContentResolver(), uri);
Bitmap bm = BitmapFactory.decodeStream(input);
Drawable d = new BitmapDrawable(bm);
button.setBackgroundDrawable(drawable);
This works however the URI it uses gets a thumbnail picture, so even if there is a big photo images looks very bad when scaled to fit the imageView. I know another method to get the URI that actually gets a big photo which is:
final Uri imageUri = Uri.parse(cur.getString(cur.getColumnIndex(ContactsContract.Contacts.PHOTO_URI)));
However I haven't managed to get it to the imageView, maybe the code above can be adapted to use the second uri. If you know how to use the second uri or if there is an easier way to get the contact image than through the URI please tell me. Any info will be thanked.
Good job in getting the URI. You're almost there. First of all consider using PHOTO_THUMBNAIL_URI instead of PHOTO_URI, as it may be what you need in terms of size.
Edit : FYI, PHOTO_THUMBNAIL_URI is available starting API 11. You can still use it conditionally.
If you want to use an external library, 'Android Universal Image Loader' is definitely what you are looking for, as starting from it's 1.7.1 version from a few days ago, it added support for content schemes and it is pretty smart, memory wise. It also has a lot of customization options.
Edit: this lib is already dead. Use Fresco instead.
If you'd rather be nicer to your final bundle size and write the code yourself,
You need to get and decode the input stream of that content; This should be done on a background thread. Check out this connivence method; You initialise it with your image view and the uri you got and start it when you want to load the ImageView.
private class ContactThumbnailTask extends AsyncTask<Void, Void, Bitmap> {
private WeakReference<ImageView> imageViewWeakReference;
private Uri uri;
private String path;
private Context context;
public ContactThumbnailTask(ImageView imageView, Uri uri, Context context) {
this.uri = uri;
this.imageViewWeakReference = new WeakReference<ImageView>(imageView);
this.path = (String)imageViewWeakReference.get().getTag(); // to make sure we don't put the wrong image on callback
this.context = context;
}
#Override
protected Bitmap doInBackground(Void... params) {
InputStream is = null;
try {
is = context.getContentResolver().openInputStream(uri);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
Bitmap image = null;
if (null!= is)
image= BitmapFactory.decodeStream(is);
return image;
}
#Override
protected void onPostExecute(Bitmap bitmap) {
if (imageViewWeakReference != null && imageViewWeakReference.get() != null && ((String)imageViewWeakReference.get().getTag()).equals(path) && null != bitmap)
imageViewWeakReference.get().setImageBitmap(bitmap);
}
}
It is suggestion. First know one thing.
When you set a contacts image. First Android show the Cropping activity for that image. Like
****carefully see above image. Android crop the image as square shape. And store the square shape image as a Blob in contacts.(It is not individual image. It is blob.)
You get a square shape image for your image view from your coding. so top and bottom show black color only. Because your mobile in rectangle shape.****
If you want to show full screen image. Please set a big image to contacts through programmatically. Lot of examples available in internet.
All the best for your try. If you have any doubts. Please provide comments.
You can try using SmartImageView: http://loopj.com/android-smart-image-view/ extends imageview and also loads images asynchronously.
Use an external library to do that for you. Or browse the code and make something similar your own way.
Here's one I use on several of my own apps: UrlImageViewHelper
The code would go like this:
Uri uri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, Long.valueOf(id));
UrlImageViewHelper.setUrlDrawable(button, uri.toString(), R.drawable.dummy_contact_photo);
You can easily set the contact photo to the image view by using the following method.
public String getImageUriString(String phoneNumber)
{
ContentResolver resolver = context.getContentResolver();
Cursor names = resolver.query(
Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phoneNumber)),
null, null, null, null);
names.moveToFirst();
String name = "";
if(!names.isAfterLast())
{
name = names.getString(names.getColumnIndex(ContactsContract.PhoneLookup.PHOTO_URI));
}
else
{
name = null;
}
names.close();
return name;
}
public void setImageView(ImageView contactPhoto) {
String photoUriString = di.getImageUriString(contactNumber);
if(photoUriString != null) {
Uri photoUri = Uri.parse(photoUriString);
contactPhoto.setImageURI(photoUri);
} else {
contactPhoto.setImageResource(R.drawable.icon_contact);
}
}
From your class, set the image view with the uri getting from the above method.
To be able to do that you have to just add one last parameter preferHighres = true:
openContactPhotoInputStream (ContentResolver cr, Uri contactUri, boolean preferHighres)
If preferHighres is true and the contact has a higher resolution photo available, it is returned. If false, this function always tries to get the thumbnail
Uri uri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, Long.valueOf(id));
InputStream input = ContactsContract.Contacts.openContactPhotoInputStream(context.getContentResolver(), uri, true);
All the images probably would have differnet sizes. In order to resize them I use next code:
Bitmap bm = BitmapFactory.decodeStream(input);
bm = Bitmap.createScaledBitmap(photo, contactImageWidth, contactImageheight, false);
Drawable d = new BitmapDrawable(getContext(), bm);
button.setBackgroundDrawable(d);

Categories