3

Background

Similar to issues I've recently had with sharing/opening APK files (here, here and here) , I now have an issue of sending an image file (in assets, res/raw or even from a URL) to specific apps: WhatsApp and Viber.

I need to be able to share an image file to all apps, especially popular ones such as WhatsApp and Viber.

The problem

Both WhatsApp and Viber have issues when I try to share the image files on Andorid 7.1.1 . On other apps, and on previous versions of Android, it worked fine.

They either show a black screen (no image) or close themselves, on all of the test I've tried.

What I tried and found

1.I started sharing a file from the assets folder of the app, using a library called "cwac-provider". It worked fine with all apps, except for WhatsApp and Viber.

On WhatsApp , I got this log (which is very similar to what I got for Viber) :

02-06 17:05:04.379 24590-24590/com.whatsapp W/Bundle: Key android.intent.extra.STREAM expected ArrayList but value was a android.net.Uri$HierarchicalUri. The default value was returned. 02-06 17:05:04.382 24590-24590/com.whatsapp W/Bundle: Attempt to cast generated internal exception: java.lang.ClassCastException: android.net.Uri$HierarchicalUri cannot be cast to java.util.ArrayList at android.os.Bundle.getParcelableArrayList(Bundle.java:916) at android.content.Intent.getParcelableArrayListExtra(Intent.java:6357) at com.whatsapp.ContactPicker.k(ContactPicker.java:618) at com.whatsapp.ContactPicker.onCreate(ContactPicker.java:360) at android.app.Activity.performCreate(Activity.java:6688) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2633) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2741) at android.app.ActivityThread.-wrap12(ActivityThread.java) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1488) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:154) at android.app.ActivityThread.main(ActivityThread.java:6169) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:888) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:778)

2.I was told (here) to try to share by putting an ArrayList<Uri> into the EXTRA_STREAM:

    ArrayList<Uri> uriArrayList=new ArrayList<>();
    uriArrayList.add(getUri());
    share.putExtra(Intent.EXTRA_STREAM, uriArrayList);

It didn't work, and the log of WhatsApp shows:

                                                    Caused by: java.lang.SecurityException: Permission Denial: opening provider

com.commonsware.cwac.provider.StreamProvider from ProcessRecord{9405e93 12914:com.whatsapp/u0a210} (pid=12914, uid=10210) that is not exported from uid 10123 at android.os.Parcel.readException(Parcel.java:1684) at android.os.Parcel.readException(Parcel.java:1637) at android.app.ActivityManagerProxy.getContentProvider(ActivityManagerNative.java:4213) at android.app.ActivityThread.acquireProvider(ActivityThread.java:5526) at android.app.ContextImpl$ApplicationContentResolver.acquireUnstableProvider(ContextImpl.java:2239) at android.content.ContentResolver.acquireUnstableProvider(ContentResolver.java:1517) at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:1131) at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:984) at android.content.ContentResolver.openInputStream(ContentResolver.java:704) at com.whatsapp.util.ah.b(MediaFileUtils.java:1290) at com.whatsapp.util.ah.a(MediaFileUtils.java:1498) at com.whatsapp.util.ah.a(MediaFileUtils.java:1543) at com.whatsapp.gallerypicker.ImagePreview$b$1.a(ImagePreview.java:901) at com.whatsapp.gallerypicker.ImagePreview$b$1.doInBackground(ImagePreview.java:896) at android.os.AsyncTask$2.call(AsyncTask.java:305) at java.util.concurrent.FutureTask.run(FutureTask.java:237) at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:243) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)

                                                          at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)

                                                          at java.lang.Thread.run(Thread.java:761)

3.I also tried to change the action of the intent to ACTION_SEND_MULTIPLE:

    Intent share = new Intent(Intent.ACTION_SEND_MULTIPLE);
    ...
    ArrayList<Uri> uriArrayList=new ArrayList<>();
    uriArrayList.add(getUri());
    share.putExtra(Intent.EXTRA_STREAM, uriArrayList);

but it also didn't help, showing this log for Viber (can't see anything special for WhatsApp) :

02-07 09:54:07.084 926-10718/system_process W/ActivityManager: Permission Denial: opening provider com.commonsware.cwac.provider.StreamProvider from ProcessRecord{adbb1ed 5565:com.viber.voip/u0a175} (pid=5565, uid=10175) that is not exported from uid 10123 02-07 09:54:07.087 926-10717/system_process W/ActivityManager: Permission Denial: opening provider com.commonsware.cwac.provider.StreamProvider from ProcessRecord{adbb1ed 5565:com.viber.voip/u0a175} (pid=5565, uid=10175) that is not exported from uid 10123 02-07 09:54:07.091 926-946/system_process W/ActivityManager: Permission Denial: opening provider com.commonsware.cwac.provider.StreamProvider from ProcessRecord{adbb1ed 5565:com.viber.voip/u0a175} (pid=5565, uid=10175) that is not exported from uid 10123

4.The weird thing is that for WhatsApp, on all of the above tries, it asked fro storage permission, even though it shouldn't (because the app provides the content by itself anyway).

5.Another weird thing I've found, is that Google Photos app works fine with sharing images to those apps, even if the image is from the server. It downloads the file somewhere and shares it. I can't see where it downloads the file, though. I thought it would be on the app's external storage path ("/.../Android/data/com.google.android.apps.photos/...") , but it's not there.

6.I tried to make a POC of sharing a file from the external storage by using the FileProvider of the support library (as I've known how to use from sharing an APK file):

manifest

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="${applicationId}.provider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/provider_paths"/>
</provider>

res/xml/provider_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path name="external_files" path="."/>
</paths>

code:

    final File bitmapFile = new File(getExternalFilesDir(null), "test.jpg");
    if (!bitmapFile.exists()) {
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), android.R.drawable.sym_def_app_icon);
        bitmap.compress(CompressFormat.JPEG, 100, new FileOutputStream(bitmapFile));
    }
    Intent intent = new Intent(Intent.ACTION_SEND);
    Uri fileUri = FileProvider.getUriForFile(this, getApplicationContext().getPackageName() + ".provider", bitmapFile);
    intent.setType(MimeTypeMap.getSingleton().getMimeTypeFromExtension("jpg"));
    intent.putExtra(Intent.EXTRA_STREAM, fileUri);
    intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    startActivity(intent);

but it worked only for WhatsApp, and not Viber, which showed a log

02-07 10:21:19.285 24043-24043/com.viber.voip W/Bundle: Key android.intent.extra.STREAM expected ArrayList but value was a android.net.Uri$HierarchicalUri. The default value was returned. 02-07 10:21:19.285 24043-24043/com.viber.voip W/Bundle: Attempt to cast generated internal exception: java.lang.ClassCastException: android.net.Uri$HierarchicalUri cannot be cast to java.util.ArrayList at android.os.Bundle.getParcelableArrayList(Bundle.java:916) at android.content.Intent.getParcelableArrayListExtra(Intent.java:6357) at com.viber.voip.util.af.f(SourceFile:156) at com.viber.voip.util.af.a(SourceFile:106) at com.viber.voip.HomeActivity.i(SourceFile:487) at com.viber.voip.HomeActivity.onCreate(SourceFile:317) at android.app.Activity.performCreate(Activity.java:6688) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2633) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2741) at android.app.ActivityThread.-wrap12(ActivityThread.java) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1488) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:154) at android.app.ActivityThread.main(ActivityThread.java:6169) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:888) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:778)

I tried to perform what I did on #2 and #3 , but it still didn't work on Viber.

7.I tried sharing a file from external storage using the old way of doing it, which was supposed to be deprecated and replaced by FileProvider :

startActivityForResult(Intent.createChooser(prepareIntentToSharePhoto(bitmapFile.getAbsolutePath(), "title",
                "body"), "choose"), 1);

public static Intent prepareIntentToSharePhoto(String imagePath, String title, String body) {
    Intent sharingIntent = new Intent(Intent.ACTION_SEND).setType("image/*")
            .putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + imagePath)).putExtra(android.content.Intent.EXTRA_SUBJECT, title)
            .putExtra(android.content.Intent.EXTRA_TEXT, body);
    return sharingIntent;
}

It works for both apps, but it works only if they both are granted with storage permission. For Viber, if it doesn't have storage permission, it shows a black image, and for WhatsApp, it asks the user to grant it.

The question

Why don't any of the above work?

How should I really share an image file to those apps correctly? What's wrong even with sharing via the FileProvider ? How come Google Photos app work well ?

Is there a workaround for this?

Is it an issue on the apps themselves, or on Android ?

Community
  • 1
  • 1
android developer
  • 114,585
  • 152
  • 739
  • 1,270
  • Take a look at this: https://www.whatsapp.com/faq/en/android/28000012 – A P Feb 07 '17 at 08:34
  • Are you using external storage, and did you ask the permission to use it? – A P Feb 07 '17 at 08:35
  • @AviParshan The link you've provided is only for sending text. I asked about sending an image file. About storage permission, starting from specific Android version (API 19 - Kitkat), you don't need storage permission to write files into your own app's dedicated external storage (read here: https://developer.android.com/reference/android/Manifest.permission.html#READ_EXTERNAL_STORAGE ). The app can write and read from this file perfectly fine, and I can see it via a file manager, that it succeeded doing so. I tried to add&grant this permission just in case. It didn't help. – android developer Feb 07 '17 at 08:44
  • I was able to do it pretty easily, for a meme maker app i built. I'll post the code in an answer later today. It basically sends a share intent, passes the image, and some text (optional) with it. I was able to post it to whatsapp. It's odd how it works with photos, but not other apps. Can you share the image of the share intent in action? – A P Feb 07 '17 at 08:46
  • @AviParshan Look at the code. The sample app creates the image dynamically. I've written a working code too on #7 (which I think is what you probably used), but it requires both WhatsApp and Viber to get storage permission, while originally they don't need it if only they used the standard way of sharing content . – android developer Feb 07 '17 at 09:02
  • @androiddeveloper Did you tried sharing directly to whatsup.. `sharingIntent.setPackage("com.whatsapp");` ? – Sunil Sunny Feb 07 '17 at 09:08
  • @sunilsunny Yes. I did. It doesn't help. – android developer Feb 07 '17 at 09:11
  • Thanks for doing so much research. I'll keep you updated if I find anything. – sbaar Mar 22 '17 at 05:40

1 Answers1

1

For now I will use solution #7, but it's not perfect, because it requires both Viber&WhatsApp apps to grant storage permission (to themselves) before being able to access the files.

Sadly, I think it requires this permission on all of the apps on the chooser.

If Viber doesn't have this permission granted yet, it shows a black screen.

android developer
  • 114,585
  • 152
  • 739
  • 1,270