diff options
| author | Fabian Henneke <FabianHenneke@users.noreply.github.com> | 2020-07-07 17:02:57 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-07-07 20:32:57 +0530 |
| commit | d192ab2d9a6f45fb23e3d3f709c144ce1be3a850 (patch) | |
| tree | 7e946de1f3e15db63be9c184d74b49d610c49d09 /app/src/main/java/com/zeapo/pwdstore/autofill | |
| parent | 5d170249cdd0050349f40d3a5852a8ba996663bc (diff) | |
| download | HardenedPass-d192ab2d9a6f45fb23e3d3f709c144ce1be3a850.tar.gz HardenedPass-d192ab2d9a6f45fb23e3d3f709c144ce1be3a850.zip | |
Work around Chrome Autofill issue (#921)
Diffstat (limited to 'app/src/main/java/com/zeapo/pwdstore/autofill')
| -rw-r--r-- | app/src/main/java/com/zeapo/pwdstore/autofill/oreo/ChromeCompatFix.kt | 93 |
1 files changed, 93 insertions, 0 deletions
diff --git a/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/ChromeCompatFix.kt b/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/ChromeCompatFix.kt new file mode 100644 index 00000000..75d9539a --- /dev/null +++ b/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/ChromeCompatFix.kt @@ -0,0 +1,93 @@ +/* + * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved. + * SPDX-License-Identifier: GPL-3.0-only + */ +package com.zeapo.pwdstore.autofill.oreo + +import android.accessibilityservice.AccessibilityService +import android.content.Context +import android.content.SharedPreferences +import android.os.Build +import android.os.Handler +import android.os.Looper +import android.view.accessibility.AccessibilityEvent +import androidx.annotation.RequiresApi +import androidx.core.content.edit +import androidx.preference.PreferenceManager +import com.github.ajalt.timberkt.i +import com.github.ajalt.timberkt.v +import com.github.ajalt.timberkt.w +import com.zeapo.pwdstore.utils.PreferenceKeys +import com.zeapo.pwdstore.utils.autofillManager + +@RequiresApi(Build.VERSION_CODES.P) +class ChromeCompatFix : AccessibilityService() { + + companion object { + fun setStatusInPreferences(context: Context, enabled: Boolean) { + PreferenceManager.getDefaultSharedPreferences(context).edit { + putBoolean(PreferenceKeys.OREO_AUTOFILL_CHROME_COMPAT_FIX, enabled) + } + } + } + + private val isEnabledInPreferences + get() = PreferenceManager.getDefaultSharedPreferences(this).getBoolean(PreferenceKeys.OREO_AUTOFILL_CHROME_COMPAT_FIX, true) + + private val handler = Handler(Looper.getMainLooper()) + private val forceRootNodePopulation = Runnable { + val rootPackageName = rootInActiveWindow?.packageName.toString() + v { "$rootPackageName: forced root node population" } + } + private val disableListener = SharedPreferences.OnSharedPreferenceChangeListener { prefs: SharedPreferences, key: String -> + if (key != PreferenceKeys.OREO_AUTOFILL_CHROME_COMPAT_FIX) + return@OnSharedPreferenceChangeListener + if (!isEnabledInPreferences) { + i { "Disabled in settings, shutting down..." } + disableSelf() + } + } + + override fun onAccessibilityEvent(event: AccessibilityEvent) { + handler.removeCallbacks(forceRootNodePopulation) + when (event.eventType) { + AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED, AccessibilityEvent.TYPE_ANNOUNCEMENT -> { + // WINDOW_STATE_CHANGED: Triggered on long press in a text field, replacement for + // the missing Autofill action menu item. + // ANNOUNCEMENT: Triggered when a password field is selected. + // + // These events are triggered only by user actions and thus don't need to be handled + // with debounce. However, they only trigger Autofill popups on the *next* input + // field selected by the user. + forceRootNodePopulation.run() + v { "${event.packageName} (${AccessibilityEvent.eventTypeToString(event.eventType)}): forced root node population" } + } + AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED -> { + // WINDOW_CONTENT_CHANGED: Triggered whenever the page contents change. + // + // This event is triggered many times during page load, which makes a debounce + // necessary to prevent huge performance regressions in Chrome. However, it is the + // only event that reliably runs before the user selects a text field. + handler.postDelayed(forceRootNodePopulation, 300) + v { "${event.packageName} (${AccessibilityEvent.eventTypeToString(event.eventType)}): debounced root node population" } + } + } + } + + override fun onServiceConnected() { + super.onServiceConnected() + // Allow the service to be activated only if the Autofill service is already enabled. + if (autofillManager?.hasEnabledAutofillServices() != true) { + w { "Autofill service not enabled, shutting down..." } + disableSelf() + return + } + // Update preferences if the user manually activated the service. + setStatusInPreferences(this, true) + + PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(disableListener) + } + + override fun onInterrupt() {} +} + |
