Smart Lock for Passwordsの実装

# Smart Lock for Passwordsとは
煩わしいID、パスワード入力を省略してくれる便利な機能です。
実装に関する説明を行っている記事が少ないので投稿します。

# 実装
## プロジェクト
Sampleの Login Activityを使用します
<img width="700" alt="スクリーンショット 2018-07-26 21.58.05.png" src="https://qiita-image-store.s3.amazonaws.com/0/199764/4d3f56d1-5d54-f588-2901-0749b5e062cb.png">

## gradle
```build.gradle(app)
dependencies {
    - 省略 -
    implementation 'com.google.android.gms:play-services-auth:15.0.1'
}
```

## 保存、読込
Acitityと分離して保存処理、読込処理を別クラスで記載するようにしました

```LoginService.kt
class LoginService(private val activity: Activity) {

    companion object {
        const val RC_SAVE = 1
        const val RC_READ = 2
    }

    // 保存、読込時に使用
    private val credentialsClient: CredentialsClient =
        Credentials.getClient(activity, CredentialsOptions.Builder().forceEnableSaveDialog().build())

    // 保存処理
    fun save(email: String, pass: String, finish: () -> Unit) {
        val credential = Credential.Builder(email).setPassword(pass).build()
        credentialsClient.save(credential).addOnCompleteListener {
            if (it.isSuccessful) {
                // ２回目以降の登録
                finish()
                return@addOnCompleteListener
            }
            val e = it.exception
            if (e is ResolvableApiException) {
                // 初回登録
                e.startResolutionForResult(activity, RC_SAVE)
            } else {
                // 登録失敗
                finish()
            }
        }
    }

    // 読込処理
    fun load(finish: (credential: Credential) -> Unit) {
        val request = CredentialRequest.Builder().setPasswordLoginSupported(true).build()
        credentialsClient.request(request).addOnCompleteListener {
            if (it.isSuccessful) {
                // 読込が１件
                finish(it.result.credential)
                return@addOnCompleteListener
            }
            val e = it.exception
            if (e is ResolvableApiException) {
                // 読込件数が複数件
                e.startResolutionForResult(activity, RC_READ)
            } else {
                e?.printStackTrace()
            }
        }
    }

    fun resolveActivityResult(requestCode: Int, resultCode: Int, data: Intent?,
                              finishLoad: (Credential) -> Unit, finishSave: () -> Unit) {
        when (requestCode) {
            RC_READ -> {
                if (resultCode == Activity.RESULT_OK) {
                    data?.getParcelableExtra<Credential>(Credential.EXTRA_KEY)?.let {
                        finishLoad(it)
                    }
                } else {
                    // 読込拒否
                }
            }
            RC_SAVE -> {
                if (resultCode == Activity.RESULT_OK) {
                    // 登録許可
                } else {
                    // 登録拒否
                }
                finishSave()
            }
        }
    }
}
```

## Activity
email入力欄とpassword入力があるActivityです

<img width="300" src="https://qiita-image-store.s3.amazonaws.com/0/199764/64c29742-cc99-f8c0-9c5e-974e9c504f49.png">

```MainActivity.kt
class LoginActivity : AppCompatActivity(){

    private lateinit var service: LoginService

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_login)

        service = LoginService(this)

        email_sign_in_button.setOnClickListener {
            service.save(email.text.toString(), password.text.toString()){
                // 保存完了後処理
            }
        }

        service.load {
            email.setText(it.id)
            password.setText(it.password)
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        when (requestCode) {
            LoginService.RC_READ, LoginService.RC_SAVE ->
                service.resolveActivityResult(requestCode, resultCode, data, {
                    // 読み込み完了後処理
                    email.setText(it.id)
                    password.setText(it.password)
                }, {
                    // 保存完了後処理
                })
        }
    }
}
```

# 動作
初回保存時に確認画面が表示されます
<img width="300" src="https://qiita-image-store.s3.amazonaws.com/0/199764/aa2ab26a-4eab-df1c-a5d7-859c37b9b08a.png">

読込時に保存が１件だけの場合、下部よりイカしたポップアップ？が表示されます
<img width="300" src="https://qiita-image-store.s3.amazonaws.com/0/199764/f3750409-9580-78ee-18ad-021b542469ca.png">

読込時に保存が複数件の場合、どのアカウントを使用するかポップアップが表示されます
<img width="300" src="https://qiita-image-store.s3.amazonaws.com/0/199764/ad4e4cf9-7ae5-30d7-a3b9-2635f78b5c47.png">

# その他参考
これらソースは[GitHub](https://github.com/yoshihitoijichi/SmartLockForPasswordsSample)にもアップしました。

動作の説明についてはクックパッドの記事がわかりやすかったです。
[Smart Lock for Passwordsを利用したAndroidアプリログインフロー改善への取り組み
](https://techlife.cookpad.com/entry/2015/12/04/142946 "Smart Lock for Passwordsを利用したAndroidアプリログインフロー改善への取り組み")

その他メールアドレス一覧を表示してくれるHINTも記載されたサンプルはGoogle Developersにあります。
[Smart Lock for Passwords on Android](https://developers.google.com/identity/smartlock-passwords/android/ "Smart Lock for Passwords on Android")