为您的应用创建自定义快速设置磁贴

快速设置是显示在 快速设置面板 中的磁贴,代表操作,用户可以点击这些操作快速完成重复性任务。您的应用可以通过 TileService 类向用户提供自定义磁贴,并使用 Tile 对象来跟踪磁贴的状态。例如,您可以创建一个磁贴,让用户可以打开或关闭由您的应用提供的 VPN。

Quick Settings panel with the VPN tile turned
  on and off
图 1. 快速设置面板,其中 VPN 磁贴已打开和关闭。

决定何时创建磁贴

我们建议为您希望用户经常访问或需要快速访问的特定功能创建磁贴。最有效的磁贴是同时满足这两种特性的磁贴,可以快速访问经常执行的操作。

例如,您可以为健身应用创建一个磁贴,让用户可以快速开始锻炼。但是,我们不建议为同一应用创建一个磁贴,让用户可以查看他们的完整锻炼历史记录。

Fitness app tile use cases
图 2. 建议和不建议的健身应用磁贴示例。

为了帮助提高磁贴的可发现性和易用性,我们建议避免某些做法

  • 避免使用磁贴启动应用。请改用 应用快捷方式 或标准启动器。

  • 避免将磁贴用于一次性用户操作。请改用应用快捷方式或 通知

  • 避免创建过多磁贴。我们建议每个应用最多创建两个磁贴。请改用应用快捷方式。

  • 避免使用显示信息但对用户不可交互的磁贴。请改用通知或 小部件

创建磁贴

要创建磁贴,您需要首先创建一个合适的磁贴图标,然后在您的应用清单文件中创建和声明您的 TileService

快速设置示例 提供了有关如何创建和管理磁贴的示例。

创建自定义图标

您需要提供一个自定义图标,该图标将显示在快速设置面板中的磁贴上。(您将在下一节中描述如何声明 TileService 时添加此图标。)图标必须为纯白色,背景透明,尺寸为 24 x 24dp,并且必须采用 VectorDrawable 的形式。

Example of a vector drawable
图 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 或系统需要与之通信时,您的 TileService 将被绑定。典型的 绑定服务生命周期 包含以下四种回调方法:onCreate()onBind()onUnbind()onDestroy()。每当服务进入新的生命周期阶段时,这些方法都会由系统调用。

TileService 生命周期概述

除了控制绑定服务生命周期的回调之外,您还必须实现 TileService 生命周期特有的其他方法。这些方法可能在 onCreate()onDestroy() 之外被调用,因为 Service 生命周期方法和 TileService 生命周期方法是在两个独立的异步线程中调用的。

TileService 生命周期包含以下方法,这些方法在您的 TileService 每次进入新的生命周期阶段时都会由系统调用

  • onTileAdded():此方法仅在用户首次添加磁贴时调用,并且如果用户再次移除和添加磁贴,则也会调用此方法。这是执行任何一次性初始化的最佳时间。但是,这可能无法满足所有必要的初始化需求。

  • onStartListening()onStopListening():每当您的应用更新磁贴时,都会调用这些方法,并且这些方法会被频繁调用。TileServiceonStartListening()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 可能在超出其控制范围的时间被创建和重新绑定。当用户没有查看磁贴时,它也可能被解绑并销毁。

当用户打开他们的快速设置面板后,您的应用会收到对 onStartListening() 的回调。您可以在 onStartListening()onStopListening() 之间根据需要多次更新您的 Tile 对象。

您不需要声明非活动模式,只需不将 META_DATA_ACTIVE_TILE 添加到您的应用的清单文件中即可。

磁贴状态概述

用户添加您的磁贴后,它始终处于以下状态之一。

  • STATE_ACTIVE:表示开启或启用状态。用户在此状态下可以与您的磁贴交互。

    例如,对于允许用户启动计时锻炼会话的健身应用磁贴,STATE_ACTIVE 表示用户已启动锻炼会话,计时器正在运行。

  • STATE_INACTIVE:表示关闭或暂停状态。用户在此状态下可以与您的磁贴交互。

    再次使用健身应用磁贴的示例,处于 STATE_INACTIVE 状态的磁贴表示用户尚未启动锻炼会话,但如果他们想要,可以启动。

  • STATE_UNAVAILABLE:表示暂时不可用状态。用户在此状态下无法与您的磁贴交互。

    例如,处于 STATE_UNAVAILABLE 状态的磁贴表示磁贴当前不可用,原因可能是某些原因。

系统只设置您的 Tile 对象的初始状态。您在磁贴对象生命周期的其余时间内设置 Tile 对象的状态。

系统可能会对磁贴图标和背景进行着色,以反映您的 Tile 对象的状态。Tile 对象设置为 STATE_ACTIVE 的颜色最深,STATE_INACTIVESTATE_UNAVAILABLE 的颜色越来越浅。确切的色调取决于制造商和版本。

VPN tile tinted to reflect object states
图 4. 磁贴着色以反映磁贴状态(分别为活动、非活动和不可用状态)的示例。

更新您的磁贴

收到对 onStartListening() 的回调后,您可以更新磁贴。根据磁贴的模式,您的磁贴至少可以更新一次,直到收到对 onStopListening() 的回调。

在活动模式下,您可以在收到对 onStopListening() 的回调之前,只更新一次磁贴。在非活动模式下,您可以在 onStartListening()onStopListening() 之间任意多次更新磁贴。

您可以通过调用 getQsTile() 来检索您的 Tile 对象。若要更新 Tile 对象的特定字段,请调用以下方法

完成将 Tile 对象的字段设置为正确的值后,必须调用 updateTile() 来更新磁贴。这将使系统解析更新的磁贴数据并更新 UI。

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() 启动活动并同时折叠面板。如果要显示比对话框内更详细的信息,或者如果您的操作高度交互式,则活动很有用。

如果您的应用需要大量的用户交互,应用应该只在万不得已时才启动活动。相反,请考虑使用对话框或切换。

长按磁贴会提示用户的“应用信息”屏幕。若要覆盖此行为,并改为启动用于设置首选项的活动,请在您的其中一个活动中添加一个 <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. 按住您的磁贴,然后将其拖动到活动磁贴列表中。

用户可以在任何时候移动或删除您的磁贴。

从 Android 13 开始,您可以使用 requestAddTileService() 方法,让用户更轻松地将您的磁贴添加到设备。此方法会向用户显示一个提示,让他们快速将您的磁贴直接添加到他们的快速设置面板中。该提示包含应用程序名称、提供的标签和图标。

Quick Settings Placement API prompt
图 5. 快速设置放置 API 提示。
public void requestAddTileService (
  ComponentName tileServiceComponentName,
  CharSequence tileLabel,
  Icon icon,
  Executor resultExecutor,
  Consumer<Integer> resultCallback
)

回调包含有关磁贴是否已添加、未添加、是否已存在或是否发生任何错误的信息。

在决定何时以及如何经常提示用户时,请谨慎行事。我们建议仅在特定情况下调用 requestAddTileService(),例如,当用户第一次与您的磁贴促成的功能进行交互时。

如果用户之前已多次拒绝请求,系统可以选择停止处理针对给定 ComponentName 的请求。用户是根据用于检索此服务的 Context 确定的,它必须与当前用户匹配。