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

クイック設定は、クイック設定パネルに表示されるタイルです。アクションを表し、ユーザーがタップして反復タスクをすばやく完了できます。アプリは 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 は非アクティブ モードです。つまり、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 から決定されます。これは現在のユーザーと一致している必要があります。