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

クイック設定は、クイック設定パネルに表示されるタイルで、アクションを表し、ユーザーはタップして繰り返しタスクをすばやく完了できます。アプリでは、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 を使用してアクティビティの 1 つに <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() メソッドを使用して、ユーザーが簡単にタイルをデバイスに追加できます。この方法では、タイルをクイック設定パネルに直接追加するよう、ユーザーに求めるメッセージが表示されます。プロンプトには、アプリ名、指定されたラベル、アイコンが含まれます。

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

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

プロンプトを表示するタイミングと頻度は、ご自身の裁量で決定してください。requestAddTileService() は、状況に応じて(タイルで実現されている機能をユーザーが初めて操作するときなど)にのみ呼び出すことをおすすめします。

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