Make PGPainless backend feature flag runtime configurable (#1654)
* Make feature flags runtime configurable * Add a settings entry for PGPainless feature flag * Add changelog entry
This commit is contained in:
parent
1738879fb3
commit
35155e5584
11 changed files with 116 additions and 17 deletions
|
@ -14,6 +14,7 @@ All notable changes to this project will be documented in this file.
|
|||
- Allow pinning shortcuts directly to the launcher home screen
|
||||
- Another workaround for SteamGuard's non-standard OTP format
|
||||
- Allow importing QR code from images
|
||||
- Introduce a new opt-in PGP backend powered by [PGPainless](https://github.com/pgpainless/pgpainless) that does not require OpenKeychain
|
||||
|
||||
### Fixed
|
||||
|
||||
|
|
|
@ -25,23 +25,27 @@ import androidx.lifecycle.ViewModelProvider
|
|||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.github.androidpasswordstore.autofillparser.FormOrigin
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import dev.msfjarvis.aps.R
|
||||
import dev.msfjarvis.aps.data.password.PasswordItem
|
||||
import dev.msfjarvis.aps.databinding.ActivityOreoAutofillFilterBinding
|
||||
import dev.msfjarvis.aps.util.FeatureFlags
|
||||
import dev.msfjarvis.aps.util.autofill.AutofillMatcher
|
||||
import dev.msfjarvis.aps.util.autofill.AutofillPreferences
|
||||
import dev.msfjarvis.aps.util.autofill.DirectoryStructure
|
||||
import dev.msfjarvis.aps.util.extensions.viewBinding
|
||||
import dev.msfjarvis.aps.util.features.Feature
|
||||
import dev.msfjarvis.aps.util.features.Features
|
||||
import dev.msfjarvis.aps.util.viewmodel.FilterMode
|
||||
import dev.msfjarvis.aps.util.viewmodel.ListMode
|
||||
import dev.msfjarvis.aps.util.viewmodel.SearchMode
|
||||
import dev.msfjarvis.aps.util.viewmodel.SearchableRepositoryAdapter
|
||||
import dev.msfjarvis.aps.util.viewmodel.SearchableRepositoryViewModel
|
||||
import javax.inject.Inject
|
||||
import logcat.LogPriority.ERROR
|
||||
import logcat.logcat
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.O)
|
||||
@AndroidEntryPoint
|
||||
class AutofillFilterView : AppCompatActivity() {
|
||||
|
||||
companion object {
|
||||
|
@ -76,6 +80,7 @@ class AutofillFilterView : AppCompatActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
@Inject lateinit var features: Features
|
||||
private lateinit var formOrigin: FormOrigin
|
||||
private lateinit var directoryStructure: DirectoryStructure
|
||||
private val binding by viewBinding(ActivityOreoAutofillFilterBinding::inflate)
|
||||
|
@ -222,7 +227,7 @@ class AutofillFilterView : AppCompatActivity() {
|
|||
AutofillMatcher.addMatchFor(applicationContext, formOrigin, item.file)
|
||||
// intent?.extras? is checked to be non-null in onCreate
|
||||
decryptAction.launch(
|
||||
if (FeatureFlags.ENABLE_PGP_V2_BACKEND) {
|
||||
if (features.isEnabled(Feature.EnablePGPainlessBackend)) {
|
||||
AutofillDecryptActivityV2.makeDecryptFileIntent(item.file, intent!!.extras!!, this)
|
||||
} else {
|
||||
AutofillDecryptActivity.makeDecryptFileIntent(item.file, intent!!.extras!!, this)
|
||||
|
|
|
@ -18,19 +18,23 @@ import androidx.core.os.bundleOf
|
|||
import com.github.androidpasswordstore.autofillparser.AutofillAction
|
||||
import com.github.androidpasswordstore.autofillparser.Credentials
|
||||
import com.github.androidpasswordstore.autofillparser.FormOrigin
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import dev.msfjarvis.aps.data.repo.PasswordRepository
|
||||
import dev.msfjarvis.aps.ui.crypto.PasswordCreationActivity
|
||||
import dev.msfjarvis.aps.ui.crypto.PasswordCreationActivityV2
|
||||
import dev.msfjarvis.aps.util.FeatureFlags
|
||||
import dev.msfjarvis.aps.util.autofill.AutofillMatcher
|
||||
import dev.msfjarvis.aps.util.autofill.AutofillPreferences
|
||||
import dev.msfjarvis.aps.util.autofill.AutofillResponseBuilder
|
||||
import dev.msfjarvis.aps.util.extensions.unsafeLazy
|
||||
import dev.msfjarvis.aps.util.features.Feature
|
||||
import dev.msfjarvis.aps.util.features.Features
|
||||
import java.io.File
|
||||
import javax.inject.Inject
|
||||
import logcat.LogPriority.ERROR
|
||||
import logcat.logcat
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@AndroidEntryPoint
|
||||
class AutofillSaveActivity : AppCompatActivity() {
|
||||
|
||||
companion object {
|
||||
|
@ -105,11 +109,14 @@ class AutofillSaveActivity : AppCompatActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
@Inject lateinit var features: Features
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val repo = PasswordRepository.getRepositoryDirectory()
|
||||
val creationActivity =
|
||||
if (FeatureFlags.ENABLE_PGP_V2_BACKEND) PasswordCreationActivityV2::class.java
|
||||
if (features.isEnabled(Feature.EnablePGPainlessBackend))
|
||||
PasswordCreationActivityV2::class.java
|
||||
else PasswordCreationActivity::class.java
|
||||
val saveIntent =
|
||||
Intent(this, creationActivity).apply {
|
||||
|
|
|
@ -24,13 +24,14 @@ import com.google.android.material.snackbar.Snackbar
|
|||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import dev.msfjarvis.aps.R
|
||||
import dev.msfjarvis.aps.injection.prefs.SettingsPreferences
|
||||
import dev.msfjarvis.aps.util.FeatureFlags
|
||||
import dev.msfjarvis.aps.util.extensions.OPENPGP_PROVIDER
|
||||
import dev.msfjarvis.aps.util.extensions.asLog
|
||||
import dev.msfjarvis.aps.util.extensions.clipboard
|
||||
import dev.msfjarvis.aps.util.extensions.getString
|
||||
import dev.msfjarvis.aps.util.extensions.snackbar
|
||||
import dev.msfjarvis.aps.util.extensions.unsafeLazy
|
||||
import dev.msfjarvis.aps.util.features.Feature
|
||||
import dev.msfjarvis.aps.util.features.Features
|
||||
import dev.msfjarvis.aps.util.services.ClipboardService
|
||||
import dev.msfjarvis.aps.util.settings.PreferenceKeys
|
||||
import java.io.File
|
||||
|
@ -63,6 +64,8 @@ open class BasePgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBou
|
|||
/** [SharedPreferences] instance used by subclasses to persist settings */
|
||||
@SettingsPreferences @Inject lateinit var settings: SharedPreferences
|
||||
|
||||
@Inject lateinit var features: Features
|
||||
|
||||
/**
|
||||
* Handle to the [OpenPgpApi] instance that is used by subclasses to interface with OpenKeychain.
|
||||
*/
|
||||
|
@ -127,7 +130,7 @@ open class BasePgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBou
|
|||
|
||||
/** Method for subclasses to initiate binding with [OpenPgpServiceConnection]. */
|
||||
fun bindToOpenKeychain(onBoundListener: OpenPgpServiceConnection.OnBound) {
|
||||
if (FeatureFlags.ENABLE_PGP_V2_BACKEND) return
|
||||
if (features.isEnabled(Feature.EnablePGPainlessBackend)) return
|
||||
val installed =
|
||||
runCatching {
|
||||
packageManager.getPackageInfo(OPENPGP_PROVIDER, 0)
|
||||
|
|
|
@ -47,7 +47,6 @@ import dev.msfjarvis.aps.ui.git.base.BaseGitActivity
|
|||
import dev.msfjarvis.aps.ui.onboarding.activity.OnboardingActivity
|
||||
import dev.msfjarvis.aps.ui.settings.DirectorySelectionActivity
|
||||
import dev.msfjarvis.aps.ui.settings.SettingsActivity
|
||||
import dev.msfjarvis.aps.util.FeatureFlags
|
||||
import dev.msfjarvis.aps.util.autofill.AutofillMatcher
|
||||
import dev.msfjarvis.aps.util.extensions.base64
|
||||
import dev.msfjarvis.aps.util.extensions.commitChange
|
||||
|
@ -57,6 +56,8 @@ import dev.msfjarvis.aps.util.extensions.isInsideRepository
|
|||
import dev.msfjarvis.aps.util.extensions.isPermissionGranted
|
||||
import dev.msfjarvis.aps.util.extensions.listFilesRecursively
|
||||
import dev.msfjarvis.aps.util.extensions.sharedPrefs
|
||||
import dev.msfjarvis.aps.util.features.Feature
|
||||
import dev.msfjarvis.aps.util.features.Features
|
||||
import dev.msfjarvis.aps.util.settings.AuthMode
|
||||
import dev.msfjarvis.aps.util.settings.PreferenceKeys
|
||||
import dev.msfjarvis.aps.util.shortcuts.ShortcutHandler
|
||||
|
@ -76,6 +77,7 @@ const val PASSWORD_FRAGMENT_TAG = "PasswordsList"
|
|||
@AndroidEntryPoint
|
||||
class PasswordStore : BaseGitActivity() {
|
||||
|
||||
@Inject lateinit var features: Features
|
||||
@Inject lateinit var shortcutHandler: ShortcutHandler
|
||||
private lateinit var searchItem: MenuItem
|
||||
private val settings by lazy { sharedPrefs }
|
||||
|
@ -428,7 +430,7 @@ class PasswordStore : BaseGitActivity() {
|
|||
(authDecryptIntent.clone() as Intent).setComponent(
|
||||
ComponentName(
|
||||
this,
|
||||
if (FeatureFlags.ENABLE_PGP_V2_BACKEND) {
|
||||
if (features.isEnabled(Feature.EnablePGPainlessBackend)) {
|
||||
DecryptActivityV2::class.java
|
||||
} else {
|
||||
DecryptActivity::class.java
|
||||
|
@ -458,7 +460,8 @@ class PasswordStore : BaseGitActivity() {
|
|||
val currentDir = currentDir
|
||||
logcat(INFO) { "Adding file to : ${currentDir.absolutePath}" }
|
||||
val creationActivity =
|
||||
if (FeatureFlags.ENABLE_PGP_V2_BACKEND) PasswordCreationActivityV2::class.java
|
||||
if (features.isEnabled(Feature.EnablePGPainlessBackend))
|
||||
PasswordCreationActivityV2::class.java
|
||||
else PasswordCreationActivity::class.java
|
||||
val intent = Intent(this, creationActivity)
|
||||
intent.putExtra(BasePgpActivity.EXTRA_FILE_PATH, currentDir.absolutePath)
|
||||
|
|
|
@ -7,15 +7,21 @@ package dev.msfjarvis.aps.ui.settings
|
|||
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import de.Maxr1998.modernpreferences.PreferenceScreen
|
||||
import de.Maxr1998.modernpreferences.helpers.checkBox
|
||||
import de.Maxr1998.modernpreferences.helpers.onClick
|
||||
import de.Maxr1998.modernpreferences.helpers.pref
|
||||
import dev.msfjarvis.aps.ui.pgp.PGPKeyImportActivity
|
||||
import dev.msfjarvis.aps.util.extensions.launchActivity
|
||||
import dev.msfjarvis.aps.util.features.Feature
|
||||
|
||||
class PGPSettings(private val activity: FragmentActivity) : SettingsProvider {
|
||||
|
||||
override fun provideSettings(builder: PreferenceScreen.Builder) {
|
||||
builder.apply {
|
||||
checkBox(Feature.EnablePGPainlessBackend.configKey) {
|
||||
title = "Enable new PGP backend"
|
||||
persistent = true
|
||||
}
|
||||
pref("_") {
|
||||
title = "Import PGP key"
|
||||
persistent = false
|
||||
|
|
|
@ -19,13 +19,17 @@ import com.github.androidpasswordstore.autofillparser.AutofillAction
|
|||
import com.github.androidpasswordstore.autofillparser.FillableForm
|
||||
import com.github.androidpasswordstore.autofillparser.fillWith
|
||||
import com.github.michaelbull.result.fold
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import dev.msfjarvis.aps.autofill.oreo.ui.AutofillSmsActivity
|
||||
import dev.msfjarvis.aps.ui.autofill.AutofillDecryptActivity
|
||||
import dev.msfjarvis.aps.ui.autofill.AutofillDecryptActivityV2
|
||||
import dev.msfjarvis.aps.ui.autofill.AutofillFilterView
|
||||
import dev.msfjarvis.aps.ui.autofill.AutofillPublisherChangedActivity
|
||||
import dev.msfjarvis.aps.ui.autofill.AutofillSaveActivity
|
||||
import dev.msfjarvis.aps.util.FeatureFlags
|
||||
import dev.msfjarvis.aps.util.features.Feature
|
||||
import dev.msfjarvis.aps.util.features.Features
|
||||
import java.io.File
|
||||
import logcat.LogPriority.ERROR
|
||||
import logcat.asLog
|
||||
|
@ -33,7 +37,17 @@ import logcat.logcat
|
|||
|
||||
/** Implements [AutofillResponseBuilder]'s methods for API 30 and above */
|
||||
@RequiresApi(Build.VERSION_CODES.R)
|
||||
class Api30AutofillResponseBuilder(form: FillableForm) {
|
||||
class Api30AutofillResponseBuilder
|
||||
@AssistedInject
|
||||
constructor(
|
||||
@Assisted form: FillableForm,
|
||||
private val features: Features,
|
||||
) {
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(form: FillableForm): Api30AutofillResponseBuilder
|
||||
}
|
||||
|
||||
private val formOrigin = form.formOrigin
|
||||
private val scenario = form.scenario
|
||||
|
@ -73,7 +87,7 @@ class Api30AutofillResponseBuilder(form: FillableForm) {
|
|||
if (!scenario.hasFieldsToFillOn(AutofillAction.Match)) return null
|
||||
val metadata = makeFillMatchMetadata(context, file)
|
||||
val intentSender =
|
||||
if (FeatureFlags.ENABLE_PGP_V2_BACKEND) {
|
||||
if (features.isEnabled(Feature.EnablePGPainlessBackend)) {
|
||||
AutofillDecryptActivityV2.makeDecryptFileIntentSender(file, context)
|
||||
} else {
|
||||
AutofillDecryptActivity.makeDecryptFileIntentSender(file, context)
|
||||
|
|
|
@ -19,20 +19,34 @@ import com.github.androidpasswordstore.autofillparser.Credentials
|
|||
import com.github.androidpasswordstore.autofillparser.FillableForm
|
||||
import com.github.androidpasswordstore.autofillparser.fillWith
|
||||
import com.github.michaelbull.result.fold
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import dev.msfjarvis.aps.autofill.oreo.ui.AutofillSmsActivity
|
||||
import dev.msfjarvis.aps.ui.autofill.AutofillDecryptActivity
|
||||
import dev.msfjarvis.aps.ui.autofill.AutofillDecryptActivityV2
|
||||
import dev.msfjarvis.aps.ui.autofill.AutofillFilterView
|
||||
import dev.msfjarvis.aps.ui.autofill.AutofillPublisherChangedActivity
|
||||
import dev.msfjarvis.aps.ui.autofill.AutofillSaveActivity
|
||||
import dev.msfjarvis.aps.util.FeatureFlags
|
||||
import dev.msfjarvis.aps.util.features.Feature
|
||||
import dev.msfjarvis.aps.util.features.Features
|
||||
import java.io.File
|
||||
import logcat.LogPriority.ERROR
|
||||
import logcat.asLog
|
||||
import logcat.logcat
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
class AutofillResponseBuilder(form: FillableForm) {
|
||||
class AutofillResponseBuilder
|
||||
@AssistedInject
|
||||
constructor(
|
||||
@Assisted form: FillableForm,
|
||||
private val features: Features,
|
||||
) {
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(form: FillableForm): AutofillResponseBuilder
|
||||
}
|
||||
|
||||
private val formOrigin = form.formOrigin
|
||||
private val scenario = form.scenario
|
||||
|
@ -61,7 +75,7 @@ class AutofillResponseBuilder(form: FillableForm) {
|
|||
if (!scenario.hasFieldsToFillOn(AutofillAction.Match)) return null
|
||||
val metadata = makeFillMatchMetadata(context, file)
|
||||
val intentSender =
|
||||
if (FeatureFlags.ENABLE_PGP_V2_BACKEND) {
|
||||
if (features.isEnabled(Feature.EnablePGPainlessBackend)) {
|
||||
AutofillDecryptActivityV2.makeDecryptFileIntentSender(file, context)
|
||||
} else {
|
||||
AutofillDecryptActivity.makeDecryptFileIntentSender(file, context)
|
||||
|
|
18
app/src/main/java/dev/msfjarvis/aps/util/features/Feature.kt
Normal file
18
app/src/main/java/dev/msfjarvis/aps/util/features/Feature.kt
Normal file
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright © 2014-2022 The Android Password Store Authors. All Rights Reserved.
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*/
|
||||
|
||||
package dev.msfjarvis.aps.util.features
|
||||
|
||||
/** List of all feature flags for the app. */
|
||||
enum class Feature(
|
||||
/** Default value for the flag. */
|
||||
val defaultValue: Boolean,
|
||||
/** Key to retrieve the current value for the flag. */
|
||||
val configKey: String,
|
||||
) {
|
||||
|
||||
/** Opt into the new PGP backend powered by the PGPainless library. */
|
||||
EnablePGPainlessBackend(false, "enable_pgp_v2_backend"),
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright © 2014-2022 The Android Password Store Authors. All Rights Reserved.
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*/
|
||||
|
||||
package dev.msfjarvis.aps.util.features
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import dev.msfjarvis.aps.injection.prefs.SettingsPreferences
|
||||
import javax.inject.Inject
|
||||
|
||||
class Features
|
||||
@Inject
|
||||
constructor(
|
||||
@SettingsPreferences private val preferences: SharedPreferences,
|
||||
) {
|
||||
|
||||
fun isEnabled(feature: Feature): Boolean {
|
||||
return preferences.getBoolean(feature.configKey, feature.defaultValue)
|
||||
}
|
||||
}
|
|
@ -23,6 +23,7 @@ import com.github.androidpasswordstore.autofillparser.cachePublicSuffixList
|
|||
import com.github.androidpasswordstore.autofillparser.passwordValue
|
||||
import com.github.androidpasswordstore.autofillparser.recoverNodes
|
||||
import com.github.androidpasswordstore.autofillparser.usernameValue
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import dev.msfjarvis.aps.BuildConfig
|
||||
import dev.msfjarvis.aps.R
|
||||
import dev.msfjarvis.aps.ui.autofill.AutofillSaveActivity
|
||||
|
@ -32,10 +33,12 @@ import dev.msfjarvis.aps.util.extensions.getString
|
|||
import dev.msfjarvis.aps.util.extensions.hasFlag
|
||||
import dev.msfjarvis.aps.util.extensions.sharedPrefs
|
||||
import dev.msfjarvis.aps.util.settings.PreferenceKeys
|
||||
import javax.inject.Inject
|
||||
import logcat.LogPriority.ERROR
|
||||
import logcat.logcat
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@AndroidEntryPoint
|
||||
class OreoAutofillService : AutofillService() {
|
||||
|
||||
companion object {
|
||||
|
@ -55,6 +58,9 @@ class OreoAutofillService : AutofillService() {
|
|||
private const val DISABLE_AUTOFILL_DURATION_MS = 1000 * 60 * 60 * 24L
|
||||
}
|
||||
|
||||
@Inject lateinit var api30ResponseBuilderFactory: Api30AutofillResponseBuilder.Factory
|
||||
@Inject lateinit var responseBuilderFactory: AutofillResponseBuilder.Factory
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
cachePublicSuffixList(applicationContext)
|
||||
|
@ -97,10 +103,11 @@ class OreoAutofillService : AutofillService() {
|
|||
return
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
Api30AutofillResponseBuilder(formToFill)
|
||||
api30ResponseBuilderFactory
|
||||
.create(formToFill)
|
||||
.fillCredentials(this, request.inlineSuggestionsRequest, callback)
|
||||
} else {
|
||||
AutofillResponseBuilder(formToFill).fillCredentials(this, callback)
|
||||
responseBuilderFactory.create(formToFill).fillCredentials(this, callback)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue