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

ההגדרות המהירות הן משבצות שמוצגות בלוח ההגדרות המהירות, ומציגות פעולות שהמשתמשים יכולים להקיש עליהן כדי להשלים במהירות משימות חוזרות. האפליקציה יכולה לספק למשתמשים משבצת בהתאמה אישית באמצעות הכיתה 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 מחויב כשהאפליקציה מבקשת זאת, או אם המערכת צריכה לתקשר איתו. מחזור החיים של שירות מקושר כולל את ארבע שיטות הקריאה החוזרת הבאות: onCreate(),‏ onBind(),‏ onUnbind() ו-onDestroy(). המערכת מפעילה את השיטות האלה בכל פעם שהשירות נכנס לשלב חדש במחזור החיים.

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

בנוסף לקריאות החזרה (callbacks) ששולטות במחזור החיים של השירות המקושר, צריך להטמיע שיטות אחרות שספציפיות למחזור החיים של TileService. אפשר להפעיל את השיטות האלה מחוץ ל-onCreate() ול-onDestroy() כי השיטות של מחזור החיים של Service והשיטות של מחזור החיים של 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 שמשמש לאחזור השירות הזה – הוא חייב להתאים למשתמש הנוכחי.