שימוש בכרטיסיות מותאמות אישית ב-Android 11

ב-Android 11 התבצעו שינויים באופן שבו האפליקציות יכולות ליצור אינטראקציה עם אפליקציות אחרות שהמשתמש התקין במכשיר. במסמכי התיעוד של Android אפשר לקרוא מידע נוסף על השינויים האלה.

כשאפליקציה ל-Android שנעשה בה שימוש בכרטיסיות מותאמות אישית מטרגטת רמת SDK 30 ומעלה, יכול להיות שיידרשו שינויים מסוימים. במאמר הזה מפורטים השינויים שצריך לבצע באפליקציות האלה.

במקרה הפשוט ביותר, ניתן להפעיל את הכרטיסיות המותאמות אישית בשורה אחת, כמו:

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

באפליקציות שמפעילות אפליקציות בגישה הזו, ואפילו בהוספה של התאמות אישיות של ממשק המשתמש, כמו שינוי הצבע של סרגל הכלים והוספת לחצן פעולה, אין צורך לבצע שינויים כלשהם באפליקציה.

עדיפות לאפליקציות מקוריות

אבל אם פעלתם בהתאם לשיטות המומלצות, יכול להיות שתצטרכו לבצע כמה שינויים.

השיטה המומלצת הראשונה היא שאפליקציות יצטרכו להעדיף אפליקציה נייטיב שתטפל בכוונה במקום כרטיסייה מותאמת אישית אם מותקנת בה אפליקציה שמסוגלת לטפל בה.

ב-Android מגרסה 11 ואילך

ב-Android 11 יש סימון Intent חדש, FLAG_ACTIVITY_REQUIRE_NON_BROWSER, שהוא הדרך המומלצת לנסות לפתוח אפליקציה נייטיב, כי האפליקציה לא מחייבת את האפליקציה להצהיר על שאילתות של מנהל חבילות.

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;
    }
}

הפתרון הוא לנסות להפעיל את ה-Intent ולהשתמש ב-FLAG_ACTIVITY_REQUIRE_NON_BROWSER כדי לבקש מ-Android להימנע משימוש בדפדפנים.

אם לא נמצאה אפליקציה נייטיב שיכולה לטפל ב-Intent הזה, יוקצה ActivityNotFoundException.

לפני Android 11

האפליקציה עשויה לטרגט ל-Android 11 או לרמת API 30, אבל גרסאות קודמות של Android לא יבינו את הדגל FLAG_ACTIVITY_REQUIRE_NON_BROWSER. לכן, עלינו לשלוח שאילתות למנהל החבילות במקרים האלה:

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;
}

הגישה שבה משתמשים כאן היא שליחת שאילתה למנהל החבילות לגבי אפליקציות שתומכות ב-Intent כללי http. האפליקציות האלה הן כנראה דפדפנים.

לאחר מכן, שולחים שאילתה לאפליקציות שמטפלות ב-ittent עבור כתובת ה-URL הספציפית שרוצים להפעיל. הפעולה הזו תחזיר גם את ההגדרות של הדפדפנים וגם של האפליקציות הנייטיבות לטיפול בכתובת ה-URL הזו.

הסירו את כל הדפדפנים שמופיעים ברשימה הראשונה מהרשימה השנייה, ונישארו רק עם אפליקציות מקוריות.

אם הרשימה ריקה, אנחנו יודעים שאין רכיבי handler מקומיים והחזרת FALSE. אחרת, אנחנו מפעילים את הכוונה של ה-handler המקורי.

סיכום של כל המידע

אנחנו צריכים לוודא שמשתמשים בשיטה המתאימה לכל אירוע:

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 מספק את המידע הדרוש לנו. אם הוא שווה ל-30 או יותר מ-30, מערכת Android מזהה את FLAG_ACTIVITY_REQUIRE_NON_BROWSER ואנחנו יכולים לנסות להשיק אפליקציית nativa עם הגישה החדשה. אחרת, ננסה להשיק עם הגישה הישנה.

אם הפעלת אפליקציה מקורית נכשלת, אנחנו מפעילים 'כרטיסיות מותאמות אישית'.

השיטה המומלצת הזו כוללת תבנית סטנדרטית. אנחנו פועלים כדי לפשט את התהליך הזה על ידי חישוב המורכבות בספרייה. חפשו עדכונים בספריית התמיכה של android-browser-helper.

זיהוי דפדפנים שתומכים בכרטיסיות מותאמות אישית

דפוס נפוץ נוסף הוא שימוש ב-PackageManager כדי לזהות אילו דפדפנים תומכים בכרטיסיות מותאמות אישית במכשיר. במקרים רבים, האפשרות הזו היא הגדרת החבילה ב-Intent כדי למנוע את הצגת תיבת הדו-שיח להבדל בין האפליקציה, או לבחור לאיזה דפדפן להתחבר כשמתחברים לשירות הכרטיסיות המותאמות אישית.

כשמטרגטים לרמת API 30, המפתחים צריכים להוסיף קטע שאילתות למניפסט של Android, ולהצהיר על מסנן Intent שתואם לדפדפנים עם תמיכה בכרטיסיות מותאמות אישית.

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

אחרי הוספת תגי העיצוב, הקוד הקיים המשמש לשליחת שאילתות לגבי דפדפנים שתומכים בכרטיסיות מותאמות אישית יפעל כצפוי.

שאלות נפוצות

ש: הקוד שמחפש שאילתות של ספקי כרטיסיות מותאמות אישית עבור אפליקציות שיכולות לטפל באובייקטים מסוג https://, אבל מסנן השאילתות מצהיר רק על שאילתת android.support.customtabs.action.CustomTabsService. האם אין להצהיר על שאילתה של אובייקטים מסוג https://?

ת: כשמצהירים על מסנן שאילתה, המערכת תסנן את התגובות לשאילתה אל PackageManager, ולא אל השאילתה עצמה. מכיוון שדפדפנים שתומכים בכרטיסיות מותאמות אישית מצהירים שהם מטפלים ב-CustomTabsService, הם לא יסוננו. דפדפנים שלא תומכים בכרטיסיות מותאמות אישית יוסרו.

סיכום

אלה כל השינויים הנדרשים כדי להתאים שילוב קיים של כרטיסיות מותאמות אישית כך שיפעל עם Android 11. למידע נוסף על שילוב כרטיסיות מותאמות אישית באפליקציה ל-Android, תוכלו לקרוא את מדריך ההטמעה ולאחר מכן לעיין בשיטות המומלצות כדי ללמוד איך ליצור שילוב ברמה הראשונה.

נשמח לשמוע אם יש לך שאלות או משוב.