Как передать изображение в Instagram из приложения для Android с Android 7+ / Nougart - PullRequest
0 голосов
/ 17 апреля 2019

Я использовал следующий код для отправки фотографии из моего приложения в 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 ), но я не совсем понимаю, как это работает и позволяет ли мне просто передавать изображение в Instagram. Все доменные вещи, которые он упоминает в статье, очень смутили меня.

Есть ли лучший способ для меня просто передать изображение в Instagram? В будущем я хотел бы также рассмотреть возможность поддержки видео, так что, если это будет принято во внимание, это будет здорово.

Пока я установил целевую версию Android на 6, чтобы приложение продолжало работать, но я хочу установить цель на что-то гораздо более современное, и это мой единственный камень преткновения.

1 Ответ

0 голосов
/ 19 апреля 2019

Полезные ссылки:

Android Developer - Безопасный общий доступ к файлам

Pro Android Dev.com - блог

Самым сложным было понять, куда именно все поместить, поэтому я включил полный контекст, чтобы упростить задачу для кого-то еще.

Сначала был Манифест Android, который включает в себя приложение 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, и важно разместить его в нужном месте ...

Where to put 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, но этог перестать работать в какой-то момент.

...