androidandroid-intentmmscommonsware-cwac

Send image in response to android.intent.action.GET_CONTENT


I am willing to create an app that sends pictures in reply to the android.intent.action.GET_CONTENT intent. My use case is the Messenger app from the play store, the most common SMS/MMS app, I guess.

I tried to send the picture, but it didn't work well. When sending the MMS to Android phones, they get it properly, however iPhones seem to display it as a fake video that never plays.
I know that it may be caused by my or the foreign operator MMSC server, that thinks it's smart and transcodes the data to what it guesses is a good format.

However, when using the same intent to another app (tried Google's Photos app, and Solid Explorer), it works well with both Android and iPhones.

My guess is that Photos and Solid Explorers send the data back in a proper format, that the MMS apps sends to the MMSC properly, which delivers the picture as-is.

Here's what I tried:

  1. Send a simple Uri of my file in the cache (through Content#getExternalCacheDir(): not working
  2. Send an Uri of my file using a StreamProvider, using CommonWare's CWAC lib, by setting a LocalPathStrategy with Context#getExternalCacheDir() as the root path: not working

Both strategies end up with the image sent back to the MMS app properly, which displays it and the button becomes "Send MMS"; then on Android it's received as a picture, and on iOS it's a fake video that doesn't work.

How should I send the data back to the calling app?

Just to actually explain what I did, here is the first strategy:

Intent result = new Intent();
result.setData(Uri.fromFile(localImage));
setResult(Activity.RESULT_OK, result);
finish();

Here's the second:

Intent result = new Intent();
result.setData(PROVIDER
        .buildUpon()
        .appendPath(StreamProvider.getUriPrefix(AUTHORITY))
        .appendPath(localImage.getName())
        .build());
result.setFlags(FLAG_GRANT_READ_URI_PERMISSION);
setResult(Activity.RESULT_OK, result);
finish();

I think I can pass the bitmap bytes as data in the intent, but I didn't figure out a way to do this.


Solution

  • Yes, it looks like setResult() also needs an Intent with FLAG_GRANT_READ_URI_PERMISSION and/or FLAG_GRANT_WRITE_URI_PERMISSION, if you are using a ContentProvider for the result Uri.

    addFlags() works to add these flags to the Intent, at least back to API Level 19. I have not tested older than this, so there may be versions where you have to use the ClipData trick:

    if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.KITKAT) {
      i.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
    }
    else if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.JELLY_BEAN) {
      ClipData clip=
        ClipData.newUri(getContentResolver(), "A photo", outputUri);
    
      i.setClipData(clip);
      i.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
    }
    else {
      List<ResolveInfo> resInfoList=
        getPackageManager()
          .queryIntentActivities(i, PackageManager.MATCH_DEFAULT_ONLY);
    
      for (ResolveInfo resolveInfo : resInfoList) {
        String packageName = resolveInfo.activityInfo.packageName;
        grantUriPermission(packageName, outputUri,
          Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
      }
    }