mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
045049243c | ||
|
|
b9813a3494 | ||
|
|
9b42a93ce1 | ||
|
|
8502bceef1 | ||
|
|
663387476f | ||
|
|
daafd83df9 | ||
|
|
f780f2725b | ||
|
|
483aca871a | ||
|
|
352e709c3b | ||
|
|
629057b2c1 | ||
|
|
0e5f53596d |
@@ -1,3 +1,7 @@
|
||||
KeePassDX(3.0.4)
|
||||
* Fix autofill inline bugs #1173 #1165
|
||||
* Small UI change
|
||||
|
||||
KeePassDX(3.0.3)
|
||||
* Change default Argon2 parameters #1098
|
||||
* Add & edit custom icon name #976
|
||||
|
||||
@@ -11,8 +11,8 @@ android {
|
||||
applicationId "com.kunzisoft.keepass"
|
||||
minSdkVersion 15
|
||||
targetSdkVersion 30
|
||||
versionCode = 90
|
||||
versionName = "3.0.3"
|
||||
versionCode = 91
|
||||
versionName = "3.0.4"
|
||||
multiDexEnabled true
|
||||
|
||||
testApplicationId = "com.kunzisoft.keepass.tests"
|
||||
|
||||
@@ -24,7 +24,7 @@ import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.view.inputmethod.InlineSuggestionsRequest
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.annotation.RequiresApi
|
||||
@@ -32,8 +32,9 @@ import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper
|
||||
import com.kunzisoft.keepass.activities.helpers.SpecialMode
|
||||
import com.kunzisoft.keepass.activities.legacy.DatabaseModeActivity
|
||||
import com.kunzisoft.keepass.autofill.AutofillComponent
|
||||
import com.kunzisoft.keepass.autofill.AutofillHelper
|
||||
import com.kunzisoft.keepass.autofill.AutofillHelper.EXTRA_INLINE_SUGGESTIONS_REQUEST
|
||||
import com.kunzisoft.keepass.autofill.CompatInlineSuggestionsRequest
|
||||
import com.kunzisoft.keepass.autofill.KeeAutofillService
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.database.search.SearchHelper
|
||||
@@ -64,18 +65,38 @@ class AutofillLauncherActivity : DatabaseModeActivity() {
|
||||
EntrySelectionHelper.retrieveSpecialModeFromIntent(intent).let { specialMode ->
|
||||
when (specialMode) {
|
||||
SpecialMode.SELECTION -> {
|
||||
intent.getBundleExtra(KEY_SELECTION_BUNDLE)?.let { bundle ->
|
||||
// To pass extra inline request
|
||||
var compatInlineSuggestionsRequest: CompatInlineSuggestionsRequest? = null
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
compatInlineSuggestionsRequest = bundle.getParcelable(KEY_INLINE_SUGGESTION)
|
||||
}
|
||||
// Build search param
|
||||
val searchInfo = SearchInfo().apply {
|
||||
applicationId = intent.getStringExtra(KEY_SEARCH_APPLICATION_ID)
|
||||
webDomain = intent.getStringExtra(KEY_SEARCH_DOMAIN)
|
||||
webScheme = intent.getStringExtra(KEY_SEARCH_SCHEME)
|
||||
manualSelection = intent.getBooleanExtra(KEY_MANUAL_SELECTION, false)
|
||||
bundle.getParcelable<SearchInfo>(KEY_SEARCH_INFO)?.let { searchInfo ->
|
||||
SearchInfo.getConcreteWebDomain(
|
||||
this,
|
||||
searchInfo.webDomain
|
||||
) { concreteWebDomain ->
|
||||
// Pass extra for Autofill (EXTRA_ASSIST_STRUCTURE)
|
||||
val assistStructure = AutofillHelper
|
||||
.retrieveAutofillComponent(intent)
|
||||
?.assistStructure
|
||||
val newAutofillComponent = if (assistStructure != null) {
|
||||
AutofillComponent(
|
||||
assistStructure,
|
||||
compatInlineSuggestionsRequest
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
SearchInfo.getConcreteWebDomain(this, searchInfo.webDomain) { concreteWebDomain ->
|
||||
searchInfo.webDomain = concreteWebDomain
|
||||
launchSelection(database, searchInfo)
|
||||
launchSelection(database, newAutofillComponent, searchInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Remove bundle
|
||||
intent.removeExtra(KEY_SELECTION_BUNDLE)
|
||||
}
|
||||
SpecialMode.REGISTRATION -> {
|
||||
// To register info
|
||||
val registerInfo = intent.getParcelableExtra<RegisterInfo>(KEY_REGISTER_INFO)
|
||||
@@ -95,10 +116,8 @@ class AutofillLauncherActivity : DatabaseModeActivity() {
|
||||
}
|
||||
|
||||
private fun launchSelection(database: Database?,
|
||||
autofillComponent: AutofillComponent?,
|
||||
searchInfo: SearchInfo) {
|
||||
// Pass extra for Autofill (EXTRA_ASSIST_STRUCTURE)
|
||||
val autofillComponent = AutofillHelper.retrieveAutofillComponent(intent)
|
||||
|
||||
if (autofillComponent == null) {
|
||||
setResult(Activity.RESULT_CANCELED)
|
||||
finish()
|
||||
@@ -194,30 +213,25 @@ class AutofillLauncherActivity : DatabaseModeActivity() {
|
||||
|
||||
companion object {
|
||||
|
||||
private const val KEY_MANUAL_SELECTION = "KEY_MANUAL_SELECTION"
|
||||
private const val KEY_SEARCH_APPLICATION_ID = "KEY_SEARCH_APPLICATION_ID"
|
||||
private const val KEY_SEARCH_DOMAIN = "KEY_SEARCH_DOMAIN"
|
||||
private const val KEY_SEARCH_SCHEME = "KEY_SEARCH_SCHEME"
|
||||
private const val KEY_SELECTION_BUNDLE = "KEY_SELECTION_BUNDLE"
|
||||
private const val KEY_SEARCH_INFO = "KEY_SEARCH_INFO"
|
||||
private const val KEY_INLINE_SUGGESTION = "KEY_INLINE_SUGGESTION"
|
||||
|
||||
private const val KEY_REGISTER_INFO = "KEY_REGISTER_INFO"
|
||||
|
||||
fun getPendingIntentForSelection(context: Context,
|
||||
searchInfo: SearchInfo? = null,
|
||||
inlineSuggestionsRequest: InlineSuggestionsRequest? = null): PendingIntent {
|
||||
compatInlineSuggestionsRequest: CompatInlineSuggestionsRequest? = null): PendingIntent {
|
||||
return PendingIntent.getActivity(context, 0,
|
||||
// Doesn't work with Parcelable (don't know why?)
|
||||
// Doesn't work with direct extra Parcelable (don't know why?)
|
||||
// Wrap into a bundle to bypass the problem
|
||||
Intent(context, AutofillLauncherActivity::class.java).apply {
|
||||
searchInfo?.let {
|
||||
putExtra(KEY_SEARCH_APPLICATION_ID, it.applicationId)
|
||||
putExtra(KEY_SEARCH_DOMAIN, it.webDomain)
|
||||
putExtra(KEY_SEARCH_SCHEME, it.webScheme)
|
||||
putExtra(KEY_MANUAL_SELECTION, it.manualSelection)
|
||||
}
|
||||
putExtra(KEY_SELECTION_BUNDLE, Bundle().apply {
|
||||
putParcelable(KEY_SEARCH_INFO, searchInfo)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
inlineSuggestionsRequest?.let {
|
||||
putExtra(EXTRA_INLINE_SUGGESTIONS_REQUEST, it)
|
||||
}
|
||||
putParcelable(KEY_INLINE_SUGGESTION, compatInlineSuggestionsRequest)
|
||||
}
|
||||
})
|
||||
},
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
// TODO Mutable
|
||||
|
||||
@@ -335,6 +335,29 @@ class GroupActivity : DatabaseLockActivity(),
|
||||
return rootContainerView
|
||||
}
|
||||
|
||||
private fun loadGroup(database: Database?) {
|
||||
when {
|
||||
Intent.ACTION_SEARCH == intent.action -> {
|
||||
finishNodeAction()
|
||||
val searchString =
|
||||
intent.getStringExtra(SearchManager.QUERY)?.trim { it <= ' ' } ?: ""
|
||||
mGroupViewModel.loadGroupFromSearch(
|
||||
database,
|
||||
searchString,
|
||||
PreferencesUtil.omitBackup(this)
|
||||
)
|
||||
}
|
||||
mCurrentGroupState == null -> {
|
||||
mRootGroup?.let { rootGroup ->
|
||||
mGroupViewModel.loadGroup(database, rootGroup, 0)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
mGroupViewModel.loadGroup(database, mCurrentGroupState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDatabaseRetrieved(database: Database?) {
|
||||
super.onDatabaseRetrieved(database)
|
||||
|
||||
@@ -344,13 +367,7 @@ class GroupActivity : DatabaseLockActivity(),
|
||||
&& database?.isRecycleBinEnabled == true
|
||||
|
||||
mRootGroup = database?.rootGroup
|
||||
if (mCurrentGroupState == null) {
|
||||
mRootGroup?.let { rootGroup ->
|
||||
mGroupViewModel.loadGroup(database, rootGroup, 0)
|
||||
}
|
||||
} else {
|
||||
mGroupViewModel.loadGroup(database, mCurrentGroupState)
|
||||
}
|
||||
loadGroup(database)
|
||||
|
||||
// Search suggestion
|
||||
database?.let {
|
||||
@@ -463,16 +480,7 @@ class GroupActivity : DatabaseLockActivity(),
|
||||
}
|
||||
// To transform KEY_SEARCH_INFO in ACTION_SEARCH
|
||||
transformSearchInfoIntent(intent)
|
||||
if (Intent.ACTION_SEARCH == intent.action) {
|
||||
finishNodeAction()
|
||||
val searchString =
|
||||
intent.getStringExtra(SearchManager.QUERY)?.trim { it <= ' ' } ?: ""
|
||||
mGroupViewModel.loadGroupFromSearch(
|
||||
mDatabase,
|
||||
searchString,
|
||||
PreferencesUtil.omitBackup(this)
|
||||
)
|
||||
}
|
||||
loadGroup(mDatabase)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1008,7 +1016,7 @@ class GroupActivity : DatabaseLockActivity(),
|
||||
|
||||
if (!sortMenuEducationPerformed) {
|
||||
// lockMenuEducationPerformed
|
||||
val lockButtonView = findViewById<View>(R.id.lock_button_icon)
|
||||
val lockButtonView = findViewById<View>(R.id.lock_button)
|
||||
lockButtonView != null
|
||||
&& groupActivityEducation.checkAndPerformedLockMenuEducation(
|
||||
lockButtonView,
|
||||
@@ -1081,22 +1089,6 @@ class GroupActivity : DatabaseLockActivity(),
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
override fun startActivityForResult(intent: Intent, requestCode: Int, options: Bundle?) {
|
||||
/*
|
||||
* ACTION_SEARCH automatically forces a new task. This occurs when you open a kdb file in
|
||||
* another app such as Files or GoogleDrive and then Search for an entry. Here we remove the
|
||||
* FLAG_ACTIVITY_NEW_TASK flag bit allowing search to open it's activity in the current task.
|
||||
*/
|
||||
if (Intent.ACTION_SEARCH == intent.action) {
|
||||
var flags = intent.flags
|
||||
flags = flags and Intent.FLAG_ACTIVITY_NEW_TASK.inv()
|
||||
intent.flags = flags
|
||||
}
|
||||
|
||||
super.startActivityForResult(intent, requestCode, options)
|
||||
}
|
||||
|
||||
private fun removeSearch() {
|
||||
intent.removeExtra(AUTO_SEARCH_KEY)
|
||||
if (Intent.ACTION_SEARCH == intent.action) {
|
||||
|
||||
@@ -4,4 +4,4 @@ import android.app.assist.AssistStructure
|
||||
import android.view.inputmethod.InlineSuggestionsRequest
|
||||
|
||||
data class AutofillComponent(val assistStructure: AssistStructure,
|
||||
val inlineSuggestionsRequest: InlineSuggestionsRequest?)
|
||||
val compatInlineSuggestionsRequest: CompatInlineSuggestionsRequest?)
|
||||
@@ -34,7 +34,6 @@ import android.service.autofill.InlinePresentation
|
||||
import android.util.Log
|
||||
import android.view.autofill.AutofillManager
|
||||
import android.view.autofill.AutofillValue
|
||||
import android.view.inputmethod.InlineSuggestionsRequest
|
||||
import android.widget.RemoteViews
|
||||
import android.widget.Toast
|
||||
import android.widget.inline.InlinePresentationSpec
|
||||
@@ -63,7 +62,7 @@ import com.kunzisoft.keepass.utils.LOCK_ACTION
|
||||
object AutofillHelper {
|
||||
|
||||
private const val EXTRA_ASSIST_STRUCTURE = AutofillManager.EXTRA_ASSIST_STRUCTURE
|
||||
const val EXTRA_INLINE_SUGGESTIONS_REQUEST = "com.kunzisoft.keepass.autofill.INLINE_SUGGESTIONS_REQUEST"
|
||||
private const val EXTRA_INLINE_SUGGESTIONS_REQUEST = "com.kunzisoft.keepass.autofill.INLINE_SUGGESTIONS_REQUEST"
|
||||
|
||||
fun retrieveAutofillComponent(intent: Intent?): AutofillComponent? {
|
||||
intent?.getParcelableExtra<AssistStructure?>(EXTRA_ASSIST_STRUCTURE)?.let { assistStructure ->
|
||||
@@ -112,7 +111,7 @@ object AutofillHelper {
|
||||
database: Database,
|
||||
entryInfo: EntryInfo,
|
||||
struct: StructureParser.Result,
|
||||
inlinePresentation: InlinePresentation?): Dataset? {
|
||||
additionalBuild: ((build: Dataset.Builder) -> Unit)? = null): Dataset? {
|
||||
val title = makeEntryTitle(entryInfo)
|
||||
val views = newRemoteViews(context, database, title, entryInfo.icon)
|
||||
val builder = Dataset.Builder(views)
|
||||
@@ -201,11 +200,7 @@ object AutofillHelper {
|
||||
}
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
inlinePresentation?.let {
|
||||
builder.setInlinePresentation(it)
|
||||
}
|
||||
}
|
||||
additionalBuild?.invoke(builder)
|
||||
|
||||
return try {
|
||||
builder.build()
|
||||
@@ -236,14 +231,16 @@ object AutofillHelper {
|
||||
@SuppressLint("RestrictedApi")
|
||||
private fun buildInlinePresentationForEntry(context: Context,
|
||||
database: Database,
|
||||
inlineSuggestionsRequest: InlineSuggestionsRequest,
|
||||
compatInlineSuggestionsRequest: CompatInlineSuggestionsRequest,
|
||||
positionItem: Int,
|
||||
entryInfo: EntryInfo): InlinePresentation? {
|
||||
compatInlineSuggestionsRequest.inlineSuggestionsRequest?.let { inlineSuggestionsRequest ->
|
||||
val inlinePresentationSpecs = inlineSuggestionsRequest.inlinePresentationSpecs
|
||||
val maxSuggestion = inlineSuggestionsRequest.maxSuggestionCount
|
||||
|
||||
if (positionItem <= maxSuggestion - 1
|
||||
&& inlinePresentationSpecs.size > positionItem) {
|
||||
&& inlinePresentationSpecs.size > positionItem
|
||||
) {
|
||||
val inlinePresentationSpec = inlinePresentationSpecs[positionItem]
|
||||
|
||||
// Make sure that the IME spec claims support for v1 UI template.
|
||||
@@ -252,20 +249,23 @@ object AutofillHelper {
|
||||
return null
|
||||
|
||||
// Build the content for IME UI
|
||||
val pendingIntent = PendingIntent.getActivity(context,
|
||||
val pendingIntent = PendingIntent.getActivity(
|
||||
context,
|
||||
0,
|
||||
Intent(context, AutofillSettingsActivity::class.java),
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
PendingIntent.FLAG_IMMUTABLE
|
||||
} else {
|
||||
0
|
||||
})
|
||||
}
|
||||
)
|
||||
return InlinePresentation(
|
||||
InlineSuggestionUi.newContentBuilder(pendingIntent).apply {
|
||||
setContentDescription(context.getString(R.string.autofill_sign_in_prompt))
|
||||
setTitle(entryInfo.title)
|
||||
setSubtitle(entryInfo.username)
|
||||
setStartIcon(Icon.createWithResource(context, R.mipmap.ic_launcher_round).apply {
|
||||
setStartIcon(
|
||||
Icon.createWithResource(context, R.mipmap.ic_launcher_round).apply {
|
||||
setTintBlendMode(BlendMode.DST)
|
||||
})
|
||||
buildIconFromEntry(context, database, entryInfo)?.let { icon ->
|
||||
@@ -273,7 +273,9 @@ object AutofillHelper {
|
||||
setTintBlendMode(BlendMode.DST)
|
||||
})
|
||||
}
|
||||
}.build().slice, inlinePresentationSpec, false)
|
||||
}.build().slice, inlinePresentationSpec, false
|
||||
)
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
@@ -303,7 +305,7 @@ object AutofillHelper {
|
||||
database: Database,
|
||||
entriesInfo: List<EntryInfo>,
|
||||
parseResult: StructureParser.Result,
|
||||
inlineSuggestionsRequest: InlineSuggestionsRequest?): FillResponse? {
|
||||
compatInlineSuggestionsRequest: CompatInlineSuggestionsRequest?): FillResponse? {
|
||||
val responseBuilder = FillResponse.Builder()
|
||||
// Add Header
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
@@ -324,7 +326,7 @@ object AutofillHelper {
|
||||
// Add inline suggestion for new IME and dataset
|
||||
var numberInlineSuggestions = 0
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
inlineSuggestionsRequest?.let {
|
||||
compatInlineSuggestionsRequest?.inlineSuggestionsRequest?.let { inlineSuggestionsRequest ->
|
||||
numberInlineSuggestions = minOf(inlineSuggestionsRequest.maxSuggestionCount, entriesInfo.size)
|
||||
if (PreferencesUtil.isAutofillManualSelectionEnable(context)) {
|
||||
if (entriesInfo.size >= inlineSuggestionsRequest.maxSuggestionCount) {
|
||||
@@ -336,14 +338,19 @@ object AutofillHelper {
|
||||
}
|
||||
|
||||
entriesInfo.forEachIndexed { _, entry ->
|
||||
val inlinePresentation = if (numberInlineSuggestions > 0 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
inlineSuggestionsRequest?.let {
|
||||
buildInlinePresentationForEntry(context, database, inlineSuggestionsRequest, numberInlineSuggestions--, entry)
|
||||
if (numberInlineSuggestions > 0
|
||||
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.R
|
||||
&& compatInlineSuggestionsRequest != null) {
|
||||
responseBuilder.addDataset(buildDataset(context, database, entry, parseResult) { builder ->
|
||||
buildInlinePresentationForEntry(context, database,
|
||||
compatInlineSuggestionsRequest, numberInlineSuggestions--, entry
|
||||
)?.let { inlinePresentation ->
|
||||
builder.setInlinePresentation(inlinePresentation)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
null
|
||||
responseBuilder.addDataset(buildDataset(context, database, entry, parseResult))
|
||||
}
|
||||
responseBuilder.addDataset(buildDataset(context, database, entry, parseResult, inlinePresentation))
|
||||
}
|
||||
|
||||
if (PreferencesUtil.isAutofillManualSelectionEnable(context)) {
|
||||
@@ -355,14 +362,14 @@ object AutofillHelper {
|
||||
}
|
||||
val manualSelectionView = RemoteViews(context.packageName, R.layout.item_autofill_select_entry)
|
||||
val pendingIntent = AutofillLauncherActivity.getPendingIntentForSelection(context,
|
||||
searchInfo, inlineSuggestionsRequest)
|
||||
searchInfo, compatInlineSuggestionsRequest)
|
||||
|
||||
parseResult.allAutofillIds().let { autofillIds ->
|
||||
autofillIds.forEach { id ->
|
||||
val builder = Dataset.Builder(manualSelectionView)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
inlineSuggestionsRequest?.let {
|
||||
compatInlineSuggestionsRequest?.inlineSuggestionsRequest?.let { inlineSuggestionsRequest ->
|
||||
val inlinePresentationSpec = inlineSuggestionsRequest.inlinePresentationSpecs[0]
|
||||
val inlinePresentation = buildInlinePresentationForManualSelection(context, inlinePresentationSpec, pendingIntent)
|
||||
inlinePresentation?.let {
|
||||
@@ -407,11 +414,11 @@ object AutofillHelper {
|
||||
StructureParser(structure).parse()?.let { result ->
|
||||
// New Response
|
||||
val response = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
val inlineSuggestionsRequest = activity.intent?.getParcelableExtra<InlineSuggestionsRequest?>(EXTRA_INLINE_SUGGESTIONS_REQUEST)
|
||||
if (inlineSuggestionsRequest != null) {
|
||||
val compatInlineSuggestionsRequest = activity.intent?.getParcelableExtra<CompatInlineSuggestionsRequest?>(EXTRA_INLINE_SUGGESTIONS_REQUEST)
|
||||
if (compatInlineSuggestionsRequest != null) {
|
||||
Toast.makeText(activity.applicationContext, R.string.autofill_inline_suggestions_keyboard, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
buildResponse(activity, database, entriesInfo, result, inlineSuggestionsRequest)
|
||||
buildResponse(activity, database, entriesInfo, result, compatInlineSuggestionsRequest)
|
||||
} else {
|
||||
buildResponse(activity, database, entriesInfo, result, null)
|
||||
}
|
||||
@@ -464,7 +471,7 @@ object AutofillHelper {
|
||||
intent.putExtra(EXTRA_ASSIST_STRUCTURE, autofillComponent.assistStructure)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R
|
||||
&& PreferencesUtil.isAutofillInlineSuggestionsEnable(activity)) {
|
||||
autofillComponent.inlineSuggestionsRequest?.let {
|
||||
autofillComponent.compatInlineSuggestionsRequest?.let {
|
||||
intent.putExtra(EXTRA_INLINE_SUGGESTIONS_REQUEST, it)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2021 Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePassDX.
|
||||
*
|
||||
* KeePassDX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePassDX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.kunzisoft.keepass.autofill
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.os.Build
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import android.service.autofill.FillRequest
|
||||
import android.view.inputmethod.InlineSuggestionsRequest
|
||||
import androidx.annotation.RequiresApi
|
||||
|
||||
/**
|
||||
* Utility class only to prevent java.lang.NoClassDefFoundError for old Android version and new lib compilation
|
||||
*/
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
class CompatInlineSuggestionsRequest : Parcelable {
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.R)
|
||||
var inlineSuggestionsRequest: InlineSuggestionsRequest? = null
|
||||
private set
|
||||
|
||||
constructor(fillRequest: FillRequest) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
this.inlineSuggestionsRequest = fillRequest.inlineSuggestionsRequest
|
||||
} else {
|
||||
this.inlineSuggestionsRequest = null
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.R)
|
||||
constructor(inlineSuggestionsRequest: InlineSuggestionsRequest?) {
|
||||
this.inlineSuggestionsRequest = inlineSuggestionsRequest
|
||||
}
|
||||
|
||||
constructor(parcel: Parcel) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
this.inlineSuggestionsRequest =
|
||||
parcel.readParcelable(FillRequest::class.java.classLoader)
|
||||
}
|
||||
else {
|
||||
this.inlineSuggestionsRequest = null
|
||||
}
|
||||
}
|
||||
|
||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
parcel.writeParcelable(inlineSuggestionsRequest, flags)
|
||||
}
|
||||
}
|
||||
|
||||
override fun describeContents(): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
companion object CREATOR : Parcelable.Creator<CompatInlineSuggestionsRequest> {
|
||||
override fun createFromParcel(parcel: Parcel): CompatInlineSuggestionsRequest {
|
||||
return CompatInlineSuggestionsRequest(parcel)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<CompatInlineSuggestionsRequest?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -109,7 +109,7 @@ class KeeAutofillService : AutofillService() {
|
||||
searchInfo.webDomain = webDomainWithoutSubDomain
|
||||
val inlineSuggestionsRequest = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R
|
||||
&& autofillInlineSuggestionsEnabled) {
|
||||
request.inlineSuggestionsRequest
|
||||
CompatInlineSuggestionsRequest(request)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
@@ -127,7 +127,7 @@ class KeeAutofillService : AutofillService() {
|
||||
private fun launchSelection(database: Database?,
|
||||
searchInfo: SearchInfo,
|
||||
parseResult: StructureParser.Result,
|
||||
inlineSuggestionsRequest: InlineSuggestionsRequest?,
|
||||
inlineSuggestionsRequest: CompatInlineSuggestionsRequest?,
|
||||
callback: FillCallback) {
|
||||
SearchHelper.checkAutoSearchInfo(this,
|
||||
database,
|
||||
@@ -155,7 +155,7 @@ class KeeAutofillService : AutofillService() {
|
||||
private fun showUIForEntrySelection(parseResult: StructureParser.Result,
|
||||
database: Database?,
|
||||
searchInfo: SearchInfo,
|
||||
inlineSuggestionsRequest: InlineSuggestionsRequest?,
|
||||
inlineSuggestionsRequest: CompatInlineSuggestionsRequest?,
|
||||
callback: FillCallback) {
|
||||
parseResult.allAutofillIds().let { autofillIds ->
|
||||
if (autofillIds.isNotEmpty()) {
|
||||
@@ -249,7 +249,7 @@ class KeeAutofillService : AutofillService() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R
|
||||
&& autofillInlineSuggestionsEnabled) {
|
||||
var inlinePresentation: InlinePresentation? = null
|
||||
inlineSuggestionsRequest?.let {
|
||||
inlineSuggestionsRequest?.inlineSuggestionsRequest?.let { inlineSuggestionsRequest ->
|
||||
val inlinePresentationSpecs = inlineSuggestionsRequest.inlinePresentationSpecs
|
||||
if (inlineSuggestionsRequest.maxSuggestionCount > 0
|
||||
&& inlinePresentationSpecs.size > 0) {
|
||||
@@ -281,8 +281,9 @@ class KeeAutofillService : AutofillService() {
|
||||
}
|
||||
// Build response
|
||||
responseBuilder.setAuthentication(autofillIds, intentSender, remoteViewsUnlock, inlinePresentation)
|
||||
}
|
||||
} else {
|
||||
responseBuilder.setAuthentication(autofillIds, intentSender, remoteViewsUnlock)
|
||||
}
|
||||
callback.onSuccess(responseBuilder.build())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ class GroupActivityEducation(activity: Activity)
|
||||
.outerCircleColorInt(getCircleColor())
|
||||
.outerCircleAlpha(getCircleAlpha())
|
||||
.textColorInt(getTextColor())
|
||||
.tintTarget(true)
|
||||
.tintTarget(false)
|
||||
.cancelable(true),
|
||||
object : TapTargetView.Listener() {
|
||||
override fun onTargetClick(view: TapTargetView) {
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="?attr/colorAccentLight" android:state_pressed="true" />
|
||||
<item android:color="@color/white_grey_darker" android:state_enabled="false" />
|
||||
<item android:color="?attr/colorAccent" android:state_enabled="true" />
|
||||
</selector>
|
||||
@@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:color="@color/white"
|
||||
tools:targetApi="lollipop">
|
||||
<item>
|
||||
<shape>
|
||||
<corners
|
||||
android:topLeftRadius="0dp"
|
||||
android:topRightRadius="40dp"
|
||||
android:bottomLeftRadius="0dp"
|
||||
android:bottomRightRadius="0dp"/>
|
||||
<solid android:color="?attr/colorAccent"/>
|
||||
</shape>
|
||||
</item>
|
||||
</ripple>
|
||||
@@ -1,20 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:color="@color/white"
|
||||
tools:targetApi="lollipop">
|
||||
<item>
|
||||
<shape>
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="?attr/textColorInverse" />
|
||||
<corners
|
||||
android:topLeftRadius="0dp"
|
||||
android:topRightRadius="40dp"
|
||||
android:bottomLeftRadius="0dp"
|
||||
android:bottomRightRadius="0dp"/>
|
||||
<solid
|
||||
android:color="@color/transparent"/>
|
||||
</shape>
|
||||
</item>
|
||||
</ripple>
|
||||
@@ -1,34 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:state_pressed="true">
|
||||
<shape>
|
||||
<corners
|
||||
android:topLeftRadius="0dp"
|
||||
android:topRightRadius="40dp"
|
||||
android:bottomLeftRadius="0dp"
|
||||
android:bottomRightRadius="0dp"/>
|
||||
<padding
|
||||
android:left="4dp"
|
||||
android:right="12dp"
|
||||
android:top="18dp"
|
||||
android:bottom="8dp"/>
|
||||
<solid android:color="@color/orange_light"/>
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<shape>
|
||||
<corners
|
||||
android:topLeftRadius="0dp"
|
||||
android:topRightRadius="40dp"
|
||||
android:bottomLeftRadius="0dp"
|
||||
android:bottomRightRadius="0dp"/>
|
||||
<padding
|
||||
android:left="4dp"
|
||||
android:right="12dp"
|
||||
android:top="18dp"
|
||||
android:bottom="8dp"/>
|
||||
<solid android:color="@color/orange"/>
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
||||
@@ -1,18 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/white">
|
||||
<item>
|
||||
<shape>
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/white_grey" />
|
||||
<corners
|
||||
android:topLeftRadius="0dp"
|
||||
android:topRightRadius="40dp"
|
||||
android:bottomLeftRadius="0dp"
|
||||
android:bottomRightRadius="0dp"/>
|
||||
<solid
|
||||
android:color="@color/transparent"/>
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
||||
15
app/src/main/res/drawable/ic_lock_white_padding_24dp.xml
Normal file
15
app/src/main/res/drawable/ic_lock_white_padding_24dp.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<group
|
||||
android:scaleX="0.8"
|
||||
android:scaleY="0.8"
|
||||
android:pivotX="12"
|
||||
android:pivotY="12">
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM12,17c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM15.1,8L8.9,8L8.9,6c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1v2z"/>
|
||||
</group>
|
||||
</vector>
|
||||
@@ -138,8 +138,8 @@
|
||||
|
||||
<include
|
||||
layout="@layout/view_button_lock"
|
||||
android:layout_width="@dimen/lock_button_size"
|
||||
android:layout_height="@dimen/lock_button_size"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start|bottom" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
@@ -100,8 +100,8 @@
|
||||
|
||||
<include
|
||||
layout="@layout/view_button_lock"
|
||||
android:layout_width="@dimen/lock_button_size"
|
||||
android:layout_height="@dimen/lock_button_size"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
|
||||
@@ -132,6 +132,7 @@
|
||||
android:orientation="vertical"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
android:layout_below="@+id/toolbar">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/nodes_list_fragment_container"
|
||||
android:layout_width="match_parent"
|
||||
@@ -168,8 +169,8 @@
|
||||
|
||||
<include
|
||||
layout="@layout/view_button_lock"
|
||||
android:layout_width="@dimen/lock_button_size"
|
||||
android:layout_height="@dimen/lock_button_size"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"/>
|
||||
|
||||
</RelativeLayout>
|
||||
@@ -59,8 +59,8 @@
|
||||
|
||||
<include
|
||||
layout="@layout/view_button_lock"
|
||||
android:layout_width="@dimen/lock_button_size"
|
||||
android:layout_height="@dimen/lock_button_size"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
@@ -42,8 +42,8 @@
|
||||
|
||||
<include
|
||||
layout="@layout/view_button_lock"
|
||||
android:layout_width="@dimen/lock_button_size"
|
||||
android:layout_height="@dimen/lock_button_size"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:layout_gravity="start|bottom" />
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:attr/windowBackground"
|
||||
android:minHeight="36dp"
|
||||
android:minHeight="48dp"
|
||||
android:orientation="horizontal">
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
|
||||
@@ -1,48 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<FrameLayout
|
||||
android:id="@+id/lock_button"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/lock_button_background"
|
||||
android:layout_width="@dimen/lock_button_size"
|
||||
android:layout_height="@dimen/lock_button_size"
|
||||
android:layout_marginStart="-2dp"
|
||||
android:layout_marginLeft="-2dp"
|
||||
android:layout_marginBottom="-2dp"
|
||||
tools:targetApi="lollipop"
|
||||
android:elevation="4dp"
|
||||
style="@style/KeepassDXStyle.Special.Button.Background"
|
||||
android:layout_gravity="bottom|start"
|
||||
android:contentDescription="@string/menu_lock" />
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/lock_button_stroke"
|
||||
android:layout_width="@dimen/lock_button_size"
|
||||
android:layout_height="@dimen/lock_button_size"
|
||||
android:layout_marginStart="-2dp"
|
||||
android:layout_marginLeft="-2dp"
|
||||
android:layout_marginBottom="-2dp"
|
||||
tools:targetApi="lollipop"
|
||||
android:elevation="4dp"
|
||||
style="@style/KeepassDXStyle.Special.Button.Stroke"
|
||||
android:layout_gravity="bottom|start"
|
||||
android:contentDescription="@string/menu_lock" />
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/lock_button_icon"
|
||||
android:layout_width="@dimen/lock_button_size"
|
||||
android:layout_height="@dimen/lock_button_size"
|
||||
android:paddingBottom="6dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingStart="4dp"
|
||||
android:paddingLeft="4dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:paddingRight="12dp"
|
||||
tools:targetApi="lollipop"
|
||||
android:elevation="4dp"
|
||||
android:src="@drawable/ic_lock_white_24dp"
|
||||
android:tint="@color/white"
|
||||
android:layout_gravity="bottom|start"
|
||||
android:contentDescription="@string/menu_lock" />
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/lock_button"
|
||||
style="@style/KeepassDXStyle.Fab.Special"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:fabSize="mini"
|
||||
android:layout_margin="8dp"
|
||||
android:contentDescription="@string/lock"
|
||||
android:src="@drawable/ic_lock_white_padding_24dp"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto" />
|
||||
</FrameLayout>
|
||||
@@ -443,21 +443,14 @@
|
||||
<item name="backgroundTint">?android:attr/windowBackground</item>
|
||||
</style>
|
||||
|
||||
<!-- Special button Style -->
|
||||
<style name="KeepassDXStyle.Special.Button.Background" parent="KeepassDXStyle.v21.Button">
|
||||
<item name="android:background">@drawable/btn_special_start_bottom_background</item>
|
||||
<item name="backgroundTint">@color/background_special_button_color</item>
|
||||
</style>
|
||||
<style name="KeepassDXStyle.Special.Button.Stroke" parent="KeepassDXStyle.v21.Button">
|
||||
<item name="android:background">@drawable/btn_special_start_bottom_stroke</item>
|
||||
<item name="backgroundTint">@color/white</item>
|
||||
<item name="backgroundTintMode">src_atop</item>
|
||||
</style>
|
||||
|
||||
<!-- FAB -->
|
||||
<style name="KeepassDXStyle.v21.Fab" parent="KeepassDXStyle.Light.v21" />
|
||||
<style name="KeepassDXStyle.Fab" parent="KeepassDXStyle.v21.Fab">
|
||||
</style>
|
||||
<style name="KeepassDXStyle.Fab.Special" parent="KeepassDXStyle.v21.Fab">
|
||||
<item name="backgroundTint">?attr/colorPrimary</item>
|
||||
<item name="tint">?attr/textColorInverse</item>
|
||||
</style>
|
||||
|
||||
<!-- Menu FAB -->
|
||||
<style name="KeepassDXStyle.v21.FabMenu" parent="KeepassDXStyle.TextAppearance.Default.TextOnPrimary">
|
||||
|
||||
2
fastlane/metadata/android/en-US/changelogs/91.txt
Normal file
2
fastlane/metadata/android/en-US/changelogs/91.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
* Fix autofill inline bugs #1173 #1165
|
||||
* Small UI change
|
||||
2
fastlane/metadata/android/fr-FR/changelogs/91.txt
Normal file
2
fastlane/metadata/android/fr-FR/changelogs/91.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
* Résolution de bogues du remplissage automatique uniligne #1173 #1165
|
||||
* Petit changement dans l'interface utilisateur
|
||||
Reference in New Issue
Block a user