Android 仅共享意向选择器电子邮件客户端
Android Share Intent Chooser EMail Client Only
我正在尝试创建一个选项,让用户仅通过电子邮件从我的应用程序发送文件。该文件在应用程序内部,可通过 FileProvider 访问。
这是 contentURI 看起来像 content://packagename.files/files/somefile.ext
如您所见,我让用户将文件共享到 PicsArt、Google Drive、OneDrive 和 EMail。
我能够成功地将内容分享给前三个客户,因为他们的应用程序非常具体。但是当涉及到电子邮件时,我需要用户从他手机上安装的应用程序中选择客户端。
这是我创建的 2 组代码:
代码选项 1:
Intent EMail = ShareCompat.IntentBuilder.from(this)
.setType("message/rfc822")
.setSubject("Emailing: File Attached")
.setText("Hello")
.setStream(contentUri)
.setChooserTitle("Send via EMail").getIntent();
startActivity(Intent.createChooser(EMail, "Send via EMail"));
上面的代码向我展示了一个选择器,其中有许多应用程序可以处理下图所示的文件。
如果我 select 任何电子邮件客户端应用程序或任何其他应用程序,这个都可以正常工作。
但问题是用户可以选择 select 任何应用程序,这不是应用程序所期望的行为。所以,我修改了如下代码:
final Intent _Intent = new Intent(Intent.ACTION_SENDTO);
_Intent.setType("text/html");
_Intent.setData(Uri.parse("mailto:"));
_Intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
_Intent.putExtra(Intent.EXTRA_STREAM, contentUri);
_Intent.putExtra(android.content.Intent.EXTRA_SUBJECT,
"Emailing: File Attached");
_Intent.putExtra(android.content.Intent.EXTRA_TEXT,
"Hello");
startActivity(Intent.createChooser(_Intent, "Send via EMail"));
这里是代码的结果:
但是,现在的问题是我无法从内容提供商 (FileProvider) 发送文件。电子邮件客户端在 selecting:
后显示如下消息
它只是没有将文件附加到上面列表中任何客户端的电子邮件中。
如果有人能帮助我,我将不胜感激。我认为,我已经尝试了这里所有可能的场景,通过更改 mime 类型、以不同方式设置内容、设置数据设置流等,但无法获得预期的结果。
如果您需要任何其他详细信息,请告诉我。
再次感谢。
您需要编写 ContentProvider,它将向您已将 ContentUri 传递给的客户端提供 InputStream,或者您可以直接提供文件路径(如果它存在于 SdCard 或内部存储中),因为您将需要处理 uri 并传递 InputStream 。注意:ExtraStream 最适合不在要从互联网访问的设备中的文件。
public class SampleContentProvider extends ContentProvider implements ContentProvider.PipeDataWriter<InputStream> {
static final UriMatcher uriMatcher;
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//Uri matcher for different
}
/**
* Database specific constant declarations
*/
private SQLiteDatabase db;
@Override
public boolean onCreate() {
return true;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
throw new SQLException("Insert operation not supported for " + uri);
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
//condition just for files. You can try something else
if (uri.toString().contains("files")) {
//you get the file name
String lastSegment = uri.getLastPathSegment();
if (projection == null) {
projection = new String[]{OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE};
}
File file = //Code to read the file as u have the directory, just get the file from the file name obtained from the uri
if (null == file) {
throw new IllegalArgumentException("Unknown File for Uri " + uri);
}
String[] cols = new String[projection.length];
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++] = //file name;
} else if (OpenableColumns.SIZE.equals(col)) {
cols[i] = OpenableColumns.SIZE;
values[i++] = //file size;
}
}
cols = copyOf(cols, i);
values = copyOf(values, i);
final MatrixCursor cursor = new MatrixCursor(cols, 1);
cursor.addRow(values);
return cursor;
}
return super.query(uri, projection, selection, selectionArgs, sortOrder);
}
@Override
public String getType(Uri uri) {
return null;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
return super.delete(uri, selection, selectionArgs);
}
private static String[] copyOf(String[] original, int newLength) {
final String[] result = new String[newLength];
System.arraycopy(original, 0, result, 0, newLength);
return result;
}
private static Object[] copyOf(Object[] original, int newLength) {
final Object[] result = new Object[newLength];
System.arraycopy(original, 0, result, 0, newLength);
return result;
}
@Nullable
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
File file = //read the file
if (file != null) {
try {
StrictMode.ThreadPolicy tp = StrictMode.ThreadPolicy.LAX;
StrictMode.setThreadPolicy(tp);
InputStream in = //Code to get the inputstream;
// Start a new thread that pipes the stream data back to the caller.
return openPipeHelper(uri, null, null, in, this);
} catch (IOException e) {
FileNotFoundException fnf = new FileNotFoundException("Unable to open " + uri);
throw fnf;
}
}
throw new IllegalArgumentException("Unknown URI " + uri);
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return super.update(uri, values, selection, selectionArgs);
}
@Override
public void writeDataToPipe(ParcelFileDescriptor output, Uri uri, String mimeType,
Bundle opts, InputStream args) {
// Transfer data from the asset to the pipe the client is reading.
byte[] buffer = new byte[8192];
int n;
FileOutputStream fout = new FileOutputStream(output.getFileDescriptor());
try {
while ((n = args.read(buffer)) >= 0) {
fout.write(buffer, 0, n);
}
} catch (IOException e) {
} finally {
try {
args.close();
} catch (IOException e) {
}
try {
fout.close();
} catch (IOException e) {
}
}
}
}
我决定将文件从内部应用存储复制到外部应用存储(不是外部 public 存储)并从那里共享文件。我有点惊讶,因为 FileProvider 能够与系统中的任何东西共享内部文件存储中的文件,但是当我想过滤仅作为电子邮件客户端的 Intent 时却无法做到这一点。
我很难在初学者级别实现自定义提供程序。
试试这个片段。
Intent testIntent = new Intent(Intent.ACTION_VIEW);
Uri data = Uri.parse("mailto:?subject=" + "Feedback" + "&body=" + "Write Feedback here....." + "&to=" + "someone@example.com");
testIntent.setData(data);
startActivity(testIntent);
我正在尝试创建一个选项,让用户仅通过电子邮件从我的应用程序发送文件。该文件在应用程序内部,可通过 FileProvider 访问。
这是 contentURI 看起来像 content://packagename.files/files/somefile.ext
如您所见,我让用户将文件共享到 PicsArt、Google Drive、OneDrive 和 EMail。
我能够成功地将内容分享给前三个客户,因为他们的应用程序非常具体。但是当涉及到电子邮件时,我需要用户从他手机上安装的应用程序中选择客户端。
这是我创建的 2 组代码:
代码选项 1:
Intent EMail = ShareCompat.IntentBuilder.from(this)
.setType("message/rfc822")
.setSubject("Emailing: File Attached")
.setText("Hello")
.setStream(contentUri)
.setChooserTitle("Send via EMail").getIntent();
startActivity(Intent.createChooser(EMail, "Send via EMail"));
上面的代码向我展示了一个选择器,其中有许多应用程序可以处理下图所示的文件。
如果我 select 任何电子邮件客户端应用程序或任何其他应用程序,这个都可以正常工作。
但问题是用户可以选择 select 任何应用程序,这不是应用程序所期望的行为。所以,我修改了如下代码:
final Intent _Intent = new Intent(Intent.ACTION_SENDTO);
_Intent.setType("text/html");
_Intent.setData(Uri.parse("mailto:"));
_Intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
_Intent.putExtra(Intent.EXTRA_STREAM, contentUri);
_Intent.putExtra(android.content.Intent.EXTRA_SUBJECT,
"Emailing: File Attached");
_Intent.putExtra(android.content.Intent.EXTRA_TEXT,
"Hello");
startActivity(Intent.createChooser(_Intent, "Send via EMail"));
这里是代码的结果:
但是,现在的问题是我无法从内容提供商 (FileProvider) 发送文件。电子邮件客户端在 selecting:
后显示如下消息它只是没有将文件附加到上面列表中任何客户端的电子邮件中。
如果有人能帮助我,我将不胜感激。我认为,我已经尝试了这里所有可能的场景,通过更改 mime 类型、以不同方式设置内容、设置数据设置流等,但无法获得预期的结果。
如果您需要任何其他详细信息,请告诉我。
再次感谢。
您需要编写 ContentProvider,它将向您已将 ContentUri 传递给的客户端提供 InputStream,或者您可以直接提供文件路径(如果它存在于 SdCard 或内部存储中),因为您将需要处理 uri 并传递 InputStream 。注意:ExtraStream 最适合不在要从互联网访问的设备中的文件。
public class SampleContentProvider extends ContentProvider implements ContentProvider.PipeDataWriter<InputStream> {
static final UriMatcher uriMatcher;
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//Uri matcher for different
}
/**
* Database specific constant declarations
*/
private SQLiteDatabase db;
@Override
public boolean onCreate() {
return true;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
throw new SQLException("Insert operation not supported for " + uri);
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
//condition just for files. You can try something else
if (uri.toString().contains("files")) {
//you get the file name
String lastSegment = uri.getLastPathSegment();
if (projection == null) {
projection = new String[]{OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE};
}
File file = //Code to read the file as u have the directory, just get the file from the file name obtained from the uri
if (null == file) {
throw new IllegalArgumentException("Unknown File for Uri " + uri);
}
String[] cols = new String[projection.length];
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++] = //file name;
} else if (OpenableColumns.SIZE.equals(col)) {
cols[i] = OpenableColumns.SIZE;
values[i++] = //file size;
}
}
cols = copyOf(cols, i);
values = copyOf(values, i);
final MatrixCursor cursor = new MatrixCursor(cols, 1);
cursor.addRow(values);
return cursor;
}
return super.query(uri, projection, selection, selectionArgs, sortOrder);
}
@Override
public String getType(Uri uri) {
return null;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
return super.delete(uri, selection, selectionArgs);
}
private static String[] copyOf(String[] original, int newLength) {
final String[] result = new String[newLength];
System.arraycopy(original, 0, result, 0, newLength);
return result;
}
private static Object[] copyOf(Object[] original, int newLength) {
final Object[] result = new Object[newLength];
System.arraycopy(original, 0, result, 0, newLength);
return result;
}
@Nullable
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
File file = //read the file
if (file != null) {
try {
StrictMode.ThreadPolicy tp = StrictMode.ThreadPolicy.LAX;
StrictMode.setThreadPolicy(tp);
InputStream in = //Code to get the inputstream;
// Start a new thread that pipes the stream data back to the caller.
return openPipeHelper(uri, null, null, in, this);
} catch (IOException e) {
FileNotFoundException fnf = new FileNotFoundException("Unable to open " + uri);
throw fnf;
}
}
throw new IllegalArgumentException("Unknown URI " + uri);
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return super.update(uri, values, selection, selectionArgs);
}
@Override
public void writeDataToPipe(ParcelFileDescriptor output, Uri uri, String mimeType,
Bundle opts, InputStream args) {
// Transfer data from the asset to the pipe the client is reading.
byte[] buffer = new byte[8192];
int n;
FileOutputStream fout = new FileOutputStream(output.getFileDescriptor());
try {
while ((n = args.read(buffer)) >= 0) {
fout.write(buffer, 0, n);
}
} catch (IOException e) {
} finally {
try {
args.close();
} catch (IOException e) {
}
try {
fout.close();
} catch (IOException e) {
}
}
}
}
我决定将文件从内部应用存储复制到外部应用存储(不是外部 public 存储)并从那里共享文件。我有点惊讶,因为 FileProvider 能够与系统中的任何东西共享内部文件存储中的文件,但是当我想过滤仅作为电子邮件客户端的 Intent 时却无法做到这一点。
我很难在初学者级别实现自定义提供程序。
试试这个片段。
Intent testIntent = new Intent(Intent.ACTION_VIEW);
Uri data = Uri.parse("mailto:?subject=" + "Feedback" + "&body=" + "Write Feedback here....." + "&to=" + "someone@example.com");
testIntent.setData(data);
startActivity(testIntent);