class: chapter-1, hero, center, middle #
Android Nougat
日本 Android の会 10 月定例会 2016/10/26 荒木佑一 --- class: chapter-1, normal # 自己紹介 .card[ 荒木佑一 [@yuichi_araki](https://twitter.com/yuichi_araki) Developer Programs Engineer @Google - サポート ライブラリ (主に design) - Google I/O アプリ - Google Santa Tracker アプリ - [d.android.com/samples](http://d.android.com/samples) のサンプルいろいろ - [CameraView](https://github.com/google/cameraview) ] --- class: chapter-1, hero, middle, center # Android 7.0 Nougat --- class: chapter-1, normal # Android 7.0 Nougat .card[  ## 新機能 - マルチ ウィンドウ - 通知の強化 - Java 8 ## 動作変更 - バックグラウンド処理の最適化 - 言語設定 などなど ] ??? - まず Android 7.0 Nougat の新機能をおさらい --- class: chapter-2, hero, center, middle # マルチ ウィンドウ --- class: chapter-2, normal # マルチ ウィンドウ .card[  同時に複数のアプリを表示 - 二分割 - 自由形式 - Picture in Picture (TV)
] ??? - 端末によって3つのモード、いずれもOverview (旧 Recents) ボタン長押しで切り替え - 端末の種類によってタイプが異なる - ほとんどの端末は縦か横に 2 分割、境界線をドラッグしてリサイズできる - 一部大型端末は自由形式 = Windows や Mac のような複数ウィンドウ - Android TV は Picture in Picture、他のアプリの上にフローティング ウィンドウが表示される - 画面のリサイズや他のアプリとの切り替えにどうやって対応すればいいか --- class: chapter-2, normal # レイアウト .card[ これまで通り 画面サイズ -
`-small`, `-normal`, `-large`, `-xlarge`
- `-sw<N>dp` 幅、高さ -
`-land`
- `-w<N>dp`, `-h<N>dp` ] ??? - ベスト プラクティスはこれまで通り。相対的なデザイン - wrap_content, match_parent の活用 - LinearLayout の layout_weight 活用 - リソース クオリファイアーの活用 - 例えば 画面サイズごとにマージンを変えたい場合は -sw - 例えば 画面の幅が十分大きければ 2 ペインのレイアウトにする、などの場合は -w - -sw や -w, -h は API Level 13 以降だが、実質問題ない --- class: chapter-2, normal # ライフサイクル .card[ これまで通り - 画面リサイズ - アクティブ/非アクティブ UI 更新の停止は onPause ではなく onStop で ] ??? - 複数アプリが表示されるが、アクティブなのは一つ - アクティブになった時に onResume - アクティブでなくなった時に onPause - ちゃんと作っていれば問題ないはず - 画面のリサイズ = 画面回転、Activity が作り直される - 画面に表示されているがアクティブでない状態 は 上にダイアログ型の Activity が出た状態、onPause は呼ばれるが、onStop は呼ばれない - ユーザーのインタラクションなしに画面が動く場合、処理の停止は onPause でなく onStop で - 動画再生、Twitter クライアントのタイムライン更新、時計アプリの表示変更 - onStop が呼ばれない場合があるので注意。必ずしないといけない後処理は onPause で。 --- class: chapter-2, normal # AndroidManifest.xml .card[ targetSdkVersion が 'N' のアプリで `<activity>` または `<application>` に ```xml android:resizeableActivity=["true" | "false"] ``` `<activity>` に ```xml android:supportsPictureInPicture=["true" | "false"] ``` ] ??? - resizeableActivity はマルチ ウィンドウをサポートするかどうか - デフォルトで true - supportsPictureInPicture は Picture in Picture をサポートするかどうか - targetSdkVersion が 23 以前の場合、強制的にマルチ ウィンドウをサポートしているものとして扱う - ただし、画面の向きを固定している場合はサポートしていないものとして扱う --- class: chapter-2, normal # マルチ ウィンドウでのサイズ .card[ AndroidManifest.xml ```xml
``` ] ??? - AndroidManifest.xml の activity 要素に新しく layout 要素を追加することができるようになった - defaultWidth, defaultHeight は自由形式モードでのデフォルトのウィンドウ サイズ - gravity は同じく自由形式モードでのデフォルトの位置 - minimalWidth, minimalHeight は自由形式モード、分割モード両方で有効。最ウィンドウ サイズ。 --- class: chapter-2, normal # マルチ ウィンドウの判定 .card[ Activity と Fragment に新しいメソッド - isInMultiWindowMode() - isInPictureInPictureMode() - onMultiWindowModeChanged() - onPictureInPictureModeChanged() ] ??? - 今マルチ ウィンドウ モードかどうか判定するためのメソッドが Activity と Fragment に追加された - Picture in Picture が true なら multi window も true --- class: chapter-2, normal # マルチ ウィンドウの切り替え .card[ ## 分割、自由形式 アプリ側からは制御できない。ユーザーが操作する。 ## Picture in Picture Activity.enterPictureInPictureMode() ] ??? - Overview (旧 Recents) ボタン長押しで切り替え - アプリ側から制御できないのは意図的な仕様。勝手に切り替わったらユーザーが混乱する。 --- class: chapter-2, normal # ドラッグ アンド ドロップ .card[
View.startDragAndDrop()
View.DRAG_FLAG_GLOBAL
View.cancelDragAndDrop()
ドラッグ中のものをキャンセル
View.updateDragShadow()
ドラッグ中の表示を変更
] ??? - ウィンドウ間でドラッグ アンド ドロップできる - 他のアプリへのドラッグ アンド ドロップを有効化するには、startDragAndDrop メソッドの第4引数にフラグを指定する --- class: chapter-3, hero, center, middle # 通知 --- class: chapter-3, normal # 直接返信 .card[   通知から直接返信できる - RemoteInput - Notification.Action - Notification.Builder#setRemoteInputHistory() ] ??? - API 自体は Lollipop からすでに存在した - Wear 用 - setRemoteInputHistory だけ新規 - 返信内容の履歴を設定できる --- class: chapter-3, normal # グループ化 .card[  Notification.Builder#setGroup()
] ??? - API 自体は Android Wear 向けに以前から存在した - Notification.InboxStyle や Notification.BigTextStyle も引き続き利用できる - 使い分けに注意 - 例えばチャット アプリで同じ人からの連続したメッセージを一つづつ別の通知にしてグループ化する意味はあまりない --- class: chapter-3, normal # カスタム ビュー .card[ ```java Notification noti = new Notification.Builder() .setSmallIcon(R.drawable.ic_stat_player) .setLargeIcon(albumArtBitmap)) * .setCustomContentView(contentView); * .setStyle(new Notification.DecoratedCustomViewStyle()) .build(); ``` Notification.Builder#setContent(RemoteView) は deprecated ] --- class: chapter-4, hero, center, middle # Java 8 ??? --- class: chapter-4, normal # build.gradle .card[ ```groovy android { ... defaultConfig { ... * jackOptions { * enabled true * } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } } ``` ] ??? - targetSdkVersion が Android N だからといって Java 8 を使う必要はない - Java 8 の機能を使うには Jack を有効する必要あり - Jack は一昨年リリースされた Android 向けの新しいコンパイラ - 従来は java のソース ファイルから javac で class ファイルにコンパイルした上で dex ファイルに変換していた - Jack は Java ソースから直接 dex にコンパイルする --- class: chapter-4, normal # 後方互換性 .card[ | 機能 | 後方互換 | |:-------------------------------------------------------|:--------:| | ラムダ (およびメソッド参照) | ○ | | インターフェイスの
デフォルトメソッド、静的メソッド | × | | 反復アノテーション | △ | | 新しい API (Stream API など) | × | ] ??? - 新しい API = Stream API, Function API, Optional --- class: chapter-4, normal # ラムダ .card[ ```java button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { say("Hi!"); } }); ``` .center[ ↓ ] ```java button.setOnClickListener(v -> say("Hi!")); ``` ] ??? - ボタンのクリック イベントをラムダで扱う例 --- class: chapter-4, normal # ラムダ .card[ ```java button.setOnClickListener(`new` View.OnClickListener() { @Override public void onClick(View v) { say("Hi!"); } }); ``` .center[ ↑ ] ```java button.setOnClickListener(v -> say("Hi!")); ``` ] ??? - Jack によるラムダ サポートは無名クラスを利用したもの - ラムダは無名クラスとしてコンパイルされる - 互換性のためそういう実装になっている - つまり、new される - onDraw やカスタム ビューのレイアウトなど new してはいけないところでは使うべきではない --- class: chapter-5, hero, center, middle # データ セーバー --- class: chapter-5, normal # 設定 > データ使用量 > データセーバー .card[   データを節約するよう設定できる アプリを選んでホワイトリストに入れることができる
] ??? - データ セーバーが ON の時はバックグラウンドで通信できなくなる --- class: chapter-5, normal # 場合分け .card[ ```java ConnectivityManager connMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); // 無制限ではないネットワーク (3G, LTE など) if (connMgr.isActiveNetworkMetered()) { // データセーバーの設定を判定 switch (connMgr.getRestrictBackgroundStatus()) { case RESTRICT_BACKGROUND_STATUS_ENABLED: // 制限が有効 = バックグラウンドでの通信はできない // フォアグラウンドでも節約すべき case RESTRICT_BACKGROUND_STATUS_WHITELISTED: // ホワイトリストされている = できれば節約 case RESTRICT_BACKGROUND_STATUS_DISABLED: // データ セーバーがオフ = できれば節約 } } else { // 存分に通信 } ``` ] --- class: chapter-6, hero, center, middle # クイック設定 --- class: chapter-6, normal # クイック設定の編集 .card[  ドラッグ アンド ドロップで項目を追加・削除できる android.service.quicksettings - Tile - TileService
] ??? - システムが提供する WiFi みたいな開く UI はまだできない --- class: chapter-7, hero, center, middle # Scoped Directory Access --- class: chapter-7, normal # 特定のディレクトリへの権限を要求 .card[  ```java StorageManager sm = (StorageManager) getSystemService( Context.STORAGE_SERVICE); StorageVolume volume = sm.getPrimaryStorageVolume(); Intent intent = volume.createAccessIntent( Environment.DIRECTORY_PICTURES); startActivityForResult( intent, request_code); ``` https://github.com/googlesamples/android-ScopedDirectoryAccess
] ??? - 写真ディレクトリへのアクセスを要求する例 --- class: chapter-8, hero, center, middle # その他の新機能 --- class: chapter-8, normal # その他の新機能 .card[ - Direct Boot - ICU4J (International Components for Unicode) - [NFC Type-F Host Card Emulation](https://developer.android.com/reference/android/nfc/cardemulation/NfcFCardEmulation.html) - Android for Work の拡張、改善 ゲームなど - Vulkan™ - OpenGL™ ES 3.2 ] --- class: chapter-9, hero, center, middle # Doze --- class: chapter-9, normal # 軽い Doze .card[ N から .large[  ] - 放電中 - スクリーン OFF ] ??? - Marshmallow で導入された Doze は端末が動いていないときだけ有効になるものだった - 机の上においているなど - ポケットに入れているときは Doze にならなかった - N から画面がオフならしばらくすると「軽い」Doze に入る - うたた寝 - ネットワーク アクセスや同期を停止する - 時々まとめて行う (メンテナンス ウィンドウ) --- class: chapter-9, normal # しっかり Doze .card[ Marshmallow から .large[  ] - 放電中 - スクリーン OFF - 静止状態 ] ??? - がっつり仮眠 - ネットワークアクセスに加えてウェイクロック、アラーム、GPS、Wi-Fi スキャンなどが制限される - Doze が二段階になっても、ベスト プラクティスは変わらず - JobScheduler や GCM を適切に使うなど --- class: chapter-10, hero, center, middle # Project Svelte ??? - Android の電池消費を改善する取り組み --- class: chapter-10, normal # 暗黙のブロードキャスト .card[ - CONNECTIVITY_ACTION AndroidManifest.xml に書いても呼ばれない - ACTION_NEW_PICTURE - ACTION_NEW_VIDEO 廃止 ] ??? - ブロードキャスト Intent - CONNECTIVITY_ACTION はネットワーク接続に何か変更があった時 - ACTION_NEW_PICTURE, ACTION_NEW_VIDEO は新しい写真や動画が撮影された時 - 受け取る方法は 2 種類 - 動的に BroadcastReceiver を register する - 静的に AndroidManifest.xml に intent filter を書く = 暗黙的 - バックグラウンドでたくさんのプロセスを起動しないといけないので電池消費が激しい --- class: chapter-10, normal # 代わりに: JobScheduler .card[ ## ネットワーク
Lollipop 以降
JobInfo.Builder#setRequiredNetworkType(int)
KitKat 以前
GcmNetworkManager (Google Play Services)
## 写真・動画 JobInfo.Builder#addTriggerContentUri( JobInfo.TriggerContentUri) ] ??? --- class: chapter-11, hero, center, middle # 表示サイズ --- class: chapter-11, normal # 設定 > ユーザー補助 > 表示サイズ .card[  ユーザーが画面表示のズームを設定できる = dp と px の比率が変わる 最大に拡大しても sw320dp まで = Nexus 4 くらい
] ??? - フォントサイズは昔から設定できた - 一郎は英語版だと Pete --- class: chapter-11, normal # すべきこと .card[ ベスト プラクティスに従っていれば特にすることはない - px ではなく dp を使う - sw320dp で正しく動くか確認 画面密度に依存したキャッシュがあれば、表示サイズ変更時にキャッシュを更新するロジックが必要 ] --- class: chapter-12, hero, center, middle # 言語設定 --- class: chapter-12, normal # 複数の言語を選択 .card[  LocaleList.getDefault() でリストを取得できる
] ??? - 同時に複数の言語を設定できるようになった - これまで通りシステムが自動的にそれぞれのアプリのリソースで一番いいものを選ぶので、多くのアプリは特にすることはない - よりきめ細かい対応がしたい場合は設定された言語のリストを取得できる - 検索結果に設定された言語すべてを含める - ユーザーが設定した言語については翻訳ボタンを非表示にする --- class: chapter-13, hero, center, middle # Android 7.1 Nougat MR1 --- class: chapter-13, normal # Android 7.1 Nougat MR1 .card[  ## 新機能 - アプリ ショートカット API - 画像キーボード - 丸型アプリ アイコン - 空き容量確保インテント - ライブ壁紙の強化 - デモモード検知 - VR モード用のスレッド スケジューリング ] --- class: chapter-14, hero, center, middle # アプリ ショートカット API --- class: chapter-14, normal # アプリ ショートカット API .card[  ランチャー アイコン長押しでメニュー表示 アプリから静的または動的に項目を登録 (個数制限あり) メニュー項目をドラッグして独立したショートカットを作成できる (ピン留め)
] --- class: chapter-14, normal # 静的なショートカット : 定義 .card[ res/xml/shortcuts.xml ```xml
``` ] ??? - インテントに基づいた API - 複数のショートカットを定義する場合は shortcut**s** 要素の中に shortcut 要素を追加する - shortLabel は英数字で 10 文字程度、longLabel は 25 文字程度を目安に --- class: chapter-14, normal # 静的なショートカット : 宣言 .card[ AndroidManifest.xml ```xml
``` ] ??? - ランチャー カテゴリーを持つ Activity にショートカットを定義したリソース ファイルをメタ データとして指定する --- class: chapter-14, normal # 動的なショートカット .card[ ```java ShortcutManager shortcutManager = getSystemService(ShortcutManager.class); ShortcutInfo shortcut = new ShortcutInfo.Builder(this, "id1") .setShortLabel("Web site") .setLongLabel("Open the web site") .setIcon(Icon.createWithResource(context, R.drawable.icon_website)) .setIntent(new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.mysite.example.com/"))) .build(); shortcutManager.setDynamicShortcuts(Arrays.asList(shortcut)); ``` ] ??? - チャット アプリで頻繁にチャットする相手など - setDynamicShortcuts はショートカットの一覧を一気に設定、addDynamicShortcuts でショートカット追加, updateShortcuts で更新 - disableShortcuts でピン留めされたショートカットの無効化 - チャット アプリで相手がアカウントを削除したときなど --- class: chapter-14, normal # 動的なショートカットの注意点 .card[ - ショートカット更新系の呼び出しには頻度制限がある ``` $ adb shell cmd shortcut reset-throttling [ --user your-user-id ] ``` - 外観上 4 つまで (API 的には現在 5 つまで) - 適切に維持する - 追加、削除、更新、無効化 - 端末のバックアップ & リストア - 動的なショートカットはリストアされない - ピン留めされたショートカットはリストアされる ] ??? - 開発・テスト時には呼び出し制限をリセットする adb コマンドを使える - 個数制限は静的・動的合わせて 5 つ - アプリ起動時に毎回、動的なショートカット一覧が適切になっているかチェックする --- class: chapter-15, hero, center, middle # 画像キーボード --- class: chapter-15, normal # 画像キーボード .card[  キーボードから画像 (アニメ GIF など) を挿入できる アプリ側がどんな種類の入力を受け付けるか宣言するための API サポート ライブラリにも互換用の API
] --- class: chapter-15, normal # アプリ側 .card[ ```java EditText editText = new EditText(this) { @Override public InputConnection onCreateInputConnection(EditorInfo editorInfo) { final InputConnection ic = super.onCreateInputConnection(editorInfo); * EditorInfoCompat.setContentMimeTypes(editorInfo, * new String [] {"image/png"}); final InputConnectionCompat.OnCommitContentListener callback = new InputConnectionCompat.OnCommitContentListener() { @Override public boolean onCommitContent(InputContentInfoCompat inputContentInfo, int flags, Bundle opts) { // read and display inputContentInfo asynchronously if (BuildCompat.isAtLeastNMR1() && (flags & InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0) { try { inputContentInfo.requestPermission(); } catch (Exception e) { return false; // return false if failed } } // read and display inputContentInfo asynchronously. // call inputContentInfo.releasePermission() as needed. return true; // return true if succeeded } }; return InputConnectionCompat.createWrapper(ic, editorInfo, callback); } }; ``` ] ??? - どんなコンテンツを受け付けるか MIME タイプで宣言する - 画像以外も OK --- class: chapter-16, hero, center, middle # その他 --- class: chapter-16, normal # 丸型アイコン .card[  `android:icon` と別に `android:roundIcon` が追加された 端末によってどちらのアイコンが使われるかが変わる Android Studio の Asset Studio でも作成できる
] --- class: chapter-16, normal # 空き容量確保インテント .card[ `ACTION_MANAGE_STORAGE` インテントで不要アプリ・コンテンツを削除するための画面を開くことができる 容量が足りない時に ] --- class: chapter-17, hero # Android 7.1 Developer Preview ## d.android.com/preview ### テスト、フィードバックをお願いします