我曾经使用以下代码将我的应用程序中的照片发送到Instagram ...
this.Publish(new MaskMessageEvent("Copy ready for Instagram App", true));
var bytes = e.Result; // get the downloaded data
/// You must first save your file in PNG or JPEG (preferred) format and use the filename extension ".ig"
/// Using the iOS Document Interaction APIs you can trigger the photo to be opened by Instagram.
/// The Identifier for our Document Interaction UTI is com.instagram.photo,
/// and it conforms to the public/jpeg and public/png UTIs.
/// Alternatively, if you want to show only Instagram in the application list
/// (instead of Instagram plus any other public/jpeg-conforming apps) you can specify the
/// extension class igo, which is of type com.instagram.exclusivegram.
//Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
string localFilename = "temp.jpg"; // <-- NOTE IGO to exclusively show instagram
string localPath = Path.Combine(ExternalStorageDirectory, localFilename);
File.WriteAllBytes(localPath, bytes); // writes to local storage
this.Publish(new MaskMessageEvent("Copy ready for Instagram App", false));
/// See the Apple documentation articles: Previewing and Opening Files and the
/// UIDocumentInteractionController Class Reference for more information.
Xamarin.Forms.Device.BeginInvokeOnMainThread(() =>
{
this.Publish(new MaskMessageEvent("Launching Instagram App", true));
Intent shareIntent = new Intent(Intent.ActionSend);
shareIntent.SetType("image/*");
Java.IO.File media = new Java.IO.File(ExternalStorageDirectory + Java.IO.File.Separator + localFilename);
Android.Net.Uri uri = Android.Net.Uri.FromFile(media);
shareIntent.PutExtra(Intent.ExtraStream, uri); // set uri
shareIntent.SetPackage("com.instagram.android");
Forms.Context.StartActivity(shareIntent);
this.Publish(new MaskMessageEvent("Launching Instagram App", false));
this.Publish(new InstagramServiceEvent(message, true));
});
但是现在如果我定位Android 7+,我会收到以下错误
[MonoDroid] UNHANDLED EXCEPTION:
[MonoDroid] Android.OS.FileUriExposedException: file:///storage/emulated/0/temp.jpg exposed beyond app through ClipData.Item.getUri()
[MonoDroid] at Java.Interop.JniEnvironment+InstanceMethods.CallNonvirtualVoidMethod (Java.Interop.JniObjectReference instance, Java.Interop.JniObjectReference type, Java.Interop.JniMethodInfo method, Java.Interop.JniArgumentValue* args) [0x00089] in <405ad2ab226e4e74ba67db96baf95129>:0
[MonoDroid] at Java.Interop.JniPeerMembers+JniInstanceMethods.InvokeVirtualVoidMethod (System.String encodedMember, Java.Interop.IJavaPeerable self, Java.Interop.JniArgumentValue* parameters) [0x0005d] in <405ad2ab226e4e74ba67db96baf95129>:0
[MonoDroid] at Android.Content.ContextWrapper.StartActivity (Android.Content.Intent intent) [0x00031] in <476f28c60f03479ab89477af687cdc1a>:0
[MonoDroid] at nativeapp.Droid.InstagramService+<>c__DisplayClass6_1.<SendToInstagram>b__1 () [0x0007a] in /Users/rob/Documents/GitHub/social-scheduler/nativeapp/Droid/Services/InstagramService.cs:122
[MonoDroid] at Java.Lang.Thread+RunnableImplementor.Run () [0x00008] in <476f28c60f03479ab89477af687cdc1a>:0
[MonoDroid] at Java.Lang.IRunnableInvoker.n_Run (System.IntPtr jnienv, System.IntPtr native__this) [0x00009] in <476f28c60f03479ab89477af687cdc1a>:0
[MonoDroid] at (wrapper dynamic-method) System.Object.17(intptr,intptr)
[MonoDroid] --- End of managed Android.OS.FileUriExposedException stack trace ---
[MonoDroid] android.os.FileUriExposedException: file:///storage/emulated/0/temp.jpg exposed beyond app through ClipData.Item.getUri()
[MonoDroid] at android.os.StrictMode.onFileUriExposed(StrictMode.java:1960)
[MonoDroid] at android.net.Uri.checkFileUriExposed(Uri.java:2356)
[MonoDroid] at android.content.ClipData.prepareToLeaveProcess(ClipData.java:942)
[MonoDroid] at android.content.Intent.prepareToLeaveProcess(Intent.java:9850)
[MonoDroid] at android.content.Intent.prepareToLeaveProcess(Intent.java:9835)
[MonoDroid] at android.app.Instrumentation.execStartActivity(Instrumentation.java:1610)
[MonoDroid] at android.app.Activity.startActivityForResult(Activity.java:4487)
[MonoDroid] at android.app.Activity.startActivityForResult(Activity.java:4445)
[MonoDroid] at android.app.Activity.startActivity(Activity.java:4806)
[MonoDroid] at android.app.Activity.startActivity(Activity.java:4774)
[MonoDroid] at mono.java.lang.RunnableImplementor.n_run(Native Method)
[MonoDroid] at mono.java.lang.RunnableImplementor.run(RunnableImplementor.java:30)
[MonoDroid] at android.os.Handler.handleCallback(Handler.java:790)
[MonoDroid] at android.os.Handler.dispatchMessage(Handler.java:99)
[MonoDroid] at android.os.Looper.loop(Looper.java:164)
[MonoDroid] at android.app.ActivityThread.main(ActivityThread.java:6494)
[MonoDroid] at java.lang.reflect.Method.invoke(Native Method)
[MonoDroid] at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
[MonoDroid] at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
[MonoDroid]
我已经看到一些使用文件处理程序FileProvider的参考(请参阅ProAndroidDev article ),但我不太明白它是如何工作的,是否使我能够简单地将图像传递给Instagram。它在文章中提到的所有域名让我非常困惑。
有没有更好的方式让我简单地将图像传递给Instagram?在未来我也想考虑视频支持,所以如果可以考虑到这将是伟大的。
现在我已将我的目标Android版本设置为6,以便应用程序继续工作,但我确实希望将目标设置为更现代的东西,这是我唯一的绊脚石。
有用的链接是
Android Developer - Secure File Sharing
最困难的部分是准确理解放置所有内容的位置,因此我已经包含了完整的上下文,以便让其他人更轻松。
首先是Android Manifest,其中包括App Center Push,但我已经清楚地显示了文件共享部分
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.devology.socialscheduler" android:versionName="1.11.3" android:versionCode="36" android:installLocation="internalOnly">
<uses-sdk android:minSdkVersion="15" android:targetSdkVersion="28" />
<application android:label="Social Scheduler App" android:icon="@drawable/icon">
<!-- App Center PUSH -->
<receiver android:name="com.google.firebase.iid.FirebaseInstanceIdInternalReceiver" android:exported="false" />
<receiver android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver" android:exported="true" android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="${applicationId}" />
</intent-filter>
</receiver>
<meta-data android:name="com.google.firebase.messaging.default_notification_icon" android:resource="@drawable/ic_notification" />
<!-- File Sharing -->
<provider android:name="android.support.v4.content.FileProvider" android:authorities="com.devology.socialscheduler.fileprovider" android:exported="false" android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_provider_paths" />
</provider>
</application>
</manifest>
文件提供程序指向名为@ xml / file_provider_paths的资源,将它放在正确的位置非常重要...
这是file_provider_paths.xml的内容
<!-- e.g. content://com.devology.socialscheduler.fileprovider/myimages/default_image.jpg -->
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="my_images" path="/"/>
</paths>
然后,我将图像从互联网下载到临时文件中,然后使用文件提供程序创建URL,然后将其传递给Android共享表
var webClient = new WebClient();
webClient.DownloadDataCompleted += (s, e) =>
{
this.Publish(new MaskMessageEvent("Downloading image from Social Scheduler", false));
if (e.Error != null)
{
this.Publish(new InstagramServiceEvent(message, false));
this.Publish(
new ToasterEvent(
"Oops - " + e.Error.Message));
}
else
{
this.Publish(new MaskMessageEvent("Copy ready for Instagram App", true));
var bytes = e.Result; // get the downloaded data
var context = Android.App.Application.Context;
AppStorageDirectory = context.FilesDir + Java.IO.File.Separator; //+"images";
/// You must first save your file in PNG or JPEG (preferred) format and use the filename extension ".ig"
/// Using the iOS Document Interaction APIs you can trigger the photo to be opened by Instagram.
/// The Identifier for our Document Interaction UTI is com.instagram.photo,
/// and it conforms to the public/jpeg and public/png UTIs.
/// Alternatively, if you want to show only Instagram in the application list
/// (instead of Instagram plus any other public/jpeg-conforming apps) you can specify the
/// extension class igo, which is of type com.instagram.exclusivegram.
//Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
string localFilename = "temp.jpg"; // <-- NOTE IGO to exclusively show instagram
string localPath = Path.Combine(AppStorageDirectory, localFilename);
File.WriteAllBytes(localPath, bytes); // writes to local storage
this.Publish(new MaskMessageEvent("Copy ready for Instagram App", false));
/// See the Apple documentation articles: Previewing and Opening Files and the
/// UIDocumentInteractionController Class Reference for more information.
Xamarin.Forms.Device.BeginInvokeOnMainThread(() =>
{
this.Publish(new MaskMessageEvent("Launching Instagram App", true));
Intent shareIntent = new Intent(Intent.ActionSend);
shareIntent.SetType("image/*");
Java.IO.File media = new Java.IO.File(AppStorageDirectory + Java.IO.File.Separator + localFilename);
var fileUri = FileProvider.GetUriForFile(
MainActivity.CURRENT_ACTIVITY,
"com.devology.socialscheduler.fileprovider",
media);
//Android.Net.Uri uri = Android.Net.Uri.FromFile(media);
shareIntent.PutExtra(Intent.ExtraStream, fileUri); // set uri
shareIntent.SetPackage("com.instagram.android");
Forms.Context.StartActivity(shareIntent);
this.Publish(new MaskMessageEvent("Launching Instagram App", false));
this.Publish(new InstagramServiceEvent(message, true));
});
}
};
webClient.DownloadDataAsync(new Uri(message.ImageUrl));
请注意,有一些关于使用文件扩展名IGO的旧评论 - 这用于确保只提供Instagram,但似乎在某些时候停止工作。