שימוש בכרטיסיות מותאמות אישית ב-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, ולכן במקרים כאלה נצטרך להשתמש בשאילתה ל-Package Manager:

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

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

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

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

אם הרשימה ריקה, אנחנו יודעים שאין מנהלים מקומיים ומחזירים את הערך false. אחרת, מריצים את הכוונה לטיפולי המקור.

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

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

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

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

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

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

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

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

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

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

שאלות נפוצות

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

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

סיכום

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

אם יש לך שאלות או משוב, אפשר לפנות אלינו.