Aangepaste tabbladen gebruiken met Android 11

Android 11 heeft wijzigingen geïntroduceerd in de manier waarop apps kunnen communiceren met andere apps die de gebruiker op het apparaat heeft geïnstalleerd. U kunt meer over deze wijzigingen lezen in de Android-documentatie .

Wanneer een Android-app die aangepaste tabbladen gebruikt, SDK-niveau 30 of hoger target, kunnen er enkele wijzigingen nodig zijn. In dit artikel worden de wijzigingen besproken die mogelijk nodig zijn voor die apps.

In het eenvoudigste geval kunnen aangepaste tabbladen worden gestart met een oneliner als volgt:

new CustomTabsIntent.Builder().build()
        .launchUrl(this, Uri.parse("https://www.example.com"));

Applicaties die applicaties starten met deze aanpak, of zelfs UI-aanpassingen toevoegen, zoals het wijzigen van de werkbalkkleur , het toevoegen van een actieknop , hoeven geen wijzigingen in de applicatie aan te brengen.

Voorkeur voor native apps

Maar als u de best practices volgt, zijn er mogelijk enkele wijzigingen nodig.

De eerste relevante best practice is dat applicaties de voorkeur moeten geven aan een native app om de intentie af te handelen in plaats van een aangepast tabblad als er een app is geïnstalleerd die deze intentie kan verwerken.

Op Android 11 en hoger

Android 11 introduceert een nieuwe Intent-vlag, FLAG_ACTIVITY_REQUIRE_NON_BROWSER , wat de aanbevolen manier is om te proberen een native app te openen, omdat de app hiervoor geen pakketbeheerderquery's hoeft te declareren.

static boolean launchNativeApi30(Context context, Uri uri) {
    Intent nativeAppIntent = new Intent(Intent.ACTION_VIEW, uri)
            .addCategory(Intent.CATEGORY_BROWSABLE)
            .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
                    Intent.FLAG_ACTIVITY_REQUIRE_NON_BROWSER);
    try {
        context.startActivity(nativeAppIntent);
        return true;
    } catch (ActivityNotFoundException ex) {
        return false;
    }
}

De oplossing is om te proberen de Intent te starten en FLAG_ACTIVITY_REQUIRE_NON_BROWSER te gebruiken om Android te vragen browsers te vermijden bij het starten.

Als er geen native app wordt gevonden die deze intentie kan verwerken , wordt er een ActivityNotFoundException gegenereerd.

Vóór Android 11

Ook al is de applicatie mogelijk gericht op Android 11 of API-niveau 30, eerdere Android-versies begrijpen de vlag FLAG_ACTIVITY_REQUIRE_NON_BROWSER niet, dus in die gevallen moeten we onze toevlucht nemen tot het bevragen van de Pakketbeheerder:

private static boolean launchNativeBeforeApi30(Context context, Uri uri) {
    PackageManager pm = context.getPackageManager();

    // Get all Apps that resolve a generic url
    Intent browserActivityIntent = new Intent()
            .setAction(Intent.ACTION_VIEW)
            .addCategory(Intent.CATEGORY_BROWSABLE)
            .setData(Uri.fromParts("http", "", null));
    Set<String> genericResolvedList = extractPackageNames(
            pm.queryIntentActivities(browserActivityIntent, 0));

    // Get all apps that resolve the specific Url
    Intent specializedActivityIntent = new Intent(Intent.ACTION_VIEW, uri)
            .addCategory(Intent.CATEGORY_BROWSABLE);
    Set<String> resolvedSpecializedList = extractPackageNames(
            pm.queryIntentActivities(specializedActivityIntent, 0));

    // Keep only the Urls that resolve the specific, but not the generic
    // urls.
    resolvedSpecializedList.removeAll(genericResolvedList);

    // If the list is empty, no native app handlers were found.
    if (resolvedSpecializedList.isEmpty()) {
        return false;
    }

    // We found native handlers. Launch the Intent.
    specializedActivityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    context.startActivity(specializedActivityIntent);
    return true;
}

De hier gebruikte aanpak is het doorzoeken van de Pakketbeheerder naar toepassingen die een generieke http intentie ondersteunen. Deze applicaties zijn waarschijnlijk browsers.

Zoek vervolgens naar toepassingen die de gegevens verwerken voor de specifieke URL die we willen starten. Hierdoor worden zowel de browsers als de native applicaties-instellingen geretourneerd om die URL te verwerken.

Verwijder nu alle browsers uit de eerste lijst uit de tweede lijst, en we houden alleen nog native apps over.

Als de lijst leeg is, weten we dat er geen native handlers zijn en retourneren we false. Anders lanceren we de intentie voor de native handler.

Alles op een rij zetten

We moeten ervoor zorgen dat we voor elke gelegenheid de juiste methode gebruiken:

static void launchUri(Context context, Uri uri) {
    boolean launched = Build.VERSION.SDK_INT >= 30 ?
            launchNativeApi30(context, uri) :
            launchNativeBeforeApi30(context, uri);

    if (!launched) {
        new CustomTabsIntent.Builder()
                .build()
                .launchUrl(context, uri);
    }
}

Build.VERSION.SDK_INT biedt de informatie die we nodig hebben. Als het gelijk is aan of groter is dan 30, kent Android de FLAG_ACTIVITY_REQUIRE_NON_BROWSER en kunnen we proberen een nativa-app te lanceren met de nieuwe aanpak. Anders proberen we te lanceren met de oude aanpak.

Als het starten van een native app mislukt, starten we vervolgens Aangepaste tabbladen.

Deze best practice bevat een aantal standaardregels. We werken eraan dit eenvoudiger te maken door de complexiteit in een bibliotheek in te kapselen. Blijf op de hoogte van updates voor de ondersteuningsbibliotheek voor Android-browser-helper .

Browsers detecteren die aangepaste tabbladen ondersteunen

Een ander veel voorkomend patroon is het gebruik van PackageManager om te detecteren welke browsers aangepaste tabbladen op het apparaat ondersteunen . Veelvoorkomende gebruiksscenario's hiervoor zijn het instellen van het pakket op de Intent om het dialoogvenster voor het ondubbelzinnig maken van de app te vermijden, of het kiezen met welke browser verbinding moet worden gemaakt bij het verbinden met de Custom Tabs-service .

Bij het targeten van API-niveau 30 moeten ontwikkelaars een querysectie toevoegen aan hun Android-manifest, waarin een intentiefilter wordt aangegeven dat browsers matcht met ondersteuning voor aangepaste tabbladen.

<queries>
    <intent>
        <action android:name=
            "android.support.customtabs.action.CustomTabsService" />
    </intent>
</queries>

Als de opmaak is aangebracht, werkt de bestaande code die wordt gebruikt voor het zoeken naar browsers die aangepaste tabbladen ondersteunen, zoals verwacht.

Veelgestelde vragen

Vraag: De code die zoekt naar query's van providers van aangepaste tabbladen voor toepassingen die https:// intenties kunnen verwerken, maar het queryfilter declareert alleen een android.support.customtabs.action.CustomTabsService -query. Moet een query voor https:// intenties niet worden gedeclareerd?

A: Wanneer u een queryfilter declareert, filtert het de antwoorden op een query naar de PackageManager, niet de query zelf. Omdat browsers die Custom Tabs ondersteunen aangeven dat ze de CustomTabsService verwerken, worden ze niet uitgefilterd. Browsers die geen aangepaste tabbladen ondersteunen, worden uitgefilterd.

Conclusie

Dit zijn alle wijzigingen die nodig zijn om een ​​bestaande Custom Tabs-integratie aan te passen zodat deze met Android 11 werkt. Voor meer informatie over het integreren van Custom Tabs in een Android-app, begint u met de implementatiegids en bekijkt u vervolgens de best practices voor meer informatie over het bouwen van een eersteklas integratie.

Laat het ons weten als je vragen of feedback hebt!