
Немного теории
Для использования готовых нативных библиотек в MAUI нам предлагают механизмы Binding native library и Native Library Interop. Binding native library работает с собранными библиотеками (aar или jar для Android, XCFrameworks для iOS/Macos), Native Library Interop (или slim bindings) - это способ добавлять в проект ссылки на нативные проекты (Xcode или gradle), в процессе компиляции проекта обещают автоматическую соборку нативной библиотеки и генерацию обёртки для неё. Кроме того есть еще старый добрый P/Invoke, но это совсем другая история.
Чего не хватает?
В списке доступных SDK RuStore много чего на все случаи жизни. Тут нам и пуши, встроенные покупки, работа с обновлениями, отзывами и прочие штуки. Кроме того тут не указаны sdk для сторонних сервисов, которые можно подключить к приложению, например, tracer от ОК.
Теоретически к приложению на MAUI для андроид можно всё это дело подключить.
Основной минус - у вас должен быть аккаунт RuStore.
Еще немного теории.
Раньше, до .Net 9, для добавления нативных зависимостей Android (*.jar или *.aar) нужно было скачать aar, подложить его в проект или куда-то рядом (с помощью IDE или руками).
Если добавляли руками, нужно было указать ссылку на файл.
<ItemGroup>
<AndroidLibrary Update="aar/my_lib.aar"/>
</ItemGroup>
С релизом .Net 9 появилась опция AndroidMavenLibrary. Как вы,наверняка поняли из названия - с помощью неё можно задать ссылку на библиотеку в удалённом репозитории Maven. Очень важно что у этой опции есть свойство Repository, с помощью которого можно указать Url для cкачивания aar. Описание по ссылке выше.
Делаем получение пушей из RuStore
Нам нужен солюшен из двух проектов:
1. Андроид биндинг (в VS Code этот шаблон называется "привязка библиотеки Java для Android"), в котором будет генерироваться обёртка для Maui
2. Проект для тестового приложения, со ссылкой на первый проект.
Открываем файл первого проекта как текст и вставляем в корневой элемент этот кусок
<ItemGroup>
<AndroidMavenLibrary
Include="ru.rustore.sdk:pushclient"
Version="7.0.0"
Repository="https://artifactory-external.vkpartner.ru/artifactory/maven" />
</ItemGroup>
Должно получиться примерно так:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0-android</TargetFramework>
<SupportedOSPlatformVersion>24</SupportedOSPlatformVersion>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<IsTrimmable>true</IsTrimmable>
</PropertyGroup>
<ItemGroup>
<AndroidMavenLibrary
Include="ru.rustore.sdk:pushclient"
Version="7.0.0"
Repository="https://artifactory-external.vkpartner.ru/artifactory/maven" />
</ItemGroup>
</Project>
Так мы добавили ссылку на библиотеку pushclient версии 7.0.0 и указали источник для скачивания, который указан в разделе "Подключение в проект" официальной документации RuStore Push SDK.
Жмём собрать проект в IDE или dotnet build в консоли и получаем кучу ошибок.

Тут мы видим что вместе с aar скачался *.pom файл с описанием зависимостей и каким-то образом их надо добавить в проект. Пока что автоматическое скачивание зависимостей не допилили, да.
Если вглядеться, то в описании некоторых ошибок пишется "Корпорация Майкрософт поддерживает пакет NuGet "Square.OkHttp3", который может выполнить эту зависимость." Это значит что можно добавить NuGet c обёрткой наподобие той что мы пытаемся сделать, а можно добавить ссылку с помощью AndroidMavenLibrary, по аналогии с основным пакетом. Можно ошибки закинуть в нейронку, но тут важно следить за версиями библиотек в ответе. Можно залипнуть на несколько дней просто из-за того что в ответе нейронки будет другая версия библиотеки. В итоге у меня получился вот такой-вот список зависимостей.
Скрытый текст
<ItemGroup>
<!-- RuStore SDK из собственного репозитория -->
<AndroidMavenLibrary Include="ru.rustore.sdk:pushclient" Version="7.0.0" Repository="https://artifactory-external.vkpartner.ru/artifactory/maven" />
<AndroidMavenLibrary Include="ru.rustore.sdk:core" Version="8.0.0" Repository="https://artifactory-external.vkpartner.ru/artifactory/maven" />
<AndroidMavenLibrary Include="ru.rustore.sdk:push-core" Version="7.0.0" Repository="https://artifactory-external.vkpartner.ru/artifactory/maven" Bind="false" />
<AndroidMavenLibrary Include="ru.rustore.sdk:push-core-remote-config" Version="7.0.0" Repository="https://artifactory-external.vkpartner.ru/artifactory/maven" Bind="false" />
<AndroidMavenLibrary Include="ru.rustore.sdk:push-common" Version="7.0.0" Repository="https://artifactory-external.vkpartner.ru/artifactory/maven"/>
<AndroidMavenLibrary Include="ru.rustore.sdk:push-core-network" Version="7.0.0" Repository="https://artifactory-external.vkpartner.ru/artifactory/maven" Bind="false" />
<AndroidMavenLibrary Include="ru.rustore.sdk:coreui" Version="8.0.0" Repository="https://artifactory-external.vkpartner.ru/artifactory/maven" Bind="false" />
<AndroidMavenLibrary Include="ru.rustore.sdk:metrics" Version="8.0.0" Repository="https://artifactory-external.vkpartner.ru/artifactory/maven" Bind="false" />
<AndroidMavenLibrary Include="ru.rustore.sdk:reactive" Version="8.0.0" Repository="https://artifactory-external.vkpartner.ru/artifactory/maven" Bind="false"/>
<!-- HMS из Huawei репозитория -->
<AndroidMavenLibrary Include="com.huawei.hms:push" Version="6.12.0.300" Repository="https://developer.huawei.com/repo/" Bind="false" />
<AndroidMavenLibrary Include="com.huawei.hms:opendevice" Version="6.12.0.300" Repository="https://developer.huawei.com/repo/" Bind="false" JavaArtifact="com.huawei.android.hms:security-encrypt:1.2.0.307;com.huawei.android.hms:security-base:1.2.0.307;com.huawei.hms:base:6.11.0.302;javax.inject:javax.inject:1" />
<AndroidMavenLibrary Include="com.huawei.android.hms:security-encrypt" Version="1.2.0.307" Repository="https://developer.huawei.com/repo/" Bind="false" JavaArtifact="com.huawei.android.hms:security-base:1.2.0.307;com.huawei.hms:base:6.11.0.302;javax.inject:javax.inject:1" />
<AndroidMavenLibrary Include="com.huawei.android.hms:security-base" Version="1.2.0.307" Repository="https://developer.huawei.com/repo/" Bind="false" JavaArtifact="com.huawei.hms:base:6.11.0.302;javax.inject:javax.inject:1" />
<AndroidMavenLibrary Include="com.huawei.hms:base" Version="6.11.0.302" Repository="https://developer.huawei.com/repo/" Bind="false" />
<AndroidMavenLibrary Include="com.huawei.hms:baselegacyapi" Version="6.11.0.302" Repository="https://developer.huawei.com/repo/" Bind="false" />
<AndroidMavenLibrary Include="com.huawei.hms:stats" Version="6.11.0.302" Repository="https://developer.huawei.com/repo/" Bind="false" />
<AndroidMavenLibrary Include="com.huawei.hms:ui" Version="6.11.0.302" Repository="https://developer.huawei.com/repo/" Bind="false" />
<AndroidMavenLibrary Include="com.huawei.hms:device" Version="6.11.0.302" Repository="https://developer.huawei.com/repo/" Bind="false" />
<AndroidMavenLibrary Include="com.huawei.hms:log" Version="6.11.0.302" Repository="https://developer.huawei.com/repo/" Bind="false" />
<AndroidMavenLibrary Include="com.huawei.hmf:tasks" Version="1.5.2.206" Repository="https://developer.huawei.com/repo/" Bind="false" />
<AndroidMavenLibrary Include="com.huawei.hms:availableupdate" Version="6.11.0.302" Repository="https://developer.huawei.com/repo/" Bind="false" />
<AndroidMavenLibrary Include="com.huawei.hms:hatool" Version="6.11.0.302" Repository="https://developer.huawei.com/repo/" Bind="false" />
<AndroidMavenLibrary Include="com.huawei.hms:network-grs" Version="6.0.11.300" Repository="https://developer.huawei.com/repo/" Bind="false" />
<AndroidMavenLibrary Include="com.huawei.agconnect:agconnect-core" Version="1.8.1.300" Repository="https://developer.huawei.com/repo/" Bind="false" />
<AndroidMavenLibrary Include="com.huawei.android.hms:security-ssl" Version="1.2.0.307" Repository="https://developer.huawei.com/repo/" Bind="false" />
<AndroidMavenLibrary Include="com.huawei.hms:network-common" Version="6.0.11.300" Repository="https://developer.huawei.com/repo/" Bind="false" />
<AndroidMavenLibrary Include="com.huawei.hms:network-framework-compat" Version="6.0.11.300" Repository="https://developer.huawei.com/repo/" Bind="false" />
<!-- RuStore SDK внешние зависимости -->
<AndroidMavenLibrary Include="ru.ok.tracer:tracer-base" Version="1.1.1" Bind="false" />
<AndroidMavenLibrary Include="ru.ok.tracer:tracer-manifest" Version="1.1.1" Bind="false" />
<AndroidMavenLibrary Include="ru.ok.tracer:tracer-lite-commons" Version="1.1.1" Bind="false" />
<AndroidMavenLibrary Include="ru.ok.tracer:tracer-lite-crash-report" Version="1.1.1" Bind="false" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Square.OkHttp3" Version="4.10.0" />
<PackageReference Include="Square.OkIO.JVM" Version="3.0.0"/>
<!-- Kotlin runtime -->
<PackageReference Include="Xamarin.AndroidX.SavedState" Version="1.3.3" />
<PackageReference Include="Xamarin.AndroidX.SavedState.SavedState.Android" Version="1.3.3" />
<PackageReference Include="Xamarin.AndroidX.SavedState.SavedState.Ktx" Version="1.3.3" />
<PackageReference Include="Xamarin.Kotlin.StdLib" Version="2.2.20" />
<PackageReference Include="Xamarin.Kotlin.StdLib.Common" Version="2.0.21.5" />
<PackageReference Include="Xamarin.Kotlin.StdLib.Jdk8" Version="2.2.20" />
<PackageReference Include="Xamarin.KotlinX.Coroutines.Core" Version="1.10.2.1" />
<PackageReference Include="Xamarin.KotlinX.Coroutines.Core.Jvm" Version="1.10.2.1" />
<PackageReference Include="Xamarin.KotlinX.Coroutines.Android" Version="1.10.2.1" />
<!-- AndroidX base -->
<PackageReference Include="Xamarin.AndroidX.Core" Version="1.17.0" />
<PackageReference Include="Xamarin.AndroidX.Core.Core.Ktx" Version="1.17.0" />
<PackageReference Include="Xamarin.AndroidX.Annotation" Version="1.9.1.5" />
<PackageReference Include="Xamarin.AndroidX.Annotation.Experimental" Version="1.5.1.1" />
<PackageReference Include="Xamarin.AndroidX.Startup.StartupRuntime" Version="1.2.0.5" />
<PackageReference Include="Xamarin.AndroidX.Work.Work.Runtime.Ktx" Version="2.10.5" />
<PackageReference Include="Xamarin.AndroidX.Room.Room.Ktx" Version="2.8.1" />
<PackageReference Include="Xamarin.AndroidX.Sqlite.Framework" Version="2.6.1" />
<PackageReference Include="Xamarin.AndroidX.DataStore.Preferences" Version="1.1.7.1" />
<!-- Lifecycle family (чтобы не было конфликтов) -->
<PackageReference Include="Xamarin.AndroidX.Lifecycle.Common" Version="2.9.4" />
<PackageReference Include="Xamarin.AndroidX.Lifecycle.Runtime" Version="2.9.4" />
<PackageReference Include="Xamarin.AndroidX.Lifecycle.Runtime.Android" Version="2.9.4" />
<PackageReference Include="Xamarin.AndroidX.Lifecycle.LiveData" Version="2.9.4" />
<PackageReference Include="Xamarin.AndroidX.Lifecycle.LiveData.Core" Version="2.9.4" />
<!-- Material & UI -->
<PackageReference Include="Xamarin.Google.Android.Material" Version="1.13.0" />
<!-- Firebase core set -->
<PackageReference Include="Xamarin.Firebase.Common" Version="122.0.1" />
<PackageReference Include="Xamarin.Firebase.Common.Ktx" Version="121.0.0.7" />
<PackageReference Include="Xamarin.Firebase.Components" Version="119.0.0.1" />
<PackageReference Include="Xamarin.Firebase.Datatransport" Version="120.0.1" />
<PackageReference Include="Xamarin.Firebase.Encoders" Version="117.0.0.23" />
<PackageReference Include="Xamarin.Firebase.Encoders.JSON" Version="118.0.1.15" />
<PackageReference Include="Xamarin.Firebase.Encoders.Proto" Version="116.0.0.18" />
<PackageReference Include="Xamarin.Firebase.Installations" Version="119.0.1" />
<PackageReference Include="Xamarin.Firebase.Installations.InterOp" Version="117.2.0.11" />
<PackageReference Include="Xamarin.Firebase.Measurement.Connector" Version="120.0.1.11" />
<!-- Google Play Services -->
<PackageReference Include="Xamarin.GooglePlayServices.Base" Version="118.9.0" />
<PackageReference Include="Xamarin.GooglePlayServices.Basement" Version="118.9.0" />
<PackageReference Include="Xamarin.GooglePlayServices.Tasks" Version="118.4.0" />
<PackageReference Include="Xamarin.GooglePlayServices.CloudMessaging" Version="117.3.0.7" />
<PackageReference Include="Xamarin.GooglePlayServices.Stats" Version="117.1.0.7" />
<PackageReference Include="Xamarin.Google.Android.DataTransport.TransportApi" Version="4.0.0.5" />
<!-- Guava / DataTransport -->
<PackageReference Include="Xamarin.Google.Guava.ListenableFuture" Version="1.0.0.29" />
<PackageReference Include="Xamarin.Google.Android.DataTransport.TransportBackendCct" Version="4.0.0.5" />
<PackageReference Include="Xamarin.Google.Android.DataTransport.TransportRuntime" Version="4.0.0.5" />
<PackageReference Include="Xamarin.JavaX.Inject" Version="1.0.0.21" />
</ItemGroup>В этом примере важны атрибуты Bind.
<AndroidMavenLibrary
Include="ru.rustore.sdk:push-core" Version="7.0.0"
Repository="https://artifactory-external.vkpartner.ru/artifactory/maven"
Bind="false" />
По умолчанию Bind="true", а это значит что будет сгенерирована обёртка на C#. Для зависимостей это нужно только когда типы из этих библиотек используются в качестве входных-выходных параметров public функций основной сборке, а если обёртка не нужна то и править возможные ошибки тоже не нужно.
После сборки, Visual Studio Code насыпал кучу ошибок. Ошибки кликабельные. Если кликнуть на первую ошибку увидим что-то такое:

Из скриншота видно что какая-то обертка всё таки получилась, но c имплементацией интерфейса что-то пошло не так, причём в двух местах: NotificationParams.CREATOR и RemoteMessage.CREATOR. Так как эти классы нам пока не нужны давайте укажем сборщику не делать обёртку.
Для этого открываем файл Transorms/Metadata.xml.Атрибут path копируем в комментарии над объявлением класса. Получилось вот так:
<metadata>
<remove-node path="/api/package[@name='com.vk.push.common.messaging']/class[@name='NotificationParams.CREATOR']"/>
<remove-node path="/api/package[@name='com.vk.push.common.messaging']/class[@name='RemoteMessage.CREATOR']"/>
</metadata>
После сборки можно заметить что ошибки с этими классами исчезли. Жмём на следующую ошибку: 'ILoggerInvoker' does not implement interface member 'ILogger.CreateLogger(string)'. 'ILoggerInvoker.CreateLogger(string)' cannot implement 'ILogger.CreateLogger(string)' because it does not have the matching return type of 'ILogger'.
Ошибка такая же, но в этом случае логгер нам может пригодится, мало ли чего?
Самое интересное что если пролистать вниз этот метод всё таки будет реализован:
// Metadata.xml XPath method reference: path="/api/package[@name='ru.rustore.sdk.pushclient.common.logger']/class[@name='DefaultLogger']/method[@name='createLogger' and count(parameter)=1 and parameter[1][@type='java.lang.Object']]"
[Register ("createLogger", "(Ljava/lang/Object;)Lcom/vk/push/common/Logger;", "")]
public unsafe global::Com.VK.Push.Common.ILogger CreateLogger (global::Java.Lang.Object p0)
{
const string __id = "createLogger.(Ljava/lang/Object;)Lcom/vk/push/common/Logger;";
try {
JniArgumentValue* __args = stackalloc JniArgumentValue [1];
__args [0] = new JniArgumentValue ((p0 == null) ? IntPtr.Zero : ((global::Java.Lang.Object) p0).Handle);
var __rm = _members.InstanceMethods.InvokeAbstractObjectMethod (__id, this, __args);
return global::Java.Lang.Object.GetObject<global::Com.VK.Push.Common.ILogger> (__rm.Handle, JniHandleOwnership.TransferLocalRef)!;
} finally {
global::System.GC.KeepAlive (p0);
}
}
Странно, ну ладно... Давайте посмотрим что за зверь такой ru.rustore.sdk.pushclient.common.logger.
Для этого переходим во внутрь aar (я это делаю с помощью mc):
mc /obj/Debug/net9.0-android/library_project_jars/pushclient-7.0.0.aar
Там переходим в консольный режим (ctrl+o), запускаем jadx-gui classes.jar

Видно что это действительно интерфейс и в нём есть метод createLogger. Просят реализовать - давайте реализуем. Для этого в корне проекта создадим класс ILoggerInvoker:
namespace RU.Rustore.Sdk.Pushclient.Common.Logger;
internal partial class ILoggerInvoker
{
Com.VK.Push.Common.ILogger Com.VK.Push.Common.ILogger.CreateLogger(string tag)
{
return CreateLogger(tag);
}
}
Для DefaultLogger решение аналогичное:
namespace RU.Rustore.Sdk.Pushclient.Common.Logger;
public partial class DefaultLogger
{
Com.VK.Push.Common.ILogger Com.VK.Push.Common.ILogger.CreateLogger(string tag)
{
return CreateLogger(tag);
}
}
Собираем и ... ошибок сборки нет. Теперь переходим к тестовому проекту.
Добавляем в манифест приложения для андроид:
<meta-data
android:name="ru.rustore.sdk.pushclient.default_notification_channel_id"
android:value="@string/notifications_data_push_channel_id" />
Добавляем пермишен:
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
strings.xml в ресурсы андроида
<resources>
<string name="app_name">RuStore Push Java Example</string>
<!-- Notification -->
<string name="notifications_data_push_channel_id">data_push_channel</string>
<string name="notifications_data_push_channel_name">Data push channel</string>
<string name="notifications_notification_push_channel_id">notification_push_channel</string>
<string name="notifications_notification_push_channel_name">Notification push channel</string>
</resources>
NotificationManagerWrapper для создания и показа пуша
using Android.Content;
using Android.Content.PM;
using AndroidX.Core.App;
using AndroidX.Core.Content;
namespace Sample.Wrapper;
public record AppNotification(int Id, string Title, string Message, string ChannelId, string ChannelName);
public class NotificationManagerWrapper
{
private readonly NotificationManagerCompat _notificationManager;
private static NotificationManagerWrapper _instance;
private NotificationManagerWrapper(NotificationManagerCompat notificationManager)
{
_notificationManager = notificationManager;
}
public static NotificationManagerWrapper GetInstance(Context context)
{
if (_instance == null)
{
_instance = new NotificationManagerWrapper(NotificationManagerCompat.From(context));
}
return _instance;
}
public void CreateNotificationChannel(string channelId, string channelName)
{
var builder = new NotificationChannelCompat.Builder(channelId, NotificationManagerCompat.ImportanceDefault)
.SetName(channelName);
_notificationManager.CreateNotificationChannel(builder.Build());
}
public void ShowNotification(Context context, AppNotification data)
{
// Создаём билдер уведомления
var builder = new NotificationCompat.Builder(context, data.ChannelId)
.SetContentTitle(data.Title)
.SetContentText(data.Message)
.SetSmallIcon(Resource.Drawable.dotnet_bot); // замените на свой значок
// Проверяем наличие канала
if (_notificationManager.GetNotificationChannel(data.ChannelId) == null)
{
CreateNotificationChannel(data.ChannelId, data.ChannelName);
}
// Проверка разрешения на уведомления (Android 13+)
if (ContextCompat.CheckSelfPermission(context, Android.Manifest.Permission.PostNotifications) != Permission.Granted)
{
return;
}
_notificationManager.Notify(data.Id, builder.Build());
}
}
Фоновый андроид-сервис для приёма пушей:
using Android.App;
using Android.Util;
using Microsoft.Maui.Controls.Internals;
using RU.Rustore.Sdk.Pushclient.Messaging.Exception;
using RU.Rustore.Sdk.Pushclient.Messaging.Model;
using RU.Rustore.Sdk.Pushclient.Messaging.Service;
using Sample.Wrapper;
namespace Sample;
[Preserve(AllMembers = true)]
[Service(Exported = true)]
[IntentFilter(new[] { "ru.rustore.sdk.pushclient.MESSAGING_EVENT" })]
public class PushListenerService : RuStoreMessagingService
{
private const string LogTag = "PushListenerService";
private NotificationManagerWrapper? _notificationManagerWrapper;
public override void OnCreate()
{
base.OnCreate();
_notificationManagerWrapper = NotificationManagerWrapper.GetInstance(this);
}
public override void OnNewToken(string token)
{
Log.Debug(LogTag, $"OnNewToken token = {token}");
}
public override void OnMessageReceived(RemoteMessage message)
{
base.OnMessageReceived(message);
var channelInfo = GetChannelInfo();
var notification = new AppNotification(
message.GetHashCode(),
message.Notification?.Title,
message.Notification?.Body,
channelInfo.Item1,
channelInfo.Item2
);
_notificationManagerWrapper.ShowNotification(this, notification);
}
private (string, string) GetChannelInfo()
{
string channelId = GetString(Resource.String.notifications_data_push_channel_id);
string channelName = GetString(Resource.String.notifications_data_push_channel_name);
return (channelId, channelName);
}
public override unsafe void OnError(IList<RuStorePushClientException> errors)
{
base.OnError(errors);
}
}
Всё это очень напоминает подключение пушей Firebase.
Не забываем включить поддержку unsafe, но если что компилятор напомнит.
Далее переходим в раздел Проверка возможности получать push-уведомления официальной документации. Проверяем ничего ли мы не забыли. Самое главное нужно добавить fingerprint(отпечаток) подписи (без этого не получить project id) и настроить подпись apk при сборке. Без этого пуши не заработают и будут сыпаться эксепшены.
В MainApplication добавляем инициализацию, всё как в документации:
public override void OnCreate()
{
base.OnCreate();
RuStorePushClient.Instance.Init(this,
"push project id", new MainActivity.MyLogger());
}
А в MainActivity запрашиваем перемешен
protected override void OnCreate(Bundle? savedInstanceState)
{
base.OnCreate(savedInstanceState);
const int requestNotification = 0;
string[] notiPermission =
{
Manifest.Permission.PostNotifications
};
if (CheckSelfPermission(Manifest.Permission.PostNotifications) != Permission.Granted)
{
RequestPermissions(notiPermission, requestNotification);
}
}
Запускаем, открываем Logcat ищем push-токен по тэгу PushListenerService, копируем токен в интерфейс отправки тестовых сообщений в Rustore. Жмём отправить.

Победа! Пуши приходят, но почему-то задваиваются при показе в шторке. Думал что это где-то у меня ошибка. Запустил официальный пример на Котлин - там такое же поведение, просто второй пуш не показывается, т.к. в сервисе эксепшен из-за того что иконка мне указана.
Выводы:
Биндинг либы из Rustore SDK делать несложно, хотя количество ошибок сборки может испугать неподготовленного человека.
Документация для java/kotlin подробная, библиотека хорошо обложена логированием в логкат. Во время отладки сыпались эксепшены, из которых понятно как их вылечить, для каждого было описание в официальной документации.
При желании к мобильному приложению на maui можно прикрутить любые нативные библиотеки.
При создании биндингов для классов на Котлин иногда встречаются нюансы, но это уже тема отдельной статьи. С пушами получилось всё стандартно.
-
С релизом .Net 9 биндинги для нативных библиотек для андроида стало делать сильно проще. Даже для таких больших как rustore push или rustore pay
Ссылка на репозиторий: тынц
Рядом с итоговым репозиторием лежат репозитории разной степени готовности. Например, rustore pay уже видит добавленные в интерфейсе Rustore покупки, Remote Config - видит конфиги на сервере, а Ok.Tracer - шлёт крэши и показывает их в Web интерфейсе.Буду рад вашим пул реквестам, вопросам и комментариям.