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

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

חלונית ההגדרות המהירות עם לחצן ה-VPN מופעל ומושבת
איור 1. חלונית ההגדרות המהירות עם לחצן ה-VPN מופעל ומכבוי.

מתי כדאי ליצור משבצת

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

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

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

כדי לשפר את החשיפה של המשבצת ואת קלות השימוש בה, מומלץ להימנע מהשיטות הבאות:

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

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

  • מומלץ לא ליצור יותר מדי משבצות. מומלץ לא יותר משתי קבצים לכל אפליקציה. במקום זאת, אפשר להשתמש בקיצור דרך לאפליקציה.

  • מומלץ להימנע משימוש בכרזות שמציגות מידע אבל לא אינטראקטיביות למשתמשים. במקום זאת, אפשר להשתמש בהתראה או בווידג'ט.

יצירת המשבצת

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

הדוגמה להגדרות מהירות ממחישה איך יוצרים ומנהלים לחצן.

יצירת סמל מותאם אישית

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

דוגמה לפריט גרפי וקטורי שניתן לשרטוט
איור 3. דוגמה לפריט גרפי וקטורי שניתן לשרטוט.

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

יצירה והצהרה על TileService

יוצרים שירות לכרטיס שמרחיב את הכיתה TileService.

Kotlin

class MyQSTileService: TileService() {

  // Called when the user adds your tile.
  override fun onTileAdded() {
    super.onTileAdded()
  }
  // Called when your app can update your tile.
  override fun onStartListening() {
    super.onStartListening()
  }

  // Called when your app can no longer update your tile.
  override fun onStopListening() {
    super.onStopListening()
  }

  // Called when the user taps on your tile in an active or inactive state.
  override fun onClick() {
    super.onClick()
  }
  // Called when the user removes your tile.
  override fun onTileRemoved() {
    super.onTileRemoved()
  }
}

Java

public class MyQSTileService extends TileService {

  // Called when the user adds your tile.
  @Override
  public void onTileAdded() {
    super.onTileAdded();
  }

  // Called when your app can update your tile.
  @Override
  public void onStartListening() {
    super.onStartListening();
  }

  // Called when your app can no longer update your tile.
  @Override
  public void onStopListening() {
    super.onStopListening();
  }

  // Called when the user taps on your tile in an active or inactive state.
  @Override
  public void onClick() {
    super.onClick();
  }

  // Called when the user removes your tile.
  @Override
  public void onTileRemoved() {
    super.onTileRemoved();
  }
}

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

 <service
     android:name=".MyQSTileService"
     android:exported="true"
     android:label="@string/my_default_tile_label"  // 18-character limit.
     android:icon="@drawable/my_default_icon_label"
     android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
     <intent-filter>
         <action android:name="android.service.quicksettings.action.QS_TILE" />
     </intent-filter>
 </service>

ניהול TileService

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

TileService הוא שירות מחויב. ה-TileService מחויב כשהאפליקציה מבקשת זאת, או אם המערכת צריכה לתקשר איתו. מחזור החיים של שירות מקושר כולל את ארבע שיטות ה-callback הבאות: onCreate(),‏ onBind(),‏ onUnbind() ו-onDestroy(). המערכת מפעילה את השיטות האלה בכל פעם שהשירות נכנס לשלב חדש במחזור החיים.

סקירה כללית על מחזור החיים של TileService

בנוסף לקריאות החזרה (callbacks) ששולטות במחזור החיים של השירות המקושר, צריך להטמיע שיטות אחרות שספציפיות למחזור החיים של TileService. יכול להיות לקרוא ל-methods האלה מחוץ ל-onCreate() ול-onDestroy() כי ה-methods של מחזור החיים Service וה-methods של מחזור החיים TileService נקראות בשני שרשורים אסינכרוניים נפרדים.

מחזור החיים של TileService מכיל את השיטות הבאות, שהמערכת מפעילה בכל פעם ש-TileService נכנס לשלב חדש במחזור החיים:

  • onTileAdded(): מפעילים את השיטה הזו רק כשהמשתמש מוסיף את המשבצת בפעם הראשונה, ואם המשתמש מסיר את המשבצת ומוסיף אותו שוב. זהו הזמן הטוב ביותר לבצע אתחול חד-פעמי. אבל יכול להיות שהפעולה הזו לא תספק את כל האתחולים הנדרשים.

  • onStartListening() ו-onStopListening(): השיטות האלה נקראות בכל פעם שהאפליקציה מעדכנת את המשבצת, והן נקראות לעיתים קרובות. השדה TileService נשאר מוגבל בין onStartListening() ל-onStopListening(), וכך מאפשר לאפליקציה לשנות את המשבצת ולשלוח עדכונים.

  • onTileRemoved(): ה-method הזה נקרא רק אם המשתמש מסיר את המשבצת שלכם.

בחירת מצב האזנה

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

אל תניחו ש-TileService יהיה מחוץ לשתי השיטות onStartListening() ו-onStopListening().

משתמשים במצב פעיל ל-TileService שמקשיב למצב שלו ומעקב אחריו בתהליך משלו. TileService במצב פעיל מוגדר ל-onTileAdded(), ל-onTileRemoved(), לאירועי הקשה, וכאשר הוא מבוקש על ידי תהליך האפליקציה.

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

אפשר לקרוא לשיטה הסטטית TileService.requestListeningState() כדי לבקש את תחילת מצב ההאזנה ולקבל קריאה חוזרת (callback) ל-onStartListening().

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

<service ...>
    <meta-data android:name="android.service.quicksettings.ACTIVE_TILE"
         android:value="true" />
    ...
</service>

מצב לא פעיל

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

האפליקציה תקבל קריאה חוזרת למספר onStartListening() אחרי שהמשתמש פותח את חלונית ההגדרות המהירות. אפשר לעדכן את האובייקט Tile כמה פעמים שרוצים בין onStartListening() ל-onStopListening().

אין צורך להצהיר על מצב לא פעיל – פשוט לא מוסיפים את הערך META_DATA_ACTIVE_TILE לקובץ המניפסט של האפליקציה.

סקירה כללית על מצבי האריחים

אחרי שמשתמש מוסיף את המשבצת שלכם, היא תמיד נמצאת באחד מהמצבים הבאים.

  • STATE_ACTIVE: מציין מצב מופעל או מופעל. במצב הזה המשתמש יכול לתקשר עם המשבצת שלכם.

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

  • STATE_INACTIVE: מציין מצב מושהה או כבוי. המשתמש יכול לבצע פעולות במשבצה שלכם במצב הזה.

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

  • STATE_UNAVAILABLE: מציין מצב שאינו זמין באופן זמני. המשתמש לא יכול לבצע פעולות עם המשבצת שלכם במצב הזה.

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

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

המערכת עשויה להוסיף גוון לסמל ולרקע של המשבצת כדי לשקף את המצב של האובייקט Tile. אובייקטים מסוג Tile שמוגדרים כ-STATE_ACTIVE הם הכי כהים, ואובייקטים מסוג STATE_INACTIVE ו-STATE_UNAVAILABLE הם בהירים יותר. הגוון המדויק הוא ספציפי ליצרן ולגרסה.

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

עדכון של אריח

אפשר לעדכן את המשבצת אחרי שתקבלו שיחה חוזרת למספר onStartListening(). בהתאם למצב של המשבצת, אפשר לעדכן אותה לפחות פעם אחת עד שמקבלים קריאה חוזרת ל-onStopListening().

במצב פעיל, אפשר לעדכן את המשבצת רק פעם אחת לפני שתקבלו קריאה חוזרת ל-onStopListening(). במצב לא פעיל, אפשר לעדכן את המשבצת כמה פעמים שרוצים בין onStartListening() ל-onStopListening().

אפשר לאחזר את האובייקט Tile באמצעות קריאה ל-getQsTile(). כדי לעדכן שדות ספציפיים באובייקט Tile, צריך להפעיל את השיטות הבאות:

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

Kotlin

data class StateModel(val enabled: Boolean, val label: String, val icon: Icon)

override fun onStartListening() {
  super.onStartListening()
  val state = getStateFromService()
  qsTile.label = state.label
  qsTile.contentDescription = tile.label
  qsTile.state = if (state.enabled) Tile.STATE_ACTIVE else Tile.STATE_INACTIVE
  qsTile.icon = state.icon
  qsTile.updateTile()
}

Java

public class StateModel {
  final boolean enabled;
  final String label;
  final Icon icon;

  public StateModel(boolean e, String l, Icon i) {
    enabled = e;
    label = l;
    icon = i;
  }
}

@Override
public void onStartListening() {
  super.onStartListening();
  StateModel state = getStateFromService();
  Tile tile = getQsTile();
  tile.setLabel(state.label);
  tile.setContentDescription(state.label);
  tile.setState(state.enabled ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE);
  tile.setIcon(state.icon);
  tile.updateTile();
}

טיפול בהקשות

אם המשבצת שלכם תופיע ב-STATE_ACTIVE או ב-STATE_INACTIVE, המשתמשים יוכלו להקיש עליה כדי להפעיל פעולה. לאחר מכן המערכת מפעילה את פונקציית ה-callback‏ onClick() של האפליקציה.

אחרי שהאפליקציה מקבלת קריאה חוזרת ל-onClick(), היא יכולה להפעיל תיבת דו-שיח או פעילות, להפעיל עבודה ברקע או לשנות את המצב של המשבצת.

Kotlin

var clicks = 0
override fun onClick() {
  super.onClick()
  counter++
  qsTile.state = if (counter % 2 == 0) Tile.STATE_ACTIVE else Tile.STATE_INACTIVE
  qsTile.label = "Clicked $counter times"
  qsTile.contentDescription = qsTile.label
  qsTile.updateTile()
}

Java

int clicks = 0;

@Override
public void onClick() {
  super.onClick();
  counter++;
  Tile tile = getQsTile();
  tile.setState((counter % 2 == 0) ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE);
  tile.setLabel("Clicked " + counter + " times");
  tile.setContentDescription(tile.getLabel());
  tile.updateTile();
}

הפעלת תיבת דו-שיח

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

התחלת פעילות

הפקודה startActivityAndCollapse() מתחילה פעילות תוך כדי כיווץ החלונית. פעילויות שימושיות אם יש מידע מפורט יותר להצגה מאשר בתיבת דו-שיח, או אם הפעולה היא אינטראקטיבית מאוד.

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

הקשה ארוכה על משבצת גורמת להצגת המסך פרטי האפליקציה למשתמש. כדי לשנות את ההתנהגות הזו ולהפעיל במקום זאת פעילות להגדרת ההעדפות, מוסיפים את הערך <intent-filter> לאחת מהפעילויות באמצעות ACTION_QS_TILE_PREFERENCES.

החל מ-Android API 28, ב-PendingIntent צריך להיות Intent.FLAG_ACTIVITY_NEW_TASK:

if (Build.VERSION.SDK_INT >= 28) {
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}

לחלופין, אפשר להוסיף את הדגל ב-AndroidManifest.xml בקטע הספציפי Activity.

סימון המשבצת כמשבצת שניתן להפעיל או להשבית

מומלץ לסמן את המשבצת כמשבצת שניתן להפעיל ולהשבית אם היא פועלת בעיקר כמתג עם שני מצבים (זהו ההתנהגות הנפוצה ביותר של משבצות). כך אפשר לספק למערכת ההפעלה מידע על ההתנהגות של המשבצת ולשפר את הנגישות הכוללת.

מגדירים את המטא-נתונים של TOGGLEABLE_TILE לערך true כדי לסמן את המשבצת כמשבצת שניתן להפעיל או להשבית.

<service ...>
  <meta-data android:name="android.service.quicksettings.TOGGLEABLE_TILE"
    android:value="true" />
</service>

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

יכול להיות שהמשבצת תוצג מעל מסך הנעילה במכשירים נעולים. אם המשבצת מכילה מידע אישי רגיש, צריך לבדוק את הערך של isSecure() כדי לקבוע אם המכשיר נמצא במצב מאובטח, ו-TileService צריך לשנות את ההתנהגות שלו בהתאם.

אם אפשר לבצע את הפעולה של המשבצת בבטחה בזמן שהמסך נעול, אפשר להשתמש ב-startActivity() כדי להפעיל פעילות מעל מסך הנעילה.

אם הפעולה בכרטיס המידע לא בטוחה, משתמשים ב-unlockAndRun() כדי לבקש מהמשתמש לבטל את נעילת המכשיר. אם הפעולה תצליח, המערכת מריצה את האובייקט Runnable שמעבירים לשיטה הזו.

בקשה מהמשתמש להוסיף את המשבצת

כדי להוסיף את המשבצת באופן ידני, המשתמשים צריכים לבצע כמה שלבים:

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

המשתמש יכול גם להזיז או להסיר את המשבצת שלכם בכל שלב.

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

הנחיה של Quick Settings Placement API
איור 5. הנחיה של Quick Settings Placement API.
public void requestAddTileService (
  ComponentName tileServiceComponentName,
  CharSequence tileLabel,
  Icon icon,
  Executor resultExecutor,
  Consumer<Integer> resultCallback
)

קריאת החזרה (callback) מכילה מידע על כך שהכרטיס נוסף או לא נוסף, אם הוא כבר היה שם או אם התרחשה שגיאה כלשהי.

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

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