ארכיטקטורת מידע

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

דוגמאות ומקור

רוב הדפים בהגדרות מוטמעים כרגע באמצעות המסגרת החדשה. דוגמה טובה לכך היא DisplaySettings: packages/apps/Settings/src/com/android/settings/DisplaySettings.java

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

  • CategoryKey: packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
  • DashboardFragmentRegistry: packages/apps/Settings/src/com/android/settings/dashboard/DashboardFragmentRegistry.java
  • DashboardFragment: packages/apps/Settings/src/com/android/settings/dashboard/DashboardFragment.java
  • AbstractPreferenceController: frameworks/base/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java
  • BasePreferenceController (הושק ב-Android 9): packages/apps/Settings/src/com/android/settings/core/BasePreferenceController.java

הטמעה

יצרני המכשירים מומלצים להתאים את הארכיטקטורה הקיימת של נתוני ההגדרות ולהוסיף דפי הגדרות נוספים לפי הצורך, כדי להתאים את הממשק לתכונות ספציפיות לשותפים. העברת ההעדפות מדף קודם (המוטמעת בתור SettingsPreferencePage) לדף חדש (שמוטמעת באמצעות DashboardFragment) יכולה להיות מורכבת. סביר להניח שההעדפה מהדף הקודם לא מיושמת באמצעות PreferenceController.

לכן, כשמעבירים העדפות מדף מדור קודם לדף חדש, צריך ליצור PreferenceController ולהעביר את הקוד לבקר לפני שמפעילים אותו ב-DashboardFragment החדש. ממשקי ה-API שנדרשים ל-PreferenceController מתוארים בשם שלהם ומתוועדים ב-Javadoc.

מומלץ מאוד להוסיף בדיקת יחידה לכל PreferenceController. אם השינוי נשלח ל-AOSP, צריך לבצע בדיקת יחידה. למידע נוסף על כתיבת בדיקות שמבוססות על Robolectric, תוכלו לעיין בקובץ readme‏ packages/apps/Settings/tests/robotests/README.md.

ארכיטקטורת מידע בסגנון פלאגין

כל פריט בהגדרות מיושם כ-Preference. אפשר להעביר בקלות העדפה מדף אחד לדף אחר.

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

DashboardFragment

DashboardFragment הוא המארח של בקרי העדפות בסגנון פלאגין. המקטע יורש מ-PreferenceFragment ויש לו קטעי הוק (hooks) להרחבה ולעדכון של רשימות העדפות סטטיות וגם רשימות של העדפות דינמיות.

העדפות סטטיות

רשימת העדפות סטטית מוגדרת ב-XML באמצעות התג <Preference>. בהטמעה של DashboardFragment, משתמשים בשיטה getPreferenceScreenResId() כדי להגדיר איזה קובץ XML מכיל את רשימת ההעדפות הסטטית שרוצים להציג.

העדפות דינמיות

פריט דינמי מייצג משבצת עם כוונה, שמובילה לפעילות חיצונית או פנימית. בדרך כלל, הכוונה מובילה לדף הגדרות אחר. לדוגמה, פריט ההגדרה "Google" בדף הבית של ההגדרות הוא פריט דינמי. פריטים דינמיים מוגדרים ב-AndroidManifest (בהמשך) ונטענים באמצעות FeatureProvider (מוגדר כ- DashboardFeatureProvider).

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

  • ההגדרה לא מיושמת ישירות באפליקציית ההגדרות (למשל, הזרקת הגדרה שמופעלת על ידי אפליקציות של יצרני ציוד מקורי או של ספקי סלולר).
  • ההגדרה אמורה להופיע בדף הבית של ההגדרות.
  • כבר יש לכם Activity עבור ההגדרה ואתם לא רוצים להטמיע את התצורה הסטטית הנוספת.

כדי להגדיר פעילות כהגדרה דינמית:

  • כדי לסמן את הפעילות כהגדרה דינמית, מוסיפים לה מסנן Intent.
  • מציינים לאפליקציית ההגדרות לאיזו קטגוריה היא שייכת. הקטגוריה היא קבועה, והיא מוגדרת ב-CategoryKey.
  • אופציונלי: מוסיפים טקסט סיכום כשההגדרה מוצגת.

הנה דוגמה מתוך אפליקציית ההגדרות עבור DisplaySettings.

<activity android:name="Settings$DisplaySettingsActivity"
                   android:label="@string/display_settings"
                   android:icon="@drawable/ic_settings_display">
             <!-- Mark the activity as a dynamic setting -->
              <intent-filter>
                     <action android:name="com.android.settings.action.IA_SETTINGS" />
              </intent-filter>
             <!-- Tell Settings app which category it belongs to -->
              <meta-data android:name="com.android.settings.category"
                     android:value="com.android.settings.category.ia.homepage" />
             <!-- Add a summary text when the setting is displayed -->
              <meta-data android:name="com.android.settings.summary"
                     android:resource="@string/display_dashboard_summary"/>
             </activity>

בזמן הרינדור, החלק הקטן יבקש רשימה של העדפות גם מ-XML סטטי וגם מהגדרות דינמיות שהוגדרו ב-AndroidManifest. בין שה-PreferenceController מוגדרים בקוד Java ובין שהם מוגדרים ב-XML, DashboardFragment מנהל את לוגיקת הטיפול בכל הגדרה באמצעות PreferenceController (כפי שמתואר בהמשך). לאחר מכן הן מוצגות בממשק המשתמש כרשימה מעורבת.

PreferenceController

יש הבדלים בהטמעה של PreferenceController ב-Android 9 וב-Android 8.x, כפי שמתואר בקטע הזה.

PreferenceController בגרסת Android 9

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

הממשק של PreferenceController מוגדר בתור BasePreferenceController. לדוגמה, הקוד ב-packages/apps/Settings/src/com/android/settings/core/ BasePreferenceController.java

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

ל-BasePreferenceController יש ממשקי API כמו getAvailabilityStatus(), ‏ displayPreference(),‏ handlePreferenceTreeClicked(), וכו'. תיעוד מפורט של כל ממשק API נמצא בכיתה של הממשק.

אחת מהמגבלות על הטמעת BasePreferenceController (וגם על תת-הסוגים שלו, כמו TogglePreferenceController) היא שחתימת המבנה צריכה להתאים לאחת מהאפשרויות הבאות:

  • public MyController(Context context, String key) {}
  • public MyController(Context context) {}

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

DashboardFragment שומרת רשימה של PreferenceController במסך. ב-onCreate() של הפלח, כל הבקרים מופעלים בשיטה getAvailabilityStatus(), ואם היא מחזירה true, מתבצעת הפעלה של displayPreference() כדי לעבד את הלוגיקה של התצוגה. getAvailabilityStatus() חשוב גם להודיע למסגרת ההגדרות אילו פריטים זמינים במהלך החיפוש.

PreferenceController במהדורות של Android 8.x

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

בהתאם לאינטראקציות עם ההעדפות, בממשק של PreferenceController יש ממשקי API isAvailable(), displayPreference(), handlePreferenceTreeClicked() וכו'. אפשר למצוא תיעוד מפורט על כל API במחלקה של הממשק.

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

ב-DashboardFragment מופיעה רשימה של PreferenceControllers במסך. ב-onCreate() של המקטע, כל הבקרים מופעלים ל-method isAvailable(). אם מוחזר TRUE, מופעל displayPreference() כדי לעבד את לוגיקת התצוגה.

שימוש ב-DashboardFragment

העברת העדפה מדף א' לדף ב'

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

העברה סטטית ב-Android 9

  1. מאתרים את קובצי ה-XML של ההעדפות של הדף המקורי ושל דף היעד. המידע הזה מופיע ב-method getPreferenceScreenResId() של הדף.
  2. מסירים את ההעדפה מה-XML של הדף המקורי.
  3. מוסיפים את ההעדפה לקובץ ה-XML של דף היעד.
  4. מסירים את PreferenceController של ההעדפה הזו מהטמעת Java של הדף המקורי. בדרך כלל הוא נמצא ב-createPreferenceControllers(). אפשר להצהיר על השלט רחוק ישירות ב-XML.

    הערה: יכול להיות שההעדפה לא תכלול את הערך PreferenceController.

  5. יוצרים את PreferenceController ב-createPreferenceControllers() של דף היעד. אם השדה PreferenceController מוגדר ב-XML בדף הישן, צריך להגדיר אותו ב-XML גם בדף החדש.

תנועה דינמית ב-Android 9

  1. בודקים איזו קטגוריה מתארחת בדף המקורי ובדף היעד. המידע הזה מופיע בDashboardFragmentRegistry.
  2. פותחים את הקובץ AndroidManifest.xml שמכיל את ההגדרה שרוצים להעביר ומאתרים את הרשומה Activity שמייצגת את ההגדרה הזו.
  3. מגדירים את ערך המטא-נתונים של הפעילות עבור com.android.settings.category למפתח הקטגוריה של הדף החדש.

מעבר סטטי בגרסאות Android 8.x

  1. מחפשים את קובצי ה-XML של ההעדפות של הדף המקורי ושל דף היעד.
  2. אפשר למצוא את המידע הזה בשיטה getPreferenceScreenResId() של הדף.
  3. מסירים את ההעדפה ב-XML של הדף המקורי.
  4. מוסיפים את ההעדפה ל-XML של דף היעד.
  5. מסירים את הערך PreferenceController של ההעדפה הזו בהטמעת Java של הדף המקורי. בדרך כלל הוא נמצא ב-getPreferenceControllers().
  6. הערה: יכול להיות שלהעדפה אין PreferenceController.

  7. יוצרים את PreferenceController ב-getPreferenceControllers() של דף היעד.

מעבר דינמי בגרסאות Android 8.x

  1. בודקים לאיזו קטגוריה שייכים דף היעד והדף המקורי. המידע הזה זמין ב-DashboardFragmentRegistry.
  2. פותחים את הקובץ AndroidManifest.xml שמכיל את ההגדרה שרוצים להעביר ומאתרים את הרשומה Activity שמייצגת את ההגדרה הזו.
  3. משנים את ערך המטא-נתונים של הפעילות עבור com.android.settings.category, ומגדירים את נקודת הערך למפתח הקטגוריה של הדף החדש.

יצירת העדפה חדשה בדף

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

יצירת העדפה סטטית

  1. מאתרים את קובצי ה-XML של ההעדפות של הדף. המידע הזה מופיע ב-method‏ getPreferenceScreenResId() של הדף.
  2. מוסיפים פריט Preference חדש ב-XML. חשוב לוודא שיש לו android:key ייחודי.
  3. מגדירים PreferenceController להעדפה הזו בשיטה getPreferenceControllers() של הדף.
    • ב-Android 8.x וב-Android 9 (אופציונלי), יוצרים מופע של PreferenceController עבור ההעדפה הזו ב-method‏ createPreferenceControllers() של הדף.

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

    • החל מ-Android 9, אפשר להצהיר על PreferenceController ב-XML לצד ההעדפה. לדוגמה:
      <Preference
              android:key="reset_dashboard"
              android:title="@string/reset_dashboard_title"
              settings:controller="com.android.settings.system.ResetPreferenceController"/>

יצירת העדפה דינמית

  1. בודקים לאיזו קטגוריה שייכים דף היעד והדף המקורי. המידע הזה מופיע ב-DashboardFragmentRegistry.
  2. יצירת פעילות חדשה ב-AndroidManifest
  3. מוסיפים את המטא-נתונים הנדרשים לפעילות החדשה כדי להגדיר את ההגדרה. מגדירים את ערך המטא-נתונים של com.android.settings.category לאותו ערך שהוגדר בשלב 1.

דף חדש

  1. יוצרים קטע חדש, שעובר בירושה מ-DashboardFragment.
  2. מגדירים את הקטגוריה שלה ב-DashboardFragmentRegistry.

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

  3. פועלים לפי השלבים להוספת ההגדרות הנדרשות לדף הזה. מידע נוסף זמין בקטע הטמעה.

אימות

  • מריצים את בדיקות Robolectric בהגדרות. כל הבדיקות הקיימות והחדשות אמורות לעבור.
  • יוצרים ומתקינים את ההגדרות, ואז פותחים באופן ידני את הדף שרוצים לשנות. הדף אמור להתעדכן באופן מיידי.