Всем привет! Меня зовут Миша Вассер, я Head of Android в AGIMA. Мы занимаемся разработкой Digital-продуктов для больших и маленьких компаний, в том числе пилим мобильные приложения.

Не так давно — по сравнению со всей историей Android — Huawei выкатил собственную операционную систему и сказал: «Ребята, вот вам новая система, кайфуйте». Многие отнеслись к новой ОС скептически. Остальным пришлось адаптировать под нее свои Android-приложения.

Мы оказались во второй группе. К нам время от времени обращаются с просьбой помочь с адаптацией под Huawei. И мы неплохо в этом вопросе прокачались. Поэтому сейчас расскажу, что надо сделать, чтобы стало хорошо. А покажу всё это на примере крупного ретейлера, с которым мы работаем.

Что сделать, чтобы было хорошо

  1. Зависимости build.gradle.

Чтобы начать работать с библиотеками Huawei, первым делом добавим нужные зависимости в наш Gradle (модуля app):

dependencies {
	implementation 'com.huawei.hms:push:6.3.0.304'
	implementation 'com.huawei.hms:maps:6.4.1.300'
	implementation 'com.huawei.hms:location:6.4.0.300'
	implementation 'com.huawei.hms:hianalytics:6.4.1.302'
	implementation 'com.huawei.agconnect:agconnect-crash:1.6.5.300'
	implementation 'com.huawei.agconnect:agconnect-remoteconfig:1.6.5.300'
}

В самый верх файла добавим:

apply plugin: 'com.huawei.agconnect'

А теперь добавим в Gradle-файл проекта еще такую зависимость:

dependencies {
		classpath 'com.huawei.agconnect:agcp:1.6.0.300'
}
  1. Добавление agconnect-services.json.

В Android с Google-сервисами мы используем файл из Firebase, который называется google-services.json. В Huawei нам потребуется такой же конфигурационный файл. Без него не получится использовать фичи, которые предоставляет вендор (например, push-уведомления, аналитика и т. д.).

Чтобы добавить этот файл, заходим в AppGalleryConnect. Затем в «Мои проекты», выбираем нужный проект, идем в «Настройки проекта» в левом боковом меню. Листаем до раздела «Данные приложения».

Теперь скачиваем файл agconnect-services и кладем его в папку App нашего проекта в Android Studio.

  1. Правила Proguard.

Чтобы не заобфусцировать ничего лишнего, попросим Proguard не трогать Huawei-библиотеки. Для этого добавим в proguard-rules.pro строки из документации для разработчиков:

-ignorewarnings
-keepattributes *Annotation*
-keepattributes Exceptions
-keepattributes InnerClasses
-keepattributes Signature
-keepattributes SourceFile,LineNumberTable
-keep class com.huawei.hianalytics.**{*;}
-keep class com.huawei.updatesdk.**{*;}
-keep class com.huawei.hms.**{*;}
  1. Проверим, доступны ли Google-сервисы.

Важная часть работы с Huawei — это проверка, точно ли у юзера на устройстве нет Google-сервисов. Полезно оставлять в коде возможность запустить Android-версию приложения. Причина банальна: APK из AppGallery кто-то может запостить в другое место, и юзеры скачают его на свои нехуавейные смартфоны.

Чтобы проверить, делаем вот такую Extension-функцию для контекста:

fun Context.areGoogleServicesAvailable(): Boolean {
    val availability = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(this)
    return availability == com.google.android.gms.common.ConnectionResult.SUCCESS
}

Теперь можно вызывать ее откуда угодно и строить логику платформозависимых функций, опираясь на неё.

  1. Определяем местоположение юзера.

Часто в приложении нужно определять местоположение юзера, чтобы, например, показать ближайший магазин. Для этого мы пользуемся FusedLocationClient. В Huawei больших изменений нет, надо просто импортировать HMS клиента вот так:

import com.huawei.hms.location.FusedLocationProviderClient

Тогда код получения Location клиента у нас получится вот таким:

if (context.areGoogleServicesAvailable()) {
    GoogleFusedLocation(
com.google.android.gms.location.LocationServices.getFusedLocationProviderClient(context)
    )
} else {
    HuaweiFusedLocation(
        com.huawei.hms.location.LocationServices.getFusedLocationProviderClient(context)
    )
}

В самих классах FusedLocation для обеих платформ все стандартно — локацию юзера получаем, вешая слушатели на Success и Failure:

providerClient.lastLocation
  .addOnSuccessListener{ //Получили location }
  .addOnFailureListener{ //Что-то упало }
  1. Меняем все Google Play на AppGallery.

Почти на всех проектах пользователей нужно перевести на страницу приложения в Google Play. Так как у Huawei свой стор AppStoreConnect, пользователя лучше вести туда. Для этого меняем URI и Package в интенте.

Вот как мы открываем магазин в Android:

val uri = Uri.parse("market://details?id=" + applicationId)
val intent = Intent().apply {
		action = Intent.ACTION_VIEW
		data = uri
		setPackage("com.android.vending")
}

А вот как открыть его в Huawei:

val uri = Uri.parse("appmarket://details?id=" + applicationId)
val intent = Intent().apply {
		action = Intent.ACTION_VIEW
		data = uri
		setPackage("com.huawei.appmarket")
}

Дальше, уже используя написанный нами ранее Checker, можем вести пользователя в тот или иной магазин

  1. Меняем number на phone во всех Layout с android:inputType.

Один из хаков, который вам пригодится.

Если в Android у EditText мы проставляем android:inputtype="number" — например, на экране ввода кода из смс при авторизации, то пользователь Huawei всё равно увидит клавиатуру QWERTY, а не клавиатуру с цифрами, как в Android. Чтобы это пофиксить, просто поменяйте inputType на “phone”.

Добавление SHA-256-ключа

Чтобы полноценно использовать SDK Huawei, нужно добавить отпечаток сертификата SHA-256 в консоль AppGalleryConnect. Если вы добавляете его в первый раз, это можно сделать по инструкции.

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

  1. Первым делом нужно узнать ваш SHA-256, который зашит в APK-шках, которые вы собираете у себя на компьютере или CI. Можно сделать несколькими способами, вот один из них:

  • собираем APK;

  • копируем путь до вашего APK;

  • идем в консоль и вводим следующее (не забудьте вставить свой путь):

keytool -printcert -jarfile .../app-debug.apk
  • консоль выведет наши ключи SHA-1 и SHA-256.

  1. Копируем ключ SHA-256. Он выглядит примерно так: 00:F3:61:A7:AD:6B:13:11:27:02:09:8C:F5:12:FF…… Затем идем в AppGalleryConnect.

  1. Теперь нужно зайти в «Мои проекты», выбрать нужный проект, открыть в левом боковом меню «Настройки проекта» и пролистать до раздела «Данные приложения».

4. Тут нажимаем «Добавить» и вводим наш полученный ранее ключ SHA-256. Теперь можем скачать файл agconnect-services.json — и всё.

А если не добавить ваш ключ в консоль? Не будут доходить пуши, не сработают In-App-Purchases, AppLinking и другие фишки AppGalleryConnect. Да и в целом нельзя будет залить приложение в стор. Поэтому не пропускайте и не игнорируйте этот момент.

Universal APK

При загрузке APK на некоторые Huawei-девайсы, в том числе и на эмулятор внутри AppGalleryConnect, важно сделать universal-сборку для всех типов процессоров. Иначе мы можем получить ошибку вроде This app is no longer compatible with your device.

Чтобы решить эту проблему, соберем универсальный APK. Добавим в Gradle (модуля app) следующие строчки:

android {
......
......
	  splits {
        abi {
            enable true
            reset()
            include 'x86', 'x86_64', 'armeabi', 'armeabi-v7a', 'mips', 'mips64', 'arm64-v8a'
            universalApk true
        }
    }
}

Теперь каждый раз при сборке APK у нас в папке build/outputs будут лежать несколько APK-файлов. Нам нужен тот, что называется app-universal-debug.apk. Смело загружайте его в эмуляторы AppGalleryConnect и отдавайте тестерам.

Push Kit и тестирование пушей

Отправка и удаление Push-токена

Зачастую крупные продукты рассылают пуши через Backend. Если вы не используете разные SDK (OneSignal, например), вам, скорее всего, нужно отдавать на свой бэк пуш-токен пользователя. Рассмотрим, как токен получить и как удалить.

В Android мы получаем пуш-токен Firebase как-то так:

suspend fun getTokenAndSendGoogle() {
     val token = FirebaseMessaging.getInstance().token.await()
     sendTokenToServer(token)
}

Huawei же тут решил немного намудрить. Когда-то пуш-токен в Huawei можно было получить синхронным запросом, но сейчас такое не прокатывает. В итоге токен надо получать в отдельном потоке. Делается это вот так:

private fun getTokenAndSendHuawei() {
        HmsInstanceId.getInstance(context).run {

            //Получаем id AppGallery приложения из конфига
            val appId = AGConnectServicesConfig.fromContext(context).getString("client/app_id")

            object : Thread() {
                override fun run() {
                    try {
                        //Получаем HMS пуш-токен по id, который достали ранее
                        val pushToken = getToken(appId, HmsMessaging.DEFAULT_TOKEN_SCOPE)

                        if (!pushToken.isNullOrBlank()) {
                            //Отправляем токен на ваш сервак
                            sendTokenToServer(pushToken)
                        }
                    } catch (e: ApiException) {

                    }
                }

            }.start()
        }
}

Используя проверку из предыдущего раздела, получаем такое условие:

supervisorScope {
   launch {
        if (getGMSAvailable()) {
	       getTokenAndSendGoogle()
        } else {
           getTokenAndSendHuawei()
		}
    }
}

С удалением токена всё так же просто. Например, при разлогине пользователя из приложения вам надо удалить токен. Получаем токен из HMS, удаляем с вашего сервера. В идеале надо провести еще одну операцию — удалить инстанс не только у вас, а ещё и из Firebase/HMS. На девайсах с гугл-сервисами делаем так:

FirebaseInstallations.getInstance().delete()

В HMS тоже никаких сложностей:

HmsInstanceId.getInstance(context).deleteAAID()

Тестирование пушей внутри эмулятора App Gallery Console

Ребята из Huawei сделали очень крутую тему. Они добавили эмулятор прямо в свою консоль. Объясняю, как протестить пуши с его помощью.

  1. Чтобы попасть в эмулятор, нужно зайти в «Мои проекты», выбрать нужный проект. Затем в левом меню в разделе «Качество» выбрать «Облачная отладка».

  2. Нажимаем и видим страницу с моделями доступных девайсов. Можно навести на каждый и либо начать отладку, если девайс свободен, либо зарезервировать симулятор на какой-то определенный тайм-слот.

  1. Когда мы запустим эмулятор, увидим вот такой экран:

Справа видим Logcat, список доступных APK и прочие штуки, помогающие нам в отладке.

  1. Устанавливаем приложение на симулятор, получаем Push-токен. Можем вывести получение токена из предыдущего раздела в логи и скопировать его.

  2. Теперь идем в раздел Push Kit, он находится во вкладке «Рост» в левом боковом меню. Попав на страницу Push Kit, можем нажать на кнопку «Добавить уведомление».

  1. Заполняем стандартные поля «Имя», «Заголовок», «Тело» и другие. Когда это готово, скроллим ниже, находим блок «Диапазон отправки». Тут нужно выбрать «Указанное устройство». Появится еще одно поле — «Токен устройства». Вставим туда токен, который мы скопировали пару пунктов назад.

  1. Когда вы заполнили все поля, выбрали время отправки и указали пуш-токен, можно нажимать кнопку «Отправить».

  2. Вернувшись в эмулятор, мы увидим пуш — вуаля! Таким образом вы сможете тестировать отображение пушей, диплинки и всё, что вам нужно в пушах.

Карты

Во многих приложениях есть географические карты. Большинство из них нуждаются в минимальном функционале — добавление маркеров, их удаление и нажатие. Если вы используете Яндекс.Карты или другую SDK, которая не гугл-сервис, скорее всего, вам ничего не придется адаптировать.

В этой части статьи постараюсь рассказать, что вам надо поменять, чтобы базовый функционал легко завелся.

Huawei снова сделал всё максимально удобно для нас. С точки зрения разработки, карты очень похожи друг на друга. Убрав лишнюю шелуху с кода, получаем следующее:

  1. Начнем с XML. Добавляем карту в наш Layout.

Google:

<com.google.android.gms.maps.MapView
    xmlns:android="<http://schemas.android.com/apk/res/android>"
    xmlns:map="<http://schemas.android.com/apk/res-auto>"
    android:id="@+id/mapView"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

Huawei:

<com.huawei.hms.maps.MapView
    xmlns:android="<http://schemas.android.com/apk/res/android>"
    xmlns:map="<http://schemas.android.com/apk/res-auto>"
    android:id="@+id/mapView"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
  1. Перейдем в наше Activity или Fragment. Затаскиваем импорты:

Google:

import com.google.android.gms.maps.CameraUpdateFactory
import com.google.android.gms.maps.GoogleMap
import com.google.android.gms.maps.model.BitmapDescriptorFactory
import com.google.android.gms.maps.model.CameraPosition
import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.maps.model.MarkerOptions

Huawei:

import com.huawei.hms.maps.CameraUpdateFactory
import com.huawei.hms.maps.HuaweiMap
import com.huawei.hms.maps.model.BitmapDescriptorFactory
import com.huawei.hms.maps.model.CameraPosition
import com.huawei.hms.maps.model.LatLng
import com.huawei.hms.maps.model.MarkerOptions
  1. Получаем карту и проводим махинации с ней. Для упрощения представим, что у нас все еще Kotlin Synthetics:

mapView.onCreate(null)
mapView.getMapAsync {
		with(it) {
			// Устанавливаем широту и долготу 
			val location = LatLng(latitude, longitude)

			// Указываем тип карты, для Google соответственно GoogleMap.MAP_TYPE_NORMAL
			mapType = HuaweiMap.MAP_TYPE_NORMAL

			// Инициализируем позицию для камеры
            val cameraPosition = CameraUpdateFactory.newCameraPosition(
                CameraPosition.fromLatLngZoom(
                    location,
                    15f
                )
            )

			// Перемещаем камеру
            moveCamera(cameraPosition)
            val icon = BitmapDescriptorFactory.fromResource(R.drawable.marker)
            val markerOptions = MarkerOptions()
                .icon(icon)
                .position(location)

			// Добавляем маркер
            addMarker(markerOptions)
            setOnMapClickListener { clickListener.invoke() }
		}
}

У Huawei подробная документация. Поэтому кучу фичей, которых можно делать с картами, можно найти тут: https://developer.huawei.com/consumer/en/doc/development/HMSCore-Guides/android-sdk-marker-0000001061779995.

Applinking

Диплинки в приложениях — очень важный инструмент для маркетинга, и ваш заказчик точно их захочет. В пуше вы можете вставить такой же диплинк, какой используете на Android. Но когда вы напишете диплинк в заметках и нажмете на него, приложение не откроется.

Как исправить эту историю? На помощь приходит AppLinking.

Доверенные форматы URL

  1. Сначала нужно добавить «Доверенные форматы URL» в нашей консоли. Фактически это хуавеевский URL, который будет префиксом перед вашим реальным диплинком. Идем в «Мои проекты», затем выбираем проект и в левом меню находим вкладку «Рост». В ней — «Создание ссылок» или AppLinking. Нажимаем и попадаем на вкладку «Префиксы URL».

  1. Нажмем на кнопку «Создать префикс URL» и введем какое-нибудь доменное имя типа testapp.

3. Теперь добавим интент-фильтр в манифест нашего приложения, чтобы мы могли обработать такой линк. Делаем такое:

<intent-filter android:autoVerify="true">
       <action android:name="android.intent.action.VIEW"/>
       <category android:name="android.intent.category.DEFAULT"/>
       <category android:name="android.intent.category.BROWSABLE"/>
       <!-- Set android:host to the prefix of your link to be processed. -->
       <data android:host="testapp.drru.agconnect.link" android:scheme="http"/>
       <!-- Set android:host to the prefix of your link to be processed. -->
       <data android:host="testapp.drru.agconnect.link" android:scheme="https"/>
</intent-filter>

Теперь мы на шаг ближе к корректной обработке внешних диплинков.

Белый список URL-адресов

Наш финальный диплинк будет иметь такой вид:

https://testapp.drru.agconnect.link?deeplink=[наш оригинальный диплинк]

Предположим, что [наш оригинальный диплинк], который мы использовали в Android, чтобы открыть историю операций, выглядел так: testapp.link://history. Запомним это и пойдем обратно в консоль.

Диплинки, которые мы хотим обрабатывать, нужно добавить в белый список. Снова проходим этот путь:

«Мои проекты» → «Рост» → «Создание ссылок», AppLinking.

Но в этот раз откроем табик «Белый список URL-адресов».

Нажимаем на кнопку «Создать формат URL-адресов белого списка» и в открывшемся окне заполняем с помощью регулярного выражения [наш оригинальный диплинк].

Наш history отлично обработается, если введем такую штуку: “^testapp.link://.*$”. Нажимаем «Опубликовать».

Отлично, мы добавили наши оригинальные диплинки в белый список, осталось только их обработать в коде.

Обработка линков в коде Android-приложения

  1. Добавим зависимость в Gradle “implementation 'com.huawei.agconnect:agconnect-applinking:1.8.0.300'”.

  2. Теперь туда, где вы обрабатываете обычный Intent (то есть обычный диплинк Android), вставим вот такой обработчик от HMS:

AGConnectAppLinking.getInstance().getAppLinking(this)
.addOnSuccessListener {
      handleDeeplink(it.deepLink.toString())
}.addOnFailureListener {
      Log.e("Deeplink main","FAIL")
}

Тут все достаточно просто. У нас есть обработчик, который может отлавливать успешный линк и также ловить ошибки. Ошибки могут возникать, например, когда вы не добавили свой оригинальный диплинк в список белых URL.

Теперь наше приложение будет открывать диплинки вида https://testapp.drru.agconnect.link?deeplink=[наш оригинальный диплинк] и обрабатывать их из любого другого приложения или сайта.

На этом адаптация под Huawei не заканчивается. Тут я привел базовые советы. Они точно помогут, если вы ни разу не сталкивались с задачами такого типа. Но внутри этой темы есть еще много особенностей и фишек. Это и продуктовая аналитика (HiAnalyticsInstance), и крашлитика, и встроенные покупки, и Huawei Barcode Detector. Но об этом я расскажу в следующей статье.

Если у вас есть какие-то вопросы, задавайте в комментариях — на все отвечу. Еще задавать вопросы можно в нашем телеграм-чате AGIMA Dev. У нас там активное сообщество, и мы всем рады.

P. S. А еще наши друзья из компании AFFINAGE проводят большое исследование по No- и Low-code. Если у вас есть опыт в этой сфере, пройдите опрос. Они даже бота для этого специального сделали.

Комментарии (19)


  1. Rusrst
    00.00.0000 00:00

    Не до конца понятно, это у вас билды под разные типы - HMS, gms?

    С проблемой инкременальной сборки в agconnect решения нет у вас часом?(там deprecated с 8 Gradle сборка, а флаг который можно использовать хоть и работает, но в экспериментальном статусе)


    1. miwas07 Автор
      00.00.0000 00:00

      Привет! Да, билды разные) Чтобы в стор не попадать с сервисами Хуавея

      Про инкрементальную сборку, ты про кэширование артефактов? Если да, то мы на CI холодную собираем для сторов всегда, чтобы проблем не было

      Возможно просто не сталкивались с твоим кейсом, поэтому не сразу понял


      1. Rusrst
        00.00.0000 00:00

        Нет, я про API 'android.registerTransform' is absolete.

        У вас разве нет такого предупреждения при сборке? И если нет то какой студией и какой версией Gradle сборка идёт?


  1. outlingo
    00.00.0000 00:00
    +1

    После if на предмет наличия хуавейских или гугловских сервисов на душе стало грустно и печально.

    А как же все эти вот SOLID, дизайн паттерны с их адаптерами, интерфейсы скрывающие реализации и другие штуки, позволяющие не плодить лапшу из if'ов?


    1. Rusrst
      00.00.0000 00:00
      +2

      Вообще странно, через flawors это делаться должно, тем более в стор с Хуавей сервисами выложить не получится...


      1. miwas07 Автор
        00.00.0000 00:00

        Кстати говоря, Хуавей летом рассылал доку насколько я помню, что можно таки выложиться в плей, если поднять все hms либы до максимальной версии, но я не проверял


    1. miwas07 Автор
      00.00.0000 00:00
      +1

      Привет! if'ы тут для упрощения

      Основная задача, которую вкладывал в статью - что делать, когда менеджер пришел и сказал "Адаптируйте-ка под Huawei". Тут скорее не про то, как красиво написать, а про то, что вообще юзать из HMS в разных кейсах. Архитектура у всех проектах своя и встраивает туда фичи кто как хочет

      Но спасибо за коммент! В следующем посте учту


  1. WraithOW
    00.00.0000 00:00
    +7

    if (context.areGoogleServicesAvailable()) {
        GoogleFusedLocation(
    com.google.android.gms.location.LocationServices.getFusedLocationProviderClient(context)
        )
    } else {
        HuaweiFusedLocation(
            com.huawei.hms.location.LocationServices.getFusedLocationProviderClient(context)
        )
    }

    Хосспаде, хотя бы typealias используйте, дети же могут увидеть.


    object : Thread() {
                    override fun run() {

    <dies from cringe> Thread принимает runnable на входе


    Thread {
        println("Look ma no hands")
    }

    Ну и да, запускать треды из метода, который вызывается из корутин — это что-то из разряда постиронии.


    Вот как мы открываем магазин в Android:

    Эх, вот если бы был какой-то способ подставлять разные строки в зависимости от build flavor.


    За статью пять, за качество кода два. Вся задача — классическая иллюстрация того, для чего нужны абстракции и IoD, и вы умудрились везде напихать ифов.


    P.S. За такое нужно советские деревянные прищепки на соски цеплять.


    catch (e: ApiException) {
    
    }


    1. miwas07 Автор
      00.00.0000 00:00

      Привет! Спасибо за коммент, учту в следующих постах. В целом статья про то, как в целом завести HMS у себя и максимально просто показать, что как у них работает, импортится и тд. Да, код в статье можно улучшать, но посыл тут скорее про то, что делать, когда к тебе с такой задачкой пришли, в какую сторону копать, куда смотреть

      P.S. Про деревянные прищепки поржал - топ))


  1. mopsicus
    00.00.0000 00:00

    На самом деле в Хуавей молодцы, они хорошо подготовились и многие (очень) штуки скопировали у Гугла. В некоторых местах вообще достаточно чуть ли не изменить название зависимости и всё сразу заводится.

    Но вот с Хуавей Айс беда ) Когда мы внедряли её в свои игры было такое ощущение что кроме нас никто больше этого не делал. Серверная верификация не работала, потому что китайцы присылали данные которые оформлены не по спецификации итд. Хорошо что с их стороны были выделены люди которые работали передастами за великую стену. В итоге получилось, но профита который ожидался нет)


    1. miwas07 Автор
      00.00.0000 00:00

      Привет! Да, в целом радует, что ребята из Huawei сделали комфортным процесс адаптации и не надо особых танцев с бубном

      В Aйс кстати не особо погружался, прикольно, не знал) Возможно реально кроме вас никто не делал ????

      А че пилите, если не NDA?)


      1. mopsicus
        00.00.0000 00:00

        Мобильные настольные онлайн в основном: карты, нарды, шашки, домино итд


      1. mopsicus
        00.00.0000 00:00

        Huawei Ads, а не Айс, конечно же)


  1. anonymous
    00.00.0000 00:00

    НЛО прилетело и опубликовало эту надпись здесь


    1. miwas07 Автор
      00.00.0000 00:00

      Привет! С AAB у них вроде все норм, там небольшие махинации с подписью и готово. У них прям в доке есть как выложить -https://developer.huawei.com/consumer/en/doc/distribution/app/agc-help-releasebundle-0000001100316672

      Практика показала, что тестерам удобнее жить с APK, поэтому собираем их

      Про PWA - топовая тема, как-то пилили на нем приложение, но они не все поддерживают пока. Хороший ресурс, где регулярно апдейтят, че может PWA - https://whatpwacando.today/

      Но тут вопрос, почему бы Flutter не завести, потому что в целом, не можешь что-то его средствами сделать - затаскиваешь нативный кусок кода и всё живет, да еще и google тему протаскивает, и в веб в целом завернуть эту историю можно


  1. Firsto
    00.00.0000 00:00
    +2

    Я требую пояснительную бригаду!

    P.S.: А в проде так же пишете? А если это кто-то скопипастит?


  1. cosic
    00.00.0000 00:00
    +1

    Для автоматизации публикаий сборок в Huawei AppGallery можно использовать Gradle плагин https://github.com/cianru/huawei-appgallery-publish-gradle-plugin


  1. zloyreznic
    00.00.0000 00:00

    можно по другому
    делаем отдельные модули для google и huawei
    в проекте gradle
    2 flavor
    и импорт можно так

    "googleImplementation"(project(mapOf("path" to ":di:google")))
    // === HUAWEI ===
    "huaweiImplementation"(project(mapOf("path" to ":di:huawei")))

    Оборачиваем сервисы интерфейсами и работаем в приложении как обычно
    а уже на уровне DI подставляются разные реализации в зависимости от сборки


    1. cosic
      00.00.0000 00:00
      +2

      Расскажу еще один вариант решение зависимостей.

      Вводные:

      1. Существует RuStore, из-за которого непонятно на какое устройство с каками Mobile Services будет установлено приложение

      2. Google Play не рад, что в него льют приложение с HMS зависимостями (может быть это уже закончилось, но инцидент был и еще раз наступать на эти грабли не хочется)

      3. В нашей частной реализации, если на устройстве есть GPS и HMS, то мы предпочтение отдаем GPS и хотели бы, чтобы пользователь использовал их.

      поэтому нужна еще универсальная реализация, которая умеет в runtime делегировать работу GPS или HMS.

      Пунктиром показан флоу подключения зависимостей только для GPS, а сплошной универстальный флоу.
      Пунктиром показан флоу подключения зависимостей только для GPS, а сплошной универстальный флоу.

      Т.о. обернули GPS и HMS в отдельные модули mobile-services-google-impl и mobile-services-huawei-impl с реализацией своих собственных общих интерфейсов из модуля mobile-services-api. Создали дополнительный модуль mobile-services-universal-impl, который так же реализует интерфейсы mobile-services-api, но под капотом через DI делегирует всю работу одной из имплиментаций моделей mobile-services-google-impl или mobile-services-huawei-impl, в зависимости от того, что доступно. В фичевые модули, где нужны Mobile Services, подключается только mobile-services-api, а нужная имплиментация пройвадится через DI.

      Теперь при сборке приложения можно указать через Flavors или -Pgradle флаг что нужно собрать подключив только модуль с GPS реализацией или универсальной реализацией.

          if (isNeedHuaweiMobileServices) {
              implementation(project(':mobile-services-universal-impl'))
          } else {
              implementation(project(':mobile-services-google-impl'))
          }

      Первый вариант можно катить в Google Play, а второй в Huawei AppGallery и RuStore.