読者です 読者をやめる 読者になる 読者になる

fastapple's blog

時系列関係なく、情報を追記・分割・統合などします。ブログに記載の内容のうち、引用ではなく、私自身が記載している文章・コードなどについては、自由にご利用ください。

Androidの音量調節について【Android6.0対応版】(後篇)


広告


振り返り

この記事には前篇があります。↓↓↓前篇はこちら↓↓↓
fastapple.hatenablog.com

さて、前篇では、Android6.0でも利用可能な、音量調整用のアプリとして、ボリュームコントロールプラスの紹介と、自動化のために、Automateを紹介し、Automateを利用して、ヘッドホンプラグの抜差しを検知して、メディア音量をコントロールするということをした。

この後篇では、さらにAutomateを利用して、家にいるときに音量をONにし、外に出た時にはOFFにするという設定を行っていきたい。読み進めるにあたって、すでに前篇を読んでいるものとして、Automateの基礎的な知識を仮定している。


家で音量をONにし、家以外ではOFFにする

それでは、早速Automateを起動し、フローを作成しよう。新規のフロー作成画面に移動する。

音量をコントロールする

まず家に帰って音量を大きくするシチュエーションに対応したい。音量を大きくするフローを作成してみよう。Androidの音量にはいくつか種類があることは説明した。詳細な説明は後述するが、Android6ではこのうち、メディア音量・着信音量・アラーム音量だけを大きくするようなフローを作ればいい。通話音量は、別に家だろうと外だろうと、変更したいニーズは薄いだろうから、ここでは考慮に入れないことにする。

CAMERA & SOUNDから、Audio volume set を選択する。ブロック詳細画面から、AUDIO STREAMMusicにする。VOLUMEについては、80%くらい鳴ればいいので、80を指定する。

そしたら、これをコピーして、次はAUDIO STREAMRingに変更する。さらにもう一つコピーで作成し、次はAUDIO STREAMAlarmに変更する。

そしたら、これらをFlow beginningから直列につなげる。

f:id:fastapple:20160430192411p:plain

さて、これを実行したあとで、ボリュームコントロールプラスで、音量を確認すると、Android5以降の場合は、下記のようになっているだろう。(Android3以前、4、5以降の3つではそれぞれ異なる可能性がある。理由は後述する。)

f:id:fastapple:20160430192737p:plain

これで、音量を大きくするという処理をAutomateで実現できた。ここで、80を指定している部分を、0を指定するようにすれば、音量をミュートにするという処理が実現できることになる。

音の分類

Androidの音量には、いくつかの種類がある。これをストリームと呼んだりする。ボリュームコントロールプラスと、Automateで利用されるストリームの呼称の対応付けは、次の図のようになる。

f:id:fastapple:20160430193633p:plain

図で△の部分は、※1、※2の記載を見ると分かる通り、Android6では全てリンクしている。だからこのうちのRingさえ変更すれば、△の部分はすべて一律で設定される。Android4では、先ほどのフローにある音量調節ブロックにSystemを増やさなければならないだろう。更にAndroid3以前では、Notificationも増やさなければならないだろう。

音量の分解能

ここまで当たり前のように音量をパーセンテージで指定してきたが、果たして音量の分解能はどの程度なのだろうか。実はボリュームコントロールプラスで見ての通り、実際の音量の分解能は、ストリームによって異なっている。(これは表示上の問題ではなく、本当に異なっている)
メディアが一番分解能が細かく、0~15までの16段階
着信・通知・アラーム・システムについては、0~7までの8段階
通話については0~6までの7段階となっている。

それでは、Automateで百分率で指定した音量は実際にはどうなるのか?実際には、すぐに音量は、指定した百分率以下で一番大きな分解能の値に収まる。

少しわかりづらいので、下記の図をみてみる。
f:id:fastapple:20160501010930p:plain

黒字が百分率を表す。今回、メディア、着信、アラームを一律80%で指定したが、メディアは0~15の分解能なので、図の一番左の80の値をとり、実際の音量は、12/15となる(つまり80%のまま)。しかし、着信とアラームは、0~7の分解能なので、80%に一番近い最大値である5/7(つまり71.4286…%)となる。

試しに(ここではフローは書かないが)実際に例えばアラームの音量を80でセットして、直後に値を表示させると、71.4286…のような数字が表示される。つまり、Automateにおける音量のパーセンテージによる入力はあくまでも便宜上のものであり、実際には即座に上記の表に従って丸められる。

位置情報の利用

GPSといいたいところだが、Androidの位置情報はGPSや、周辺のWi-Fiの電波、セルタワーなど複数の情報源を活用し、精度をあげるハイブリッド方式である。そのため、GPSとは言わず、位置情報といっている。(勿論、GPSのデータのみから情報を取得するように選択することもできる。)

次に位置情報を調べるためのブロックを利用して、位置情報を調べて家周辺なら音量80%、違うなら音量0%という処理を実現しよう。

位置情報は、LOCATION(場所)のLocation get(位置情報取得ブロック)で行えるが、さらにLocation at?(位置情報比較ブロック)もある。

今回は、取得した位置情報と、比較したい位置情報(家の位置)を比較したいので、Location at?を利用しよう。

位置情報取比較ブロックを配置し、ブロックの詳細画面へ移動する。まず、PROCEED(進み方)は、Maybe immediately(なるべくすぐに)を選択しておく。ここで、Maybeがついているのはどういうことか?Maybeは多分という意味より、「期待される意図と反することが発生するかもしれないという暗黙の前置き」を意図して使われる事が多いようだ。つまるところ、位置情報ブロックは、情報をすぐには取れないことが多い。Maybe immediatelyを指定した場合でも、だいたい10秒~20秒程度の時間がかかる。条件が悪ければもっと時間がかかることも予想される。(これを改善する方法は後述する。)

次に、INPUT ARGUMENTSを設定する。直接、LATITUDE(緯度)、LONGITUDE(経度)、RADIUS(半径)を指定することもできるが、Pick a location on map...を利用して、地図から値を取得するのがいいだろう。ここで家の位置を設定する。なお、範囲については、音量が出ても問題ない範囲で、なるべく広く取るほうが誤認識が少ない。(例えば、家と最寄り駅との中間地点くらいまでを範囲にするなど。)

作成した位置情報比較ブロックのYESNOの後にそれぞれ、yesと表示するトースト、noと表示するトーストを配置し、実行してみる。

f:id:fastapple:20160504013605p:plain

家にいるのなら、(だいたい10秒~20秒後に)yesと表示されれば上手くいっている。noと表示される場合は、違う場所にいると認識されてしまっている。

もし、家にいて、設定も問題ないはずなのに、noと表示される場合は、AutomateのSettings(設定)で、Google Play Services Use Google Play Services location API. という項目にチェックが入っているので、そのチェックを外してうまくいくか試してみよう。

f:id:fastapple:20160501030215p:plain

これを無効にすると、Maybe immediatelyで取得に10~20秒程度掛かっていたのがおそらく改善されるばかりか、うまくいくことが多い。

実は位置情報取得の方法には、従来のLocation APIを利用する方法と、上記チェックをいれて利用可能なFused Location APIを利用する方法がある。これのチェックをはずすことで、従来の方法を利用可能であると思われる。(ちなみに、Fused Location APIのほうが省電力のようだが、実際のところ計測をしていないので、どの程度省電力効果があるいのかはわからない)

しかしながら、このチェックは入れたままでも、しばらくそのままにしていれば、それなりの精度で検出できたりするので、よくテストして利用するようにしたい。

変数の利用

位置情報比較ブロックができて、トーストを使ったテストも上手くいったら、これを音量調節ブロックの前に配置することにより、家にいるなら、音量onというのが実現できる。とりあえず、フローのタイトルを家音量on家以外offとしておく。さて、家以外でoffにするにはNOの後に音量をoffにするブロックをおかなければいけないが、ここでまたブロックを3つ(下記図の赤点線枠部分に)配置して、NOからつなげるのはブロックが少しもったいない。ここは変数を利用しよう。

f:id:fastapple:20160504013632p:plain

変数を使うには、GENERALから、Variable set(変数代入ブロック)を利用する。

変数代入ブロックの詳細画面で、VALIABLEにvolと入力する。これは変数の名前になる。そして、INPUT ARGUMENTSVALUEの=の右側に80と入力する。これで、このブロックに到達した際に、vol=80と定義することができる。

f:id:fastapple:20160504011638p:plain

あとは、家にいるならvol=80とし、家以外ならvol=0とするように、このブロックをもうひとつ(vol=0)で配置する。そして音量調節ブロックで80をセットしていた部分をvolにする。そうすれば、その時のvolの値に応じて、音量がセットされる。

※音量調節ブロックで80をセットしていた部分を、volという変数をセットするようにする。(赤丸部分にて、関数入力モードに変更していることに注意!)
f:id:fastapple:20160504013415p:plain

※ここまでの流れで作成される変数を利用したフロー
f:id:fastapple:20160504014228p:plain

二段構えのループ

ここまでできたら、ループさせたいが、今まで設定した位置情報比較ブロックは、Maybe immediatelyを指定しているために、これにループをつなげると、忙しいループが出来てしまい電池使用量増加の懸念がある。そこで、今ある位置情報比較ブロックをコピーし、隣にWhen Changed(変化があったら)で配置し、そちらにループをつなげる。

f:id:fastapple:20160506104632p:plain

こうすれば、初回実行時はすぐに場所に応じて音量が調整され、それ以降は場所が変わるたびに音量が調整される。このような配置を二段構えのループと呼びたい。こういう手法は頻繁に利用可能である。

例外の捕捉

ここまでで、ブロック数は8。もう十分ともいえるが、実は位置情報取得中に位置情報をオフにしたりすると、例外(exception)が発生してしまう。例外が起きると処理が途中でストップし、フローごと停止してしまう。

下記は、例外が発生した際のログである。
f:id:fastapple:20160506114257p:plain

ログに赤字で、
FAIL <ファイバー番号>@<ブロック番号>: com.llamalab.android.util.GoogleApiException: addGeofences faild: GEOFENCE_NOT_AVAILABLE
のように出力されている。

例外がおきてもフローを止めないようにするには、あらかじめ例外が起きる可能性がある処理の前段階で、例外捕捉ブロック(後述)で、例外が起きた際の処理を書いておく。例外捕捉ブロックは、FLOWから、Failure catchを選んで使用する。

下記図では、例外発生時にディレイブロックにて、待機し、リトライするようにしている。(下記図では、ディレイブロックの待ち時間をまだ設定していないが、設定する必要がある。リトライする際に、再度例外捕捉ブロックを通るようにしている。そうしないと、2回目以降の例外発生時に、例外が捕捉されない。

f:id:fastapple:20160506140854p:plain

wifiの併用

ここまでで、ブロック数は10になった。もう少し精度を上げたければ、さらにブロック数が増えることになるが、自宅にWi-Fiルータがあれば、精度を上げることができる。自宅のWi-Fiにつながれば音量はONにしても問題ないだろう。位置情報と比べて自宅のWi-Fiの方が誤差に強い。

自宅にいるかどうかを、位置情報だけではなく自宅のWi-Fiへの接続可否も判断材料にしてジャッジすることにしたい。どちらかの基準が満たされた時に、自宅にいると考えることにしよう。CONNECTIBITYWi-Fi connected?ブロックが、指定するWi-Fiに接続されているかの判断に利用できる。

細かい部分は省略するが、Wi-Fi connected?も二段構えのループで設定したい(つまり、ImmediatelyとWhen changedのブロック、2種類にて構成したい)と、考えるのが自然の流れだろう。しかし、これは少し困ったことになる。どうなるか見てみよう。INPUT ARGUMENTSPick a network...から、自宅のWi-Fiを選択すればよい。配置は下記のようにする。(下の方が切れているが、下のブロックから上に線が伸びている)

※配置に困るの図
f:id:fastapple:20160506173626p:plain

図では、左側にImmediatelyの流れ、右側にWhen changedの流れを配置している。しかし、右側でWhen changedを2回使うと、本来は、位置情報が変わっただけでも(Wi-Fiに変化がなくても)変更を検知したいのに、Wi-Fiの判定に(位置情報の判定が)ブロックされてしまい、できない。つまりフローを直列でつなぐ以上は、厳密な意味ではOR条件を表現することはできない。並列化する方法もあるが、今回は妥協した直列で処理することにしたい。通常、Wi-Fiと位置情報のうち、細かく変化しやすいのは位置情報の方だ。よって、位置情報を直列化の先頭にもってきて、When Changedで配置し、次のWi-FiのチェックはImmediatelyで処理することにしよう。

つまり、次のようにする。

f:id:fastapple:20160506175413p:plain

これで、二段構えのループのうち、二回目以降は、基本的に位置情報の変化をトリガーにチェックが走ることになる。Wi-Fiは取りこぼしを防ぐための補助的役割のため、これで十分とし、並列化を省くことでブロック数を削減した。

ここで、Wi-Fi connected?が、同じImmediatelyの設定で2つあることに気づく。まとめることができそうだ。まとめてしまうと、Wi-Fiのチェックが失敗すると、再度位置情報のチェックが走るので、CPU時間的には二度手間かもしれないが、ここではブロックの削減を優先し、まとめることにしよう。

※まとめた図
f:id:fastapple:20160506180333p:plain

これで11ブロック。いろいろやっている割にはすっきりした。

まとめ

ここまでの内容で作成したフローを、コミュニティからダウンロードできる。ただし、位置情報やWi-Fi情報を入力してから使用する必要がある。(もし、位置情報を入力せずに使用した場合は、com.llamalab.automate.Required ArgumentNullException: latitude などが発生するだろう。)

また、ディレイブロックの待ち時間も仮の値が入っているので、必要に応じて調整するといい。

コミュニティに家音量on家以外offという名前でアップロードしている。

前篇・後篇を通して2つのブロックを作成した。ヘッドセットについては、4ブロック。音量調節については11ブロック。両方使用しても15ブロックなので、無料ユーザーでもまだ残り15ブロックを含むフローを同時に動作することができる。

Automateの面白い活用方法については、まだまだたくさんあるだろう。順次紹介していきたい。面白そうなトピックとしては、Google Nowとの連携や、シェルコマンドの実行などである。あとは、Frepなどのスクリーンでの指の動きを再現するソフトと連携させても面白そうだ。