Roman Nurik(github:romannurik)さんのenvリポジトリにAndroidのコマンドに関する知見があった話
Android Asset Studioとかを作ったGoogleのRoman Nurikさん。
Roman Nurik(github:romannurik)さんのenvってリポジトリを眺めてたらAndroidに関するコマンドがいくつかあった。
その中で「これは使えるかもなー」と思ったものを紹介。
紹介したもの以外でもためになりそう or 知見!!っぽいのはあるので見てみるといいよー
android-resize - 端末の解像度を変更する adb shell wm sizeのShell Script
adb shell wm size
はとりあえず端末の解像度を変更できるすげーもん!だと思ってもらえればいい。
確かAndroid 4.3くらいから使えるやつだった気がするなーどうだろー。内部ではWindowManager
ゴニョゴニョしてるだけ。
以下のQiitaとか見てもらえればいいかな。
んで、そのadb shell wm size
のラッパー的なものがandroid-resize
ってShell Scriptで書いてあった。
中身は以下のような感じ。
#!/bin/sh if [[ ("$1" != "4" && "$1" != "5" && "$1" != "7" && "$1" != "reset") ]]; then echo "Usage: `basename $0` <size (4|5|7|reset)> <optional orientation (land|port)>" >&2 exit fi adb root PREFIX="adb shell wm size" #PREFIX="adb shell am display-size" case "$1" in 4) W=1280 H=768 ;; 5) W=1280 H=720 ;; 7) W=1920 H=1200 ;; reset) COMMAND="$PREFIX reset" esac if [ -z "$COMMAND" ]; then case "$2" in port) COMMAND="$PREFIX ${H}x${W}" ;; *) COMMAND="$PREFIX ${W}x${H}" ;; esac fi echo $COMMAND $COMMAND
env/android-resize at master · romannurik/env · GitHub
数字の割り当てや解像度の数値は自分に合った感じでカスタマイズすれば使えそう!
ちなみに、adb root
があるのが気になるけど、使うならこれは消しておいた方がいいね...
android-screen-gif - screenrecordの結果をanimation gifに変換してくれるShell Script
Android 4.4から追加されてるscreenrecord
を使って、その結果のmp4をanimation gifに変換するものがandroid-screen-gif
ってShell Scriptで書いてあった。
android-screen-gif
って打って、端末で録画したい操作をして、終わりにしたかったらターミナルでなんかキーを打つと録画した内容をanimation gifにしてくれるすぐれもの。
中身は以下のような感じ。
#!/bin/bash NAME=screencap if [[ "$1" != "" ]]; then NAME="$1" fi adb shell screenrecord --bit-rate=20M "/sdcard/$NAME.mp4" & PID=$! echo $PID read -n1 -r -p "Press any key to stop" key kill -SIGHUP $PID sleep 0.2 adb pull "/sdcard/$NAME.mp4" && gifify -np 360:640 -r 16 -o "$NAME" "$NAME.mp4" adb shell rm "/sdcard/$NAME.mp4"
env/android-screen-gif at master · romannurik/env · GitHub
mp4をanimation gifに変換するためにgifify
というものを使ってるけど、多分以下を使ってるんじゃないかなーと推測。
gififyのオプションがそのままだと動かなかったので以下のように-np
オプションを-p
オプションに変えたらうまく動いた。
adb pull "/sdcard/$NAME.mp4" && gifify -p 360:640 -r 16 -o "$NAME" "$NAME.mp4"
あと、sleepが0.2だとうまく動かなかったのでsleep 1
に変えたらうまく動いた。それでもさらにダメそうだったらここのsleep伸ばしてみるといいかも。
これと似たことができるGUI Toolを使っていつもやってたけど、こっちの方がターミナルでできるに楽かもなー。
まとめ
adb厨、adb shell厨な私としてはこーゆーのすげー好き!!
Google Compute Engine上でSlackのBotkitを動かすぞい!
Google Compute Engine(GCE)でSlackのBotkitを動かすぞい!という話。
以降 Google Compute EngineはGCEと書きます!
Slackのbotkitの詳細については割愛しますが、全く動かしたことないです!って人は一度以下とか読んで簡単なものでも動かしておくといいかもです。
Google Compute EngineのVM インスタンスを作成する
公式のドキュメントにそってやれば簡単にインスタンスは作れます。ちなみに、私は操作に慣れているUbuntuを使ってます。
Quickstart Using a Linux VM | Compute Engine Documentation | Google Cloud Platform
インスタンス作るまでの手順は割愛しますが、公式のドキュメントより細かく手順が欲しい!!って方は以下のQiitaとかがオススメです。
マシンタイプは「f1-micro(vCPU x 1、メモリ 0.6 GB)」とかで一番安いのでいいでしょう。
VM インスタンスにSSHで入ってnodeをインストールする
gcloudとか使ってターミナルから操作もできますが、そこまで長い作業にならないのでWebのSSHで作業します。
VMインスタンスの一覧画面で、作業したいインスタンスのSSHから以下のように「ブラウザ ウィンドウで開く」を選択すれば、WebのSSHが開きます。
んで、開いたらVM インスタンスにnodeをインストールします。
nodeのサイトに各メジャーOSでnodeをインストールする方法が書いてありますので、作成したVMインスタンスのOSに合わせてコマンドを見つける。
Installing Node.js via package manager | Node.js
私が普段VMインスタンスで使うOSはUbuntuなので以下のコマンドを実行すればnodeがインストールできます。npmもね。
curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash - sudo apt-get install -y nodejs
インストールが終わったら一応nodeとnpmのバージョンを確認してインストールされていることとか確認しておくといいですね。
VMインスタンス上で動かすbotkitのサンプルをcloneする
今回使ったのはSlackのBotkitでbotにメンション付きで発言すると、内容をオウム返しするbotさんを動かします!
上記のRepositoryを以下のようにcloneします。ついでにnpm installもして実行に必要なモジュールもインストールしておく。
git clone https://github.com/operando/slack-botkit-sample.git cd slack-botkit-sample npm install
別途自身で作成したbotがあればそちらを使うでもいいですねー。
ちなみに、Ubuntuは元からgitが入っているので追加で入れる必要はないですが、立ち上げるOSによっては別途追加が必要かもです。
VMインスタンス上でbotkitを動かすぞい!!
VMインスタンス上で動かすbotが用意できたら、あとは実行するだけです。
私が作ったbotなら以下のように実行します。
// your_api_tokenのところに自身のSlackのAPI tokenを入れる token=your_api_token node parrot-bot.js
起動が確認できたらbotが動いてるSlackに移動して動作確認してみます。
動いた!!
やたー!!動いたー!!
後処理
動いた!!と満足してもうVMインスタンス不要だったら停止するなり削除するなりしておくといいかと。
まとめ
GCEはインスタンスの作成も操作も簡単なのでいいですねー
Android Nのbugreportが進化してた話
普通のアプリからbugreportの取得を行うことができるのかどうか調べていたら、Android Nからbugreportがちょっと進化していることがわかった。
bugreportってなに??って人は adb shellで adb shell bugreport
とかしてみればわかるよ。雑に言うとdebugのために端末の情報をdumpするみたいなー。
内部的な話は今コード読んでるので、なんとなくわかってきたら別途まとめようかなー
取得できるbugreportの形式が増えてる
開発者向けオプションの「バグレポートを取得」を押すと、以下のように取得するbugreportの形式を選べ!ってダイアログが出てくるようになった。
具体的に各形式で取得できるbugreportがどんなものなのかまでは詳しくみてない。
ちなみにコード読んだ感じ、形式は上の画像 + リモートから取得するbugreportの形式が用意されてるっぽい。
電源メニューからバグレポート取得のメニューをロングタップすることで取得できるようになってる
開発者向けオプションの「バグレポートのショートカット」を有効にすると、電源メニューにバグレポート取得のメニューが増える。
これは前からあったね。
んで、実際に電源メニューを開いてバグレポート取得のメニューをロングタップすると、bugreportの取得が始まる。これはどうやらNから追加されたみたい。
わかりにくいけど以下のような感じ。
ちなみにコード読んだ感じ、ロングタップからの取得だと上で出てたbugreportの形式的には、「完全レポート」の形式のbugreportになる。
電源メニューからバグレポート取得のメニューをクリックするとすぐにbugreportの取得が始まる
わかりにくけど以下のような感じ。
ちなみにコード読んだ感じ、クリックからの取得だと上で出てたbugreportの形式的には、「対話型レポート」の形式のbugreportになる。
Android Mまではメニューをクリックするを以下のようなダイアログが出てたけど、出なくなったのはうれしいかなー
まとめ
Android Nからbugreportが進化している。取得するbugreportに形式が増えたのは興味深い。
どうやらNからならDevicePolicyManager経由で取得もできるかもしれないのでそこら辺も後々調べてみよう
あとbugreport取得したらlogcatのeventにlogを落ちるようになってるのがわかって、log周りも色々増えてることがわかったので知見だ!
TextInputLayoutのpassword visibility toggleを試してみた
Android support library 24.2.0からTextInputLayoutに追加されたpassword visibility toggleを試してみた。
試したサンプルコードはGithubにあげてあります。
password visibility toggleってなんぞ??
簡単に説明するとパスワード入力する際に、入力している内容をtoggleで表示・非表示と切り替えるやーつーですね(雑だな...
Material Designのドキュメントに詳しく書いてあるので、それを見れば「あーこれかー」となるはず。
Text fields - Components - Material design guidelines
んで、今までこれと似たようなものを実装したことがある人ならわかるだろうけど、今まで独自の方法で実装するしかなかったわけだ。
その実装がsupport library 24.2.0からTextInputLayoutに導入されたってこと。うれしいです☺
さようなら、独自実装...😇
TextInputLayoutでどのようにpassword visibility toggleを使うか
基本はすごく簡単です。
以下のような感じでTextInputLayoutの子ViewのEditTextにandroid:inputType="textPassword"
を指定するだけで、デフォルトのpassword visibility toggleが出てきます。
<android.support.design.widget.TextInputLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="textPassword" android:inputType="textPassword" /> </android.support.design.widget.TextInputLayout>
こんな感じで動きます!簡単ですね!!
もしpassword visibility toggleを出したくなければ、TextInputLayoutにapp:passwordToggleEnabled="false"
を追加すると出なくまります。
inputTypeのxxxPasswordすべて試してみた
以下のような感じでinputTypeのxxxPasswordすべて試してみます。(長いのでlayout_widthとlayout_height省いてます
<android.support.design.widget.TextInputLayout ... > <EditText android:hint="textPassword" android:inputType="textPassword" /> </android.support.design.widget.TextInputLayout> <android.support.design.widget.TextInputLayout ... > <EditText android:hint="numberPassword" android:inputType="numberPassword" /> </android.support.design.widget.TextInputLayout> <android.support.design.widget.TextInputLayout ... > <EditText android:hint="textVisiblePassword" android:inputType="textVisiblePassword" /> </android.support.design.widget.TextInputLayout> <android.support.design.widget.TextInputLayout ... > <EditText android:hint="textWebPassword" android:inputType="textWebPassword" /> </android.support.design.widget.TextInputLayout>
hintに出ている値が各EditTextのinputTypeに設定されています。
android:inputType="textVisiblePassword"
を設定しているものはpassword visibility toggleが出てないですね。当たり前かー。
passwordToggleDrawableでアイコンを変更する
デフォルトの目のアイコンはpasswordToggleDrawable
属性で変えられます。
サンプルコードではVectorのドロイド君にアイコンを変更してます(デフォルトの目のアイコンもVectorです
<android.support.design.widget.TextInputLayout android:layout_width="match_parent" android:layout_height="wrap_content" app:passwordToggleDrawable="@drawable/ic_android_black_24dp"> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="passwordToggleDrawable" android:inputType="textPassword" /> </android.support.design.widget.TextInputLayout>
変更すると以下の感じでアイコンが変わります。
XMLの属性だけじゃなくてメソッドもちゃんとありますよ
今回のサンプルコードでは全てXMLの属性で指定してますが、ちゃんと属性とマッチしてるメソッドも用意されてます。
詳しくはTextInputLayoutのドキュメントを見てみてください。
今回は使用しませんでしたが属性も他にpasswordToggleTint
やpasswordToggleTintMode
があります。
自身のアプリのデザインに合わせてカスタマイズすると良さそうですね。
まとめ
デフォルトのデザインでいいのならTextInputLayoutでpassword visibility toggleを試すのはすごく簡単だったなー
support libraryでこういうのが増えてくれるのは独自で実装する部分が減るのでとても助かりますねー
ANDROID IDがどのように生成されているかざっくり調べた
悪名高い??ANDROID IDがどのように生成されているのか気になったのでざっくり調べた。
悪名高いと言われる闇の話は置いておいて....とりあえず生成方法だけざっくり調べた。
どのような手順で生成されるのか?という疑問はバージョンによって実装が異なるっぽかったので調べない。
ANDROID IDとは
ドキュメント見てもらえばわかるけどざっくり以下のようなもの。
- ランダムに生成される64ビットの数値を16進数文字列にしたもの
- 端末の最初の設定時に生成される。その後、ファクトリーリセットするまではその時に生成された値が常に取得できる
- ファクトリーリセットすることで値は再生成される
- 4.2以上でmultiple usersが導入され、ユーザごとにANDROID IDが生成される
ANDROID IDは以下のような感じで簡単に取得できる。
String androidId = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
ANDROID IDを使用する際の気をつけていること
ざっくり以下かなー他にもなにかあれば教えてほしいかなー
- 「ランダムに生成される64ビットの数値を16進数文字列にしたもの」なので文字数は固定ではない
- どんなアプリでも同じ値がAPIを通して取得できる
- ファクトリーリセットすることで値が変わる
- 同じANDROID IDを持った端末が存在する可能性がある
- multiple users環境ではユーザごとにANDROID IDが生成されるため、ANDROID IDは端末に1つではない
ANDROID IDがどのように生成されているのか
とりあえずAndroid Nのコードから見てみる。
ANDROID IDを生成しているのはSettingsProvider.java
のensureSecureSettingAndroidIdSetLocked
メソッドだね。
全体の処理は以下のような感じ。詳しくは見ないけどドキュメントどおりmultiple usersの想定もされてる実装になってる。
private void ensureSecureSettingAndroidIdSetLocked(SettingsState secureSettings) { Setting value = secureSettings.getSettingLocked(Settings.Secure.ANDROID_ID); if (!value.isNull()) { return; } final int userId = getUserIdFromKey(secureSettings.mKey); final UserInfo user; final long identity = Binder.clearCallingIdentity(); try { user = mUserManager.getUserInfo(userId); } finally { Binder.restoreCallingIdentity(identity); } if (user == null) { // Can happen due to races when deleting users - treat as benign. return; } String androidId = Long.toHexString(new SecureRandom().nextLong()); secureSettings.insertSettingLocked(Settings.Secure.ANDROID_ID, androidId, SettingsState.SYSTEM_PACKAGE_NAME); Slog.d(LOG_TAG, "Generated and saved new ANDROID_ID [" + androidId + "] for user " + userId); // Write a drop box entry if it's a restricted profile if (user.isRestricted()) { DropBoxManager dbm = (DropBoxManager) getContext().getSystemService( Context.DROPBOX_SERVICE); if (dbm != null && dbm.isTagEnabled(DROPBOX_TAG_USERLOG)) { dbm.addText(DROPBOX_TAG_USERLOG, System.currentTimeMillis() + "," + DROPBOX_TAG_USERLOG + "," + androidId + "\n"); } } }
ANDROID IDの生成処理は以下のような感じで実装されてる。
String androidId = Long.toHexString(new SecureRandom().nextLong());
SecureRandomでランダムなlong値を生成して、それをLong.toHexString
で16進数表現の文字列にする。
それがANDROID IDってことになるね。
ちなみにSecureRandom.nextLong
の実装はRandom.nextLong)なんだけど、ドキュメントと実装見ればわかるけど、long 値の一部しか返さないっぽいんだよね。
なのでlong値の幅 -9223372036854775808~9223372036854775807の中の一部ってことだね。うーん...思ってた以上に範囲が狭い気もする...
あと、SecureRandom.nextLong
で生成される値によって16進数文字列にした時の文字列の長さは変わる。
雑に以下のようなやつを実行してみればわかるけど、文字列の長さが16だったり、15だったり、14だったりする。
for (int i = 0; i < 100000; i++) { String androidId = Long.toHexString(new SecureRandom().nextLong()); System.out.println(androidId.length()); }
これが最初の気にしておかなければいけない点で書いた「ランダムに生成される64ビットの数値を16進数文字列にしたもの」なので文字数は固定ではないってこと。
他のAndroidのバージョンではどう実装されているか
全部のバージョン見るのは面倒なので、いくつかのバージョンだけ。
4.0.1ではensureAndroidIdIsSet
メソッドでANDROID IDが生成されてる。
もちろんSecureRandom.nextLong
+ Long.toHexString
でANDROID IDが生成されてる。
てかSlog.d
じゃなくて普通のLog.d
使ってる...まあいいや...
private boolean ensureAndroidIdIsSet() { final Cursor c = query(Settings.Secure.CONTENT_URI, new String[] { Settings.NameValueTable.VALUE }, Settings.NameValueTable.NAME + "=?", new String[] { Settings.Secure.ANDROID_ID }, null); try { final String value = c.moveToNext() ? c.getString(0) : null; if (value == null) { final SecureRandom random = new SecureRandom(); final String newAndroidIdValue = Long.toHexString(random.nextLong()); Log.d(TAG, "Generated and saved new ANDROID_ID [" + newAndroidIdValue + "]"); final ContentValues values = new ContentValues(); values.put(Settings.NameValueTable.NAME, Settings.Secure.ANDROID_ID); values.put(Settings.NameValueTable.VALUE, newAndroidIdValue); final Uri uri = insert(Settings.Secure.CONTENT_URI, values); if (uri == null) { return false; } } return true; } finally { c.close(); } }
ちなみに、コードは割愛しますが2.3.7の実装はだいたい4.0.1と同じ。もちろんSecureRandom.nextLong
+ Long.toHexString
でANDROID IDが生成されてる。
その他のバージョンもざっくり見たけど、SecureRandom.nextLong
+ Long.toHexString
でANDROID IDが生成されていることは変わらない。当たり前か。
まとめ
Long.toHexString(new SecureRandom().nextLong())
で生成されるものがANDROID IDだねーということ
思ってたよりANDROID IDの生成処理があっさりしたもので驚きだったかなー
モヤモヤするのはドキュメントに書いてある「 ランダムに生成される64ビットの数値を16進数文字列にしたもの」というのは表面上はあっているが、内部処理の話をすると「えー」と言いたくなる気分...
気になったら調べてみるもんだなー