aboutsummaryrefslogtreecommitdiff
path: root/app/src/nonFree/java
diff options
context:
space:
mode:
authorFabian Henneke <FabianHenneke@users.noreply.github.com>2020-07-02 13:49:32 +0200
committerGitHub <noreply@github.com>2020-07-02 13:49:32 +0200
commitca9c951a536e9ccd2bf3e8f0e2e0a48992d0d655 (patch)
treebcf32f9bf6178051632baed95d5c70d8355f8e29 /app/src/nonFree/java
parentc702d4aa9ea09ae27e613d85440a207b37995e86 (diff)
Fill OTP fields with SMS codes (#900)
* Fill OTP fields with SMS codes * Allow SMS OTP fill also for web origins * Introduce free and nonFree build variants * Fix up workflow * Improve layout and feature detection * Workflow changes * Add Changelog entry * github: update release workflow for nonFree/Free split Signed-off-by: Harsh Shandilya <me@msfjarvis.dev> * Switch to lifecycleScope * github: make snapshot deploy free variant Signed-off-by: Harsh Shandilya <me@msfjarvis.dev> Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
Diffstat (limited to 'app/src/nonFree/java')
-rw-r--r--app/src/nonFree/java/com/zeapo/pwdstore/autofill/oreo/ui/AutofillSmsActivity.kt136
1 files changed, 136 insertions, 0 deletions
diff --git a/app/src/nonFree/java/com/zeapo/pwdstore/autofill/oreo/ui/AutofillSmsActivity.kt b/app/src/nonFree/java/com/zeapo/pwdstore/autofill/oreo/ui/AutofillSmsActivity.kt
new file mode 100644
index 00000000..02394867
--- /dev/null
+++ b/app/src/nonFree/java/com/zeapo/pwdstore/autofill/oreo/ui/AutofillSmsActivity.kt
@@ -0,0 +1,136 @@
+/*
+ * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+package com.zeapo.pwdstore.autofill.oreo.ui
+
+import android.app.Activity
+import android.app.PendingIntent
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.content.IntentSender
+import android.os.Build
+import android.os.Bundle
+import android.view.autofill.AutofillManager
+import androidx.annotation.RequiresApi
+import androidx.appcompat.app.AppCompatActivity
+import androidx.lifecycle.lifecycleScope
+import com.github.ajalt.timberkt.e
+import com.github.ajalt.timberkt.w
+import com.google.android.gms.auth.api.phone.SmsCodeRetriever
+import com.google.android.gms.auth.api.phone.SmsRetriever
+import com.google.android.gms.common.ConnectionResult
+import com.google.android.gms.common.GoogleApiAvailability
+import com.google.android.gms.common.api.ResolvableApiException
+import com.google.android.gms.tasks.Tasks
+import com.zeapo.pwdstore.autofill.oreo.AutofillAction
+import com.zeapo.pwdstore.autofill.oreo.Credentials
+import com.zeapo.pwdstore.autofill.oreo.FillableForm
+import com.zeapo.pwdstore.databinding.ActivityOreoAutofillSmsBinding
+import com.zeapo.pwdstore.utils.viewBinding
+import kotlinx.coroutines.launch
+
+@RequiresApi(Build.VERSION_CODES.O)
+class AutofillSmsActivity : AppCompatActivity() {
+
+ companion object {
+
+ private var fillOtpFromSmsRequestCode = 1
+
+ fun shouldOfferFillFromSms(context: Context): Boolean {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P)
+ return false
+ val googleApiAvailabilityInstance = GoogleApiAvailability.getInstance()
+ val googleApiStatus = googleApiAvailabilityInstance.isGooglePlayServicesAvailable(context)
+ if (googleApiStatus != ConnectionResult.SUCCESS) {
+ w { "Google Play Services unavailable or not updated: ${googleApiAvailabilityInstance.getErrorString(googleApiStatus)}" }
+ return false
+ }
+ // https://developer.android.com/guide/topics/text/autofill-services#sms-autofill
+ if (googleApiAvailabilityInstance.getApkVersion(context) < 190056000) {
+ w { "Google Play Service 19.0.56 or higher required for SMS OTP Autofill" }
+ return false
+ }
+ return true
+ }
+
+ fun makeFillOtpFromSmsIntentSender(context: Context): IntentSender {
+ val intent = Intent(context, AutofillSmsActivity::class.java)
+ return PendingIntent.getActivity(
+ context,
+ fillOtpFromSmsRequestCode++,
+ intent,
+ PendingIntent.FLAG_CANCEL_CURRENT
+ ).intentSender
+ }
+ }
+
+ private val binding by viewBinding(ActivityOreoAutofillSmsBinding::inflate)
+
+ private lateinit var clientState: Bundle
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(binding.root)
+ setResult(RESULT_CANCELED)
+ binding.cancelButton.setOnClickListener {
+ finish()
+ }
+ }
+
+ override fun onStart() {
+ super.onStart()
+ clientState = intent?.getBundleExtra(AutofillManager.EXTRA_CLIENT_STATE) ?: run {
+ e { "AutofillSmsActivity started without EXTRA_CLIENT_STATE" }
+ finish()
+ return
+ }
+
+ registerReceiver(smsCodeRetrievedReceiver, IntentFilter(SmsCodeRetriever.SMS_CODE_RETRIEVED_ACTION), SmsRetriever.SEND_PERMISSION, null)
+ lifecycleScope.launch {
+ waitForSms()
+ }
+ }
+
+ // Retry starting the SMS code retriever after a permission request.
+ @Suppress("DEPRECATION")
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ super.onActivityResult(requestCode, resultCode, data)
+ if (resultCode != Activity.RESULT_OK)
+ return
+ lifecycleScope.launch {
+ waitForSms()
+ }
+ }
+
+ private fun waitForSms() {
+ val smsClient = SmsCodeRetriever.getAutofillClient(this@AutofillSmsActivity)
+ try {
+ Tasks.await(smsClient.startSmsCodeRetriever())
+ } catch (e: ResolvableApiException) {
+ e.startResolutionForResult(this, 1)
+ } catch (e: Exception) {
+ e(e)
+ finish()
+ }
+ }
+
+ private val smsCodeRetrievedReceiver = object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ val smsCode = intent.getStringExtra(SmsCodeRetriever.EXTRA_SMS_CODE)
+ val fillInDataset =
+ FillableForm.makeFillInDataset(
+ this@AutofillSmsActivity,
+ Credentials(null, null, smsCode),
+ clientState,
+ AutofillAction.FillOtpFromSms
+ )
+ setResult(RESULT_OK, Intent().apply {
+ putExtra(AutofillManager.EXTRA_AUTHENTICATION_RESULT, fillInDataset)
+ })
+ finish()
+ }
+ }
+}