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

クイック設定は、クイック設定パネルに表示されるタイルです。アクションを表し、ユーザーがタップして反復タスクをすばやく完了できます。アプリは 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 は、アプリからリクエストされた場合、またはシステムがアプリと通信する必要がある場合にバインドされます。一般的な bound-service のライフサイクルには、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 を使用していずれかのアクティビティに <intent-filter> を追加します。

Android API 28 以降では、PendingIntentIntent.FLAG_ACTIVITY_NEW_TASK が必要です。

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

または、特定の Activity セクションの AndroidManifest.xml にフラグを追加することもできます。

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

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

Quick Settings Placement API プロンプト
図 5. Quick Settings Placement API プロンプト
public void requestAddTileService (
  ComponentName tileServiceComponentName,
  CharSequence tileLabel,
  Icon icon,
  Executor resultExecutor,
  Consumer<Integer> resultCallback
)

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

ユーザーにプロンプトを表示するタイミングと頻度は、状況に応じて判断してください。requestAddTileService() を呼び出すのは、コンテキストがあるとき(タイルで実現されている機能をユーザーが初めて操作するときなど)に限定することをおすすめします。

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