Share an image with a content provider in Android app
Asked Answered
B

1

12

I'm developing an Android app that is a gallery of images in which the images are downloaded from internet for display on the screen of smathphone. Images are displayed one at a time and the application has a button to share the image that is displayed.

Following the directions I've found in some StackOverflow post which indicated that the right way to share an image was using a ContentProvider I have implemented the following code that works to share the images of certain applications (eg Twitter, Gmail ...) but does not work for others (Facebook, Yahoo, MMS ...).

Then I show the code used hoping you can help me get the correct implementation to share images in all applications.

Initially I capture the button press to share:

@Override
public boolean onOptionsItemSelected(MenuItem item) {

    if (item.getItemId() == R.id.menu_share) {

        // I get the image being displayed on the screen
        View root = getView();
        ImageView imageView = (ImageView) root.findViewById(R.id.image);
        Drawable imageToShareDrawable = imageView.getDrawable();

        if (imageToShareDrawable instanceof BitmapDrawable) {

            // I convert the image to Bitmap
            Bitmap imageToShare = ((BitmapDrawable) imageToShareDrawable).getBitmap();

            // Name of de image extracted from a bean property
            String fileName = quote.getImage(); 

            // I keep the image in the folder "files" of internal storage application
            TempInternalStorage.createCachedFile(fileName, imageToShare, getActivity().getApplicationContext());

            // I start the Activity to select the application to share the image after the intent Built with the method "getDefaultShareIntent"
            startActivity(getDefaultShareIntent(fileName));
        } else {
            Toast.makeText(getActivity().getApplicationContext(), "Please wait, the quote is being downloaded", Toast.LENGTH_SHORT).show();
        }
    } 

    return true;
}

The method for saving the image to the internal storage of the application is as follows:

public static void createCachedFile(String fileName, Bitmap image, Context context) {

    try {
        File file = new File(context.getFilesDir(), fileName);

        if (!file.exists()) {
            FileOutputStream fos = new FileOutputStream(file);
            image.compress(Bitmap.CompressFormat.JPEG, 100, fos);
            fos.flush();
            fos.close();
        } 
    } catch (Exception e) {
        Log.e("saveTempFile()", "**** Error");
    }
}

The method that constructs the Intent to share it:

private Intent getDefaultShareIntent(String fileName) {
    final Intent shareIntent = new Intent();
    shareIntent.setAction(Intent.ACTION_SEND);
    shareIntent.setType("image/jpeg");
    shareIntent.putExtra(Intent.EXTRA_TEXT, "Test text");
    shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://" + CachedFileProvider.AUTHORITY + File.separator + "img" + File.separator + fileName));
    shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

    return shareIntent;
}

Finally ContentProvider code is as follows:

public class CachedFileProvider extends ContentProvider {

private static final String CLASS_NAME = "CachedFileProvider";

public static final String AUTHORITY = "com.example.appname.cachefileprovider";

private UriMatcher uriMatcher;

@Override
public boolean onCreate() {
    uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

    uriMatcher.addURI(AUTHORITY, "img/*", 1);

    return true;
}

@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {

    String LOG_TAG = CLASS_NAME + " - openFile";

    Log.i(LOG_TAG, "Called with uri: '" + uri + "'." + uri.getLastPathSegment());

    switch (uriMatcher.match(uri)) {

    case 1:

        String fileLocation = getContext().getFilesDir() + File.separator + uri.getLastPathSegment();

        ParcelFileDescriptor image = ParcelFileDescriptor.open(new File(fileLocation), ParcelFileDescriptor.MODE_READ_ONLY);

        return image;

    default:
        Log.i(LOG_TAG, "Unsupported uri: '" + uri + "'.");
        throw new FileNotFoundException("Unsupported uri: " + uri.toString());
    }
}

@Override
public int update(Uri uri, ContentValues contentvalues, String s, String[] as) {
    return 0;
}

@Override
public int delete(Uri uri, String s, String[] as) {
    return 0;
}

@Override
public Uri insert(Uri uri, ContentValues contentvalues) {
    return null;
}

@Override
public String getType(Uri uri) {
    return null;
}

@Override
public Cursor query(Uri uri, String[] projection, String s, String[] as1, String s1) {

    MatrixCursor c = null;

    Log.i(">>>> projection", java.util.Arrays.toString(projection));

    String fileLocation = getContext().getFilesDir() + File.separator + uri.getLastPathSegment();

    File file = new File(fileLocation);

    long time = System.currentTimeMillis();

    c = new MatrixCursor(new String[] { "_id", "_data", "orientation", "mime_type", "datetaken", "_display_name" });

    c.addRow(new Object[] { 0,  file, 0, "image/jpeg", time, uri.getLastPathSegment() });

    return c;
}

@Override
public String[] getStreamTypes(Uri uri, String mimeTypeFilter) {
    return null;
}

}

I have found that when the image is sharing some applications only call the method "query" (these are where the code does not work and I can not share the image) while there are others that also call the method "query" also call the method "openFile" and these do work and I can share the image.

I hope you can help me, thank you very much in advance.

Blameworthy answered 13/6, 2013 at 8:20 Comment(7)
Did you found a solution? By the way this link could be interesting for you: github.com/xperimental/BinaryContentPaulpaula
have u successfully shared your image can u please help me how can i share image from drawable folder ?Bijou
@Blameworthy can u please share what was the mistake in your code ?Bijou
I faced the same problem. Simply return null in query().Outburst
I also face this problem. Did you found a solution for this?Thagard
As some apps can read data from Content URI while some other not, is this an issue of Facebook, Yahoo or so? Since recent update, twitter no longer reads image from content uri, but G+ and instagram still works.Hagar
does your problem solved .. ?Indigent
S
1

As @Sun Ning-s comment noted some "share target apps" can handle URI-s starting with "content://.." which you have implemented.

Other apps handle file uri-s starting with "file://...." so you have to implement a 2nd share menue "share as file"

private Intent getFileShareIntent(String fullPathTofile) {
    final Intent shareIntent = new Intent();
    shareIntent.setAction(Intent.ACTION_SEND);
    shareIntent.setType("image/jpeg");
    shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + fullPathTofile));
    shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

    return shareIntent;
}

You can use the android app intentintercept to find out what other "share source apps" provide.

Seal answered 4/1, 2016 at 15:18 Comment(1)
Update 2024: I don't see way to create a fileProvider with prefix "file://" anymore that works (tried to use "file://" + file.getAbsolutePath() + "/" + fileName led to error "java.exception.expose..." . I used fileProvider that returns URI started with "content://" but it worked when I save the file to external folder using File myFile = new File(getExternalFilesDir("MyExternalFiles"), fileName);. It failed to share when I replaced getExternalFilesDir by getFilesDir()Kight

© 2022 - 2024 — McMap. All rights reserved.