If you landed here, I'm confident you:
- know what are the Huawei Mobile Services (HMS) , Huawei's alternative to Google Play Services (GMS)
- are aware that - because of US-China trade war - the new Huawei Android powered devices (P40 and Mate 30 series upwards) will not have GMS preinstalled anymore, therefore users cannot access Google Play (GP) to download your apps and instead they can do it via Huawei's AppGallery.
- have the desire to onboard AppGallery (AG) with your apps already published on GP.
Visit: https://developer.huawei.com/consumer/en/appgallery/ and look for the “Sign up” in the upper-right corner of the page. The process will guide you through the following steps:
- first, create a Huawei ID (your identity in the Huawei universe, same as the Google account): this ID will be the (future) AG account holder (further explanations here ). Should you need to only create a developer account (without access to AG - i.e. just to try Health Kit), then please visit: https://developer.huawei.com/consumer/en/console and look again for "Sign up" in the upper-right corner.
- next, verify your identity as a developer or enterprise; a comparison between the benefits of both roles can be found here and the different requirements to successfully verify the identity, here.
- it takes between 1 to 2 working days to be approved (but the good part is that the account creation is FREE)
- now the AG account is ready to be used and you can access the portal at the same link used for registration: https://developer.huawei.com/consumer/en/appgallery/
Similar to GP, you'll basically create a new app in AG -> My apps and customize its appearance on the store: upload an icon, descriptions, screenshots and so on. Explanations are provided directly in the portal, but if you ever get stuck you can find the full documentation here.
A project is a collection of resources and settings such as: API management, user and apps data, data storage location, authenticated signing certificates, etc. A project can contain multiple apps that share the same configuration. On AG each app is identified by a unique integer named appId . A further requirement - same as on GP - is that the app needs to have an unique package name = applicationId.
Projects are needed only if you plan to integrate HMS at some point (in Step 4).
This step is optional as you can directly skip to Step 4 if you wish to integrate HMS in your apps now.
But there could be 2 reasons why this step would still make sense.
Just a quick reminder: all that's pure Android still works on HMS devices, only the GMS don't.
Generally speaking, any dependency starting with com.google.android.gms:play-services-
is an indication you rely on GMS. Google provides a list here: https://developers.google.com/android/guides/setup#list-dependencies
Furthermore, parts of Firebase are counting on GMS to function properly: https://firebase.google.com/docs/android/android-play-services (as you can see, many services were recently decoupled from GMS)
Ultimately, you could use this service from Huawei called QuickHMS - you simply search for a dependency/framework/library and find out if it can run as it is on HMS devices (or which HMS SDKs can replace that dependency - but we'll come back to this later, during step 5): https://quickhms.com/en/compatibility-check/
Reason 2: it's ok for now to distribute your apps only to those Huawei devices still having Google Play Services
There are plenty of users on Huawei devices that still have GMS, devices released before the US trade ban was enforced. Those devices are still quite powerful - which means they will most likely stick around for the time being. Although it's true that users can go to GP to find and download your apps, sometimes they don't - simply because they might be more familiar with AG that benefits from better exposure on those devices.
Before submitting a new version, specify in the Review information text field: only GMS
. Having trouble locating the field? Instructions here, under Completing other version information.
Later, when a newer version doesn’t need anymore to be distributed only on GMS (because it supports HMS as well now), change the Review information text field to GMS and HMS
. This is needed only if there was a only GMS
mention previously, otherwise apps are by default distributed on GMS and HMS devices.
It’s not possible to distribute only on HMS devices. The choices are only 2: only GMS
and GMS and HMS
(the default)
Anticipating the next step and how to grow the apps further inside AG it's maybe a good time now to discuss different choices when it comes to app signing.
If you use Google Play App Signing then it's obvious you will have to use a different signing key for AG as Google controls now the signing key and you have no access to it anymore (you only have the upload key).
-
Sign with the same key for both GP and AG:
This makes sense especially if at step 5 you plan to opt for including both GMS and HMS SDKs in the same build/binary. Since there is only one build (therefore an unique applicationId), it's convenient to have also only one binary - that can be uploaded at the same time to both GP and AG. Furthermore, on Huawei devices still having GMS, both app stores exist for now, but there is no guarantee that GP will still be available in the future. What you want in that case is to have a seamless upgrade experience where both GP and AG - because both stores can update your app since the signature is the same.
Don't use the same key if you know for sure you want to have a different build for HMS, but you wish to keep the same applicationId - because competing updates from GP and AG (on devices having both) can make the user experience weird: i.e. version
v
(coming from GP) has Google Maps, the next onev+1
happens to be from AG, so it uses HMS Maps and maybe later GP updates tov+2
and users are back on Google Maps. -
Sign with different keys:
Opting for this option on purpose makes sense in the situation you wish to distinguish updates coming via AG from the ones from GP for the same applicationId. Different signatures means that all updates will come only from the app store used to download the app in the first place. The other store will silently fail to update the app because signatures don't match.
Warning: Google Play Protect might block the initial app installation from AG since it detects that the version on GP is signed with a different key. You could try to file an appeal with Google here, but their answers are not always satisfying: the approval process is as obscure and random as the initial app blocking. To generalize from some data points: apps with lots of downloads on GP have a higher chance of a positive outcome of the appeal.
To summarize this step, all you need to do for now is to to complete the app listing at step 2 by creating a new release and uploading an apk/aab. More details here.
Before using any of the HMS SDKs (see full offering here: https://developer.huawei.com/consumer/en/hms) there are several configuration steps.
They are detailed here and again in this codelab, but let's do a short summary of every step.
It can have any name (not visible to users) and you need to link to it the app(s) that will share the project's configuration. Create a new project from AG -> My projects (direct link). Not sure why you need a project?
Correctly setting the data location at project creation time is important considering that many services you might later need (i.e. push notifications) work only with the project's data location. The current options are: China, Germany (GDPR compliant), Russia and Singapore. To reduce delays at a minim, chose the data location that's closest to where most of your users are located.
Your app's Android project has to contain a special configuration file - same as for Google Play and the google-services.json. This file can be easily generated from AG immediately after creating a new app on the portal or at any time from App Information under General information. Later changes in the used AG APIs could add additional information to the file, so you might need to re-generate it.
Back to your Android project, place the file under the module that uses HMS SDKs (usually that's the main module /app).
What if you have multiple build variants and/or flavors that alter the applicationId?
Most of the time you will have one production flavor (that needs to go live on the store) and other flavors for different testing purposes, each one with a different applicationId. For AG they count as different apps (since the applicationId is different), therefore, you will need to create in AG, under the same project, a new app entry for every applicationId. You only have to configure the package name information (equal to the applicationId) - no need to fill anything additional. Then generate a different agconnect-services.json file for each app and place them under the respective source sets. See an example here.
Go to the current project in AG and under Manage APIs you can toggle on/off various APIs. Some will require to generate a new agconnect-services.json.
AG will reject any API requests if your app's signing configuration is unknown to AG. To add (any number of) signing certificate SHA-256 fingerprints, simply go the current project in AG and look under General information for SHA-256 certificate fingerprint : press the add icon and paste the fingerprints you wish to authenticate. As guessed, the debug variant needs to authenticate as well.
This is a quick guide on how to retrieve the fingerprint for all the signing configurations. Just remember AG accepts only SHA-256.
allprojects {
repositories {
...
maven {url 'https://developer.huawei.com/repo/'}
}
}
...
buildscript {
repositories {
...
maven {url 'https://developer.huawei.com/repo/'}
}
...
dependencies {
classpath 'com.huawei.agconnect:agcp:1.6.3.300'
}
}
Check the newest versions of agcp here and for any other dependency here.
Let's quickly get this out of the way: having both GMS and HMS SDKs in the same build & binary poses no problem or compatibility issues and both GP and AG accept such apps.
In-app-purchases might be a sensitive topic, as GP explicitly forbids the usage of other payment systems and - although it's not the case now - it might in the future scan your app binary and simply reject the app based on the fact that it contains another IAP SDK (even if not used on GMS phones). Besides this, it makes sense to go separate variants if you're sensitive about increasing your app size (although each HMS SDK is merely a couple hundreds KB) or you're simply more comfortable with different source sets for GMS and HMS.
Otherwise, having GMS and HMS together - and deciding at runtime which one to use based on availability - has its advantages:
- a single resulting binary to upload to both GP and AG
- a natural upgrade path in AG - once the already published GMS-only binary (discussed at step 3) receives support for HMS
- allows the use of wrappers (more about that later) which instantly add equivalent HMS support to already existent GMS code
Perhaps you already test the device if it has GMS with a snippet like this:
boolean isGmsAvailable(android.content.Context context) {
return com.google.android.gms.common.GoogleApiAvailability
.getInstance()
.isGooglePlayServicesAvailable(context) ==
com.google.android.gms.common.ConnectionResult.SUCCESS;
}
The API for doing the HMS check is identical:
boolean isHmsAvailable(android.content.Context context) {
return com.huawei.hms.api.HuaweiApiAvailability
.getInstance()
.isHuaweiMobileServicesAvailable(context) ==
com.huawei.hms.api.ConnectionResult.SUCCESS;
}
Or, maybe you have code that makes use of more features from GoogleApiAvailability : besides a simple check, allow users to react on the missing GMS. Collect the result and decide how to proceed ( continueWithGmsFeatures()
or continueWithoutAnyMobileServicesFeatures()
) in a single place: the activity's onActivityResult as illustrated by this example:
class MyActivity extends Activity {
final int ANY_INTEGER_REALLY = 16041982;
final int REQUEST_CODE_GMS_CHECK = ANY_INTEGER_REALLY;
final int AVAILABLE = Activity.RESULT_OK;
final int UNAVAILABLE = Activity.RESULT_FIRST_USER + 1;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
startGmsCheck();
}
}
void startGmsCheck() {
final com.google.android.gms.common.GoogleApiAvailability apiAvailability = com.google.android.gms.common.GoogleApiAvailability.getInstance();
final int availabilityCheckResult = apiAvailability.isGooglePlayServicesAvailable(this);
if (availabilityCheckResult == com.google.android.gms.common.ConnectionResult.SUCCESS) {
onActivityResult(REQUEST_CODE_GMS_CHECK, AVAILABLE, null);
} else if (
apiAvailability.isUserResolvableError(availabilityCheckResult)
&& apiAvailability.showErrorDialogFragment(
this, availabilityCheckResult, REQUEST_CODE_GMS_CHECK)) {
// user can do something about the missing GMS on the device -> receive the result via the activity's onActivityResult()
} else {
onActivityResult(
REQUEST_CODE_GMS_CHECK, UNAVAILABLE, null);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_GMS_CHECK) {
if (resultCode == AVAILABLE) {
continueWithGmsFeatures();
} else {();
continueWithoutAnyMobileServicesFeatures();
}
}
}
}
}
Again, the HMS equivalent API is identical, so you could add a new constant REQUEST_CODE_HMS_CHECK
and a method to check and respond to a missing HMS Core:
void startHmsCheck() {
final com.huawei.hms.api.HuaweiApiAvailability apiAvailability = com.huawei.hms.api.HuaweiApiAvailability.getInstance();
final int availabilityCheckResult = apiAvailability.isHuaweiMobileNoticeAvailable(this);
if (availabilityCheckResult == com.huawei.hms.api.ConnectionResult.SUCCESS) {
onActivityResult(REQUEST_CODE_HMS_CHECK, AVAILABLE, null);
} else if (apiAvailability.isUserResolvableError(availabilityCheckResult)
&& apiAvailability.showErrorDialogFragment(
this, availabilityCheckResult, REQUEST_CODE_HMS_CHECK)) {
// user can do something about the missing HMS on the device -> receive the result via the activity's onActivityResult()
} else {
onActivityResult(REQUEST_CODE_HMS_CHECK, UNAVAILABLE, null);
}
}
In theory, you can install the latest HMS Core on any arm/arm64 device. Either from AG - after installing first AG from here - or directly from this direct link to the apk. But maybe it makes sense to guide users into updating/installing HMS Core only on Huawei devices and that can be quickly checked:
boolean isHuaweiDevice() {
return "huawei".equalsIgnoreCase(android.os.Build.MANUFACTURER);
}
Putting it all together and collecting all the outcomes again in MyActivity.onActivityResult
:
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
startGmsHmsCheck();
}
}
void startGmsHmsCheck() {
if (isGmsAvailable(this)) {
startGmsCheck(); //or directly call: continueWithGmsFeatures();
} else if (isHmsAvailable(this)) {
startHmsCheck(); //or directly call: continueWithHmsFeatures();
} else if (isHuaweiDevice()) {
startHmsCheck();
} else {
startGmsCheck();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
continueWithGmsOrHmsFeatures(requestCode, resultCode);
}
void continueWithGmsOrHmsFeatures(int requestCode, int resultCode) {
if (requestCode == REQUEST_CODE_GMS_CHECK && resultCode == AVAILABLE) {
continueWithGmsFeatures();
} else if (requestCode == REQUEST_CODE_HMS_CHECK && resultCode == AVAILABLE) {
continueWithHmsFeatures();
} else {
continueWithoutAnyMobileServicesFeatures();
}
}
When you opt to include both GMS and HMS SDKs in the same build, it would be very convenient if you could add support for HMS without changing your codebase. The wrapper libraries make it possible with just a package rename/replace - from the GMS packages to the wrapper's packages. A rename is all it takes, because the wrapper's API is completely identical to the GMS SDK it wraps. Then, the wrapper takes care of routing the calls to GMS or HMS - depending on the availability on the device - and of adapting the HMS differences to the GMS interface.
Available to use now are these 2 wrapper libraries for:
- Fused Location: https://github.com/abusuioc/hms-gms-wrapper-location
- Maps: https://github.com/franalma/MapsWrapper (with a Kotlin only variant: https://github.com/m0skit0/maps-wrapper)
Consider also this (Kotlin only) collection of wrappers if you accept a few changes in your code: https://github.com/bluesource/ChoiceSDK
Being by far the most used feature that depends on GMS, let's look at the challenges of making your apps receive push notifications on HMS devices.
First of all, if you're using an SDK from the following push notifications providers, they already include support for the HMS Push Kit. Since you already configured your app at step 4 it might just work out of the box (read more in the description link if there are additional steps required).
Already using SNS? Then this might be helpful: https://serverlessrepo.aws.amazon.com/applications/eu-west-1/436063517074/sns-push-hms
Otherwise, you'll need to write some code to integrate the HMS Push SDK into the mobile app and then establish a connection from your server to the HMS Push Server. This last part is optional when you wish to send push notifications directly from the App Gallery console.
Before anything:
- review the project's data storage location because you need one especially if you plan to use topic messages and send push notifications to iOS devices. If the data storage location is not correctly set, there are ways to change it.
- enable the push notifications service in AG
The main things you can do:
- generate a push token for the current session so that you can send transactional push messages: how to
- in addition to receiving simple notifications messages, you could register a Service for receiving data messages: how to
The HMS Push Server exposes a REST API with full specs here.
A full example implementation is provided by Java code samples that call this API: https://github.com/HMS-Core/hms-push-serverdemo-java
Authentication is done via an access token: https://developer.huawei.com/consumer/en/doc/development/parts-Guides/generating_app_level_access_token. Currently, the validity period is 1h (returned with the token response). To refresh the token, simply call the same endpoint again. You can find an example here: https://github.com/HMS-Core/hms-push-serverdemo-java/blob/master/src/main/java/com/huawei/push/messaging/HuaweiCredential.java
This is work in progress ... watch this repo to receive updates.