问题
I\'m trying to share an image from my assets folder. My code is:
Intent share = new Intent(Intent.ACTION_SEND);
share.setType(\"image/jpg\");
share.putExtra(Intent.EXTRA_STREAM, Uri.parse(\"file:///assets/myImage.jpg\"));
startActivity(Intent.createChooser(share, \"Share This Image\"));
but it doesn\'t work. Do you have any ideas?
回答1:
It is possible to share files (images including) from the assets folder through a custom ContentProvider
You need to extend ContentProvider
, register it in your manifest and implement the openAssetFile
method. You can then assess the assets via Uri
s
@Override
public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
AssetManager am = getContext().getAssets();
String file_name = uri.getLastPathSegment();
if(file_name == null)
throw new FileNotFoundException();
AssetFileDescriptor afd = null;
try {
afd = am.openFd(file_name);
} catch (IOException e) {
e.printStackTrace();
}
return afd;
}
回答2:
This blog explains it all:
http://nowherenearithaca.blogspot.co.uk/2012/03/too-easy-using-contentprovider-to-send.html
Basically, this goes in the manifest:
<provider android:name="yourclass.that.extendsContentProvider" android:authorities="com.yourdomain.whatever"/>
The content provider class has this:
@Override
public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
AssetManager am = getContext().getAssets();
String file_name = uri.getLastPathSegment();
if(file_name == null)
throw new FileNotFoundException();
AssetFileDescriptor afd = null;
try {
afd = am.openFd(file_name);
} catch (IOException e) {
e.printStackTrace();
}
return afd;//super.openAssetFile(uri, mode);
}
And the calling code does this:
Uri theUri = Uri.parse("content://com.yourdomain.whatever/someFileInAssetsFolder");
Intent theIntent = new Intent(Intent.ACTION_SEND);
theIntent.setType("image/*");
theIntent.putExtra(Intent.EXTRA_STREAM,theUri);
theIntent.putExtra(android.content.Intent.EXTRA_SUBJECT,"Subject for message");
theIntent.putExtra(android.content.Intent.EXTRA_TEXT, "Body for message");
startActivity(theIntent);
回答3:
Complementing what @intrepidis answered:
You will need override methods like example class above:
package com.android.example;
import android.content.ContentProvider;
import android.net.Uri;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import java.io.FileNotFoundException;
import android.content.ContentValues;
import android.database.Cursor;
import java.io.IOException;
import android.os.CancellationSignal;
public class AssetsProvider extends ContentProvider
{
@Override
public AssetFileDescriptor openAssetFile( Uri uri, String mode ) throws FileNotFoundException
{
Log.v( TAG, "AssetsGetter: Open asset file" );
AssetManager am = getContext( ).getAssets( );
String file_name = uri.getLastPathSegment( );
if( file_name == null )
throw new FileNotFoundException( );
AssetFileDescriptor afd = null;
try
{
afd = am.openFd( file_name );
}
catch(IOException e)
{
e.printStackTrace( );
}
return afd;//super.openAssetFile(uri, mode);
}
@Override
public String getType( Uri p1 )
{
// TODO: Implement this method
return null;
}
@Override
public int delete( Uri p1, String p2, String[] p3 )
{
// TODO: Implement this method
return 0;
}
@Override
public Cursor query( Uri p1, String[] p2, String p3, String[] p4, String p5 )
{
// TODO: Implement this method
return null;
}
@Override
public Cursor query( Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder, CancellationSignal cancellationSignal )
{
// TODO: Implement this method
return super.query( uri, projection, selection, selectionArgs, sortOrder, cancellationSignal );
}
@Override
public Uri insert( Uri p1, ContentValues p2 )
{
// TODO: Implement this method
return null;
}
@Override
public boolean onCreate( )
{
// TODO: Implement this method
return false;
}
@Override
public int update( Uri p1, ContentValues p2, String p3, String[] p4 )
{
// TODO: Implement this method
return 0;
}
}
I needed to override two times the query method. And add these lines above tag in your androidmanifest.xml:
<provider
android:name="com.android.example.AssetsProvider"
android:authorities="com.android.example"
android:grantUriPermissions="true"
android:exported="true" />
And with this, all work like a charm :D
回答4:
Many apps require you to provide name and size of the image. So here is an improved code (using Google's FileProvider code as an example):
public class AssetsProvider extends ContentProvider {
private final static String LOG_TAG = AssetsProvider.class.getName();
private static final String[] COLUMNS = {
OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE };
@Override
public boolean onCreate() {
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
/**
* Source: {@link FileProvider#query(Uri, String[], String, String[], String)} .
*/
if (projection == null) {
projection = COLUMNS;
}
final AssetManager am = getContext().getAssets();
final String path = getRelativePath(uri);
long fileSize = 0;
try {
final AssetFileDescriptor afd = am.openFd(path);
fileSize = afd.getLength();
afd.close();
} catch(IOException e) {
Log.e(LOG_TAG, "Can't open asset file", e);
}
final String[] cols = new String[projection.length];
final Object[] values = new Object[projection.length];
int i = 0;
for (String col : projection) {
if (OpenableColumns.DISPLAY_NAME.equals(col)) {
cols[i] = OpenableColumns.DISPLAY_NAME;
values[i++] = uri.getLastPathSegment();
} else if (OpenableColumns.SIZE.equals(col)) {
cols[i] = OpenableColumns.SIZE;
values[i++] = fileSize;
}
}
final MatrixCursor cursor = new MatrixCursor(cols, 1);
cursor.addRow(values);
return cursor;
}
@Override
public String getType(Uri uri) {
/**
* Source: {@link FileProvider#getType(Uri)} .
*/
final String file_name = uri.getLastPathSegment();
final int lastDot = file_name.lastIndexOf('.');
if (lastDot >= 0) {
final String extension = file_name.substring(lastDot + 1);
final String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
if (mime != null) {
return mime;
}
}
return "application/octet-stream";
}
@Override
public Uri insert(Uri uri, ContentValues values) {
return null;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
return 0;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return 0;
}
@Override
public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
final AssetManager am = getContext().getAssets();
final String path = getRelativePath(uri);
if(path == null) {
throw new FileNotFoundException();
}
AssetFileDescriptor afd = null;
try {
afd = am.openFd(path);
} catch(IOException e) {
Log.e(LOG_TAG, "Can't open asset file", e);
}
return afd;
}
private String getRelativePath(Uri uri) {
String path = uri.getPath();
if (path.charAt(0) == '/') {
path = path.substring(1);
}
return path;
}
}
回答5:
AFAIK, there's no way to share an image from the assets
folder. But it's possible to share resources from the res
folder.
回答6:
To share from assets folder I can only recommend the cwac-provider library (StreamProvider).
Among avoiding to develop your own content provider, it adds some support for capricious legacy apps (check USE_LEGACY_CURSOR_WRAPPER
).
来源:https://stackoverflow.com/questions/5888718/android-share-images-from-assets-folder