アプリ用のカスタム クイック設定タイルを作成する

クイック設定は、クイック設定パネルに表示されるタイルで、アクションを表し、ユーザーはこれをタップすると、繰り返すタスクをすばやく完了できます。アプリでは、TileService クラスを使用してユーザーにカスタムタイルを提供し、Tile オブジェクトを使用してタイルの状態を追跡できます。たとえば、アプリが提供する VPN をユーザーが有効または無効にできるタイルを作成できます。

VPN タイルのオンとオフが切り替わったクイック設定パネル
図 1. VPN タイルがオンまたはオフになっているクイック設定パネル。

タイルを作成するタイミングを決定する

ユーザーが頻繁にアクセスするか、すばやいアクセスする必要がある(あるいはその両方)特定の機能については、タイルを作成することをおすすめします。両方の品質に一致するタイルが最も効果的なタイルです。これにより、頻繁に実行されるアクションにすばやくアクセスできます。

たとえば、ユーザーがワークアウト セッションをすぐに開始できるフィットネス アプリのタイルを作成できます。ただし、ユーザーがワークアウト履歴全体を確認できる同じアプリにタイルを作成することはおすすめしません。

フィットネス アプリのタイルのユースケース
図 2. フィットネス アプリの推奨タイルと非推奨タイルの例

タイルの見つけやすさと使いやすさを向上させるために、次のような方法は避けてください。

  • タイルを使用してアプリを起動することは避け、代わりにアプリ ショートカットまたは標準のランチャーを使用してください。

  • 1 回限りのユーザー操作にはタイルを使用しないでください。代わりに、アプリのショートカットまたは通知を使用してください。

  • あまり多くのタイルを作成しないでください。アプリごとに 2 つまでにすることをおすすめします。代わりにアプリのショートカットを使用してください。

  • 情報を表示するだけで、ユーザーにとってインタラクティブでないタイルは使用しないでください。代わりに通知またはウィジェットを使用してください。

タイルを作成する

タイルを作成するには、まず適切なタイルアイコンを作成してから、アプリのマニフェスト ファイルで TileService を作成して宣言する必要があります。

クイック設定のサンプルでは、タイルの作成方法と管理方法を確認できます。

カスタム アイコンを作成する

クイック設定パネルのタイルに表示されるカスタム アイコンを用意する必要があります。(このアイコンは、次のセクションで説明するように、TileService を宣言する際に追加します)。アイコンは、背景が透明な白一色で、サイズが 24 x 24 dp で、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() の 4 つのコールバック メソッドが含まれます。これらのメソッドは、サービスが新しいライフサイクル フェーズに入るたびにシステムによって呼び出されます。

TileService のライフサイクルの概要

バインドされたサービスのライフサイクルを制御するコールバックに加えて、TileService ライフサイクルに固有の他のメソッドを実装する必要があります。Service ライフサイクル メソッドと TileService ライフサイクル メソッドは 2 つの別々の非同期スレッドで呼び出されるため、これらのメソッドは onCreate()onDestroy() の外部で呼び出すことができます。

TileService ライフサイクルには、TileService が新しいライフサイクル フェーズに入るたびにシステムによって呼び出される次のメソッドが含まれます。

  • onTileAdded(): このメソッドは、ユーザーが初めてタイルを追加したときと、ユーザーがタイルを削除して再度追加したときにのみ呼び出されます。これは、1 回限りの初期化を行うのに最適なタイミングです。ただし、必要な初期化をすべて満たすとは限りません。

  • onStartListening()onStopListening(): これらのメソッドは、アプリがタイルを更新するたびに呼び出され、頻繁に呼び出されます。TileServiceonStartListening()onStopListening() の間のバインドされたままになるため、アプリはタイルを変更して更新をプッシュできます。

  • onTileRemoved(): このメソッドは、ユーザーがタイルを削除した場合にのみ呼び出されます。

リスニング モードを選択する

TileService は、アクティブ モードまたは非アクティブ モードでリッスンします。アクティブ モードの使用をおすすめします。アクティブ モードはアプリ マニフェストで宣言する必要があります。それ以外の場合、TileService は標準モードであるため、宣言する必要はありません。

TileServiceonStartListening()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() へのコールバックを受け取ります。Tile オブジェクトは、onStartListening()onStopListening() の範囲内で何度でも更新できます。

非アクティブ モードを宣言する必要はありません。単にアプリのマニフェスト ファイルに META_DATA_ACTIVE_TILE を追加しないだけです。

タイルの状態の概要

ユーザーがタイルを追加すると、タイルは常に次のいずれかの状態になります。

  • STATE_ACTIVE: オンまたは有効状態を示します。この状態でも、ユーザーはタイルを操作できます。

    たとえば、ユーザーが時間制限のあるワークアウト セッションを開始できるフィットネス アプリのタイルの場合、STATE_ACTIVE は、ユーザーがワークアウト セッションを開始し、タイマーが動作していることを意味します。

  • STATE_INACTIVE: オフまたは一時停止状態を示します。この状態でも、ユーザーはタイルを操作できます。

    フィットネス アプリのタイルの例を再度使用すると、STATE_INACTIVE のタイルは、ユーザーがワークアウト セッションを開始していないが、必要に応じて開始できることを意味します。

  • STATE_UNAVAILABLE: 一時的に利用できない状態を示します。この状態では、ユーザーはタイルを操作できません。

    たとえば、STATE_UNAVAILABLE のタイルは、なんらかの理由でユーザーがタイルを現在利用できないことを意味します。

システムは Tile オブジェクトの初期状態のみを設定します。残りのライフサイクルを通じて、Tile オブジェクトの状態を設定します。

システムは、Tile オブジェクトの状態を反映するために、タイルアイコンと背景の色合いを調整することがあります。STATE_ACTIVE に設定された Tile オブジェクトは最も暗く、STATE_INACTIVESTATE_UNAVAILABLE は徐々に明るくなります。正確な色相はメーカーとバージョンによって異なります。

VPN タイルの色合いがオブジェクトの状態を反映している
図 4. タイルの状態(それぞれアクティブ、非アクティブ、利用不可の状態)を反映するように色付けされたタイルの例。

タイルを更新する

onStartListening() へのコールバックを受け取ったら、タイルを更新できます。タイルのモードによっては、onStopListening() へのコールバックを受け取るまで、タイルを少なくとも 1 回更新できます。

アクティブ モードでは、onStopListening() へのコールバックを受け取る前にタイルを 1 回だけ更新できます。非アクティブ モードでは、onStartListening()onStopListening() の間でタイルを何度でも更新できます。

Tile オブジェクトを取得するには、getQsTile() を呼び出します。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_ACTIVE または STATE_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() は、パネルを折りたたみながらアクティビティを開始します。アクティビティは、ダイアログよりもさらに詳細な情報を表示する場合や、アクションが非常にインタラクティブな場合に役立ちます。

大量のユーザー操作が必要なアプリでは、最後の手段としてのみアクティビティを起動する必要があります。代わりに、ダイアログまたは切り替えボタンの使用を検討してください。

タイルを長押しすると、[アプリ情報] 画面が表示されます。この動作をオーバーライドして、設定を行うアクティビティを起動するには、ACTION_QS_TILE_PREFERENCES を使用して、アクティビティの 1 つに <intent-filter> を追加します。

タイルを切り替え可能としてマークする

タイルが主に 2 状態スイッチ(タイルの最も一般的な動作)として機能する場合は、タイルを切り替え可能としてマークすることをおすすめします。これにより、タイルの動作に関する情報をオペレーティング システムに提供し、全体的なユーザー補助を改善できます。

タイルを切り替え可能としてマークするには、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() メソッドを使用して、ユーザーが簡単にタイルをデバイスに追加できるようになります。このメソッドは、タイルをクイック設定パネルに直接すばやく追加するようユーザーに求めます。プロンプトには、アプリケーション名、指定されたラベル、アイコンが含まれます。

クイック設定 Placement API のプロンプト
図 5.クイック設定 Placement API のプロンプト
public void requestAddTileService (
  ComponentName tileServiceComponentName,
  CharSequence tileLabel,
  Icon icon,
  Executor resultExecutor,
  Consumer<Integer> resultCallback
)

このコールバックには、タイルが追加されているかどうか、すでに追加されているかどうか、エラーが発生したかどうかの情報が含まれています。

ユーザーにプロンプトを表示するタイミングと頻度は、エージェントの裁量で決定してください。requestAddTileService() は、タイルが提供する機能をユーザーが初めて操作したときなどのコンテキストでのみ呼び出すことをおすすめします。

特定の ComponentName に対するリクエストが、以前にユーザーによって十分な回数拒否されている場合、システムはそのリクエストの処理を停止できます。ユーザーは、このサービスの取得に使用された Context から特定されます。現在のユーザーと一致している必要があります。