aboutsummaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/build.gradle4
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/autofill/AutofillService.kt127
2 files changed, 60 insertions, 71 deletions
diff --git a/app/build.gradle b/app/build.gradle
index 220514c1..78557cc1 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -90,6 +90,10 @@ dependencies {
}
}
implementation deps.androidx.material
+
+ implementation deps.kotlin.coroutines.android
+ implementation deps.kotlin.coroutines.core
+
implementation deps.third_party.commons_io
implementation deps.third_party.commons_codec
implementation deps.third_party.fastscroll
diff --git a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillService.kt b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillService.kt
index cc9ff2f2..0634cdfe 100644
--- a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillService.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillService.kt
@@ -6,8 +6,6 @@ package com.zeapo.pwdstore.autofill
import android.accessibilityservice.AccessibilityService
import android.app.PendingIntent
-import android.content.ClipData
-import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
@@ -15,15 +13,16 @@ import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
-import android.os.Bundle
import android.provider.Settings
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
+import android.view.Window
import android.view.WindowManager
import android.view.accessibility.AccessibilityEvent
import android.view.accessibility.AccessibilityNodeInfo
import android.view.accessibility.AccessibilityWindowInfo
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
+import androidx.core.os.bundleOf
import androidx.preference.PreferenceManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.zeapo.pwdstore.PasswordEntry
@@ -39,6 +38,11 @@ import java.net.MalformedURLException
import java.net.URL
import java.util.ArrayList
import java.util.Locale
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
import me.msfjarvis.openpgpktx.util.OpenPgpApi
import me.msfjarvis.openpgpktx.util.OpenPgpServiceConnection
import org.apache.commons.io.FileUtils
@@ -46,7 +50,7 @@ import org.openintents.openpgp.IOpenPgpService2
import org.openintents.openpgp.OpenPgpError
import timber.log.Timber
-class AutofillService : AccessibilityService() {
+class AutofillService : AccessibilityService(), CoroutineScope by CoroutineScope(Dispatchers.Default) {
private var serviceConnection: OpenPgpServiceConnection? = null
private var settings: SharedPreferences? = null
private var info: AccessibilityNodeInfo? = null // the original source of the event (the edittext field)
@@ -76,6 +80,12 @@ class AutofillService : AccessibilityService() {
instance = this
}
+ override fun onDestroy() {
+ super.onDestroy()
+ instance = null
+ cancel()
+ }
+
override fun onServiceConnected() {
super.onServiceConnected()
serviceConnection = OpenPgpServiceConnection(this@AutofillService, "org.sufficientlysecure.keychain")
@@ -135,7 +145,7 @@ class AutofillService : AccessibilityService() {
if (event.eventType == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED ||
event.packageName != null && event.packageName == "org.sufficientlysecure.keychain" ||
event.packageName != null && event.packageName == "com.android.systemui") {
- dismissDialog(event)
+ dismissDialog()
return
}
@@ -145,7 +155,7 @@ class AutofillService : AccessibilityService() {
return
} else {
// nothing to do if not password field focus
- dismissDialog(event)
+ dismissDialog()
return
}
}
@@ -182,9 +192,7 @@ class AutofillService : AccessibilityService() {
if (info == null) return
// save the dialog's corresponding window so we can use getWindows() in dismissDialog
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- window = info!!.window
- }
+ window = info!!.window
val packageName: String
val appName: String
@@ -246,18 +254,8 @@ class AutofillService : AccessibilityService() {
}
// dismiss the dialog if the window has changed
- private fun dismissDialog(event: AccessibilityEvent) {
- // the default keyboard showing/hiding is a window state changed event
- // on Android 5+ we can use getWindows() to determine when the original window is not visible
- // on Android 4.3 we have to use window state changed events and filter out the keyboard ones
- // there may be other exceptions...
- val dismiss: Boolean = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- !windows.contains(window)
- } else {
- !(event.eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED &&
- event.packageName != null &&
- event.packageName.toString().contains("inputmethod"))
- }
+ private fun dismissDialog() {
+ val dismiss = !windows.contains(window)
if (dismiss && dialog != null && dialog!!.isShowing) {
dialog!!.dismiss()
dialog = null
@@ -380,9 +378,12 @@ class AutofillService : AccessibilityService() {
builder.setMessage(getString(R.string.autofill_paste_username, password.username))
dialog = builder.create()
- this.setDialogType(dialog)
- dialog!!.window!!.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)
- dialog!!.window!!.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
+ require(dialog != null) { "Dialog should not be null at this stage" }
+ dialog!!.window!!.apply {
+ setDialogType(this)
+ addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)
+ clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
+ }
dialog!!.show()
}
@@ -446,8 +447,8 @@ class AutofillService : AccessibilityService() {
}
dialog = builder.create()
- setDialogType(dialog)
dialog?.window?.apply {
+ setDialogType(this)
val density = context.resources.displayMetrics.density
addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)
setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
@@ -458,15 +459,13 @@ class AutofillService : AccessibilityService() {
dialog?.show()
}
- private fun setDialogType(dialog: AlertDialog?) {
- dialog?.window?.apply {
- setType(
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
- WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
- else
- WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
- )
- }
+ @Suppress("DEPRECATION")
+ private fun setDialogType(window: Window) {
+ window.setType(if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
+ WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
+ else
+ WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
+ )
}
override fun onInterrupt() {}
@@ -482,7 +481,7 @@ class AutofillService : AccessibilityService() {
}
}
- private fun decryptAndVerify() {
+ private fun decryptAndVerify() = launch {
packageName = info!!.packageName
val data: Intent
if (resultData == null) {
@@ -492,40 +491,45 @@ class AutofillService : AccessibilityService() {
data = resultData!!
resultData = null
}
- var `is`: InputStream? = null
- try {
- `is` = FileUtils.openInputStream(items[lastWhichItem])
- } catch (e: IOException) {
- e.printStackTrace()
+
+ var inputStream: InputStream? = null
+ withContext(Dispatchers.IO) {
+ try {
+ inputStream = FileUtils.openInputStream(items[lastWhichItem])
+ } catch (e: IOException) {
+ e.printStackTrace()
+ cancel("", e)
+ }
}
val os = ByteArrayOutputStream()
val api = OpenPgpApi(this@AutofillService, serviceConnection!!.service!!)
- // TODO we are dropping frames, (did we before??) find out why and maybe make this async
- val result = api.executeApi(data, `is`, os)
+ val result = api.executeApi(data, inputStream, os)
when (result?.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) {
OpenPgpApi.RESULT_CODE_SUCCESS -> {
try {
- val entry = PasswordEntry(os)
- pasteText(info!!, entry.password)
-
+ var entry: PasswordEntry? = null
+ withContext(Dispatchers.IO) {
+ entry = PasswordEntry(os)
+ }
+ withContext(Dispatchers.Main) { pasteText(info!!, entry?.password) }
// save password entry for pasting the username as well
- if (entry.hasUsername()) {
+ if (entry?.hasUsername() == true) {
lastPassword = entry
val ttl = Integer.parseInt(settings!!.getString("general_show_time", "45")!!)
- Toast.makeText(this, getString(R.string.autofill_toast_username, ttl), Toast.LENGTH_LONG).show()
+ Toast.makeText(applicationContext, getString(R.string.autofill_toast_username, ttl), Toast.LENGTH_LONG).show()
lastPasswordMaxDate = System.currentTimeMillis() + ttl * 1000L
}
} catch (e: UnsupportedEncodingException) {
- Timber.tag(Constants.TAG).e(e, "UnsupportedEncodingException")
+ Timber.tag(Constants.TAG).e(e)
}
}
OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED -> {
Timber.tag("PgpHandler").i("RESULT_CODE_USER_INTERACTION_REQUIRED")
val pi = result.getParcelableExtra<PendingIntent>(OpenPgpApi.RESULT_INTENT)
// need to start a blank activity to call startIntentSenderForResult
- val intent = Intent(this@AutofillService, AutofillActivity::class.java)
+ val intent = Intent(applicationContext, AutofillActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
intent.putExtra("pending_intent", pi)
startActivity(intent)
@@ -533,9 +537,7 @@ class AutofillService : AccessibilityService() {
OpenPgpApi.RESULT_CODE_ERROR -> {
val error = result.getParcelableExtra<OpenPgpError>(OpenPgpApi.RESULT_ERROR)
if (error != null) {
- Toast.makeText(this@AutofillService,
- "Error from OpenKeyChain : " + error.message,
- Toast.LENGTH_LONG).show()
+ Toast.makeText(applicationContext, "Error from OpenKeyChain : ${error.message}", Toast.LENGTH_LONG).show()
Timber.tag(Constants.TAG).e("onError getErrorId: ${error.errorId}")
Timber.tag(Constants.TAG).e("onError getMessage: ${error.message}")
}
@@ -548,25 +550,8 @@ class AutofillService : AccessibilityService() {
// but this will open another dialog...hack to ignore this
// & need to ensure performAction correct (i.e. what is info now?)
ignoreActionFocus = node.performAction(AccessibilityNodeInfo.ACTION_FOCUS)
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- val args = Bundle()
- args.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, text)
- node.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, args)
- } else {
- val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
- var clip = ClipData.newPlainText("autofill_pm", text)
- clipboard.setPrimaryClip(clip)
- node.performAction(AccessibilityNodeInfo.ACTION_PASTE)
-
- clip = ClipData.newPlainText("autofill_pm", "")
- clipboard.setPrimaryClip(clip)
- if (settings!!.getBoolean("clear_clipboard_20x", false)) {
- for (i in 0..19) {
- clip = ClipData.newPlainText(i.toString(), i.toString())
- clipboard.setPrimaryClip(clip)
- }
- }
- }
+ val args = bundleOf(Pair(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, text))
+ node.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, args)
node.recycle()
}