Continuity is The Father of Success

Androidアプリ開発でいきてる

ChronometerがAndroid OからSwedish countdownsに対応してた件

はじめに

www.youtube.com

EuropeのThe Final Countdown、名曲ですね。 お求めとはこちらから。

The Final Countdown

The Final Countdown

今日はAndroidのChronometerのAPI 26から追加された便利機能を紹介しようと思います。

android.widget.Chronometer

Chronometerクラスは、Androidアプリで簡単にカウントアップを実装/表示できるウィジェットのクラスになります。 APIレベル1から存在しますね。

Chronometer  |  Android Developers

そんなChronometerクラスですが、現在でも機能拡張が行われています。 具体的には API24からsetCountDownisCountDownメソッドが追加されて、カウントアップだけでなくカウントダウンにも対応するようになりました。

※とはいえsupport libraryに追加されていないので、大半のアプリでは利用できなさそうですが。

Chronometer#isTheFinalCountDown

本題です。API level 26からisTheFinalCountDownが追加されました。
isが付いてるし、FinalCountDownかどうかを判定するメソッドでしょうか?

とりあえずDocを確認してみると。

whether this is the final countdown

Chronometer  |  Android Developers

とのこと、やはりFinalCountDownかどうかを判定してくれるようです。

じゃあコードを読んでみてみましょう。*1

/**
* @return whether this is the final countdown
*/
public boolean isTheFinalCountDown() {
    try {
        getContext().startActivity(
                new Intent(Intent.ACTION_VIEW, Uri.parse("https://youtu.be/9jK-NcRmVcw"))
                        .addCategory(Intent.CATEGORY_BROWSABLE)
                        .addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT
                                | Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT));
        return true;
    } catch (Exception e) {
        return false;
    }
}

Intentを飛ばして成功したらtrue, 失敗したらfalseみたいですね。 気になるのでリンクを踏んで見ましょう。

https://youtu.be/9jK-NcRmVcw

コミットは?

追加時のコミットログはこちら。

Enhance Chronometer to better support Swedish countdowns.

Adds a method isTheFinalCountDown that allows to correctly
determine whether it is the final countdown.

Test: None
Change-Id: I786ae3455479bac25ccf25efba1c3dce18185117

お分りいただけただろうか。

ひとこと

ぜひ使ってみてくださいね!!! 僕は使いません!!!

AndroidStudio3.2.1を利用しているとFlutterでapkがビルドできなくなる問題

雑感

※2018.10.31 10:30時点で対応策は出ているけどCI上なんかだと難しそうです
※対応できるまではGradle Pluginは3.1.2とかの方が良さそう

主題

AndroidStudio3.2.1がリリースされて喜び勇んでアップデートし、合わせてFlutterプロジェクトの中のGradle Pluginも3.2.1にあげたところ下記のエラーが出るようになりました。

* What went wrong:
A problem occurred evaluating project ':app'.
> Could not resolve all artifacts for configuration 'classpath'.
   > Could not find lint-gradle-api.jar (com.android.tools.lint:lint-gradle-api:26.1.2).
     Searched in the following locations:
         https://jcenter.bintray.com/com/android/tools/lint/lint-gradle-api/26.1.2/lint-gradle-api-26.1.2.jar

困った。ということで調査した結果をメモがわりにまとめておきます。

関連するGithubのチケット

たぶん下記2件が一番話が進んでます。

Fixes gradle error: 'Could not find lint-gradle-api.jar' (#23095) by mrmitew · Pull Request #23397 · flutter/flutter · GitHub

Could not find aapt2-proto.jar (com.android.tools.build:aapt2-proto:0.3.1). · Issue #23404 · flutter/flutter · GitHub

betaチャンネルの場合、プロジェクト側で3.2.1にあげてもFlutter SDK側で3.1.2を利用しているため、変わっちゃったjarのpathが取得できずエラーになっている模様。
上記のPRはそれに付け加えて、jCenterよりもGoogle's Maven repositoryを優先して検索させるように修正しているみたいです。

PRはマージされたものの、本日(2018.10.31)のbeta(0.9.4)もdev(0.10.1)にもコミットが反映されていないのでmasterを利用する or ローカル環境だけ手動で書き換えるしかないようです。残念。

自分が使っているBitriseではタグとチャンネル名からFlutter SDKを取得しているようなので、静観することにしました。
急ぎならFlutterプロジェクト内のGradle Pluginを3.1.2とかにしておくと良さそうですね。flutter.gradleにある程度合わせて運用するほうが現実的な運用に思えてきました。

(最新版のProguardとか使いたくなるんですよね。。。)

EmptyView付きのRecyclerViewを書いた

ListViewにはsetEmptyViewメソッドが生えていて、リストが空の時に「リストが空でっせ」という表示が簡単にできるようになっている。一方でRecyclerViewには特にそういったメソッドが生えている訳でもない。RecyclerViewにsetEmptyViewをする方法を調べてみると、Gistがちょいちょい見つかるのだけれど、RecyclerView側でDataObserverを仕込んでいることが(やりたいことに対して)見合ってない気がしたこともあり、軽量に書けないか試してみた。

github.com

JitPack大好きなので導入はJitPack経由でどうぞ。

jitpack.io

見た目

使い方

  1. EmptyRecyclerViewをRecyclerViewの代わりにxmlに記述
  2. EmptyRecyclerViewにsetEmptyViewでリストが空の時に表示するViewを追加(xmlでも、Java/Kotlinでも)

対応してみたので、RecyclerViewの高さがwrap_contentでも表示できる(はず)。横スクロールは対応を確認中。

実装

  1. EmptyViewをViewのフィールドインスタンスとして保持する
  2. EmptyRecyclerView#onMeasureにて、adapterが管理しているItemCountが0だった場合にEmptyViewのサイズを計算
  3. EmptyRecyclerView#onDrawにて、adapterが管理しているItemCountが0だった場合にEmptyViewを描画

attributeも書いたので、xmlでempty viewのレイアウトを記述しても動きます。動的にコード側でlayout resource idを追加しても、Viewそのものを追加してもいけます。

ひとこと

公式で実装されそうな気もしている。

Flutter Meetup Tokyo #5に登壇しました。

Flutter Meetup Tokyo #5に5分LT枠で参加してきました。

flutter-jp.connpass.com

発表資料はこちら。

なお、作成したコードはこちらです。

GitHub - koji-1009/webview_checker: Android and iOS webview component testing application by flutter.

発表当日までの経緯

結論:Flutterはいいぞ。スピード感がでるぞ。

  • 10月上旬に社内のサーバーサイドの方がAndroid/iOSのWebViewを利用して開発する話が出てくる(10月5日頃)
  • Flutterのプラグインを調べたところネイティブのWebViewを呼び出していることがわかる
  • Flutterの開発環境を改めて整え、社内で「作ってみます〜」と連絡(10月10日)
  • Flutterで開発、Andorid/iOSでビルドを確認(10月13日)
    • 当時のツイート
Android iOS
  • BitriseとDeployGateの設定(10月14日)
  • Flutter Meetup TokyoにLT登壇申し込み(10月15日)
  • LT登壇(10月17日)

技術的な話

  • WebViewの話

pub.dartlang.org

webview_checker/webview.dart at master · koji-1009/webview_checker · GitHub

import 'package:flutter/material.dart';
import 'package:flutter_webview_plugin/flutter_webview_plugin.dart';

class WebView extends StatelessWidget {
  final _url;
  final _withJavascript;
  final _scrollBar;

  WebView(this._url, this._withJavascript, this._scrollBar);

  @override
  Widget build(BuildContext context) {
    return WebviewScaffold(
      url: _url,
      withJavascript: _withJavascript,
      scrollBar: _scrollBar,
      appBar: AppBar(
        title: Text(_url),
      ),
    );
  }
}
  • Bitrise

www.bitrise.io

  • 概要
    • モバイル向けのCI環境(もちろんモバイル以外も)
    • iOSのビルドができるのがうれしい
    • 無料枠だと1ビルド10分以内、月200ビルド以内
      • FlutterだとAndroid/iOSの両方のビルドがしたくなるので、お試しで使うのにも十分
    • workflowはGUIでも、bitrise.ymlの記述でも可能
  • 今回組んだフロー
Android iOS
f:id:D_R_1009:20181020233145p:plain f:id:D_R_1009:20181020233159p:plain
- Android
  - Flutter は `build apk --release' でビルド実行
  - 成果物を `build/app/outputs/apk/release/app-release.apk` で指定して署名
- iOS
  - Flutterは `build ios --release` でビルド実行
  - 成果物をappからipaに変換
  • DeployGate
    • 出来上がったものをDeployGateの配布画面を用意して、下記手順に従って設定

medium.deploygate.com

発表の感想

上記のようなことを話そうかなと思ったんですが、ちょっと長すぎるなと思ったので「やったらできましたー! BitriseとDeployGateはいいぞー!」という感じに。
発表を聞いて「BitriseでCI試してみようかな」と思ってくれる人がいれば、嬉しいです。

またどっかで発表するぞー!

Android 28対応でAndroid Xに移行してみた話

アプリをフルリファクタリングしました。

play.google.com

このアプリって?

1回目の無職期間に、Androidアプリの練習をしようと思って作ったものです。
やりたかったことは「毎日 or 曜日ごとに繰り返すTODOの管理」になります。そのため、下記2点ほどを実現することとしています。

  1. TODOにDBに保存
  2. 登録した時間になったら繰り返し実行
旧バージョンでの実現方法

作成したのが2017年1月ごろだったので、基礎の基礎から組み上げてやろうと考えてました。

  1. TODOをDBに保存
    • SQLiteHelperクラスの実装により、直にSQLiteを操作することに
    • RealmとOrmaの利用を検討したが、そもそもSQLがよくわかってなかったの組んでみることに
  2. 登録した時間になったら繰り返し実行
    • Android 5.0以上を対象としたため、JobSchedulerによる実現をしてみることに
    • 実現のため、LocalDate型の導入のためにThreeTenABPに頼ることに

そのほかはDataBindingとか、Snackbarを入れてみるとか程度のことをしていました。

新バージョンでの実現方法

9月から転職したので、その直前の有給期間からAndroidX(当時はalpha版)を利用してフルリファクタしてみようと考えていました。 前職ではAndroid Architecture Componentsを導入してMVVMな感じにアプリをリファクタしたこともあったので、「じゃあ今一番新しいものを使って書くとしたらどうなるんだろう」ってな感じで興味が湧いたためです。

  1. TODOをDBに保存
    • AAC Roomを利用し、VM層でやり取りをすることに
    • 操作スレッドはKotlin Coroutinesにより、asyncとlaunchを切り替えてコストを低く
    • 基本的には取り出したデータをLiveDataでView層へ送信
  2. 登録した時間になったら繰り返し実行
    • AlarmManagerを選択
      • 結局、一度もAlarmManagerを触ることなくAndroidエンジニア4年目に入ってしまったで、ここで触らないと触る機会なさそうだなと感じたのが一番の理由
      • JobSchedulerやWorkManagerを利用することも検討し、バージョンアップで置き換えてみれば置き換えの経験も積めそうだなーというのが目論見に

8月中はがっつりと、9月以降は土日とか平日の夜とか使って書いてました。途中、AndroidStudio3.2系から3.3へのアップデートが発生したり、Dagger + ViewModelの関係でビルドが通らなくなったりといったトラブルに巻き込まれたので、期間としては2ヶ月ほどかかってしまいましたね。もうちょっとコンパクトにできればなーと思いますが。

AndroidX対応

どうせならtargetSDKを28にしてしまえということで、androidxを入れました。感想です。

  • よかったこと
    • Material Design 2がのテキストフィールド、SnackBarの動作が良い
    • 複数の選択をさせたい時のChipの使い勝手が非常に良い、革新的
  • 大変だったこと
    • PreferenceFragmentCompatを利用していたのですが、Preferenceクラスの実装がリファクタされていたのに気づくのが遅れ、大幅に実装し直すことに
      • 実装はライブラリ化し、v1系からv2系への更新で対応したので、気になる方は差分を見ていただければ
      • 一部のバグ(ボタンのデザインが崩れる、Android Lintで警告が出る)のが残っているので、早めに対応したいところです
      • リファクタリングの意図としては、これまでPreferenceDialogクラスが「リストの表示 + ダイアログの表示」になっていたので、後半をPreferenceDialogFragmentとして切り離しています
    • SupportLibraryのクラスを追加する時、それがandroidxでどのパッケージに入っているか自分で調べるのが(少し)難しい
    • (alphaだからだけど)ビルドエラーが頻発する

対応タイミングとしては、製品なら年明けぐらいがいいのかなーと思っています。早くBrowserのバグ直ってほしい。

今後

DailyToDoをiOSでも動かしたいので、Flutterで改めて実装してみようかなーと。 新しい技術、触っていきたいですね。

ライブラリを作ってみている話

作り始めてみた。楽しいです。

github.com

モチベーション

2017年ごろから開発しているDailyToDoというタスク管理アプリのリファクタ中、「設定画面から時計盤の画面呼び出してそのまま使いたいなぁ」となる場所が。 ザーッと調べたところAndroidの標準で提供しているクラスになく、自分で作る必要があることが判明。 なら、後ほど別のアプリでも使えるようにライブラリ化してしまえばいいじゃん、という発想で着手することに。

進捗

Time側は24時間設定のON/OFFをxmlで指定できるようになったので7割完成、Date側はカレンダーの表示までなので4割完成。 全体進捗として、7割動くところまで進んだらJitpackとかMavenとかにあげて、自分で利用できるようにしたいなーと。 おおよそ一週間でここまできているので、来週頭には進めてしまいたい。

作ってよかったこと

  • AndroidのViewクラスの理解が進んだ
  • TimePickerとDatePicker、EditTextDialogPreferenceあたりのコードを読み込むいい機会に
  • HH:mm形式でSharedPreferenceに保存、保存した値を更新できるとすごい便利

これから

GithubのREAD MEに何を書こう……。