Grade Sync Issues: (5 warnings) 回避

数ヶ月ぶりに Android アプリ案件をやったところ表題のようなエラーに出くわして回避したお話です。

内容はリンク先にもある通り、先方の都合で2018年末に使えなくなるから変更しろということで、 Gradle Script/build.gradle(Module:app) に記述のある “compile” 文字列を全て “implementation” に書き換えてから右上の Sync Now リンクを押します。具体的には、dependencies の中にある4箇所、compile fileTreeandroidTestCompilecompiletestCompile を implementation fileTreeandroidTestImplementation implementationtestImplementation とします。

解決前:

解決後:

参考:

Googleファミリーリンクで子供の端末を制御する

昨年、安価な子供の位置情報確認方法について書いたのですが、iOS のようなペアレンタルコントロール機能が Android には無いので、アプリを無効化しても Chrome ブラウザを使い際限なく Youtube 視聴をしてしまったり、子供の友人が勝手にロック画面からカメラを起動して街中で動画を撮影するといったような子供あるある問題が表面化してきたので対策を考えていたところ、Google が「ファミリーリンク」という制御アプリを日本向けにリリースしたので導入してみました。環境は前回同様、子供の端末に Huawei SIMフリースマートフォン P8 lite に 0 SIM を利用します。

まず現在年齢を詐称して登録していた子供の端末のアカウントのデータをバックアップしました。
$ adb backup -f backup_20180604.ab -all -nosystem -apk -obb

次にこの端末に親管理者となる自分のアカウントを追加し Google ファミリーリンクのアプリをインストールして進むのかと思いきや、別端末から管理する必要がある事が分かりました。公式の文書には「〜の端末」や「〜の Android」と言った記述がなく分かり難い事半端ありませんでした。

気を取り直して子供の端末を初期化し、自分の端末(iOS と Android 共)にも Google ファミリーリンクのアプリをインストールし、子供のアカウントを正しく再取得した後に2台並べて設定、Chrome のフィルタで youtube.com ドメインの追加、アプリ管理、位置情報、その他からフォト共有をオフにし、1日の利用上限とおやすみ時間を指定して無事完了しました。

次に上部スワイプからショートカットで Wi-Fi をオフにしてデータ通信での挙動を確認してみたのですが、先のアプリの管理で「端末管理」と「EMUI」をブロックすると「設定>データ通信量の管理」の項目が非表示になってしまう事が分かったので、一旦許可済みにして「ネットワーク通信を行うアプリ」と「1ヶ月の利用可能なデータ通信量」の設置を行いました。

そしてネットワーク通信を行うアプリを観測してみたところ、10085 というアプリが位置情報を取得する際に必要な事が判明したので「ネットワーク通信を行うアプリ>システムアプリ」でモバイルデータのチェックを入れてみたのですが、Google ファミリーリンクのアプリにある他の設定を変更する事ができませんでした。

もう一度設定画面を確認してみると、10085 前後に存在している 10008, 10012, 10014, 10017, 10026, 10029, 10031, 10032, 10045, 10050, 10058, 10064, 10070, 10074, 10080, 10081, そして 10086 という多数の怪しい名称のアプリが関係しているだろうと推測して全てにチェックを入れるとうまく動きました。

それぞれのアプリがどの設定に関連するのか調べるのは面倒だったので放置していますが、 10032 をオフにする際「モバイルデータ通信の無効化」というタイトルのダイアログを表示し「MMS メッセージの送受信」が不可能になる旨の警告をしていました。結構な数のアプリなので通信量が若干心配なのですが、幸運にも 6.0(Marshmallow)端末で対応できましたし、フィルタリングや時間制限など MacOS に近い制御が可能だという事で、iPhone SE2 が出たらどうなるか分からないところ、暫くこれで運用してみようと思います。

参考:

照会カレンダーを作る

4年に1度のサッカーの祭典が迫って来たところで恒例のカレンダーファイルを作成し共有したいと思います。技術的な要件としては次の3つになります。

  1. 日本語表記にする
  2. DropBox を使って照会カレンダーの形式で共有する
  3. スマホの2大 OS とデスクトップでの表示に対応する

(技術的な事はどうでも良いのでカレンダーファイルを入手したい方はこちらからどうぞ。)

  1. 日本語表記にする

    まず ICS ファイルを作成するのですが、英語やロシア語などの表記を使ったカレンダーファイルは既に幾つか存在しているのでそれを利用します。今回は Fixture が提供しているものを利用しました。一旦カレンダーアプリに読み込んだ後、ファイルメニュー>書き出す>書き出すを選び、書き出した ICS ファイルをテキストエディタで開き、日本語に書き換えます。国名やスタジアム名、対戦ラウンド名やグループ節などは一括置換してグループ組名のアルファベットを追加しました。

    更新:詳細にスタジアムの所在地名と放送テレビ局名を追加し、決勝トーナメント以降の「未定」となっていた対戦国名に試合 ID を利用した表記にしました。

    参考:
  2. DropBox を使って照会カレンダーの形式で共有する

    決勝トーナメント以降の未定となっている対戦国名を更新するためにダウンロード形式では無く照会カレンダーとして共有します。コストを掛けない保存先として DropBox を選択しましたが、ここで一つ罠がありました。

    DropBox でファイルを共有した時にデフォルトで付与される dl 引数は 0 なのですが、これを 1 にすることによって解決しました。

    http://hkitago.tumblr.com/post/173940229306

    また DropBox の共有リンクアドレスはファイル名が変わらない限り普遍だと言う事で、更新が必要な照会カレンダーファイルを配置する条件に合致しています。

    参考:
  3. スマホの2大 OS とデスクトップでの表示に対応する

    具体的には、Apple の MacOS と iOS のカレンダーアプリ、Google カレンダーのウェブアプリ版と Android のカレンダーアプリになり、デスクトップとスマホで同期できるようにします。実はここにも一つトリックがあって、アドレス文字列をクリップボードにコピーした後にカレンダーアプリのコマンドを使って照会(追加)する場合と、ハイパーリンクを使ってカレンダーアプリを起動し照会(追加)する場合で方法が異なります。参考まで、Apple では「照会 (Subscribe)」、 Google では「追加 (Add by URL)」)と言うコマンド名称になっています。

    最初に、アドレスをクリップボードにコピーしてカレンダーアプリから直接追加する場合、MacOS/iOS 共に https://webcal:// のどちらのスキームを使う事が許されていますが、Google カレンダーでは webcal:// のみ使う事ができます。https:// のアドレスを使うと一見読み込みに成功したように見えますが内容を表示する事ができません。また、iOS で webcal:// を使うと SSL オプションが外れた状態になるので気になる方は https:// を使うと良いと思います(MacOS のカレンダーアプリには SSL オプションがありません謎)。

    次に、ウェブ上にあるハイパーリンクを踏んでカレンダーアプリを起動して照会(追加)する場合、MacOS/iOS では href="https://… とすると ICS ファイルがダウンロードされ照会カレンダーの扱いになりませんので href="webcal://… を使う必要があります。この場合も先述した通り SSL オプションはオフのままなのでご注意ください。そして Google カレンダーへ遷移し追加する場合は、href="https://www.google.com/calendar/render?cid=webcal://… と記述する必要があります。

    参考:

と言う訳で長々と書きましたが、リンク先はこちらになります。

Apple 用
FIFA World Cup 2018 照会カレンダー日本語版
Google 用
FIFA World Cup 2018 照会カレンダー日本語版

それぞれのカレンダーアプリから直接照会(追加)する場合は、このアドレスをコピーしてお使いください。

最後に注意点としては、iOS で照会すると MacOS へ同期する事ができないので両端末で同期する場合は MacOS で照会し場所を iCloud にする事と、Android のカレンダーアプリはデフォルトで同期がオフになっている事があります。それでは楽しいW杯ライフを!

参考:

安価な子供の位置情報確認方法

注意:Googleファミリーリンクで子供の端末を制御する方法へ更新しました。

昨年から子供が習い事を始めたのでスマホを使った位置情報の共有方法を模索してきました。最善策は、iOS にデフォルトで組み込まれている「友達を探す」アプリの利用(実際夫婦間ではこれ)なのですが iPhone 端末が高価なので、開発時に検証用に使っている「Huawei SIMフリースマートフォン P8 lite」に、月間通信量500MB未満は無料の「0 SIM」を挿して渡すことにしました。そしてこの頃、Google の位置情報共有サービスは Google+ を使った複雑な設置が必要だったので Glympse をインストールしました。

iPhone と比較して Android 端末は GPS の性能が貧相な(位置情報ゲームではこのブレを悪用する場合もあるとかないとか…)ので、少しでも改善すべく GPS Locker をインストールしました。

また10秒以上電源ボタンを押すと強制再起動するというハードウェアのユーザインタフェースの難癖があり鞄の中での置き方を教えるという事もありました。さらに Glympse は最大で4時間までしか共有の設定ができず、Gps Tracker を使った自作システムなどを試しましたが、端末のスリープ時に位置情報を更新できない Android 特有の問題を解決できず悶々としていたところ、遂に Google マップがサービスに対応したので移行しました。

子供用の Google アカウントを取得し Google マップの共有設定は問題なく Wi-Fi 環境下で検証できましたが、端末の「設定>データ通信量の管理>ネットワーク通信を行うアプリ>システムアプリ」の設定はどれを選ぶべきか説明がなく一つずつチェックを入れて試してみたところ、次の3つを有効にする必要がありました。

  • マップ
  • Google Backup Transport
  • Android OS

Google マップアプリは常時起動しておく必要がなく、位置情報共有も停止にチェックを入れるまで事実上恒久的に設定しておく(急に遊びに行く場合に手渡すだけ)ことができ、ブラウザ上のマップでも問題なく位置情報の確認が可能です。一つ問題が残るのは、友達を探すアプリのような出発/到着の通知機能がないので FB Messenger アプリをインストールしました。前述のように GPS の性能上、ごく稀に1Km以上ブレたりすることがあるので、その場合メッセージを送って確認するようにしています。

最後に気になるデータ通信量について、設定>Googleから全てのデータの自動同期をオフに設定し、1日3時間ほどの利用で(メッセ通信などが)多い時でも10MB程度で、もちろん毎月の運用費は掛かりません。

以上、携帯端末を公共の場所で利用する場合のルールとマナーについて子供に教える良い機会にもなっている、お勧めの利用方法の紹介でした。お年寄りにも是非どうぞ。

P.S. バッテリ容量を考慮すると今から端末を用意する場合、アマゾンでベストセラー1位の P9 lite が良いかと思います。

参考:

WebViewのフォーム入力でImmersiveモードを維持する

前回、Javascript の alertconfirm 関数を使って WebView から AlertDialog を呼び出した際に全画面表示を止めてしまう問題について取り組んでいた時に、入力フィールドへフォーカスした際に Immersive モードが解除されるのは仕方がないとしても、その扱いについて考えていたところ、こちらも一癖ある挙動だったので紹介します。

通常 Immersive モードではない場合、WebView にキーボードが被るという問題を解決すべく、AndroidManifest.xml の activity ノード属性として android:windowSoftInputMode="adjustResize" と書くのですが、Immersive モードの場合はこの方法がうまくいきません。そこで試してみたのが、入力フィールドがフォーカスした時に Immersive モードを解除する、という方法です。

まず、MainActivity クラスの中に次のような JavaScript インターフェースを用意し、

public class WebViewJavaScriptInterface{
  private Context context;
  public WebViewJavaScriptInterface(Context context){
    this.context = context;
  }
  @JavascriptInterface
  public void disableFullScreen() {
    getWindow().getDecorView().setSystemUiVisibility(0);
  }
}

WebView について色々と書いてある onCreate の中で JavaScript から呼び出せるように設定しました。

webView.addJavascriptInterface(new WebViewJavaScriptInterface(this), "app");

そしてテキスト入力フィールドを1つ持っている HTML ではこのように JavaScript から Immersive モードを解除するようにしました。

document.querySelector('input[type=text]').ontouchend = function(){
  app.disableFullScreen();
};

ところが、Logcat に “Uncaught Error: Java exception was raised during method invocation” というエラーを出力しうまく動かなかったので、MainActivity で Handler を呼び出し、JavaScript インターフェース内の関数を次のようにすると期待通りの動作をエミュレータで確認しました。

@JavascriptInterface
public void disableFullScreen() {
  handler.postDelayed(new Runnable() {
    @Override
    public void run() {
      getWindow().getDecorView().setSystemUiVisibility(0);
    }
  }, 0);
}

また、JavaScript インターフェース内にもう一つ次のようなものを用意して(前回不要だと思った enableImmersiveMode 関数を再度用意)、

@JavascriptInterface
public void enableFullScreen() {
  handler.postDelayed(new Runnable() {
    @Override
    public void run() {
      enableImmersiveMode();
    }
  }, 0);
}

送信ボタンを押した時に Immersive モードとなるようにしました。

document.querySelector('#submit').ontouchend = function(){
  app.enableFullScreen();
};

実際にこのような挙動で動くようになったのですが、

ソフトウェアキーボード以外の場所で画面を押すと入力フィールドからフォーカスが外れてキーボードが隠れるという OS の設計上、このように加えておくと更に安心です。

document.querySelector('input[type=text]').onblur = function(){
  app.disableFullScreen();
};

実機で検証してみると、処理能力が劣る古い機種ではフルスクリーンになる際にステータスバーが途中で止まってしまったりということが確認できたので、postDelayed で遅延を750〜1000と与えると(動作的にはギクシャクする感は否めないのですが)解決しました。そして、最初に書いていた android:windowSoftInputMode="adjustResize" を外してみると…なんと入力フィールドが隠れずにキーボードに合わせて移動するという世にも奇妙な…。

参考:

WebViewのAlertDialogでImmersiveモードを維持する

Android の上部にあるツールバーと下部にあるナビゲーションバーを隠しフルスクリーンで表示する「Immersive モード」を使ってハイブリッドアプリを開発している際に、WebView 内の JavaScript で confirm を呼び出すと、全画面表示が解除されてツールバー及びナビゲーションバーが再び表示されてしまうという問題に直面したので調べました。

元々は confirm のボタンラベル名を拡張すべく WebChromeClient 内でこのようなコードを使っていました。

@Override
public boolean onJsConfirm(WebView view, String url, String message, final JsResult result)  {
  new AlertDialog.Builder(MainActivity.this)
    .setMessage(message)
    .setPositiveButton("確認",
      new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int id) {
          result.confirm();
        }
      })
    .setNegativeButton("消去",
      new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int id) {
          result.cancel();
        }
      })
    .setCancelable(false).create().show();
  return true;
};

この問題の根本にある原因はダイアログがフォーカスを持ってしまうことにあるようなので、参考にした文献のコードを利用し(ワンライナーフェチとしては少し残念な) AlertDialog を一旦 alertToShow に入れるようにして addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) を追加しました。

@Override
public boolean onJsConfirm(WebView view, String url, String message, final JsResult result)  {
  AlertDialog alertToShow = new AlertDialog.Builder(MainActivity.this)
(中略)
  alertToShow.getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
  alertToShow.show();
  return true;
};

今回の失敗談を一つ紹介すると、MainActivity に次のような全画面設定関数を用意して setPositiveButtonsetNegativeButton のそれぞれの onClick で呼び出したのですが、案の定ボタンを押した後に Immersive モードを再開するという間抜けな結果になり、上述したコードで不要になりました汗。

@SuppressLint("NewApi")
private void enableImmersiveMode() {
  if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
    getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
  }
}

この例は JavaScript の confirm でしたが、もちろん alert でも同じような処理で解決できると思います。更に調べているとテキストフォーム入力時の input にフォーカスした際に下部からニュッと現れるソフトキーボードと共に Immersive モードが解除されるという問題について Stackoverflow で多く議論されていることが分かりました。こちらも原因は同様なのですが WebView と連携した場合にどうなるのか、別投稿で紹介したいと思います。

参考:

Android StudioでAPIレベルを変更する

Android アプリ案件では社内向けのような限られた端末を対象とした形で野良アプリを配布することが多く、雛形として複製したプロジェクトで API レベルを変更しなければいけない(特に Lollipop から Jelly Bean や KitKat へ)という状況が多いのですが、今回は Android Studio で簡単に処理する方法を Stack Overflow で見つけたので紹介します。

具体的には、build.gradle ファイルに記述されている minSdkVersiontargetSdkVersion の値を目的の API レベルに変更するとエディタの上部右肩に表示される “Sync Now” リンクをクリックしてからメニューコマンド「Build>Rebuild Project」を選択するだけです。

スクリーンショット 2016-02-04 17.10.46

build.gradle ファイルの場所は src/build.gradle と書かれていますが、こちらの環境では Android Studio 1.5 内の遷移で Project/app/build.gradle にありましたので、参考リンクにある内容を照らしながら環境に応じて編集すべき該当ファイルを検討されると良いかと思います。

参考:

Nexus7でシステムアップデートを削除する

ここ最近モバイルアプリの受託案件がほぼ Android 端末用になっているということもあり、ロリポップに更新してからもっさりと動作が重たくなって放置していた Nexus7 2012版を引っ張り出してきてファクトリーイメージでキットカット4.4.4にダウングレードし実機検証に使っているのだけど、どうしても消えない「システムアップデートをダウンロードしました」という通知をうっかり押してしまいそうなので、ステータスバーの通知の削除方法と「設定>システム>タブレット情報>システムアップデート」にあるインストール画面を表示しないようにすべくファイルの削除方法について調べてみました。

まず一つ目に参考したリンクでルート化しているか否かで方法が違うことが分かりました。こちらの環境ではルート化していない条件なので「設定>アプリ>(右へスワイプして)すべて」と進み「Google プレイストア>通知を表示」チェックボックスをオフにする…と思いきや2つ目の参考リンクでは「設定>アプリ>すべて>ConfigUpdater>無効にする」を押せ!とあるのでもう少し調べることにしました。

更に3つ目の参考リンクで判明した面白い方法は、通知を長押しすることで「アプリ情報」というコンテクストメニューが表示されるということでした。そうすることで該当する「Googleサービスフレームワーク」の設定へ誘導してくれました。

Screenshot_2016-02-01-14-08-02

その画面で「通知を表示」のチェックボックスを切り「データを消去」ボタンを押すと、ステータスバーの通知が消え、システムアップデート設定画面で「お使いのシステムは最新の状態です。」と表示されるようになりました。(最終確認日が1970年となっているのはご愛嬌)

http://hkitago.tumblr.com/post/138460676496

Screenshot_2016-02-01-14-09-26

やれやれこれで〜と思った矢先に再度通知が…。開発者登録に関係するお話は別投稿で。

参考:

アンドロイドでダウンロードフォルダを見る

意外に知らなかった Android でダウンロードフォルダを参照する方法について、基本的には Download アプリを使えば全く問題ないのですが、端末によってはダウンロードフォルダを参照させないようにこのアプリが無いものもあり、そういった場合はファイルブラウザアプリをストアから落としてくることになります。適当な検索語で調べてみると、次の3つくらいの無料アプリが有名なようです。

というのは、通称野良アプリを納品する必要に迫られてサポートに少し時間を要したことがあります。参考サイトではダウンロードフォルダに触れず、すぐにファイルブラウザアプリの話になっているので、ひょっとしたら国産 Android 端末にダウンロードアプリはないのかも…ゴクリ

P.S. 野良アプリ流通が Android の自由度だ!という人もいますが、現在では配布方法に若干の違いがあるだけで iOS のアプリもストアを通さずに簡単に流通させることが可能です。

更に余談

http://hkitago.tumblr.com/post/129050429649

参考:

Nexus7で通知の履歴を見る

iOS と比較して使いやすいのが Android の上部メニューバーにある通知の表示機能だと思うのだけど、利用頻度が然程高くないような場合にアイコンが並び過ぎるのが気になって大量の通知を削除した後に「あれ、今のは!?」ということがあって、Nexus7 で通知の履歴を見る方法について調べました。

  1. ホーム画面でアプリアイコン(下部中央ホームボタンの直上)を押す。
  2. ウィジェットタブに移動(登録アプリの数によりけりで数回右へスワイプ)する。Screenshot_2014-07-14-08-47-28
  3. 「設定をショートカットとする」をタップし続けホーム画面に配置する。
  4. 配置後指を離すと、どの設定をショートカットにするか選択する画面になるので「通知」を選択する。Screenshot_2014-07-14-08-35-16

OS や機種を問わず通知設定は携帯なソーシャル時代に巧く利用したいところ、国産の Android 機種は独自設定があるようなので共通にできるのかは不明です。

参考: