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

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

VPN タイルがオンとオフに切り替えられたクイック設定パネル
図 1. VPN タイルがオンとオフに切り替えられたクイック設定パネル。

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

ユーザーが頻繁にアクセスする、または迅速にアクセスする必要がある(あるいはその両方)特定の機能のタイルを作成することをおすすめします。最も効果的なタイルは、これら 2 つの特性を兼ね備え、頻繁に行う操作にすばやくアクセスできるタイルです。

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

フィットネス アプリのタイルのユースケース
図 2. フィットネス アプリの推奨されるタイルと推奨されないタイルの例。

タイルの見つけやすさと使いやすさを向上させるため、特定のプラクティスを避けることをおすすめします。

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

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

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

  • 情報を表示するものの、ユーザーが操作できないタイルは使用しないでください。代わりに通知またはウィジェットを使用してください。

タイルを作成する

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

クイック設定のサンプルでは、タイルの作成方法と管理方法の例を示しています。

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

クイック設定パネルのタイルに表示されるカスタム アイコンを指定する必要があります。(このアイコンは、次のセクションで説明する TileService を宣言するときに追加します)。アイコンは、背景が透明な白一色で、サイズが 24 x 24 dp の VectorDrawable 形式にする必要があります。

ベクター型ドローアブルの例
図 3. ベクター型ドローアブルの例。

タイルの目的を視覚的に示すアイコンを作成します。これにより、ユーザーはタイルがニーズに合っているかどうかを判断できます。たとえば、ユーザーがワークアウト セッションを開始できるフィットネス アプリのタイルに、ストップウォッチのアイコンを作成できます。

TileService を作成して宣言する

TileService クラスを拡張するタイルのサービスを作成します。

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()
    }
}

アプリのマニフェスト ファイルで TileService を宣言します。TileService の名前とラベル(android:label の場合は最大 18 文字)、前のセクションで作成したカスタム アイコン、適切な権限を追加します。

 <!-- 18-character limit for android:label. -->
 <service
     android:name=".MyQSTileService"
     android:exported="true"
     android:label="@string/my_default_tile_label"
     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(): これらのメソッドは、アプリがタイルを更新するたびに呼び出され、頻繁に呼び出されます。TileService は onStartListening() と onStopListening() の間でバインドされたままになり、アプリはタイルを変更して更新を push できます。

  • 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 オブジェクトの状態を反映するために、タイルアイコンと背景に色を付けることがあります。STATE_ACTIVE に設定された Tile オブジェクトが最も暗く、STATE_INACTIVE、STATE_UNAVAILABLE の順に明るくなります。正確な色合いは、メーカーとバージョンによって異なります。

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

タイルを更新する

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

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

Tile オブジェクトを取得するには、getQsTile() を呼び出します。Tile オブジェクトの特定のフィールドを更新するには、次のメソッドを呼び出します。

Tile オブジェクトのフィールドを正しい値に設定したら、updateTile() を呼び出してタイルを更新する必要があります。これにより、システムは更新されたタイルデータを解析し、UI を更新します。

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 = state.label
    qsTile.state = if (state.enabled) Tile.STATE_ACTIVE else Tile.STATE_INACTIVE
    qsTile.icon = state.icon
    qsTile.updateTile()
}

タップを処理する

タイルが STATE_ACTIVE または STATE_INACTIVE にある場合、ユーザーはタイルをタップしてアクションをトリガーできます。その後、システムはアプリの onClick() コールバックを呼び出します。

アプリが onClick() へのコールバックを受信すると、ダイアログやアクティビティを起動したり、バックグラウンド処理をトリガーしたり、タイルの状態を変更したりできます。

var counter = 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()
}

ダイアログを起動する

showDialog() をタップすると、クイック設定パネルが折りたたまれ、ダイアログが表示されます。追加の入力やユーザーの同意が必要な場合は、ダイアログを使用してアクションにコンテキストを追加します。

アクティビティを起動する

startActivityAndCollapse() は、パネルを折りたたみながらアクティビティを開始します。アクティビティは、ダイアログ内よりも詳細な情報を表示する場合や、アクションが高度にインタラクティブな場合に便利です。

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

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

Android API 28 以降では、PendingIntent に Intent.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 オブジェクトが実行されます。

タイルを分類する

クイック設定のユーザー エクスペリエンスを向上させるために、タイルを分類できます。システムは、タイルを接続、ディスプレイ、プライバシーなどのカテゴリに分類します。システムは、これらのカテゴリを使用して、クイック設定の編集モードでタイルを並べ替え、グループ化します。これにより、ユーザーはタイルを簡単に見つけて管理できるようになります。

実装

TileService のカテゴリを指定するには、AndroidManifest.xml ファイルのサービス宣言にメタデータ フィールドを追加します。

  • AndroidManifest.xml で、TileService の <service> 要素内に <meta-data> 要素を追加します。
  • android:name: android.service.quicksettings.TILE_CATEGORY に設定します。
  • android:value: android.service.quicksettings.CATEGORY_CONNECTIVITY や android.service.quicksettings.CATEGORY_DISPLAY など、事前定義されたカテゴリ定数のいずれかを割り当てます。

次の例をご覧ください。

<service
    android:name=".MyConnectivityTileService"
    [...]
    >
    <meta-data android:name="android.service.quicksettings.TILE_CATEGORY"
        android:value="android.service.quicksettings.CATEGORY_CONNECTIVITY" />
</service>

API には、選択可能な事前定義されたカテゴリのセットが用意されています。これらのカテゴリは、TileService クラス内で文字列定数として定義されています。

カテゴリが指定されていない場合、システムはデフォルトのカテゴリを自動的に割り当てます。

  • システムアプリから: システムアプリの一部であるタイルの場合。
  • インストールしたアプリから: ユーザーがインストールしたアプリのタイル。

Google Pixel デバイスではクイック設定のカテゴリを使用していますが、OEM はそれぞれのシステム UI でこのカテゴリ情報を使用することも、無視することもできます。

お題に参加するようユーザーに促す

タイルを手動で追加するには、いくつかの手順を行う必要があります。

  1. 下にスワイプしてクイック設定パネルを開きます。
  2. [編集] ボタンをタップします。
  3. デバイス上のすべてのタイルをスクロールして、あなたのタイルを見つけます。
  4. タイルを長押しして、アクティブなタイルのリストにドラッグします。

ユーザーはいつでもタイルを移動または削除できます。

Android 13 以降では、requestAddTileService() メソッドを使用して、ユーザーがデバイスにタイルを簡単に追加できるようにすることができます。このメソッドは、クイック設定パネルにタイルを直接追加するようユーザーにリクエストします。プロンプトには、アプリケーション名、指定されたラベル、アイコンが含まれます。

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

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

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

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