為應用程式建立自訂「快速設定」方塊

「快速設定」是快速設定面板中顯示的資訊方塊,代表各項動作,使用者只要輕觸即可快速完成週期性工作。應用程式可透過 TileService 類別為使用者提供自訂資訊方塊,並使用 Tile 物件追蹤資訊方塊狀態。舉例來說,您可以建立資訊方塊,讓使用者開啟或關閉應用程式提供的 VPN。

開啟及關閉 VPN 圖塊的快速設定面板
圖 1 「快速設定」面板的「VPN」設定方塊會開啟或關閉。

決定建立資訊方塊的時機

建議您根據預期使用者會經常存取或需要快速存取 (或兩者皆有) 的特定功能建立資訊方塊。最有效的資訊方塊就是同時符合這兩項特質的圖塊,可以快速存取經常執行的動作。

舉例來說,您可以為健身應用程式建立資訊方塊,讓使用者能快速啟動健身工作階段。不過,我們不建議為同一個應用程式建立資訊方塊,讓使用者查看完整的健身記錄。

健身應用程式圖塊使用情境
圖 2. 健身應用程式建議與非建議資訊方塊的範例。

為提升資訊方塊的曝光度和使用便利性,我們建議避免採行下列做法:

  • 請避免使用資訊方塊啟動應用程式,請改用應用程式捷徑或標準啟動器。

  • 避免使用資訊方塊進行一次性使用者動作。請改用應用程式捷徑或通知

  • 避免建立過多資訊方塊。建議每個應用程式最多兩個。請改用應用程式捷徑。

  • 避免使用會顯示資訊但使用者無法互動的資訊方塊。請改用通知或小工具

建立資訊方塊

如要建立資訊方塊,您必須先建立適當的資訊方塊圖示,然後在應用程式的資訊清單檔案中建立並宣告 TileService

「快速設定」範例提供如何建立及管理資訊方塊的範例。

建立自訂圖示

你需要提供自訂圖示,這個圖示顯示在「快速設定」面板的資訊方塊上。(您會在宣告 TileService 時新增這個圖示,如下一節所述)。圖示必須是純白色,且背景為透明背景,尺寸應為 24 x 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 生命週期總覽

除了控管繫結服務生命週期的回呼外,您也必須實作 TileService 生命週期特有的其他方法。您可以在 onCreate()onDestroy() 之外呼叫這些方法,因為 Service 生命週期方法和 TileService 生命週期方法會在兩個不同的非同步執行緒中呼叫。

TileService 生命週期包含以下方法,系統會在 TileService 進入新的生命週期階段時,叫用這些方法:

  • onTileAdded():只有在使用者首次新增資訊方塊,又再次移除並新增資訊方塊時,系統才會呼叫這個方法。這就是執行任何一次性初始化的最佳時機。不過,這可能無法滿足所有必要的初始化作業。

  • onStartListening()onStopListening():每當應用程式更新資訊方塊時,系統就會呼叫這些方法,且經常呼叫這些方法。TileService 仍介於 onStartListening()onStopListening() 之間,可讓應用程式修改資訊方塊及推送更新。

  • onTileRemoved():只有在使用者移除資訊方塊時,才會呼叫這個方法。

選取收聽模式

TileService 會以「啟用」模式或「未啟用」模式監聽。建議您使用啟用模式,您必須在應用程式資訊清單中進行宣告。否則,TileService 為標準模式,不需要宣告。

不要假設 TileService 會在 onStartListening()onStopListening() 方法組合之外。

TileService 使用正常模式,以自己的程序監聽及監控其狀態。處於啟用模式的 TileService 會針對 onTileAdded()onTileRemoved()、輕觸事件,以及應用程式程序要求時繫結。

如果 TileService 會在資訊方塊狀態應以自己的程序更新時接收通知,建議您使用啟用模式。使用中的圖塊會限制系統上的壓力,因為使用者每次看見「快速設定」面板時,不需要繫結。

您可以呼叫靜態 TileService.requestListeningState() 方法,要求啟動監聽狀態並接收對 onStartListening() 的回呼。

您可以在應用程式的資訊清單檔案中加入 META_DATA_ACTIVE_TILE,以宣告啟用模式。

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

非啟用模式

非主動模式為標準模式。如果在使用者能看到資訊方塊時,TileService 會處於非活動模式,就會處於非活動模式。這表示您的 TileService 可能會在其無法控管的時間點建立和重新繫結。當使用者在不查看資訊方塊時,這個 ID 也可能會取消繫結並刪除。

使用者開啟「快速設定」面板後,應用程式會收到對 onStartListening() 的回呼。您可以在 onStartListening()onStopListening() 之間任意更新 Tile 物件,次數不限。

您不需要宣告非使用模式,只要在應用程式的資訊清單檔案中加入 META_DATA_ACTIVE_TILE 即可。

資訊方塊狀態總覽

使用者新增的資訊方塊後,資訊方塊一律會處於下列其中一種狀態。

  • STATE_ACTIVE:表示已啟用或已啟用的狀態。使用者可以在這個狀態下與資訊方塊互動。

    舉例來說,如果健身應用程式資訊方塊可讓使用者啟動計時健身工作階段,STATE_ACTIVE 表示使用者已啟動健身工作階段,且計時器正在運作。

  • STATE_INACTIVE:表示關閉或暫停狀態。使用者可以在這個狀態下與資訊方塊互動。

    如要再次使用健身應用程式資訊方塊範例,STATE_INACTIVE 中的資訊方塊表示使用者尚未啟動健身工作階段,但可以這麼做。

  • STATE_UNAVAILABLE:表示暫時無法使用狀態。在這種狀態下,使用者無法與資訊方塊互動。

    舉例來說,STATE_UNAVAILABLE 中的資訊方塊表示使用者目前因故無法使用該資訊方塊。

系統只會設定 Tile 物件的初始狀態。您會在整個生命週期內設定 Tile 物件的狀態。

系統可能會根據 Tile 物件的狀態,調整資訊方塊圖示和背景的顏色。設為 STATE_ACTIVETile 物件最暗,其 STATE_INACTIVESTATE_UNAVAILABLE 的亮度會增加。確切的色調為製造商和版本

將 VPN 圖塊色調以反映物件狀態
圖 4. 色調以反映圖塊狀態的圖塊範例 (分別處於有效、無效和無法使用的狀態)。

更新動態磚

收到對 onStartListening() 的回呼後,即可更新資訊方塊。視資訊方塊模式而定,資訊方塊至少可以更新一次,直到收到對 onStopListening() 的回呼為止。

在正常模式中,您可以只更新一次資訊方塊,然後再收到 onStopListening() 的回呼。在非主動模式下,您可以在 onStartListening()onStopListening() 之間無限次更新資訊方塊。

您可以呼叫 getQsTile() 來擷取 Tile 物件。如要更新 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_ACTIVESTATE_INACTIVE,使用者可以輕觸圖塊來觸發動作。接著,系統會叫用應用程式的 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() 在收合面板時啟動活動。如果比起對話方塊顯示的詳細資訊,或是動作的互動程度很高,活動就非常實用。

如果您的應用程式需要進行大量的使用者互動,在不得已的情況下,請只讓應用程式啟動活動。建議您改用對話方塊或切換鈕。

長按資訊方塊會提示使用者的「App Info」畫面。如要覆寫此行為並改為啟動用於設定偏好設定的活動,請使用 ACTION_QS_TILE_PREFERENCES<intent-filter> 新增至其中一項活動。

從 Android API 28 開始,PendingIntent 必須具備 Intent.FLAG_ACTIVITY_NEW_TASK

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

您也可以在特定的 Activity 區段的 AndroidManifest.xml 中新增標記。

將動態磚標示為可切換

如果資訊方塊主要功能為雙狀態切換 (此為最常見的資訊方塊行為),建議您將其標示為可切換。這有助於提供資訊方塊對作業系統的行為資訊,並提升整體的無障礙功能。

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. 按住動態磚,並拖曳至活動動態磚清單。

使用者也可以隨時移動或移除您的動態磚。

從 Android 13 開始,您可以使用 requestAddTileService() 方法,讓使用者更輕鬆地將資訊方塊新增至裝置。這個方法會提示使用者要求,讓他們快速將您的資訊方塊直接新增至他們的「快速設定」面板。提示包含應用程式名稱、提供的標籤和圖示。

快速設定刊登位置 API 提示
圖 5.快速設定刊登位置 API 提示。
public void requestAddTileService (
  ComponentName tileServiceComponentName,
  CharSequence tileLabel,
  Icon icon,
  Executor resultExecutor,
  Consumer<Integer> resultCallback
)

回呼內含資訊方塊是否新增、尚未新增、是否已在當中,或是否發生任何錯誤。

請自行斟酌是否要顯示提示訊息的時機和頻率。建議您只在使用情境下呼叫 requestAddTileService(),例如使用者首次與資訊方塊提供的功能互動時。

如果特定 ComponentName 之前遭使用者多次拒絕,系統可以選擇停止處理要求。使用者取決於用於擷取這項服務的 Context,必須與目前的使用者相符。