parent
2f657108b3
commit
681c557e9e
10 changed files with 9 additions and 195 deletions
|
@ -18,7 +18,6 @@ All notable changes to this project will be documented in this file.
|
|||
- Fix authentication failure with usernames that contain the `@` character
|
||||
- Text input boxes were illegible on dark theme
|
||||
- Top-level password names had inconsistent top margin making them look askew
|
||||
- Autofill can now be made more reliable in Chrome by enabling an accessibility service that works around known Chrome limitations
|
||||
- Password Store no longer ignores the selected OpenKeychain key
|
||||
- Password export now happens in a separate process, preventing possible freezes
|
||||
|
||||
|
|
|
@ -99,19 +99,6 @@
|
|||
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" />
|
||||
|
|
|
@ -41,9 +41,7 @@ import com.github.ajalt.timberkt.w
|
|||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.zeapo.pwdstore.autofill.AutofillPreferenceActivity
|
||||
import com.zeapo.pwdstore.autofill.AutofillService
|
||||
import com.zeapo.pwdstore.autofill.oreo.BrowserAutofillSupportLevel
|
||||
import com.zeapo.pwdstore.autofill.oreo.ChromeCompatFix
|
||||
import com.zeapo.pwdstore.autofill.oreo.getInstalledBrowsersWithAutofillSupportLevel
|
||||
import com.zeapo.pwdstore.crypto.BasePgpActivity
|
||||
import com.zeapo.pwdstore.crypto.GetKeyIdsActivity
|
||||
|
@ -72,7 +70,6 @@ class UserPreference : AppCompatActivity() {
|
|||
class PrefsFragment : PreferenceFragmentCompat() {
|
||||
|
||||
private var autoFillEnablePreference: SwitchPreferenceCompat? = null
|
||||
private var oreoAutofillChromeCompatFix: SwitchPreferenceCompat? = null
|
||||
private var clearSavedPassPreference: Preference? = null
|
||||
private lateinit var autofillDependencies: List<Preference>
|
||||
private lateinit var oreoAutofillDependencies: List<Preference>
|
||||
|
@ -118,7 +115,6 @@ class UserPreference : AppCompatActivity() {
|
|||
|
||||
// Autofill preferences
|
||||
autoFillEnablePreference = findPreference(PreferenceKeys.AUTOFILL_ENABLE)
|
||||
oreoAutofillChromeCompatFix = findPreference(PreferenceKeys.OREO_AUTOFILL_CHROME_COMPAT_FIX)
|
||||
val oreoAutofillDirectoryStructurePreference = findPreference<ListPreference>(PreferenceKeys.OREO_AUTOFILL_DIRECTORY_STRUCTURE)
|
||||
val oreoAutofillDefaultUsername = findPreference<EditTextPreference>(PreferenceKeys.OREO_AUTOFILL_DEFAULT_USERNAME)
|
||||
val oreoAutofillCustomPublixSuffixes = findPreference<EditTextPreference>(PreferenceKeys.OREO_AUTOFILL_CUSTOM_PUBLIC_SUFFIXES)
|
||||
|
@ -277,16 +273,6 @@ class UserPreference : AppCompatActivity() {
|
|||
true
|
||||
}
|
||||
|
||||
oreoAutofillChromeCompatFix?.onPreferenceClickListener = ClickListener {
|
||||
if (oreoAutofillChromeCompatFix!!.isChecked) {
|
||||
startActivity(Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS))
|
||||
true
|
||||
} else {
|
||||
// Service will disable itself on startup if the preference has the value false.
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
findPreference<Preference>(PreferenceKeys.EXPORT_PASSWORDS)?.apply {
|
||||
isVisible = sharedPreferences.getBoolean(PreferenceKeys.REPOSITORY_INITIALIZED, false)
|
||||
onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||
|
@ -409,20 +395,16 @@ class UserPreference : AppCompatActivity() {
|
|||
}
|
||||
|
||||
private fun updateAutofillSettings() {
|
||||
val isAccessibilityAutofillServiceEnabled = callingActivity.isAccessibilityAutofillServiceEnabled
|
||||
val isAccessibilityServiceEnabled = callingActivity.isAccessibilityServiceEnabled
|
||||
val isAutofillServiceEnabled = callingActivity.isAutofillServiceEnabled
|
||||
autoFillEnablePreference?.isChecked =
|
||||
isAccessibilityAutofillServiceEnabled || isAutofillServiceEnabled
|
||||
isAccessibilityServiceEnabled || isAutofillServiceEnabled
|
||||
autofillDependencies.forEach {
|
||||
it.isVisible = isAccessibilityAutofillServiceEnabled
|
||||
it.isVisible = isAccessibilityServiceEnabled
|
||||
}
|
||||
oreoAutofillDependencies.forEach {
|
||||
it.isVisible = isAutofillServiceEnabled
|
||||
}
|
||||
oreoAutofillChromeCompatFix?.apply {
|
||||
isChecked = callingActivity.isChromeCompatFixServiceEnabled
|
||||
isVisible = callingActivity.isChromeCompatFixServiceSupported
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateClearSavedPassphrasePrefs() {
|
||||
|
@ -443,16 +425,13 @@ class UserPreference : AppCompatActivity() {
|
|||
}
|
||||
|
||||
private fun onEnableAutofillClick() {
|
||||
if (callingActivity.isAccessibilityAutofillServiceEnabled) {
|
||||
if (callingActivity.isAccessibilityServiceEnabled) {
|
||||
startActivity(Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS))
|
||||
} else if (callingActivity.isAutofillServiceEnabled) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||||
callingActivity.autofillManager!!.disableAutofillServices()
|
||||
ChromeCompatFix.setStatusInPreferences(requireContext(), false)
|
||||
updateAutofillSettings()
|
||||
} else {
|
||||
else
|
||||
throw IllegalStateException("isAutofillServiceEnabled == true, but Build.VERSION.SDK_INT < Build.VERSION_CODES.O")
|
||||
}
|
||||
} else {
|
||||
val enableOreoAutofill = callingActivity.isAutofillServiceSupported
|
||||
MaterialAlertDialogBuilder(callingActivity).run {
|
||||
|
@ -744,32 +723,14 @@ class UserPreference : AppCompatActivity() {
|
|||
File("$filesDir/.ssh_key").writeText(lines.joinToString("\n"))
|
||||
}
|
||||
|
||||
private val isAccessibilityAutofillServiceEnabled: Boolean
|
||||
private val isAccessibilityServiceEnabled: Boolean
|
||||
get() {
|
||||
val am = getSystemService<AccessibilityManager>() ?: return false
|
||||
val runningServices = am
|
||||
.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_GENERIC)
|
||||
return runningServices
|
||||
.mapNotNull { it?.resolveInfo?.serviceInfo }
|
||||
.any { it.packageName == BuildConfig.APPLICATION_ID && it.name == AutofillService::class.java.name }
|
||||
}
|
||||
|
||||
private val isChromeCompatFixServiceEnabled: Boolean
|
||||
get() {
|
||||
val am = getSystemService<AccessibilityManager>() ?: return false
|
||||
val runningServices = am
|
||||
.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_GENERIC)
|
||||
return runningServices
|
||||
.mapNotNull { it?.resolveInfo?.serviceInfo }
|
||||
.any { it.packageName == BuildConfig.APPLICATION_ID && it.name == ChromeCompatFix::class.java.name }
|
||||
}
|
||||
|
||||
private val isChromeCompatFixServiceSupported: Boolean
|
||||
get() {
|
||||
// Autofill compat mode is only available starting with Android Pie and only makes sense
|
||||
// when used with Autofill enabled.
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) return false
|
||||
return isAutofillServiceEnabled
|
||||
.map { it.id.substringBefore("/") }
|
||||
.any { it == BuildConfig.APPLICATION_ID }
|
||||
}
|
||||
|
||||
private val isAutofillServiceSupported: Boolean
|
||||
|
|
|
@ -1,94 +0,0 @@
|
|||
/*
|
||||
* 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() {}
|
||||
}
|
||||
|
|
@ -40,7 +40,6 @@ object PreferenceKeys {
|
|||
const val OPENPGP_KEY_IDS_SET = "openpgp_key_ids_set"
|
||||
const val OPENPGP_KEY_ID_PREF = "openpgp_key_id_pref"
|
||||
const val OPENPGP_PROVIDER_LIST = "openpgp_provider_list"
|
||||
const val OREO_AUTOFILL_CHROME_COMPAT_FIX = "oreo_autofill_chrome_compat_fix"
|
||||
const val OREO_AUTOFILL_CUSTOM_PUBLIC_SUFFIXES = "oreo_autofill_custom_public_suffixes"
|
||||
const val OREO_AUTOFILL_DEFAULT_USERNAME = "oreo_autofill_default_username"
|
||||
const val OREO_AUTOFILL_DIRECTORY_STRUCTURE = "oreo_autofill_directory_structure"
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
<?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
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<bool name="enable_chrome_compat_fix">true</bool>
|
||||
</resources>
|
|
@ -7,5 +7,4 @@
|
|||
<bool name="leak_canary_allow_in_non_debuggable_build">true</bool>
|
||||
<bool name="enable_accessibility_autofill">true</bool>
|
||||
<bool name="light_status_bar">true</bool>
|
||||
<bool name="enable_chrome_compat_fix">false</bool>
|
||||
</resources>
|
||||
|
|
|
@ -275,16 +275,6 @@
|
|||
<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 work more reliably in Chrome. It can only be activated if you are already using
|
||||
Password Store as your Autofill service.\n\nThis service is only active while you are
|
||||
using Chrome. It does not access any data or take any actions on your behalf, but forces
|
||||
Chrome to properly forward user interactions to the Password Store Autofill
|
||||
service.\n\nChrome\'s performance should not be noticeably affected. If you are experiencing
|
||||
any problems with this service, please create an issue at
|
||||
https://msfjarvis.dev/aps.
|
||||
</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>
|
||||
|
@ -399,7 +389,6 @@
|
|||
<string name="otp_import_success">Successfully imported TOTP configuration</string>
|
||||
<string name="otp_import_failure">Failed to import TOTP configuration</string>
|
||||
<string name="oreo_autofill_chrome_compat_fix_preference_title">Improve reliability in Chrome</string>
|
||||
<string name="oreo_autofill_chrome_compat_fix_preference_summary">Requires activating an accessibility service and may affect overall Chrome performance</string>
|
||||
<string name="exporting_passwords">Exporting passwords…</string>
|
||||
<string name="invalid_filename_text">File name must not contain \'/\', set directory above</string>
|
||||
<string name="directory_hint">Directory</string>
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
<?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="feedbackGeneric"
|
||||
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" />
|
|
@ -10,11 +10,6 @@
|
|||
app:defaultValue="true"
|
||||
app:key="autofill_enable"
|
||||
app:title="@string/pref_autofill_enable_title" />
|
||||
<SwitchPreferenceCompat
|
||||
app:defaultValue="true"
|
||||
app:key="oreo_autofill_chrome_compat_fix"
|
||||
app:summary="@string/oreo_autofill_chrome_compat_fix_preference_summary"
|
||||
app:title="@string/oreo_autofill_chrome_compat_fix_preference_title" />
|
||||
<ListPreference
|
||||
app:defaultValue="file"
|
||||
app:entries="@array/oreo_autofill_directory_structure_entries"
|
||||
|
|
Loading…
Reference in a new issue