Open13

[Google I/O 2022 メモ]Basics for System Back

Naoki IshiiNaoki Ishii

Backがどのように進化したか

  • Home、Recents、Backは、アプリ内/異なるアプリ間両方で、ナビゲーションに関する重要なAction
  • 最初はハードウェアボタン、ソフトウェアの戻るボタンを経て、ジェスチャーナビゲーションへと進化した
  • 現在のアプローチ(ジェスチャー)だとボタンは不要で、画面のスペースを広くできる
  • Homeへ移動、Recentsを表示、はシームレスなアニメーションを提供しているが、Backはまだできていない
  • GoalはBackを直感的にすること
Naoki IshiiNaoki Ishii

Improvements to back experience

  • Android13はジェスチャーナビゲーションを予測可能でユーザーフレンドリーにする取り組みの始まり
  • on-screen アフォーダンスをアップデート。hapticsを改善してシステム全体の一貫性を提供する。
  • Unpredictable Back
    • ユーザー調査で、バックが予期せずランチャーに戻ってしまった場合、最もイライラする事がわかった
    • これを改善するのが最初の取り組み。APIの提供を予定している
  • Going Home/Back
    • ユーザーがジェスチャーでHome移動したり、Recentsを開いたりすると、システムは事前に何が起こっているかを認識し、直感的なフィードバックを与える
    • 戻るジェスチャーを開始すると、システムはアプリがそれを処理しているかを認識しないので、適切なフィードフォワードを与えられない
Naoki IshiiNaoki Ishii

Ahead-of-time model

  • Android13以降、アプリはバックイベントを処理するかどうかを事前にシステムに通知する必要がある
  • システムはジェスチャー開始前にアプリがバックを処理するかを事前に認識する
  • アプリが処理しない場合、システムはフィードフォワードヒントを表示することができる
  • これをBack-to-Homeの例に適用すると、事前にアプリが処理するか知っていると、システムはアプリが消えるのをプレビューし、背後のランチャーを表示することができる
  • これでユーザーは自分が何をしているかを事前に確認できるので、誤った行動を防ぐことができる
Naoki IshiiNaoki Ishii

The new platform API

  • onBackInvokedCallBackという新しいAPIを導入する
  • インターフェイスには1つのonBackInvokdedメソッドがあり、Activity/Dialogからの従来のonBackPressedの代わりに呼び出される
  • 新しい動作を使用して、アプリへのキーイベント/キーコールバック送信は停止する
  • これを登録するには、Activity/DialogからOnBackInvokedDispatcherを取得する必要がある
  • コールバックを登録することで、コードがシステムバックを処理することを事前にシステムに通知していることになる
  • Backナビゲーションが開始されると、コードが呼び出され、システムは他に何もしない
  • コールバックを登録しない/解除した場合、システムはジェスチャー/ボタン押下が終了するとActivity/Dialogを閉じる
Naoki IshiiNaoki Ishii

Migrating to ahead-of-time Back

  • Android13ではアプリにこの新しい動作を明示的にopt-inするよう求めている。opt-inするまで影響を受けることは無い
  • 最初に行うことはManifestのapplication部分に enableOnBackInvoked=true を追加すること
  • Android14では、デフォルト動作が今回の新しい動作に完全に置き換わるので注意
  • この動作をOnにすると、Android13をターゲットにする前でも早期にopt-inできる
Naoki IshiiNaoki Ishii

AndroidX Activity

  • new Back APIは、AndroidX Activityをバージョン1.6にすると使える
  • このアップデートではAndroidXなどのライブラリがNavigation Componentを推奨している
  • BackHandlerとJetpackComposeの使い方はAndroid13と即座に互換性があるが、前バージョンとの互換性もある
  • ActivityのサブクラスにはonBackPressed() dispatcherがある
  • 以前のAndroidバージョンをターゲットにすると、dispatcherはonBackPressedを自動的に呼び出すが、Android13以降は新しいplatformAPIが呼び出される
  • Dialogクラスにもこれを拡張
Naoki IshiiNaoki Ishii

Callback flavors

  • スタイルが2つある
  • ①コンポーネント作成の一部としてコールバックを追加
    • コンポーネントの状態に応じて、isEnabledフラグを切り替える
    • ネストされたコンポーネントは常に同じ順序で追加されるため、dispatcherも常に同じ順序で処理を実行する
    • dispatcherは有効になっていないレイヤーをスキップする
  • ②一時的なオーバーレイ
    • すべてのコンポーネントがenabledになるまで待つ
    • コールバックが常に最後に追加されたものになるようにすると、デフォルトのネストされた順序でなくなることに注意
Naoki IshiiNaoki Ishii

Stack Ordering

  • 複数のコールバックを使った複雑な例
  • バックスタックの実現にFragmentを含むNavigation Componentを利用
  • NavControllerにバックスタックがあるため、内部コールバックが有効になっている
  • 各Fragmentには独自のchildFragmentManagerがあり、バックスタックを持つことができる。しかし、それらをまだ使っていないため、無効になっている
  • この例では最上位Fragmentは入力フォームと仮定。ユーザーがデータを失うことを望んでいない
  • 「送信ダイアログをキャンセルしてよいか?」を開くためのコールバックを登録する
  • 未入力状態の場合は、コールバックは無効になっている。その場合はNavControllerが最上位の有効なコールバックであり、ダイアログを中断することなくユーザーを戻すことができる
  • 入力された状態では、FragmentCallbackが有効になり、SystemBackを受信する
  • コールバックを追加するためのFragmentのコードは単純
  • Activityでdispatcherを取得し、コールバックを登録する
  • LifecycleOwnerを渡すことで、Lifecycle開始時のみトリガーされ、ライフサイクルが破棄されると解除される
  • 末尾のラムダはシステムバックを処理するために呼ばれるもの。デフォルト状態のenabledはfalseにしておく
  • 入力状態になったらcallbackのenabledをtrueにし、バックをアプリで処理するようにする
  • Composeの場合は、BackHandler APIにラップされているため、さらに簡単なコードになる
  • AndroidX Activityを1.6にアップデートすると、このコードとFragmentコードの両方がAndroid13デバイスと古いデバイスの両方で機能する
Naoki IshiiNaoki Ishii

Backward compatibility

  • AndroidX Activity APIはPlatformAPIに接続するための下位互換のある方法を提供し、Predictive Backの拡張に、将来性のある方法で実装される
  • ActivityのonBackPressedをオーバーライドしている場合は、今すぐそのコードをCallback方式に移行し、Android13への移行とは独立してテストできる
Naoki IshiiNaoki Ishii

The Platform API

  • AndroidX Activityは、System Backを処理するための高レベルのフックを提供するが、アプリにはBackを処理するためにキーコールバックをインターセプトするCustomViewのような低レベルコンポーネントが含まれる場合もある
  • その場合、Platform APIを直接使用する必要がある
  • ドロワーレイアウトと新しいPlatformAPIを統合する方法を紹介
  • Android13より前をサポートするため、 ドロワーレイアウトはonKeyDown/onKeyUpをオーバーライド
  • onKeyUpにはドロワーが表示されているかどうか、ロックモードが解除されているかどうか、の2つのプロパティがある
  • クラスレベルでBackInvokedCallbackにフレームワークを作成する。
  • このコールバックが呼び出された時、closeDrawersメソッドを呼ぶ
  • コールバックが登録されているかどうか、のフラグを作成する
  • 状態によってコールバックを登録/解除するメソッドを作成。Backシグナルを待つのではなく、状態変化時に呼び出すもの
  • これで、Viewの世界観がフレームワークの世界観と同期される
  • ドロワーの状態変化時に作成した更新メソッドを呼ぶ
  • これで完了!
Naoki IshiiNaoki Ishii

The long term plan

  • ComposeやFragmentNavigationによるアプリ内遷移のアニメーションは、まだプレビューを表示しない
  • これらのアニメーションのプレビューをどこでも適用できるかについての議論を開始できることを嬉しく思う