How we added Harmony OS support to the inDriver Android application

It all started with the fact that the US Department of Commerce included Huawei in the list of companies with which it is forbidden to conduct business with American companies . Huawei's response was the Harmony OS operating system for its smartphones, and the refusal to use Google services in it. And in order not to lose part of the market, we integrated its support in inDriver. Although the "OS integration" sounds loud - our application, like any other written for Android, will run on Harmony, but for the full work it is necessary to replace Google services with similar Huawei ones.



In inDriver, we use Google services for 3 purposes:

  • Sending push notifications;
  • Definition of location;
  • Google maps.

Push Kit - send push notifications


For starters, Huawei has some pretty good documentation . But for a quick start, just use the step-by-step tutorial .

I will not retell what is written there, I will dwell only on some points, since comments are needed here.

Enabling the Service

You will be asked to set up Data Storage Location. Note that you cannot change it later . The location does not affect the direct operation of the push, but is only a place for storing and processing data about them.

Integrating the HMS SDK

After you complete the integration, you will probably have problems building the project. In any case, this was the case on our Ubuntu 16.04.5 LTS build server (GNU / Linux 4.4.0-174-generic x86_64), while locally on the working Mac mini (late 2018, Mojave) everything worked fine. You can fix this if you add

Execution failed for task ':app:processIndriverReleaseManifest'. org.gradle.api.GradleException: ERROR: no manifest file found



agcp {
   manifest false
}

at the end of the file build.gradleat the application level to disable the manifest for agcp. The problem here is rather that it is agcplooking for a manifest file, but cannot find and load it, which is most likely a problem agconnect. At least that is how official Huawei technical support answered us.

Configuring AndroidManifest.xml

I do not know why, but it does not say about automatic initialization, which is described in section 2.6 Automatic Initialization of the full documentation. This is necessary if you send a token to the server in the method

public void onNewToken(String token) {}

Sending Messages: HUAWEI Push Kit Console.

With it, you can check the operation of the push. An interesting fact: if you select in the consoleType: Notification Message, theyonMessageReceived(RemoteMessage message)will immediately be displayed on the phonebypassing the handler.

When I set up the push, I often had to use the console. Both she and the pushes sent from the server had delays up to 30 minutes. I hope these were temporary difficulties that the developers have already dealt with.

Now we have two push services on our phone, but how can we organize their joint work? First you need to determine what services are available on the phone. At our place, we decided that Google services would be a priority for us. Suppose if both Google and Huawei services are available on the phone, we work with Google.

We send the status flag of services to the server with possible values:

- UNKNOWN
- GOOGLE
- HUAWEI

fun checkServices(context: Context): PlayServicesState {
   val googleAPI = GoogleApiAvailability.getInstance()
   when (googleAPI.isGooglePlayServicesAvailable(context)) {
       ConnectionResult.SUCCESS,
       ConnectionResult.SERVICE_MISSING,
       ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED,
       ConnectionResult.SERVICE_DISABLED -> return PlayServicesState.GOOGLE
   }
   val huaweiAPI = HuaweiApiAvailability.getInstance()
   when (huaweiAPI.isHuaweiMobileNoticeAvailable(context)) {
       com.huawei.hms.api.ConnectionResult.SUCCESS,
       com.huawei.hms.api.ConnectionResult.SERVICE_MISSING,
       com.huawei.hms.api.ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED,
       com.huawei.hms.api.ConnectionResult.SERVICE_DISABLED -> return PlayServicesState.HUAWEI
   }
   return PlayServicesState.UNKNOWN
}

Depending on the received value, the server will send push notifications to the corresponding service.

Organizing their work is a matter of technology. I did just that.

Event Handling for FCM:

class AppFcmListenerService : FirebaseMessagingService() {
   private val cloudMessageHandler: CloudMessageHandler = GoogleMessageHandler(this)
   override fun onMessageReceived(message: RemoteMessage) {
       cloudMessageHandler.onMessageReceived(message)
   }
}

and HCM:

class AppHmsListenerService : HmsMessageService() {
   private val cloudMessageHandler: CloudMessageHandler = HuaweiMessageHandler(this)
   override fun onMessageReceived(message: RemoteMessage) {
       cloudMessageHandler.onMessageReceived(message)
   }
}

delegated to the abstract class:

abstract class CloudMessageHandler { 
fun onMessageReceived(message: Any) {
   val data = getData(message)
   onMessageReceived(data)
}
private fun onMessageReceived(data: Map<String, String>) {
   //   
}
abstract fun getData(message: Any) :  Map<String, String> {
}

which has two implementations:

for FCM:

class FirebaseMessageHandler(override val context: Context) : CloudMessageHandler(context) {
   override fun getData(message: Any): Map<String, String> {
       return (message as RemoteMessage).data
   }
}

for HCM:

class HuaweiMessageHandler(override val context: Context) : CloudMessageHandler(context) {
   override fun getData(message: Any): Map<String, String> {
       return (message as RemoteMessage).dataOfMap
   }
}

Huawei classes have an almost similar interface with Google services. This "almost" I have personally manifested in the fact that RemoteMessageout com.google.firebase.messagingfor Map<String, String>you want to call RemoteMessage.getData(), and for RemoteMessageout com.huawei.hms.push- getDataOfMap().

Location Kit - location determination


Locations can be dealt with LOCATION_SERVICE, which is available without Google and Huawei services. But for the passenger transportation service, accurate location determination is an important parameter. Fortunately, the interfaces of the Location Kit classes were no different from those of the same name for Google services, so their application in the code did not differ. However, Huawei services were less accurate in operation. Hope this is a temporary issue.
We also encountered the fact that the geolocation request is sent through the HMS Core, which means that for correct operation it is necessary to set the rights for geolocation as “Always allow”.

location

Replacing Google Maps


In the CIS, we used an OSM engine with 2GIS tiles. At the first stage of integration, we decided not to work with the Map Kit from HUAWEI, but simply use the OSM engine with OSM tiles. Everything is simple.

Before release


I advise everyone to pay attention to the common causes of failure before launching your application in AppGallery.

For example, the Huawei release team informed us of the following violations.

Non-Standard Presentation of Hong Kong and Macau

China is scrupulous about the sovereignty of its autonomous administrative regions and asks them to be called "region." This forced us to change all the headings that mention the country to mention the country or region.



Information about Third-party Competing Products in App Details

Do not forget to rate the application by sending the user to the page in the App Gallery, not the Google Play Store :)

In general, Huawei services are similar to Google services and supporting them directly in the code does not cause difficulties. Questions can only be to the Huawei “ecosystem” as a whole, but they can be attributed to its youth and with a high probability everything will be better with time.

All Articles