Zxcvbn + RxBinding で入力されたパスワードの強度をチェックする

![passcheck.gif](https://qiita-image-store.s3.amazonaws.com/0/33071/41a8a73d-d3d7-b6c4-4be9-502033979fe7.gif)

EditTextに入力されたパスワードを検知して強度をチェックするという内容が、[こちら](https://blog.davidmedenjak.com/android/2017/01/14/password-strength.html)の記事で紹介されていたので試してみました。
RxBindingがRxJava2に対応していたので、本記事ではそちらを使用しています。

#ライブラリを準備
以下のライブラリを使用します。

- [RxJava2](https://github.com/ReactiveX/RxJava)
- [RxBinding２](https://github.com/JakeWharton/RxBinding)　
- [zxcvbn4j](https://github.com/nulab/zxcvbn4j)

```groovy:app/build.gradle
    compile "io.reactivex.rxjava2:rxjava:2.1.3"
    compile 'com.jakewharton.rxbinding2:rxbinding:2.0.0'
    compile 'com.nulab-inc:zxcvbn:1.2.3'
```
※上記に記載していませんが、[retrolambda](https://github.com/evant/gradle-retrolambda)を使ってラムダ式で記述します。

##zxcvbn4jについて
DropBoxが公開しているパスワード強度チェックライブラリ[zxcvbn](https://github.com/dropbox/zxcvbn)のJava移植版で、Nulab社から公開されています。

READMEに記載の通り、使い方も非常に簡単で、```measure``` メソッドに文字列を渡すと結果を得られます。

```java
Zxcvbn zxcvbn = new Zxcvbn();
Strength strength = zxcvbn.measure("This is password");
```

#レイアウトを作成
パスワードを入力するEditTextを追加します。

```xml
<EditText
    android:id="@+id/password"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:inputType="textPassword"
    android:maxLines="1" />
```
入力されたパスワードに応じて、結果を表示するViewを追加します。
zxcvbnのスコアは、0〜4の整数で返ってくるので、```android:max="4"```としています。
弱いパスワード [0] <-----> [4] 強いパスワード

```xml
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal">

    <ProgressBar
        android:id="@+id/password_strength_bar"
        style="?android:progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:max="4" />

    <TextView
        android:id="@+id/password_strength_text"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="4"
        android:gravity="center"
        android:minLines="2" />

</LinearLayout>
```

# 入力を検知して、結果を更新する。
RxBindingを使ってEditTextの内容変更を通知、zxcvbnでスコアを測定して、結果を他のViewに渡します。

```java:SignUpActivity.java
Zxcvbn zxcvbn = new Zxcvbn();
// EditTextをObservable化
RxTextView.textChanges(mPasswordEditText)
　　　　　// スコアの測定は計算スレッドで行う
        .observeOn(Schedulers.computation())
        .map(CharSequence::toString)
        .map(zxcvbn::measure)
　　　　　// 0〜4の整数でスコアを取得
        .map(Strength::getScore)
         // メインスレッドで他のViewに渡す
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(score -> {
            mStrengthProgressBar.setProgress(score);
            mStrengthTextView.setText(PASSWORD_STRENGTH_MESSAGES[score]);
        });
```
ここでは、以下のような配列を用意して、スコアに応じたテキストをTextViewに渡しています。

```java
private static final String[] PASSWORD_STRENGTH_MESSAGES = new String[] {
        "Weak", "Fair", "Good", "Strong", "Very Strong"
};
```
[元の記事](https://blog.davidmedenjak.com/android/2017/01/14/password-strength.html)では、 MissingBackpressureExceptionの回避のために```.onBackpressureLatest()``` を指定していましたが、RxJava2ではBackPressureがFlowableのみの機能となりました。
Flowableはサーバサイドなどで大量のデータを扱う場合を想定しているようで、GUIイベント程度の少量のデータであれば、オーバーヘッドの少ないObservableを使う方が良いとのことなので、削っています。
https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0#observable-and-flowable
入力の度に無駄な処理を走らせたくないのであれば、Throttlingで入力後に間隔が空いたら通知するようにすれば良さそう。

# その他
```strength.getScore()``` ではなく、 ```strength.getFeedback()```とすると、そのパスワードに対してのフィードバックが得られます。
ローカライズにも対応していて ```getFeedback().getSuggestions(Locale.JAPAN)```のようにすると上記のGIFのように日本語で修正の提案のメッセージが得られます。便利！