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

Commit

Permalink
Integrate ChromeCompatFix with UserPreferences
Browse files Browse the repository at this point in the history
  • Loading branch information
fmeum committed Jul 7, 2020
1 parent b3203f8 commit 178e90a
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 13 deletions.
57 changes: 48 additions & 9 deletions app/src/main/java/com/zeapo/pwdstore/UserPreference.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ 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
Expand Down Expand Up @@ -73,6 +75,7 @@ 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>
Expand Down Expand Up @@ -118,6 +121,7 @@ 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)
Expand Down Expand Up @@ -276,6 +280,16 @@ 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 {
Expand Down Expand Up @@ -398,16 +412,20 @@ class UserPreference : AppCompatActivity() {
}

private fun updateAutofillSettings() {
val isAccessibilityServiceEnabled = callingActivity.isAccessibilityServiceEnabled
val isAccessibilityAutofillServiceEnabled = callingActivity.isAccessibilityAutofillServiceEnabled
val isAutofillServiceEnabled = callingActivity.isAutofillServiceEnabled
autoFillEnablePreference?.isChecked =
isAccessibilityServiceEnabled || isAutofillServiceEnabled
isAccessibilityAutofillServiceEnabled || isAutofillServiceEnabled
autofillDependencies.forEach {
it.isVisible = isAccessibilityServiceEnabled
it.isVisible = isAccessibilityAutofillServiceEnabled
}
oreoAutofillDependencies.forEach {
it.isVisible = isAutofillServiceEnabled
}
oreoAutofillChromeCompatFix?.apply {
isChecked = callingActivity.isChromeCompatFixServiceEnabled
isVisible = callingActivity.isChromeCompatFixServiceSupported
}
}

private fun updateClearSavedPassphrasePrefs() {
Expand All @@ -428,13 +446,16 @@ class UserPreference : AppCompatActivity() {
}

private fun onEnableAutofillClick() {
if (callingActivity.isAccessibilityServiceEnabled) {
if (callingActivity.isAccessibilityAutofillServiceEnabled) {
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()
else
ChromeCompatFix.setStatusInPreferences(requireContext(), false)
updateAutofillSettings()
} else {
throw IllegalStateException("isAutofillServiceEnabled == true, but Build.VERSION.SDK_INT < Build.VERSION_CODES.O")
}
} else {
val enableOreoAutofill = callingActivity.isAutofillServiceSupported
MaterialAlertDialogBuilder(callingActivity).run {
Expand Down Expand Up @@ -710,14 +731,32 @@ class UserPreference : AppCompatActivity() {
File("$filesDir/.ssh_key").writeText(lines.joinToString("\n"))
}

private val isAccessibilityServiceEnabled: Boolean
private val isAccessibilityAutofillServiceEnabled: Boolean
get() {
val am = getSystemService<AccessibilityManager>() ?: return false
val runningServices = am
.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_GENERIC)
return runningServices
.map { it.id.substringBefore("/") }
.any { it == BuildConfig.APPLICATION_ID }
.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
}

private val isAutofillServiceSupported: Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,50 @@
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 android.widget.Toast
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()
}
}

@RequiresApi(Build.VERSION_CODES.O)
override fun onAccessibilityEvent(event: AccessibilityEvent) {
handler.removeCallbacks(forceRootNodePopulation)
when (event.eventType) {
Expand All @@ -47,6 +75,20 @@ class ChromeCompatFix : AccessibilityService() {
}
}

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() {}
}

Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ 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"
Expand Down
11 changes: 9 additions & 2 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -277,8 +277,13 @@
<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.
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/ga.
</string>

<!-- Autofill -->
Expand Down Expand Up @@ -393,4 +398,6 @@
<string name="add_otp">Add OTP</string>
<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>
</resources>
2 changes: 1 addition & 1 deletion app/src/main/res/xml/oreo_autofill_chrome_compat_fix.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
-->
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeWindowContentChanged|typeAnnouncement|typeWindowStateChanged"
android:accessibilityFeedbackType="feedbackVisual"
android:accessibilityFeedbackType="feedbackGeneric"
android:accessibilityFlags="flagDefault"
android:canRetrieveWindowContent="true"
android:description="@string/oreo_autofill_chrome_compat_fix_description"
Expand Down
5 changes: 5 additions & 0 deletions app/src/main/res/xml/preference.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@
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:title="@string/oreo_autofill_chrome_compat_fix_preference_title"
app:summary="@string/oreo_autofill_chrome_compat_fix_preference_summary" />
<ListPreference
app:defaultValue="file"
app:entries="@array/oreo_autofill_directory_structure_entries"
Expand Down

0 comments on commit 178e90a

Please sign in to comment.