< Zpět na články

Proč a jak (ne)vydávat aplikace v Huawei AppGallery

Aplikace pro operační systém Android jsou nejčastěji distribuovány prostřednictvím Google Play Store, se kterým se setkal snad každý uživatel nějakého zařízení s Androidem. I když je Play Store zdaleka nejrozšířenějším aplikačním obchodem s nejširší nabídkou aplikací, není jediný. Telefony a tablety Samsung obsahují Galaxy Store, Amazon provozuje svůj Appstore, pro nadšence do open-source technologií je tu F-Droid a existuje i řada dalších aplikačních obchodů. V poslední době se začíná čím dál tím více aplikací distribuovat prostřednictvím Huawei AppGallery, se kterým jsme se v Ackee nedávno seznámili.

AppGallery je dnes předinstalován na všech Huawei zařízeních s Androidem. Háček je v tom, že zatímco donedávna jste vedle něj našli také starý známý Play Store (alespoň v distribucích určených mimo čínský trh), dnes už tomu tak v případě nově vydaných telefonů, jako je třeba Mate 30 Pro, není. Americké ministerstvo obchodu zakázalo v polovině roku 2019 americkým firmám spolupracovat s některými čínskými firmami, mezi které patří právě i Huawei. Čínský výrobce tak sice může stále vydávat nové produkty se systémem Android, protože jeho zdrojový kód je volně přístupný pod svobodnou licencí, ztratil ovšem přístup ke klíčovým službám Googlu, mezi které patří jeho aplikace včetně již zmíněného Play Store.

Pro čínský trh se nic nemění, jelikož tam je z politických důvodů zakázáno působení amerických firem dlouhou dobu a Číňané tak provozují svoje interní klony známých služeb jako je např. Google (Baidu), Facebook (WeChat) nebo Amazon (Alibaba). Nyní je však Huawei nucen prorazit se svými službami i mimo Čínu. Snaží se proto aktivně rozšiřovat nabídku AppGallery o známé aplikace a kontaktuje vývojáře s nabídkou výhodných podmínek, pokud svoje appky do aplikačního obchodu Huawei přidají. Jednou z těchto aplikací je i Ventusky, graficky propracovaná předpověď počasí pro celý svět, jejíž Androidí verzi máme v Ackee na starost.

Technická implementace

Před samotným vydáním aplikace do Huawei AppGallery je nutné zajistit její správné fungování i na zařízeních bez Google služeb. V případě jednodušších aplikací, které Google služby nepoužívají, není třeba žádných speciálních úprav. Dnes ale většina appek alespoň jednu takovou službu obsahuje – nejčastěji mezi ně patří například Firebase Analytics, Firebase Crashlytics, Google Location Services a další.

Pro aplikaci Ventusky jsme museli najít náhradu za následující dva nástroje:

  • Google Location Services – používá se k získávání aktuální polohy uživatele pro zobrazení lokální předpovědi počasí,
  • Google Play Billing Library – nákupy v aplikaci sloužící pro odemčení prémiového obsahu (vrstev s dalšími druhy předpovědi).

Naším cílem bylo poskytovat stávajícím uživatelům veškerou funkcionalitu beze změn, tedy stále pomocí Google služeb, zároveň ale přidat podporu pro aplikace nainstalované z AppGallery. Nyní si ukážeme dva přístupy, které jsme použili pro náhradu výše zmíněných služeb.

Nahrazení Google Location Services

Přidání podpory získávání polohy na Huawei zařízeních bylo jednoduché. Huawei totiž poskytuje Location Kit, což je z pohledu vnějšího rozhraní naprosto identická kopie Location Services od Googlu. Stačilo tak vzít třídu LocationProvider a rozdělit ji na GoogleLocationProvider se současnou implementací prostřednictvím Google služeb a poté přidat HuaweiLocationProvider využívající implementaci od Huawei. Jelikož je jejich rozhraní shodné, stačilo pouze změnit importy a voilá – máme funkční polohové služby na Huawei zařízeních. Výsledek tedy vypadá takto:

// LocationProvider.kt  
  
abstract class LocationProvider {  
        abstract fun getFusedLocation(context: Context): Single    // Common functions for both providers  
}  
  
// GoogleLocationProvider.kt  
import com.google.android.gms.location.LocationServices  
  
class HuaweiLocationProvider : LocationProvider() {  
  
    override fun getFusedLocation(context: Context): Single {  
        val fusedLocationClient = LocationServices.getFusedLocationProviderClient(context)  
        // ...  
    }  
}  
  
// HuaweiLocationProvider.kt  
import com.huawei.hms.location.LocationServices  
  
class HuaweiLocationProvider : LocationProvider() {  
  
    override fun getFusedLocation(context: Context): Single {  
        val fusedLocationClient = LocationServices.getFusedLocationProviderClient(context)  
        // ...  
    }  
}

Při testování na Huawei zařízeních se však ukázalo, že získávání polohy nemusí vždy fungovat. Aby totiž všechno pracovalo správně, musí být na telefonu nainstalována aplikace HMS Core (obdoba Google Play Services) a mít přiděleno oprávnění získávání polohy. To však ve výchozím nastavení vždy mít nemusí, alespoň na námi použitých zařízeních. Bylo proto rozhodnuto, že se aplikace i ve verzi pro Huawei pokusí nejprve připojit ke Google službám, a teprve pokud nebudou dostupné, využije Huawei služeb. Tento scénář běžně nastane u starších Huawei zařízení s Google Play, které jsou používány mimo Čínu. Tedy i přesto, že je aplikace nainstalována z AppGallery, může za těchto podmínek využívat Google služeb bez omezení. Tohoto požadavku bylo docíleno jednoduše:

val Context.isGoogleServicesAvailable: Boolean    
    get() = GoogleApiAvailability.getInstance()  
                .isGooglePlayServicesAvailable(this) == GoogleConnectionResult.SUCCESS  
  
// Koin DI library  
val locationModule = module {      
    single {    
        if (androidContext().isGoogleServicesAvailable) {    
            GoogleLocationProvider(settings = get())    
        } else {    
            HuaweiLocationProvider(settings = get())    
        }    
    }  
}

Použitý přístup se zduplikovanou implementací má ovšem nevýhodu – je nutné každou změnu propsat v obou třídách. V tomto případě jde ale o nejjednodušší řešení, implementace daných tříd je jednoduchá a změny se neprovádí často. Ideální by bylo mít logiku implementovánu jen jednou a pouze vybrat požadovanou implementaci polohových služeb. To bohužel v tomto případě nebylo možné, jelikož Google a Huawei polohové služby neimplementují žádný společný interface, i když je fakticky jejich rozhraní totožné.

Nahrazení Google Play Billing Library

Zatímco v případě polohových služeb jsme chtěli rozhodnout o použité implementaci až za běhu, v případě in-app nákupů můžeme vybrat implementaci již při kompilaci. Zde už tedy neplatí, že aplikace stažená z Huawei AppGallery bude mít přístup k in-app nákupům prostřednictvím Google Play. Huawei navíc zavedl omezení pro application ID aplikací, které poskytují in-app nákupy prostřednictvím AppGallery - application ID musí končit na .huawei, jinak nákupy v aplikaci nebudou fungovat. 

Prvním krokem bylo rozdělení aplikace na dva flavory - google a huawei:

// app/build.gradle  
  
flavorDimensions "market"    
  
productFlavors {    
    google {    
        dimension "market"    
        applicationId appProperties['google_package_name']    
    }    
    huawei {    
        dimension "market"    
        applicationId appProperties['huawei_package_name']    
    }    
}

Způsob implementace od polohových služeb trochu liší – opět vytvoříme abstraktní bázovou třídu, konkrétní implementace ale budou umístěny v jednotlivých flavorech

// app/src/main/java/ventusky/billing  
abstract class BaseBillingManager(    
    // ...  
) {    
  
    abstract fun getPremiumPrice(listener: PremiumPriceRetrievedListener)    
    abstract fun buyPremium(activity: Activity)  
  
    protected fun savePremiumStatus(premiumStatus: Boolean) {    
        // ...    
    }  
}  
  
// app/src/google/ventusky/billing  
class BillingManager(    
    // Google In-App dependencies  
) : BaseBillingManager(/* ... */) {  
  
    override fun getPremiumPrice(listener: PremiumPriceRetrievedListener) {  
        // Google Billing implementation    
    }  
        override fun buyPremium(activity: Activity) {  
        // Google Billing implementation   
    }  
}  
  
// app/src/huawei/ventusky/billing  
class BillingManager(    
    // Huawei In-App dependencies  
) : BaseBillingManager(/* ... */) {  
    override fun getPremiumPrice(listener: PremiumPriceRetrievedListener) {  
        // Huawei Billing implementation    
    }  
  
    override fun buyPremium(activity: Activity) {  
        // Huawei Billing implementation  
    }  
}

Za běhu tedy nyní nemůžeme rozhodovat, zda použijeme Google nebo Huawei implementaci, jelikož se do výsledného APK dostane pouze jedna konkrétní třída BillingManager, jejíž podoba závisí na zvoleném flavoru. To je ale výhoda – nemusíme v kódu zjišťovat dostupnost jednotlivých služeb a teprve poté vybírat, kterou použít. Máme k dispozici rovnou správnou implementaci – zkompilovaná aplikace navíc druhou, nepotřebnou implementaci vůbec neobsahuje, ušetří se tím tak i velikost instalačního balíčku.

Zde už neplatí, že Huawei knihovna pro in-app nákupy kopíruje rozhraní Google, obě dvě varianty lze tak aktualizovat nezávisle. Pro zájemce o konkrétní podobu implementace je k dispozici dokumentace Google Play Billing knihovny a Huawei In-App Purchases.

Huawei AppGallery: Ano, nebo ne?

Vyplatí se tedy vydávat aplikaci do Huawei AppGallery? U celosvětových aplikací s početnou uživatelskou základnou určitě ano. Dokáží tak oslovit nemalé množství dalších uživatelů, jejichž počet bude pravděpodobně dále narůstat. U menších appek je třeba zvážit potenciální přínosy vůči vynaložené práci s migrací služeb a udržováním více variant aplikace. Zejména u lokálních aplikací působících v rámci jedné země pak bude přínos pravděpodobně velmi malý (pokud tou zemí není Čína :)).

Zájemcům o nový mobilní telefon s Androidem lze pak doporučit, aby se zařízením bez Google Play raději vyhnuli. Byť se nabídka alternativních aplikačních obchodů včetně AppGallery stále rozšiřuje, stále je oproti Play Store chudá a spousta aplikací chybí, což používání telefonu značně omezuje.

Ondřej John
Ondřej John
Android DeveloperOndra má prsty ve všech aplikacích pro Android, ze kterých jde pustit Ulice, nebo těch, co předpovídají počasí. Pokud se jde na oběd pro poutine, tak nikdy nesmí chybět. Má rád hory, lyže a všechno, co jezdí na benzín.

Máte zájem o spolupráci? Pojďme to probrat osobně!

Napište nám >