Skip to content
This repository has been archived by the owner on Oct 15, 2024. It is now read-only.

Work around Chrome Autofill issue #921

Merged
merged 7 commits into from
Jul 7, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,19 @@
android:name="android.accessibilityservice"
android:resource="@xml/autofill_config" />
</service>

<service
android:name=".autofill.oreo.ChromeCompatFix"
android:enabled="@bool/enable_chrome_compat_fix"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
<meta-data
android:name="android.accessibilityservice"
android:resource="@xml/oreo_autofill_chrome_compat_fix" />
</service>

<service
android:name=".ClipboardService"
android:process=":clipboard_service_process" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* 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.os.Build
import android.os.Handler
import android.os.Looper
import android.view.accessibility.AccessibilityEvent
import androidx.annotation.RequiresApi
import com.github.ajalt.timberkt.v

class ChromeCompatFix : AccessibilityService() {

private val handler = Handler(Looper.getMainLooper())
private val forceRootNodePopulation = Runnable {
val rootPackageName = rootInActiveWindow?.packageName.toString()
v { "$rootPackageName: forced root node population" }
}

@RequiresApi(Build.VERSION_CODES.O)
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 onInterrupt() {}
}

4 changes: 4 additions & 0 deletions app/src/main/res/values-v28/bools.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="enable_chrome_compat_fix">true</bool>
</resources>
1 change: 1 addition & 0 deletions app/src/main/res/values/bools.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
<resources>
<bool name="leak_canary_allow_in_non_debuggable_build">true</bool>
<bool name="enable_accessibility_autofill">true</bool>
<bool name="enable_chrome_compat_fix">false</bool>
</resources>
5 changes: 5 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,11 @@
<string name="oreo_autofill_enable_dialog_description">Password Store can offer to fill login forms and even save credentials you enter in apps or on websites.</string>
<string name="oreo_autofill_enable_dialog_instructions">To enable this feature, tap OK to go to Autofill settings. There, select Password Store from the list and confirm the confirmation prompt with OK.</string>
<string name="oreo_autofill_enable_dialog_installed_browsers">Autofill support with installed browsers:</string>
<string name="oreo_autofill_chrome_compat_fix_summary">Make Autofill more reliable in Chrome</string>
<string name="oreo_autofill_chrome_compat_fix_description">This accessibility service makes
Autofill apps work more reliably in Chrome. It is only useful if you are also using an
Autofill app, but this app need not be Password Store.
</string>

<!-- Autofill -->
<string name="autofill_description">Autofills password fields in apps. Only works for Android versions 4.3 and up. Does not rely on the clipboard for Android versions 5.0 and up.</string>
Expand Down
13 changes: 13 additions & 0 deletions app/src/main/res/xml/oreo_autofill_chrome_compat_fix.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
~ SPDX-License-Identifier: GPL-3.0-only
-->
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeWindowContentChanged|typeAnnouncement|typeWindowStateChanged"
android:accessibilityFeedbackType="feedbackVisual"
android:accessibilityFlags="flagDefault"
android:canRetrieveWindowContent="true"
android:description="@string/oreo_autofill_chrome_compat_fix_description"
android:notificationTimeout="100"
android:packageNames="com.android.chrome,com.chrome.beta,com.chrome.dev,com.chrome.canary"
android:summary="@string/oreo_autofill_chrome_compat_fix_summary" />