В первой части этой серии было рассмотрено как зарегистрировать приложение Xamarin.Forms в iOS, чтобы получать и отображать PDF файлы. В этом посте речь пойдет о том же, но для Android.
Сценарий
Снова сценарий, о котором пойдет речь:
- Написание Forms приложения.
- Это приложение позволяет обрабатывать файлы определенного типа (в демо-приложении, рассмотренном в этом посте это PDF файлы).
- ОС должна знать, что приложение способно оперировать с файлами данного типа.
- Приложение должно появиться в списке приложений, чтобы отправить файл.
Приступим.
Регистрация в Android
Также как Intent’ы используются для обмена данными между компонентами Activity, можно использовать Intent’ы для того же между приложениями.
Явные и неявные Intent’ы
Когда точно понятно какой компонент Activity необходимо открыть в приложении – нужно создать явный Intent. Когда создается явный Intent – указывается тип класса компонента Activity, который должен быть запущен, затем начинает работать ОС, когда находит StartActivity().
Достаточно просто. Но что происходит, когда разработчик исходного приложения не знает какой компонент Activity должен быть запущен, так как этот компонент Activity находится в отдельном приложении? Приложение «высылает» уведомление, что имеется что-то, что необходимо доставить в другое приложение и «спрашивает», может ли выполнить действие над этим.
Вот тут на помощь приходит неявный Intent. Разработчик создает неявный Intent, не специфицируя компонент Activity, который должен быть запущен.
Однако неявный Intent все еще нуждается в достаточной информации, чтобы ОС Android могла определить, какое приложение должно получить информацию. Здесь не будет рассматриваться создание неявных Intent’ов, а только их применение. В этой статье рассказано об Intent’ах подробно.
Intent-фильтры
Вся информация о явных и неявных Intent’ах приводит к концепции Intent-фильтров. Intent-фильтры – это способ, с помощью которого приложение, принимающее файл, регистрируется в Android для обработки входящих данных.
Intent-фильтры ассоциируются с компонентами Activity в приложении, которые должны запуститься, когда получен файл. Описание Intent-фильтра появляется в файле AndroidManifest.xml вместе с описанием для компонента Activity.
Чтобы определить, какое приложение будет запущено и отображено в опциях, когда пользователь посылает файл определенного типа в другое приложение, Android «просматривает» все Intent-фильтры, зарегистрированные ОС и фильтрует приложения по принципу сравнения критериев Intent-фильтров с критериями, объявленными в неявном Intent’е.
Как создать Intent-фильтр?
Объявление Intent -фильтра
Как было отмечено, описание Intent-фильтра появляется вместе с описанием компонента Activity, которые должны быть запущены, когда получен файл.
Пример объявления компонента Activity и Intent-фильтра в файле AndroidManifest.xml выглядит следующим образом:
1 2 3 4 5 6 7 8 9 10 11 | <activity android:configChanges="orientation|screenSize" android:icon="@drawable/icon" android:label="OpenFiles.Droid" android:theme="@style/MyTheme" android:name="md5d37a78bf190038b83be9873b4223f8d1.MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.SEND" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="application/pdf" /> </intent-filter> </activity> |
Здесь нужно отметить пару моментов. Первый – один компонент Activity может иметь более одного Inent-фильтра. Второй – ключевые слова, включенные в узел <intent-filter>. Эти ключевые слова помогают Android отфильтровать приложения, которые могут выполнять определенные операции. Вот объяснение значений в данном случае получения файла:
- <actionandroid:name=»android.intent.action.SEND» /> — компонент Activity только собирается отреагировать на неявный Intent, т.е. послать данные в другие приложения.
- <categoryandroid:name=»android.intent.category.DEFAULT» /> Это необходимо всегда и всегда должно быть такое значение для получения неявного Intent’а.
- <dataandroid:mimeType=»application/pdf» /> Типданных. Или тип MIME, который может обрабатывать приложение.
На этом работа с Android-частью закончена. Настало время a Xamarin.Forms-приложения.
Интеграция с Xamarin.Forms
Сначала необходимо понять как добавить Intent-фильтр в Android-часть Xamarin.Forms-приложения
Xamarin обеспечивает множество инструментов, так что нет необходимости часто касаться файла AndroidManifest.xml. Если понадобиться добавить значение в этом файле, можно обратиться к визуальному дизайнеру (дважды кликнув файл) или к различным атрибутам классов.
Так случилось, что Intent-фильтры можно добавить с помощью класса IntentFilterAttribute.
Так как в Forms-приложении только один компонент Activity, необходимо класс MainActivity дополнить этим атрибутом.
Чтобы сгенерировать XML-код в файле AndroidManifest.xml, идентичный рассмотренному выше, объявление класса MainActivity должно выглядеть следующим образом:
1 2 3 | [Activity(Label = "OpenFiles.Droid", Icon = "@drawable/icon", Theme = "@style/MyTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)] [IntentFilter(new[] { Intent.ActionSend }, Categories = new[] { Intent.CategoryDefault }, DataMimeType = @"application/pdf")] public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity { |
Есть еще класс ActivityAttribute, который создаст узел Activity в манифесте. Затем класс IntentFilterAttribute добавляет Intent-фильтр в этот узел Activity. Полную документацию по классу IntentFilterAttribute можно найти здесь. Он определяет action, category и тип входящих данных. action и category – это массивы, и они могут быть определены. В них могут быть еще параметры, помогающие фильтровать по совпадающим критериям. С помощью такого дополнения класса Android признает приложение как способное обрабатывать PDF-файлы.
Обработка входящего файла
Теперь, когда приложение опознано Android как подходящее, необходимо рассмотреть ситуацию, когда что-то попадает в него.
Когда приходит файл, вызывается функция OnCreate в MainActivity. Свойство Activity Intent содержит информацию, которая необходима для получения базового файла. Во время получения файла можно ожидать необходимую информацию в объекте ClipData свойства Intent.
Эта информация — Android.Net.Uri, которая используется с помощью ContentResolver, чтобы запустить поток данных. Затем можно сохранить этот поток данных в приложении как локальный файл.
В зависимости от приложения, отправляющего данные, также можно посмотреть URI отправляемого файла в Extras Intent’а — Intent.ExtraStream.
Вне зависимости от того, откуда получен Uri, необходимо удостовериться, что файл сохранен в приложении. В этом случае появится уверенность, что к файлу будет полный доступ.
Весь OnCreate показан ниже
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | App _mainForms; protected override void OnCreate(Bundle bundle) { TabLayoutResource = Resource.Layout.Tabbar; ToolbarResource = Resource.Layout.Toolbar; base.OnCreate(bundle); global::Xamarin.Forms.Forms.Init(this, bundle); var mainForms = new App(); LoadApplication(mainForms); if (Intent.Action == Intent.ActionSend) { // This is just an example of the data stored in the extras var uriFromExtras = Intent.GetParcelableExtra(Intent.ExtraStream) as Android.Net.Uri; var subject = Intent.GetStringExtra(Intent.ExtraSubject); // Get the info from ClipData var pdf = Intent.ClipData.GetItemAt(0); // Open a stream from the URI var pdfStream = ContentResolver.OpenInputStream(pdf.Uri); // Save it over var memOfPdf = new System.IO.MemoryStream(); pdfStream.CopyTo(memOfPdf); var docsPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal); var filePath = System.IO.Path.Combine(docsPath, "temp.pdf"); System.IO.File.WriteAllBytes(filePath, memOfPdf.ToArray()); mainForms.DisplayThePDF(filePath); } } |
Это обычный Xamarin.Forms OnCreate. Значительное отличие состоит в том, что после вызова Xamarin.Forms LoadApplication, он проверяет, был ли загружен Intent информацией из другого приложения, вызывая неявный Intent.
Если он находит данные в свойстве Intent, которое получает действие Intent.ActionSend (т.е. файл отсылается в приложение), то файл копируется в локальное хранилище приложения, затем вызывается функция DisplayThePDF().
Как известно из последнего поста, DisplayThePDF() отображает модальную страницу в web-обозревателе, и это web-обозреватель отображает загруженный PDF файл. Этот обозреватель работает через кастомный рендерер. Код для этого рендерера написан с помощью сайта документации Xamarin.
Когда все будет сделано приложение Android будет отображать PDF-файлы как на изображении ниже.
Итог
В целом, поняв то, как Android обменивается данными между приложениями, что похоже на обмен данными между компонентами Activity в одном приложении, задача получения файлов в Android становится легко разрешимой. Необходимо объявить Intent-фильтр в классе MainActivity в Android-проекте в Xamarin.Forms, используя IntentFilterAttribute. После этого соответствующая разметка разместиться в файле AndroidManifest.xml, и теперь все, что нужно сделать – обработать входящие данные с помощью функции OnCreate(), которая в идеале просто передаст работу функции, уже имеющейся в Xamarin.Forms проекте.
Источник: Официальный блог Xamarin
Что то не работает у меня, добавлял и в манифест и в код. Пытался сделать ассоциацию своего типа файла для своего приложения, нечего не поменялось в обще. Нашел аналогичиные примеры в сети, но тоже для меня не работают, так и не понял в чем дело.