Сервисная платформа Huawei — что это за программа и нужна ли она?

Сервисная платформа Huawei — что это за программа и нужна ли она? Каталог

Что это такое?

Имеет отношение к Huawei Mobile Services (HMS) — альтернатива сервисам Google, была выпущена в связи с санкциями. Включает в себя AppGallery (магазин), Browser, Mobile Cloud, Themes, Music, Video, Reader, Assistant и другие программы.

Что такое Сервисы Google? Это не совсем программа, открыть ее нельзя. Но установлено на любом Андроиде. Кроме этого у него немалый список разрешений, содержит множество функция (API для программистов. Простыми словами — все основные гугловские приложения завязаны на этом компоненте.

Без гугловских сервисов не работает и Google Play, приложения нужно качать из App Gallery (всего там около 11 тыс). Да, популярные типа социальных клиентов, известные игры — присутствуют. Однако например приложение местной доставки пиццы или местного такси — там уже не найти.

На телефоне может быть следующая картина в аккаунте Huawei:

Что такое huawei mobile services?

Huawei Mobile Services — это внутренняя начинка смартфона — программы, компоненты и услуги, которые входят в категорию «Предустановленные», и значительно упрощают работу на устройстве. Рассмотрим, что же предлагает нам китайский гигант:

  • AppGallery — фирменный магазин, где пользователям предлагают платные и бесплатные лицензионные программы для гаджета;
  • Huawei Browser — поисковик, который практически ничем не отличается от тех, которыми мы привыкли пользоваться на других устройствах типа Самсунга, Ксиоми или Айфона;
  • Mobile Cloud — удобное и функциональное мобильное облако, куда вы загружаете на хранение свои важные файлы; благодаря такой программе они всегда будут под рукой, и останутся в безопасности, даже если вы потеряете доступ к телефону;
  • Themes — набор модных, стильных и функциональных оформлений для рабочего стола;
  • Music — плеер и музыкальный архив, который под рукой;
  • Video — программа, которая открывает современные форматы видеофайлов и транслирует их в высоком качестве;
  • Reader — «читалка», при помощи которой открываются разные форматы текстовых документов.
  • Assistant и другие программы для комфортного управления мобильным гаджетом.

В этом наборе есть одно безусловное преимущество — телефон стал самодостаточным, и больше не требует поддержки со стороны американского Гугла. Хочется рассказать еще пару особенностей об этих софтах:

Основные причины отказа в публикации

  1. Политика конфиденциальности не соответствует стандарту

    • Отсутствует ссылка на политику конфиденциальности.

    • Ссылка на политику конфиденциальности недоступна.

    • Ссылка на политику конфиденциальности ведет на официальный сайт компании, на котором нет ссылки на политику конфиденциальности.

  2. Указанный статус Гонконга и Макао не соответствует стандарту.Гонконг и Макао не могут быть указаны как страны на странице выбора региона. Китай очень трепетно относится к этому. Пример:

3.Приведены ссылки на сторонние магазины приложений

Функция для оценки и написания отзыва в приложении содержит ссылку на сторонние магазины приложений без ссылки на AppGallery

Почему стоит обратить внимание на экосистему huawei

Смартфоны Huawei очень популярны: в 2020 году в России они занимали почти 18% рынка (Рис.1), а в мире — 11% (Рис.2), (

). Huawei заявила, что более 490 млн человек в более чем в 170 странах мира пользуются AppGallery (

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

Сервисная платформа Huawei — что это за программа и нужна ли она?
Рис.1Сервисная платформа Huawei — что это за программа и нужна ли она?
Рис.2

Polyutil. расшифровка с помощью polyline


Теперь нам нужно отображать перемещение курьера. Для этого нам нужно расшифровывать строку, которая приходит с сервера. Для этого воспользуемся тем же алгоритмом от Google, с помощью которого строка кодировалась.

После расшифровки мы получили список координат курьера.

Product flavors

Создадим два product flavor – “hms” и “gms”:

  • Общий код будет располагаться в директории main/ 

  • Укажем sourceSets в файлах build.gradle модулей (только там, где необходимо разделение на hms и gms)

  • Код с GMS имплементацией будет в папке gms/,  а с HMS соответственно в hms/

  • У hms flavor’a указываем applicationIdSuffix = ‘.huawei’

  • Если же нет необходимости заводить целые файлы отдельно для каждого flavor’a, то можно проверять текущий flavor через BuildConfig.FLAVOR

android {
	
    …
    flavorDimensions 'services'
    productFlavors {
        hms {
            dimension 'services'
            applicationIdSuffix '.huawei'
        }
        gms {
            dimension 'services'
        }
    }
}

По умолчанию, Android Studio заводит sourceSet main, в котором содержатся общие файлы с кодом. Создаем папки для каждого flavor’a:

 New -> Folder -> Выбираем нужный тип папки:

Затем в build.gradle того модуля, где мы создали папку, должен автоматически вставиться следующий код (например, если мы выбрали hms): 

android {
	
    …
    productFlavors {
        ...
    }
    sourceSets {
        hms {
            java {
                srcDirs 'src/hms/java'
            }
            ...
        }
    }
}

Теперь Gradle будет включать в сборку файлы, соответствующие выбранному flavor’у. Важно, чтобы файлы в этих директориях имели одинаковое название и лежали в пакетах с одним и тем же названием.

Пример. Мы используем Auth API. У нас будет абстракция — интерфейс AuthRepository, хранящийся в main/, а его имплементации для разных сервисов лежат в gms/ и hms/ директориях — тогда в сборку, например, для HMS, попадет именно имплементация с huawei сервисами.

Если проект многомодульный, то в каждом модуле необходимо прописать flavor’ы и при необходимости source sets. Код с flavor’ами можно вынести в отдельный файл. 

Создадем .gradle файл в корневой папке проекта, назовем его flavors.gradle:

ext.flavorConfig = {

    flavorDimensions 'services'
    productFlavors {
        hms {
            dimension 'services'
            ext.mApplicationIdSuffix = '.huawei'
        }
        gms {
            dimension 'services'
        }
    }
    productFlavors.all { flavor ->
        if (flavor.hasProperty('mApplicationIdSuffix') && isApplicationProject()) {
            flavor.applicationIdSuffix = flavor.mApplicationIdSuffix
        }
    }
}

def isApplicationProject() {
    return 
    project.android.class.simpleName.startsWith('BaseAppModuleExtension')
}

Помимо самих flavor’ов, в экстеншене flavorConfig лежит код с циклом по flavor’ам — там будет определяться app модуль, которому присваивается applicationIdSuffix.

Затем в каждом модуле прописываем следующее:

apply from: "../flavors.gradle"

android {
    buildTypes {
        ...
    }
    ...
    with flavorConfig
}

Для использования подходящих плагинов во время процесса компиляции можем добавлять такие if-else конструкции:

apply plugin: 'kotlin-kapt'
...
if(getGradle().getStartParameter().getTaskNames().toString().toLowerCase().contains("hms")) {
    apply plugin: 'com.huawei.agconnect'
} else {
    apply plugin: 'com.google.gms.google-services'
    apply plugin: 'com.google.firebase.crashlytics'
}
...

Для каждого flavor’а  мы можем включать dependencies, необходимые только ему. Перед implementation прописываем его название:

// Firebase
gmsImplementation platform('com.google.firebase:firebase-bom:26.1.0')
gmsImplementation 'com.google.firebase:firebase-crashlytics-ktx'
gmsImplementation 'com.google.firebase:firebase-analytics-ktx'

// Huawei services
hmsImplementation 'com.huawei.agconnect:agconnect-core:1.4.2.300'
hmsImplementation 'com.huawei.hms:push:5.0.4.302'
hmsImplementation 'com.huawei.hms:hwid:5.0.3.301'

Push kit – cloud message push- cross-platform messaging solution – huawei developer

HUAWEI Push Kit предлагает быстрый, надежный и точный сервис отправки сообщений. Благодаря этому ваше приложение сможет адаптироваться к широкому спектру сценариев, значительно повышая вовлеченность пользователей.

Внедряем hms сервисы в проект

Почти на каждый сервис Google у Huawei есть альтернатива:

  • Push Kit. Отправка пуш-уведомлений пользователям.

  • Auth Service. В дополнение к привычным способам аутентификации здесь присутствует вход по Huawei ID.

  • Crash Service. Cервис для отслеживания крашей приложения.

  • Cloud Storage, Cloud DB. Хранение различных файлов и база данных.

  • Location Kit. Получение местоположения пользователя.

  • Analytics Kit. Анализ статистических данных приложения.

  • In-App Purchases. Совершение покупок в приложении.

  • Cloud Testing, Cloud Debugging. Тестирование приложений на удаленных устройствах Huawei.

Этот список можно продолжать долго — у Huawei довольно обширный перечень сервисов. Как же подключить их в наш проект?

Прежде всего, нам нужно определиться, как мы будем внедрять сервисы. Есть несколько вариантов:

  • Полностью заменяем GMS сервисы на HMS сервисы

  • Делаем комбинацию GMS и HMS сервисов в одном проекте

В этом деле нам пригодится плагин HMS Toolkit. Он поможет интегрировать HMS Core с минимальными затратами и высокой эффективностью. К сожалению, HMS Toolkit поддерживает конвертацию не всех сервисов, и лучше заранее уточнить, сможет ли он перенести работу ваших.

Нам нужен инструмент Convertor. Он проанализирует проект на наличие GMS сервисов и покажет места, где требуется заменить код с GMS на HMS.

  1. В меню выбираем HMS > Convertor > New Conversion:

2.В появившемся окошке указываем директорию, где создастся бэкап проекта до конвертации.

3.Здесь плагин представляет результаты анализа проекта: какие GMS сервисы у нас содержатся и какие из них конвертируемые. Также нам предлагается проверить sdk version для соответствия требованиям HMS.

На этом шаге мы должны выбрать стратегию конвертации:

  • Add HMS API. На основе существующих в проекте GMS APIs генерируется XMS adapter (как дополнительный модуль в проекте). Он представляет собой прослойку между нашим кодом и непосредственно вызовом сервисов. Это такие Extension-классы, в которых лежит код, поддерживающий HMS и GMS сервисы одновременно. В runtime определяется поддерживаемый девайсом вид сервисов и вызываются соответствующие методы.

  • To HMS API – полностью заменяются GMS APIs на HMS APIs.

4.После анализа проекта, мы видим список мест в коде, где необходима конвертация.

По клику на каждый пункт произойдет навигация в файл, где будет предложена конвертация:

Для чего нужен huawei mobile services?

Основное предназначение этого пака утилит — заменить недостающие элементы системы, которые раньше покрывала компания Google. Это были:

  • браузер;
  • почта;
  • карты;
  • диск;
  • Play Фильмы;
  • Play Музыка;
  • Погода;
  • Google Photo;
  • Ютуб и другие.

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

Как отключить huawei mobile services

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

Как удалить huawei mobile services

Эти программы не получится стереть с памяти устройства, так как софты предустановлены разработчиком. Если они вам мешают, то остановите их через настройки.

Как установить

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

Какие приложения совместимы с hms?

Мобильные службы Huawei — функциональные и защищенные, имеют высокий процент адаптированности под другие программы, поэтому конфликта ПО замечено не было. Здесь активно функционирует все, что понадобится вам для комфортного использования телефона.

Плюсы и минусы

Преимущества такого набора программ очевидны:

  • новые возможности;
  • самостоятельность компании;
  • хорошая техническая сборка;
  • регулярные обновления.

К явным минусам относится только то, что многих программ вы не найдете в App Gallery. Например, такси, доставка еды и прочее. Но если будет спрос, разработчики их добавят.

Подводные камни

В политике Google Play есть замечание:

“Any existing app that is currently using an alternative billing system will need to remove it to comply with this update. For those apps, we are offering an extended grace period until September 30, 2021 to make any required changes. New apps submitted after January 20, 2021 will need to be in compliance.”

Что это значит для нас? Теперь, если приложение одновременно поддерживает HMS и GMS сервисы, и в нем есть In-App Purchases, то Google Play не допустит его публикации, а существующим приложениям придется удалить этот функционал.В итоге, если был выбран первый способ конвертации (Add HMS API), мы имеем:

  • Большое количество сгенерированных классов.

  • Увеличенный размер APK приложения.

  • Невозможность публикации приложения в Google Play, если в нем есть In-App Purchases.

  • Неполную поддержку одновременной работы HMS & GMS для некоторых сервисов.

Решение: Более привлекательным вариантом кажется второй способ конвертации — простая замена GMS APIs на HMS APIs. Но вместе с этим используем product flavors, чтобы получать сборки приложения отдельно для Google Play и AppGallery.

Проблема: «карта не работает»

Сервисная платформа Huawei — что это за программа и нужна ли она?

Однажды нам сообщили о баге. Пользователь с устройством Huawei, находившийся в центре Москвы (Рис.3), открыл приложение, нажал на кнопку «Переместиться на своё местоположение», и его перенесло в пустоту (Рис.4). Пользователь не видит, ни улиц, ни зданий, и он решил, что карта не работает.

Мы попробовали воспроизвести у себя эту проблему. И действительно попадали в неопределённое пространство. Когда попробовали чуть-чуть уменьшить масштаб карты, то оказалось, что мы попали в пригород Мариуполя (Рис.5). То есть из московских координат (55.819207, 37.493424) перенеслись в мариупольские (47.187447, 37.593137).

Мы были в полном недоумении. Может быть, где-то у нас с числами что-то не то происходит. Возможно, происходят некие вычитания наших координат. Очень долго искали решение этой проблемы или хотя бы причину. Оказалось, что мы заменили импорты из Google-карт, и поэтому всё перестало работать. В конце концов мы добрались до padding’а.

Давайте быстро вспомним, что такое padding у карты. На (Рис.6) показан экран авторизации, карта занимает всю область экрана, даже под плашкой ручного ввода адреса. В таком случае, если мы не добавим padding карте, её центр будет находиться на месте зелёного треугольника, но мы хотим, чтобы он был в центре рабочей области карты.

Padding сужает рабочую область (Рис.7). Не видимую, а именно рабочую. Карта будет по-прежнему занимать весь экран, но размер её рабочей области изменится. И когда вы будете переходить в новую координату, она будет принимать положение новой рабочей карты. Как оказалось, баг был именно из-за этого.

Первое решение: убрать padding. Как вы понимаете, такой вариант нам не подошёл. Мы хотели, чтобы всё отображалось красиво.

Второе решение проблемы: использовать анимированное перемещение, но с масштабированием.

val zoom = map.cameraPosition.zoom
map.animateCamera(CameraUpdateFactory.newLatLngZoom(position, zoom))

При переходе с изменением масштаба карты всё работало правильно. Здорово! Мы подумали, что это нам подходит. На самом деле нет. У нас ещё есть третий экран, на котором нужно увеличивать карту относительно двух маркеров, чтобы

zoom

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

Третье решение проблемы: вообще отказаться от анимации. Как оказалось, если вместо animateCamera сделать просто move, то перемещение будет происходить правильно. Так мы и сделали. Надеемся, в скором времени Huawei устранит эту проблему.

Публикуем приложение в appgallery

После внедрения сервисов и успешного тестирования приложения, мы готовы публиковаться в AppGallery.

1.Переходим в AppGallery Connect и заполняем данные:

2.Грузим иконку приложения и скриншоты. Есть возможность прикрепить видео.

3.Указываем страны/регионы для публикации и грузим APK приложения. Кроме того, нужно загрузить подпись приложения.

4.Отмечаем способ покупок в приложении и рейтинг.

5.Грузим политику конфиденциальности (обязательно) и предоставляем данные тестового аккаунта, если это необходимо. Указываем дату публикации.

6.Нажимаем кнопочку “Отправить на проверку” и ждем! Проверка по регламенту занимает около 3-5 дней.

Реализация поддержки двух карт


Для поддержки нескольких карт необходимо создать обёртку для самих карт и для объекта.

Добавляем общий интерфейс, например, IMapWidget. Не забываем сделать общий класс для LatLng — список координат курьера. У Google он лежит в пакете com.google.android.gms.maps.model.LatLng, а у Huawei в com.huawei.hms.maps.model.LatLng. Кладём список в PolyLineOptions и задаём ширину и цвет линии маршрута.

interface IMapWidget {

    void animateCamera(...);

    void setListener(OnMapEventListener listener);

    void setMapPadding(...);

    MapMarker addMarker(...);

    ...
}

Добавляем Custom Map View реализующего интерфейс

IMapWidget

Добавляем обёртку, которая позволяет нам указать, где мы хотим отрисовывать карту:

class MapWrapper : FrameLayout() {

    fun setupMap(widget: IMapWidget) {
        removeAllViews()
        addView(widget as View)
    }
}

И в нужном месте вызываем метод добавления карты.

override fun onCreateView(...) {
    ...
    val map: IMapWidget = MapFactory.createMap()
    viewMapWrapper.setupMap(map)
    ...
}

Такие обёртки класса нужно создать для всего: объектов, маркеров,

PolyUtilPolyLine

и т.д.

С чего начать?

Чтобы взаимодействовать с Huawei-системой, нужно завести Huawei ID. Это аналог google-аккаунта, с помощью которого предоставляется доступ к сервисам системы. Далее нужно зарегистрировать аккаунт разработчика: индивидуальный или корпоративный.

  • Индивидуальному разработчику нужно ввести свои ФИО, адрес, телефон, почту. В отличие от регистрации аккаунта разработчика в Google Play, нужны также сканы паспорта и банковской карты. Да-да, документы требуются для удостоверения личности. Huawei обещает удалить их после регистрации. 

  • Для регистрации корпоративного аккаунта требуются данные компании, либо DUNS number (международный идентификатор юридических лиц), либо бизнес лицензия.  

Ждем одобрения аккаунта. За 1-2 дня Huawei обещают проверить наши данные. После этого можно подключать приложение к HMS. Для этого заходим в консоль AppGallery Connect.

  1. Создаем проект, а в нем добавляем приложение

Обращаем внимание, что для приложения, в котором используются HMS, название пакета должно оканчиваться на “.huawei”.

2.Помещаем конфигурационный файл agconnect-services.json в корневую папку приложения. Также сохраняем хэш SHA-256. Он потребуется для аутентификации приложения, когда оно попытается получить доступ к службам HMS Core.

Примечание. Для того, чтобы получить SHA-256, можно выполнить команду в терминале, подставив необходимые данные из вашего keystore:

keytool -list -v -keystore <keystore path> -alias <key alias> -storepass <store password> -keypass <key password>

Для работы некоторых сервисов нужно указать место хранения данных:

3.Добавляем зависимости в проект Android Studio.В build.gradle на уровне проекта:

Тестируем и отлаживаем приложение

После того, как мы внедрили Huawei сервисы в приложение, нам нужно протестировать его работоспособность.

У Huawei есть облачная платформа DigiX Lab, в которой представлены 2 сервиса.

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

По окончании каждого теста доступен подробный отчет. Можно узнать о сбоях, ANR, утечках ресурсов, также увидеть загрузку процессора, использование памяти и многое другое. Плюсом будут доступны скриншоты, которые снимаются во время тестирования и журналы Logcat.

Тесты можно запускать либо с помощью плагина в Android Studio:

Либо в консоли AppGallery, выгрузив туда свой APK:

Служба облачной отладки решает проблему отсутствия реальных устройств Huawei. Предоставляется список удаленных устройств, а разовый сеанс работы до 2 часов. Сервис дает 24 часа работы бесплатно после подтверждения личности. Можно подавать заявки на продление срока действия неограниченное количество раз. Отладка также доступна из Android Studio и консоли.

Этап 1: проверка наличия services

Если у вас в приложении при входе есть проверка наличия Google Services, то придётся от этого отказаться, и проверять наличие соответствующих сервисов только по мере необходимости.

Этап 2: карты

В приложении Delivery Club три основные страницы:

  • карта с местоположением клиента;
  • информация о ресторане, который будет готовить заказ, с указанием его местоположения на карте;
  • карта с отслеживанием местоположения курьера, везущего заказ.


На устройствах Huawei все эти карты не работают. Чтобы это исправить, можно просто заменить зависимости: вместо пакета

com.google.android.gms

использовать

com.huawei.hms

Конечно, есть нюансы, но мы уже сделали большу́ю часть работы. Huawei сделала Maps SDK с контрактами, по большей части соответствующий Google Maps SDK. Однако у Google есть deprecated-методы, если вы их используете, то аналогов у Huawei может и не найтись. Например, для получения местоположения пользователя мы используем:

LocationServices.FusedLocationApi.getLastLocation(googleApiClien)

Такой подход считается deprecated, и если мы просто скопируем код для Huawei Maps и заменим зависимости, то работать не будет. Нужно поменять так:

LocationServices.getFusedLocationProviderClient(…)
.getLastLocation()
.addOnSuccessListener(…)

Этап 3: push-сервис


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

FirebaseMessaging.getInstance().token.addOnCompleteListener { task ->
    if (task.isSuccessful) {
        val token = task.result
    }
}

Наше решение:

class ImplementationHuaweiMessagingService : HmsMessageService() {

    override fun onNewToken(token: String?) {
        val commonApi = getComponentFactory().get(CommonApi::class.java)
        commonApi.settingsManager().setPushToken(token)
    }

    override fun onMessageReceived(message: RemoteMessage?) {
        message?.let {
            val appManagersComponent = getComponentFactory().get(AppManagersApi::class.java)
            appManagersComponent.pushManager().handle(it.dataOfMap)
        }
    }

Выглядит всё так же, как и с реализацией

FirebaseMessagingService()

, даже есть

callbackonNewTokenonMessageReceived

. Однако без нюансов не обойтись. Случается, что на некоторых редких устройствах

onMessageReceived

вызывается в главном потоке, поэтому лучше не использовать здесь долго выполняющиеся задачи.

Получаем токены на Huawei:

val token = HmsInstanceId.getInstance(context)
    .getToken(appId, com.huawei.hms.push.HmsMessaging.DEFAULT_TOKEN_SCOPE)



public static final String DEFAULT_TOKEN_SCOPE = "HCM";

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

Мы можем вообще не использовать getToken, а прописать в манифесте автоматическую инициализацию или в коде методом setAutoInitEnabled() и всегда получать token в onNewToken (подробнее). Это решит ещё одну проблему: getToken в версиях EMUI ниже 10 вообще возвращает null.

Этап 4: chrome custom tabs


Наше приложение при запуске регулярно вылетает с ошибкой

ActivityNotFoundException

. Чтобы от этого избавиться, нужно обработать отсутствие Chrome Tabs.

fun Context.openLink(url: String, customTabsSession: CustomTabsSession? = null): Boolean {
    try {
        openLinkInCustomTab(url, customTabsSession)
        return true
    } catch (throwable: Throwable) {
        Timber.tag("Context::openLink").e(throwable, "CustomTabsIntent error on url: $url")
    }

    return openLinkInBrowser(url)
}

@Throws(Throwable::class)
fun Context.openLinkInCustomTab(url: String, customTabsSession: CustomTabsSession? = null) {
    CustomTabsIntent.Builder(customTabsSession)
        .build()
        .launchUrl(this, Uri.parse(url))
}

private fun Context.openLinkInBrowser(url: String): Boolean {
    val intent: Intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)).apply {
        addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY or Intent.FLAG_ACTIVITY_NEW_DOCUMENT)
    }

    if (intent.resolveActivity(packageManager) != null) {
        startActivity(intent)
        return true
    }

    return false
}

Мы просто обернули

openLinkInCustomTab()try catch

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

intent

. Поэтому если метод

openLinkInBrowser()

возвращает

false

, мы открываем страницу в

webview

Этап 5: аналитика


Аналитика у Huawei похожа на Google Analytics. Покажу замену на примере Firebase. Сначала инициализируем:

HiAnalytics.getInstance(context)

. Затем с помощью

HAEventType.STARTCHECKOUT

копируем все наши события из Firebase в отдельный файл

huaweiAnalytics

huaweiAnalytics.onEvent(name, bundle)

Системные параметры:

HAParamType.PRICEHAParamType.CURRNAME

Даже если у вас нет Firebase, добавить аналитику в Huawei очень просто. У них отличная документация, контракт соблюдается. Также у Huawei есть отличные инструменты для исследования аудитории.

Этап 6: crashlytics

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

AGConnectCrash

. Она позволяет с минимальными усилиями собирать и анализировать информацию о падении приложения.

Инициализируем crashlytics:

AGConnectCrash.getInstance().enableCrashCollection(true)

Добавляем свои ключи и журналируем нужные события:

Этап 7: покупки в приложении

Если в вашем приложении есть встроенные покупки, вы должны подписать согласие на передачу персональных данных, и отослать письмом в конверте или через курьера в московский офис Huawei. И только через пару дней они вам дадут доступ для реализации этой функциональности.

Всё очень похоже на реализацию Google. При запуске приложения запрашиваем все прошлые покупки пользователя:

fun getOwnedPurchases(
    activity: Activity,
    ownedPurchasesResultOnSuccessListener: OnSuccessListener<OwnedPurchasesResult>,
    failureListener: OnFailureListener
) {
    val ownedPurchasesReq = OwnedPurchasesReq()

    // priceType: 0: consumable; 1: non-consumable; 2: auto-renewable subscription
    ownedPurchasesReq.priceType = IapClient.PriceType.IN_APP_SUBSCRIPTION

    // To get the Activity instance that calls this API.
    val task: Task<OwnedPurchasesResult> = Iap.getIapClient(activity)
        .obtainOwnedPurchases(ownedPurchasesReq)

    task.addOnSuccessListener(ownedPurchasesResultOnSuccessListener)
        .addOnFailureListener(failureListener)
}

Если какой-то товар был куплен, мы разблокируем его функциональность. Потом запрашиваем подробности по товарам, доступным для продажи — цену и описание:

fun loadProduct(
    context: Context,
    productInfoResultOnSuccessListener: OnSuccessListener<ProductInfoResult>,
    onFailureListener: OnFailureListener
) {
    // obtain in-app product details configured in AppGallery Connect, and then show the products
    val iapClient: IapClient = Iap.getIapClient(context)
    val task: Task<ProductInfoResult> = iapClient.obtainProductInfo(createProductInfoReq())
    task.addOnSuccessListener(productInfoResultOnSuccessListener)
        .addOnFailureListener(onFailureListener)
}

private fun createProductInfoReq(): ProductInfoReq {
    val req = ProductInfoReq()
    // 0: consumable ; 1: non-consumable ; 2: auto-renewable subscription
    req.priceType = IapClient.PriceType.IN_APP_SUBSCRIPTION

    val productIds = ArrayList<String>()
    productIds.add("PRODUCT_ID")
    req.productIds = productIds
    return req
}

Когда пользователь кликает на товар, мы открываем страницу с оплатой. Она не такая красивая, как у Google, и не выезжает снизу.

fun gotoPay(activity: Activity, productId: String, type: Int) {
    val client: IapClient = Iap.getIapClient(activity)
    val task: Task<PurchaseIntentResult> = client.createPurchaseIntent(createPurchaseIntentReq(type, productId))
    task.addOnSuccessListener { result ->
        result?.let {
            val status: Status = result.status
            if (status.hasResolution()) {
                try {
                    status.startResolutionForResult(activity, PAY_RESULT_ARG)
                } catch (exception: SendIntentException) {
                    Timber.e(exception)
                }
            } else {
                Timber.d("intent is null")
            }
        }
    }.addOnFailureListener { exception ->
        Timber.e(exception)
    }
}


Так как это Activity, мы передаём ему аргумент, по которому можно отловить

OnActivityResult

и понять, успешно ли прошла оплата и как закончилась транзакция:

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if (resultCode == PAY_RESULT_ARG) {
        val purchaseResultInfo: PurchaseResultInfo = Iap.getIapClient(this).parsePurchaseResultInfoFromIntent(data)
        when (purchaseResultInfo.returnCode) {
            OrderStatusCode.ORDER_STATE_SUCCESS -> {
                successResult(purchaseResultInfo)
            }

            OrderStatusCode.ORDER_STATE_CANCEL -> {

            }

            OrderStatusCode.ORDER_PRODUCT_OWNED -> {

            }
        }
    }
}

У нас есть специальные статусы:

ORDER_SUCCESSCANCELOWNED

. Первый означает успешную оплату. Второй — пользователь просто закрыл страницу без покупки, тогда мы обрабатываем этот callback и предлагаем скидку, чтобы уговорить на покупку. А третий статус означает, что товар уже куплен пользователем. Если товар разовый или подписочный, то на этом моменте нужно остановиться, в противном случае виртуально доставить покупку.

В случае успешной оплаты доставляем пользователю купленный товар:

private fun successResult(purchaseResultInfo: PurchaseResultInfo) {
    val inAppPurchaseData = InAppPurchaseData(purchaseResultInfo.inAppPurchaseData)
    val req = ConsumeOwnedPurchaseReq()
    req.purchaseToken = inAppPurchaseData.purchaseToken

    val client: IapClient = Iap.getIapClient(this)
    val task: Task<ConsumeOwnedPurchaseResult> =
        client.consumeOwnedPurchase(req)

    task.addOnSuccessListener {
        // Consume success
    }.addOnFailureListener { exception ->
        Timber.e(exception)
    }
}

Если не сделать доставку, то функциональность товара будет у пользователя заблокирована, а деньги возвращены. В Google Play Billing Library до третьей версии этого делать не нужно было, но потом Google тоже это добавил, и если мы не доставим товар, через 48 часов покупка отменится, а деньги вернутся пользователю. То есть в Huawei покупки реализованы как в третьей версии Google Play Billing.

Заключение

  1. Сервисная платформа Huawei — относится к Huawei Mobile Services, что является альтернативной гугловским сервисам (поиск Гугл, маркет, карты).
  2. Отключать не стоит, могут перестать работать многие предустановленные программы.
  3. При огромном желании — создать заранее бэкап, и не удалять, а заморозить используя Titanium Backup (необходимы root-права).

Удачи.

На главную!29.02.2020

Итоги

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

Выводы


На реализацию поддержки Huawei-устройств не уйдёт много времени. Даже без реальных устройств вы сможете проверить работоспособность вашего приложение: у Huawei есть своя тестовая лаборатория с виртуальными устройствами наподобие

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

Видеозапись доклада с конференции Mobius 2020.

Оцените статью
Huawei Devices
Добавить комментарий