How We Handle GMS & HMS at Papara

Emre Hamurcu
Papara Tech
Published in
5 min readJun 28, 2024

Papara, a leading financial technology company, is always driven by our utmost brigade to guarantee the user applications provide a seamless and consistent experience throughout different devices and platforms. With Huawei slowly eating up the market and pursuing their fork of Android with the absence of Google Mobile Services (GMS), the issue came to us as to which among the applications to deploy on our Android platform should support both GMS and Huawei Mobile Services(HMS). It is my pleasure to share with you our strategy and how we operate the software in pursuit of this goal.

The Challenge

Supporting both GMS and HMS requires detecting the available mobile services on a device and ensuring our application can function correctly regardless of the underlying services. This involves:

  • Identifying GMS or HMS, for example, recognizing that some Huawei devices may not feature GMS data, is one of the ways our strategy makes it possible.
  • Developing code to differentiate which of the given platform the application is running and then solve it by using the appropriate services would be one of the key processes we followed.
  • Including only necessary and clean code that does not conflict with the logic in the application but allows these functions to be moved to another class in a more organized and understandable way.

The Importance of Separation

It is vital to mention that the separation of GMS and HMS is a symmetric part of our code because every device has its own abilities and limits:

  • Device Compatibility: Certain Huawei devices are not Google Play Services(GMS) compliant, as they only utilize Huawei Mobile Services(HMS) for their core functions. The honing of GMS and HMS is done in such a way that the app is supposed to play easily on each and every device. This is achieved by the device not being dependent on the service, and it will still have to operate with the app.
  • Functionality Optimization: GMS and HMS have a number of different features and APIs. As we are able to distinguish between the two, we are able to make sure our applications use the different platforms’ capabilities in an optimal manner.
  • Future-Proofing: The mobile surroundings still change to include new devices with possible new dependencies. By having a strict separation of GMS and HMS processing, we can create an intelligent design that will be able to adapt to those changes, without need of much modifications.

Our Initial Approach

Initially, we experimented with using BuildConfig variables and build variants to manage GMS and HMS differences. However, we encountered several issues:

  • Complexity: We had to struggle with the fact that the build system had much more complexity due to managing different build variants.
  • Maintenance: The process of the configuration and the maintenance of the two systems GMS and HMS was a difficult situation to handle.
  • Scalability: We needed to include more features on the platform and decide which services should be added, but we did it in a way that caused increasing difficulty.

Our Final Approach

To address these challenges, we adopted a solution that emphasizes separation and abstraction:

Step 1: Define the Mobile Platform Enum

We start by defining an enum to represent the different mobile platforms.

/**
* Enum class to represent different mobile platforms.
*/
internal enum class MobilePlatform {
GMS,
HMS,
NONE;

/**
* Check if the current mobile platform is available.
*/
fun isAvailable(context: Context): Boolean {
return when (this) {
GMS -> GoogleApiAvailability.getInstance()
.isGooglePlayServicesAvailable(context) == ConnectionResult.SUCCESS
HMS -> HuaweiApiAvailability.getInstance()
.isHuaweiMobileServicesAvailable(context) == HmsConnectionResult.SUCCESS
NONE -> false
}
}
}

Step 2: Create an Interface for Platform Detection

Next, we define an interface MobilePlatformHelper that will be used to encapsulate the platform detection logic.

/**
* Interface for detecting mobile platforms.
*/
interface MobilePlatformHelper {

val isGms: Boolean // Indicates if GMS is available

val isHms: Boolean // Indicates if HMS is available

fun getMobilePlatform(): MobilePlatform // Returns the current mobile platform
}

Step 3: Implement Context Extension Functions

We create extension functions on the Context class to check the availability of GMS and HMS.

/**
* Extension function to get the current mobile platform.
*/
internal fun Context.getMobilePlatform(): MobilePlatform {
return MobilePlatform.values().firstOrNull { it.isAvailable(this) } ?: MobilePlatform.NONE
}

/**
* Extension function to check if GMS is available.
*/
internal fun Context.isGms() = MobilePlatform.GMS.isAvailable(this)

/**
* Extension function to check if HMS is available.
*/
internal fun Context.isHms() = MobilePlatform.HMS.isAvailable(this)

Step 4: Implement the MobilePlatformHelper Interface

We create an implementation of the MobilePlatformHelper interface.

/**
* Implementation of the MobilePlatformHelper interface.
*/
internal class MobilePlatformHelperImpl @Inject constructor(
@ApplicationContext private val context: Context
) : MobilePlatformHelper {

override val isGms: Boolean
get() = context.isGms()

override val isHms: Boolean
get() = context.isHms()

override fun getMobilePlatform() = context.getMobilePlatform()
}

Step 5: Configure Dependency Injection with Hilt

We use Dagger Hilt to manage dependency injection for our MobilePlatformHelper.

/**
* Dagger Hilt module for providing platform detection dependencies.
*/
@Module
@InstallIn(SingletonComponent::class)
internal interface PlatformKitModule {

@Binds
fun bindMobilePlatformHelper(
impl: MobilePlatformHelperImpl
): MobilePlatformHelper
}

Conclusion

By abstracting the GMS and HMS detection logic into a dedicated interface and implementation, we ensure our application remains compatible with a diverse range of devices, including those that rely solely on Huawei Mobile Services (HMS). This approach enables us to deliver a consistent user experience while leveraging the unique features and capabilities of each platform. At Papara, we remain committed to maintaining a high standard of quality and performance in our applications, and our approach to handling GMS and HMS is a testament to that commitment.

Extending the Approach

Our separation of GMS and HMS handling extends beyond platform detection. We leverage this approach in various aspects of our application development:

  1. Push Notifications: We utilize platform-specific implementations to deliver push notifications, ensuring compatibility with both GMS and HMS devices.
  2. Maps Integration: Our maps integration relies on platform-specific APIs, allowing us to provide seamless map functionality regardless of the underlying services.
  3. Remote Configurations: We manage remote configurations using platform-specific services, enabling us to dynamically adjust application behavior based on device capabilities.
  4. SMS Retrieval: SMS retrieval functionality is implemented using platform-specific methods, ensuring reliable SMS verification across different devices.
  5. Crash Reporting : We integrate Crashlytics to monitor and analyze app crashes, ensuring a smoother user experience by promptly identifying and resolving issues.
/**
* Sample of our Crashkit implementation with MobilePlatformHelper
*/
@Module
@InstallIn(SingletonComponent::class)
internal object CrashKitModule {
@Provides
fun provideCrashKit(mobilePlatformHelper: MobilePlatformHelper): CrashKit {
return when (mobilePlatformHelper.getMobilePlatform()) {
MobilePlatform.GMS -> GmsCrashKitImpl()
MobilePlatform.HMS -> HmsCrashKitImpl()
MobilePlatform.NONE -> NoneCrashKitImpl()
}
}
}

By applying our approach to these areas, we maintain consistency and reliability in the functionality of our application, regardless of the mobile platform it runs on.

At Papara, we’re committed to staying at the forefront of mobile technology, continually refining our processes and adopting best practices to ensure our applications meet the evolving needs of our users. See you next time

--

--

Papara Tech
Papara Tech

Published in Papara Tech

We are a technology company that aims to make all financial services faster, easier, more affordable and more enjoyable for everyone since 2016! Our middle account is here to get to know our work environment, business structures and us better.

Emre Hamurcu
Emre Hamurcu

Written by Emre Hamurcu

Senior Android Developer & Youtuber

No responses yet