androidandroid-intentwhatsappcommonsware-cwacviber

How to share an image file to WhatsApp and Viber on Android 7.1.1?


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 ?


Solution

  • 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.