Wear でアプリを表示したままにする

一部の Wear OS アプリは常にユーザーに表示されます。

Android バージョン 5.1 以降が搭載されている Wear OS デバイスでは、電池の消耗を抑えながらアプリをフォアグラウンドで実行し続けることができます。Wear OS アプリは、スマートウォッチが省電力モード(常に画面表示モード)になっている間、スマートウォッチの表示内容を制御することができます。常に画面表示モードとインタラクティブ モードの両方で実行される Wear アプリを常時オンアプリと呼びます。

こうしたアプリにより、たとえばジョギング中のユーザーは、スマートウォッチで走行距離や経過時間を一目で確認できます。また、買い物リストを記録して、買い物中にそのリストのアイテムをすばやく確認することもできます。

アプリを常に表示すると電池寿命が短くなるため、そのような機能をアプリに追加する場合は電池寿命への影響について注意深く検討する必要があります。

重要: Android サポート ライブラリのバージョン 27.1.0 では、常に画面表示モードをサポートする新しい方法(WearableActivity クラスではなく AmbientModeSupport クラスを使用する方法)を使用できます。常に画面表示モードをサポートする新たな方法(推奨)を使用することも、WearableActivity クラスを拡張することも可能です。

注: AmbientMode は非推奨クラスとなっており、AmbientModeSupport クラスに置き換えられています。

以下の関連リソースもご覧ください。

プロジェクトを設定する

常に画面表示モードをサポートするには、Android SDK を更新し、開発プロジェクトを設定する必要があります。必要な変更を行う手順は次のとおりです。

  1. ウェアラブル アプリの作成と実行ページの設定に基づいて、プロジェクトを作成または更新します。
  2. WAKE_LOCK 権限を Android マニフェスト ファイルに追加します。
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    

AmbientModeSupport クラスを使用した常に画面表示モード

常に画面表示モードをサポートするために AmbientModeSupport クラスを使用すると、以下によるメリットが得られます。

AmbientModeSupport クラスを使用するには、FragmentActivity サブクラスのいずれかまたは FragmentActivity 自体を拡張し、プロバイダ インターフェースを実装します。このインターフェースを使用すると、常に画面表示モードのアップデートをリッスンできるようになります。

注: AmbientModeSupport.attach(FragmentActivity) メソッドは、指定の FragmentActivity クラスまたはサブクラスにヘッドレス フラグメントをアタッチします。その後で FragmentManager.getFragments() を呼び出すと、このフラグメントへの参照が返されます(この参照をなんらかの方法で使用することを意図したものではありません)。

以下に、AmbientModeSupport クラスの一般的な使用方法を示します。

  1. FragmentActivity クラスのいずれかのサブクラスを作成します。
  2. 以下の例のように AmbientCallbackProvider インターフェースを実装します。Android システムからの常に画面表示イベントに対応するのに必要なコールバックを提供するように getAmbientCallback() メソッドをオーバーライドします。ステップ 4 では、カスタムのコールバック クラスを作成します。

    Kotlin

        class MainActivity : AppCompatActivity(), AmbientModeSupport.AmbientCallbackProvider {
            …
            override fun getAmbientCallback(): AmbientModeSupport.AmbientCallback = MyAmbientCallback()
            …
        }
        

    Java

        public class MainActivity extends AppCompatActivity implements AmbientModeSupport.AmbientCallbackProvider {
            …
            @Override
            public AmbientModeSupport.AmbientCallback getAmbientCallback() {
                return new MyAmbientCallback();
            }
            …
        }
        
  3. onCreate() メソッドで AmbientModeSupport.attach(FragmentActivity) を呼び出して、常に画面表示モードを有効にします。このメソッドは AmbientModeSupport.AmbientController を返します。コントローラを使用すると、コールバックの外部で「常に画面表示」の状態を確認できます。AmbientModeSupport.AmbientController オブジェクトへの参照は保持することをおすすめします。

    Kotlin

        class MainActivity : AppCompatActivity(), AmbientModeSupport.AmbientCallbackProvider {
            ...
            /*
             * Declare an ambient mode controller, which will be used by
             * the activity to determine if the current mode is ambient.
             */
            private lateinit var ambientController: AmbientModeSupport.AmbientController
            ...
            override fun onCreate(savedInstanceState: Bundle?) {
                super.onCreate(savedInstanceState)
                setContentView(R.layout.activity_main)
                ...
                ambientController = AmbientModeSupport.attach(this)
            }
            ...
        }
        

    Java

        public class MainActivity extends AppCompatActivity implements AmbientModeSupport.AmbientCallbackProvider {
            ...
            /*
             * Declare an ambient mode controller, which will be used by
             * the activity to determine if the current mode is ambient.
             */
            private AmbientModeSupport.AmbientController ambientController;
            ...
            @Override
            public void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_main);
                ...
                ambientController = AmbientModeSupport.attach(this);
            }
            ...
        }
        
  4. 常に画面表示イベントに対処するために、AmbientCallback クラスを拡張する内部クラスを作成します。このクラスがステップ 2 で作成したメソッドから返されるオブジェクトになります。

    Kotlin

        private class MyAmbientCallback : AmbientModeSupport.AmbientCallback() {
    
            override fun onEnterAmbient(ambientDetails: Bundle?) {
              // Handle entering ambient mode
            }
    
            override fun onExitAmbient() {
              // Handle exiting ambient mode
            }
    
            override fun onUpdateAmbient() {
              // Update the content
            }
        }
        

    Java

        private class MyAmbientCallback extends AmbientModeSupport.AmbientCallback {
            @Override
            public void onEnterAmbient(Bundle ambientDetails) {
              // Handle entering ambient mode
            }
    
            @Override
            public void onExitAmbient() {
              // Handle exiting ambient mode
             }
    
            @Override
            public void onUpdateAmbient() {
              // Update the content
            }
        }
        

詳細とおすすめの方法については、AlwaysOn のサンプルをご覧ください。

WearableActivity クラスを使用した常に画面表示モード

新規と既存のどちらのプロジェクトでも、プロジェクトの設定を更新することによって Wear アプリに対する常に画面表示モードのサポートを強化できます。

常に画面表示モードをサポートするアクティビティを作成する

WearableActivity クラスを使用することで、アクティビティ内で常に画面表示モードを有効にすることができます。

  1. WearableActivity を拡張するアクティビティを作成します。
  2. アクティビティの onCreate() メソッド内で setAmbientEnabled() メソッドを呼び出します。

以下のように、アクティビティ内で常に画面表示モードを有効にします。

Kotlin

    class MainActivity : WearableActivity() {

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)

            setAmbientEnabled()
        ...
        }
    }

Java

    public class MainActivity extends WearableActivity {
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            setAmbientEnabled();
            ...
        }
    

モードの遷移に対処する

アプリが表示されている間にユーザーがしばらくアプリを操作しなかった場合や、ユーザーが画面を手のひらで覆った場合、システムによってアクティビティが常に画面表示モードに切り替えられます。アプリが常に画面表示モードに切り替わったら、アクティビティの UI をより基本的なレイアウトに変更して消費電力を削減します。黒色の背景を使用し、白色のグラフィックとテキストを最小限に抑えます。インタラクティブ モードから常に画面表示モードに簡単に遷移できるようにするには、画面上のアイテムの配置がほぼ同じになるようにします。常に画面表示モードにおけるコンテンツの表示について詳しくは、Wear OS 向けウォッチフェイスの設計ガイドをご覧ください。

ハードウェア ボタンがないデバイスでアプリを実行している場合、画面を手のひらで覆ってもアプリは常に画面表示モードに切り替わりません。その代わり、アプリが終了してホーム画面が表示されます。この動作は、ユーザーがアプリをスムーズに終了できるようにすることを目的としています。ただし、このようなデバイスでも、画面がタイムアウトしたときには常に画面表示モードに切り替わります。

注: 常に画面表示モードでは、画面上のインタラクティブな要素(ボタンなど)をすべて無効にしてください。常時オンアプリのユーザー操作を設計する方法について詳しくは、Wear OS 向けのアプリ構造の設計ガイドをご覧ください。

アクティビティが常に画面表示モードに切り替わると、システムによって onEnterAmbient() メソッドがウェアラブル アクティビティ内で呼び出されます。次のコード スニペットは、システムによって常に画面表示モードに切り替えられた後にテキストの色を白に変更し、アンチ エイリアスを無効にする方法を示しています。

Kotlin

    override fun onEnterAmbient(ambientDetails: Bundle?) {
        super.onEnterAmbient(ambientDetails)

        stateTextView.setTextColor(Color.WHITE)
        stateTextView.paint.isAntiAlias = false
    }
    

Java

    @Override
    public void onEnterAmbient(Bundle ambientDetails) {
        super.onEnterAmbient(ambientDetails);

        stateTextView.setTextColor(Color.WHITE);
        stateTextView.getPaint().setAntiAlias(false);
    }
    

ユーザーが画面をタップするか、手首を上げると、アクティビティが常に画面表示モードからインタラクティブ モードに切り替わり、システムによって onExitAmbient() メソッドが呼び出されます。アプリがフルカラーのインタラクティブな状態で表示されるよう、このメソッドをオーバーライドして UI レイアウトを変更します。

次のコード スニペットは、システムによってインタラクティブ モードに切り替えられたときにテキストの色を緑に変更し、アンチ エイリアスを有効にする方法を示しています。

Kotlin

    override fun onExitAmbient() {
        super.onExitAmbient()

        stateTextView.setTextColor(Color.GREEN)
        stateTextView.paint.isAntiAlias = true
    }
    

Java

    @Override
    public void onExitAmbient() {
        super.onExitAmbient();

        stateTextView.setTextColor(Color.GREEN);
        stateTextView.getPaint().setAntiAlias(true);
    }
    

常に画面表示モードでコンテンツを更新する

常に画面表示モードでは、ユーザーの新しい情報で画面を更新できますが、表示の更新と電池寿命のバランスを慎重に取る必要があります。常に画面表示モードでは、画面の更新を 1 分に 1 回にするように onUpdateAmbient() メソッドをオーバーライドすることだけを真剣に検討してください。更新頻度を上げる必要がある場合は、電池寿命と更新頻度の間にトレードオフがあることを考慮します。電池の消耗を抑えるためには、更新頻度を 10 秒に 1 回以下にする必要があります。ただし実際には、更新頻度をさらに下げる必要があります。

1 分に 1 回更新する

電池の消耗を抑えるためには、ほとんどの Wear アプリで常に画面表示モードの間、画面の更新頻度を下げる必要があります。このモードでは、画面の更新頻度を 1 分に 1 回にするようにアプリを設計することをおすすめします。システムに用意されているコールバック メソッド onUpdateAmbient() を使用すると、この推奨頻度で画面を更新することができます。

アプリのコンテンツを更新するには、ウェアラブル アクティビティ内で onUpdateAmbient() メソッドをオーバーライドします。

Kotlin

    override fun onUpdateAmbient() {
        super.onUpdateAmbient()
        // Update the content
    }
    

Java

    @Override
    public void onUpdateAmbient() {
        super.onUpdateAmbient();
        // Update the content
    }
    

更新頻度を上げる

常に画面表示モードでの Wear アプリの更新頻度を 1 分に 1 回より上げることは可能ですが、推奨されません。頻繁に更新する必要があるアプリでは、AlarmManager オブジェクトを使用してプロセッサを復帰させ、画面を頻繁に更新することができます。

常に画面表示モードでコンテンツをより頻繁に更新するアラームを実装する手順を以下に示します。

  1. アラーム マネージャーを準備します。
  2. 更新頻度を設定します。
  3. アクティビティが常に画面表示モードに切り替わったとき、または、アクティビティが常に画面表示モードのときに、次回の更新のスケジュールを設定します。
  4. アクティビティがインタラクティブ モードに切り替わったとき、または、アクティビティが停止したときに、アラームをキャンセルします。

注: アラーム マネージャーは、アクティビティの新しいインスタンスの作成とトリガーを同時に行うことがあります。このような状況が発生しないようにするには、マニフェストで android:launchMode="singleInstance" パラメータを使用してアクティビティを宣言します。

以下のセクションで、この手順について詳しく説明します。

アラーム マネージャーを準備する

アラーム マネージャーは、画面を更新して次回のアラームのスケジュールを設定するペンディング インテントを開始します。次の例は、アクティビティの onCreate() メソッドでアラーム マネージャーとペンディング インテントを宣言する方法を示しています。

Kotlin

    // Action for updating the display in ambient mode, per our custom refresh cycle.
    private const val AMBIENT_UPDATE_ACTION = "com.your.package.action.AMBIENT_UPDATE"
    ...
    private lateinit var ambientUpdateAlarmManager: AlarmManager
    private lateinit var ambientUpdatePendingIntent: PendingIntent
    private lateinit var ambientUpdateBroadcastReceiver: BroadcastReceiver

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setAmbientEnabled()

        ambientUpdateAlarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager

        ambientUpdatePendingIntent = Intent(AMBIENT_UPDATE_ACTION).let { ambientUpdateIntent ->
            PendingIntent.getBroadcast(this, 0, ambientUpdateIntent, PendingIntent.FLAG_UPDATE_CURRENT)
        }

        ambientUpdateBroadcastReceiver = object : BroadcastReceiver() {
            override fun onReceive(context: Context, intent: Intent) {
                refreshDisplayAndSetNextUpdate()
            }
        }
        ...
    }
    

Java

    // Action for updating the display in ambient mode, per our custom refresh cycle.
    private static final String AMBIENT_UPDATE_ACTION = "com.your.package.action.AMBIENT_UPDATE";

    private AlarmManager ambientUpdateAlarmManager;
    private PendingIntent ambientUpdatePendingIntent;
    private BroadcastReceiver ambientUpdateBroadcastReceiver;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setAmbientEnabled();

        ambientUpdateAlarmManager =
            (AlarmManager) getSystemService(Context.ALARM_SERVICE);

        Intent ambientUpdateIntent = new Intent(AMBIENT_UPDATE_ACTION);

        ambientUpdatePendingIntent = PendingIntent.getBroadcast(
            this, 0, ambientUpdateIntent, PendingIntent.FLAG_UPDATE_CURRENT);

        ambientUpdateBroadcastReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                refreshDisplayAndSetNextUpdate();
            }
        };
        ...
    }
    

次に、onResume()onPause()ブロードキャスト レシーバの登録と登録解除を行う必要があります。

Kotlin

    override fun onResume() {
        super.onResume()
        IntentFilter(AMBIENT_UPDATE_ACTION).also { filter ->
            registerReceiver(ambientUpdateBroadcastReceiver, filter)
        }
    }

    override fun onPause() {
        super.onPause()
        unregisterReceiver(ambientUpdateBroadcastReceiver)
        ambientUpdateAlarmManager.cancel(ambientUpdatePendingIntent)
    }
    

Java

    @Override
    public void onResume() {
        super.onResume();
        IntentFilter filter = new IntentFilter(AMBIENT_UPDATE_ACTION);
        registerReceiver(ambientUpdateBroadcastReceiver, filter);
            ...
    }

    @Override
    public void onPause() {
        super.onPause();
        unregisterReceiver(ambientUpdateBroadcastReceiver);
        ambientUpdateAlarmManager.cancel(ambientUpdatePendingIntent);
        ...
    }
    
画面を更新してデータ更新のスケジュールを設定する

この例のアクティビティでは、常に画面表示モードの場合、アラーム マネージャーが 20 秒ごとにトリガーされます。タイマーが動作している場合、アラームがインテントをトリガーして画面を更新し、次回の更新に対する遅延を設定します。

次の例は、画面上の情報を更新し、次回の更新に対するアラームを設定する方法を示しています。

Kotlin

    // Milliseconds between waking processor/screen for updates
    private val AMBIENT_INTERVAL_MS: Long = TimeUnit.SECONDS.toMillis(20)
    ...
    private fun refreshDisplayAndSetNextUpdate() {
        if (isAmbient) {
            // Implement data retrieval and update the screen for ambient mode
        } else {
            // Implement data retrieval and update the screen for interactive mode
        }
        val timeMs: Long = System.currentTimeMillis()
        // Schedule a new alarm
        if (isAmbient) {
            // Calculate the next trigger time
            val delayMs: Long = AMBIENT_INTERVAL_MS - timeMs % AMBIENT_INTERVAL_MS
            val triggerTimeMs: Long = timeMs + delayMs
            ambientUpdateAlarmManager.setExact(
                    AlarmManager.RTC_WAKEUP,
                    triggerTimeMs,
                    ambientUpdatePendingIntent)
        } else {
            // Calculate the next trigger time for interactive mode
        }
    }
    

Java

    // Milliseconds between waking processor/screen for updates
    private static final long AMBIENT_INTERVAL_MS = TimeUnit.SECONDS.toMillis(20);
    private void refreshDisplayAndSetNextUpdate() {
        if (isAmbient()) {
            // Implement data retrieval and update the screen for ambient mode
        } else {
            // Implement data retrieval and update the screen for interactive mode
        }
        long timeMs = System.currentTimeMillis();
        // Schedule a new alarm
        if (isAmbient()) {
            // Calculate the next trigger time
            long delayMs = AMBIENT_INTERVAL_MS - (timeMs % AMBIENT_INTERVAL_MS);
            long triggerTimeMs = timeMs + delayMs;
            ambientUpdateAlarmManager.setExact(
                AlarmManager.RTC_WAKEUP,
                triggerTimeMs,
                ambientUpdatePendingIntent);
        } else {
            // Calculate the next trigger time for interactive mode
        }
    }
    
次回のアラームのスケジュールを設定する

onEnterAmbient() メソッドと onUpdateAmbient() メソッドをオーバーライドして、アクティビティが常に画面表示モードになろうとしているとき、または、アクティビティがすでに常に画面表示モードになっているときに、画面を更新するアラームのスケジュールを設定します。

Kotlin

    override fun onEnterAmbient(ambientDetails: Bundle?) {
        super.onEnterAmbient(ambientDetails)

        refreshDisplayAndSetNextUpdate()
    }

    override fun onUpdateAmbient() {
        super.onUpdateAmbient()
        refreshDisplayAndSetNextUpdate()
    }
    

Java

    @Override
    public void onEnterAmbient(Bundle ambientDetails) {
        super.onEnterAmbient(ambientDetails);
        refreshDisplayAndSetNextUpdate();
    }

    @Override
    public void onUpdateAmbient() {
        super.onUpdateAmbient();
        refreshDisplayAndSetNextUpdate();
    }
    

注: この例では、画面の更新が必要なときに refreshDisplayAndSetNextUpdate() メソッドが呼び出されます。このメソッドを呼び出すタイミングの詳細な例については、AlwaysOn のサンプルをご覧ください。

アラームをキャンセルする

デバイスがインタラクティブ モードに切り替わったら、onExitAmbient() メソッドでアラームをキャンセルします。

Kotlin

    override fun onExitAmbient() {
        super.onExitAmbient()

        ambientUpdateAlarmManager.cancel(ambientUpdatePendingIntent)
    }
    

Java

    @Override
    public void onExitAmbient() {
        super.onExitAmbient();
        ambientUpdateAlarmManager.cancel(ambientUpdatePendingIntent);
    }
    

ユーザーがアクティビティを終了または停止したら、アクティビティの onDestroy() メソッドでアラームをキャンセルします。

Kotlin

    override fun onDestroy() {
        ambientUpdateAlarmManager.cancel(ambientUpdatePendingIntent)
        super.onDestroy()
    }
    

Java

    @Override
    public void onDestroy() {
        ambientUpdateAlarmManager.cancel(ambientUpdatePendingIntent);
        super.onDestroy();
    }
    

下位互換性を維持する

常に画面表示モードをサポートするアクティビティは、Android のバージョンが 5.1(API レベル 22)より前の Wear デバイスでは、自動的に通常のアクティビティに戻されます。これらのバージョンの Android では、デバイスのサポートのために特別なアプリコードは必要ありません。デバイスが常に画面表示モードに切り替わるとホーム画面に戻り、アクティビティが終了します。

Android のバージョンが 5.1 より前のデバイスでアプリのインストールまたは更新を行うことができない場合は、以下を使用してマニフェストを更新します。

    <uses-library android:name="com.google.android.wearable" android:required="true" />