Add auto search info

This commit is contained in:
J-Jamet
2020-04-06 17:36:51 +02:00
parent 3b3583a416
commit 0fb4d26949
10 changed files with 148 additions and 43 deletions

View File

@@ -48,9 +48,11 @@ import com.kunzisoft.keepass.activities.stylish.StylishActivity
import com.kunzisoft.keepass.adapters.FileDatabaseHistoryAdapter
import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction
import com.kunzisoft.keepass.autofill.AutofillHelper
import com.kunzisoft.keepass.autofill.AutofillHelper.KEY_SEARCH_INFO
import com.kunzisoft.keepass.database.action.ProgressDialogThread
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.education.FileDatabaseSelectActivityEducation
import com.kunzisoft.keepass.model.SearchInfo
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_CREATE_TASK
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.utils.*
@@ -217,7 +219,8 @@ class FileDatabaseSelectActivity : StylishActivity(),
try {
PasswordActivity.launchForAutofillResult(this@FileDatabaseSelectActivity,
databaseUri, keyFile,
assistStructure)
assistStructure,
intent.getParcelableExtra(KEY_SEARCH_INFO))
} catch (e: FileNotFoundException) {
fileNoFoundAction(e)
}
@@ -229,16 +232,21 @@ class FileDatabaseSelectActivity : StylishActivity(),
private fun launchGroupActivity(readOnly: Boolean) {
EntrySelectionHelper.doEntrySelectionAction(intent,
{
GroupActivity.launch(this@FileDatabaseSelectActivity, readOnly)
GroupActivity.launch(this@FileDatabaseSelectActivity,
readOnly)
},
{
GroupActivity.launchForKeyboardSelection(this@FileDatabaseSelectActivity, readOnly)
GroupActivity.launchForKeyboardSelection(this@FileDatabaseSelectActivity,
readOnly)
// Do not keep history
finish()
},
{ assistStructure ->
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
GroupActivity.launchForAutofillResult(this@FileDatabaseSelectActivity, assistStructure, readOnly)
GroupActivity.launchForAutofillResult(this@FileDatabaseSelectActivity,
assistStructure,
intent.getParcelableExtra(KEY_SEARCH_INFO),
readOnly)
}
})
}
@@ -459,10 +467,13 @@ class FileDatabaseSelectActivity : StylishActivity(),
*/
@RequiresApi(api = Build.VERSION_CODES.O)
fun launchForAutofillResult(activity: Activity, assistStructure: AssistStructure) {
fun launchForAutofillResult(activity: Activity,
assistStructure: AssistStructure,
searchInfo: SearchInfo?) {
AutofillHelper.startActivityForAutofillResult(activity,
Intent(activity, FileDatabaseSelectActivity::class.java),
assistStructure)
assistStructure,
searchInfo)
}
}
}

View File

@@ -62,6 +62,7 @@ import com.kunzisoft.keepass.database.element.node.Type
import com.kunzisoft.keepass.education.GroupActivityEducation
import com.kunzisoft.keepass.icons.assignDatabaseIcon
import com.kunzisoft.keepass.magikeyboard.MagikIME
import com.kunzisoft.keepass.model.SearchInfo
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_COPY_NODES_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_CREATE_GROUP_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_DELETE_NODES_TASK
@@ -353,7 +354,7 @@ class GroupActivity : LockingActivity(),
// If it's a search
if (Intent.ACTION_SEARCH == intent.action) {
val searchString = intent.getStringExtra(SearchManager.QUERY)?.trim { it <= ' ' } ?: ""
return mDatabase?.search(searchString)
return mDatabase?.createVirtualGroupWithSearchResult(searchString)
}
// else a real group
else {
@@ -964,27 +965,41 @@ class GroupActivity : LockingActivity(),
private const val SEARCH_FRAGMENT_TAG = "SEARCH_FRAGMENT_TAG"
private const val OLD_GROUP_TO_UPDATE_KEY = "OLD_GROUP_TO_UPDATE_KEY"
private fun buildIntent(context: Context, group: Group?, readOnly: Boolean,
private fun buildIntent(context: Context,
group: Group?,
searchInfo: SearchInfo?,
readOnly: Boolean,
intentBuildLauncher: (Intent) -> Unit) {
val intent = Intent(context, GroupActivity::class.java)
if (group != null) {
intent.putExtra(GROUP_ID_KEY, group.nodeId)
}
if (searchInfo != null) {
intent.action = Intent.ACTION_SEARCH
val searchQuery = searchInfo.webDomain ?: searchInfo.applicationId
intent.putExtra(SearchManager.QUERY, searchQuery)
}
ReadOnlyHelper.putReadOnlyInIntent(intent, readOnly)
intentBuildLauncher.invoke(intent)
}
private fun checkTimeAndBuildIntent(activity: Activity, group: Group?, readOnly: Boolean,
private fun checkTimeAndBuildIntent(activity: Activity,
group: Group?,
searchInfo: SearchInfo?,
readOnly: Boolean,
intentBuildLauncher: (Intent) -> Unit) {
if (TimeoutHelper.checkTimeAndLockIfTimeout(activity)) {
buildIntent(activity, group, readOnly, intentBuildLauncher)
buildIntent(activity, group, searchInfo, readOnly, intentBuildLauncher)
}
}
private fun checkTimeAndBuildIntent(context: Context, group: Group?, readOnly: Boolean,
private fun checkTimeAndBuildIntent(context: Context,
group: Group?,
searchInfo: SearchInfo?,
readOnly: Boolean,
intentBuildLauncher: (Intent) -> Unit) {
if (TimeoutHelper.checkTime(context)) {
buildIntent(context, group, readOnly, intentBuildLauncher)
buildIntent(context, group, searchInfo, readOnly, intentBuildLauncher)
}
}
@@ -993,11 +1008,9 @@ class GroupActivity : LockingActivity(),
* Standard Launch
* -------------------------
*/
@JvmOverloads
fun launch(context: Context,
readOnly: Boolean = PreferencesUtil.enableReadOnlyDatabase(context)) {
checkTimeAndBuildIntent(context, null, readOnly) { intent ->
checkTimeAndBuildIntent(context, null, null, readOnly) { intent ->
context.startActivity(intent)
}
}
@@ -1008,10 +1021,9 @@ class GroupActivity : LockingActivity(),
* -------------------------
*/
// TODO implement pre search to directly open the direct group
fun launchForKeyboardSelection(context: Context,
readOnly: Boolean = PreferencesUtil.enableReadOnlyDatabase(context)) {
checkTimeAndBuildIntent(context, null, readOnly) { intent ->
checkTimeAndBuildIntent(context, null, null, readOnly) { intent ->
EntrySelectionHelper.startActivityForEntrySelection(context, intent)
}
}
@@ -1021,14 +1033,13 @@ class GroupActivity : LockingActivity(),
* Autofill Launch
* -------------------------
*/
// TODO implement pre search to directly open the direct group
@RequiresApi(api = Build.VERSION_CODES.O)
fun launchForAutofillResult(activity: Activity,
assistStructure: AssistStructure,
searchInfo: SearchInfo?,
readOnly: Boolean = PreferencesUtil.enableReadOnlyDatabase(activity)) {
checkTimeAndBuildIntent(activity, null, readOnly) { intent ->
AutofillHelper.startActivityForAutofillResult(activity, intent, assistStructure)
checkTimeAndBuildIntent(activity, null, searchInfo, readOnly) { intent ->
AutofillHelper.startActivityForAutofillResult(activity, intent, assistStructure, searchInfo)
}
}
}

View File

@@ -49,11 +49,13 @@ import com.kunzisoft.keepass.activities.stylish.StylishActivity
import com.kunzisoft.keepass.app.database.CipherDatabaseEntity
import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction
import com.kunzisoft.keepass.autofill.AutofillHelper
import com.kunzisoft.keepass.autofill.AutofillHelper.KEY_SEARCH_INFO
import com.kunzisoft.keepass.biometric.AdvancedUnlockedManager
import com.kunzisoft.keepass.database.action.ProgressDialogThread
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.exception.DuplicateUuidDatabaseException
import com.kunzisoft.keepass.education.PasswordActivityEducation
import com.kunzisoft.keepass.model.SearchInfo
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_LOAD_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.CIPHER_ENTITY_KEY
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.DATABASE_URI_KEY
@@ -255,16 +257,21 @@ open class PasswordActivity : StylishActivity() {
private fun launchGroupActivity() {
EntrySelectionHelper.doEntrySelectionAction(intent,
{
GroupActivity.launch(this@PasswordActivity, readOnly)
GroupActivity.launch(this@PasswordActivity,
readOnly)
},
{
GroupActivity.launchForKeyboardSelection(this@PasswordActivity, readOnly)
GroupActivity.launchForKeyboardSelection(this@PasswordActivity,
readOnly)
// Do not keep history
finish()
},
{ assistStructure ->
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
GroupActivity.launchForAutofillResult(this@PasswordActivity, assistStructure, readOnly)
GroupActivity.launchForAutofillResult(this@PasswordActivity,
assistStructure,
intent.getParcelableExtra(KEY_SEARCH_INFO),
readOnly)
}
})
}
@@ -789,13 +796,15 @@ open class PasswordActivity : StylishActivity() {
activity: Activity,
databaseFile: Uri,
keyFile: Uri?,
assistStructure: AssistStructure?) {
assistStructure: AssistStructure?,
searchInfo: SearchInfo?) {
if (assistStructure != null) {
buildAndLaunchIntent(activity, databaseFile, keyFile) { intent ->
AutofillHelper.startActivityForAutofillResult(
activity,
intent,
assistStructure)
assistStructure,
searchInfo)
}
} else {
launch(activity, databaseFile, keyFile)

View File

@@ -34,6 +34,7 @@ import androidx.annotation.RequiresApi
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper
import com.kunzisoft.keepass.model.EntryInfo
import com.kunzisoft.keepass.model.SearchInfo
@RequiresApi(api = Build.VERSION_CODES.O)
@@ -42,6 +43,7 @@ object AutofillHelper {
private const val AUTOFILL_RESPONSE_REQUEST_CODE = 8165
private const val ASSIST_STRUCTURE = AutofillManager.EXTRA_ASSIST_STRUCTURE
const val KEY_SEARCH_INFO = "KEY_SEARCH_INFO"
fun retrieveAssistStructure(intent: Intent?): AssistStructure? {
intent?.let {
@@ -118,9 +120,15 @@ object AutofillHelper {
/**
* Utility method to start an activity with an Autofill for result
*/
fun startActivityForAutofillResult(activity: Activity, intent: Intent, assistStructure: AssistStructure) {
fun startActivityForAutofillResult(activity: Activity,
intent: Intent,
assistStructure: AssistStructure,
searchInfo: SearchInfo?) {
EntrySelectionHelper.addEntrySelectionModeExtraInIntent(intent)
intent.putExtra(ASSIST_STRUCTURE, assistStructure)
searchInfo?.let {
intent.putExtra(KEY_SEARCH_INFO, it)
}
activity.startActivityForResult(intent, AUTOFILL_RESPONSE_REQUEST_CODE)
}

View File

@@ -31,6 +31,7 @@ import androidx.appcompat.app.AppCompatActivity
import com.kunzisoft.keepass.activities.FileDatabaseSelectActivity
import com.kunzisoft.keepass.activities.GroupActivity
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.model.SearchInfo
import com.kunzisoft.keepass.timeout.TimeoutHelper
@RequiresApi(api = Build.VERSION_CODES.O)
@@ -40,12 +41,16 @@ class AutofillLauncherActivity : AppCompatActivity() {
// Pass extra for Autofill (EXTRA_ASSIST_STRUCTURE)
val assistStructure = AutofillHelper.retrieveAssistStructure(intent)
if (assistStructure != null) {
val searchInfo: SearchInfo? = SearchInfo().apply {
applicationId = intent.getStringExtra(KEY_SEARCH_APPLICATION_ID)
webDomain = intent.getStringExtra(KEY_SEARCH_DOMAIN)
}
if (Database.getInstance().loaded && TimeoutHelper.checkTime(this))
GroupActivity.launchForAutofillResult(this,
assistStructure)
assistStructure, searchInfo)
else {
FileDatabaseSelectActivity.launchForAutofillResult(this,
assistStructure)
assistStructure, searchInfo)
}
} else {
setResult(Activity.RESULT_CANCELED)
@@ -62,10 +67,20 @@ class AutofillLauncherActivity : AppCompatActivity() {
companion object {
fun getAuthIntentSenderForResponse(context: Context): IntentSender {
val intent = Intent(context, AutofillLauncherActivity::class.java)
private const val KEY_SEARCH_APPLICATION_ID = "KEY_SEARCH_APPLICATION_ID"
private const val KEY_SEARCH_DOMAIN = "KEY_SEARCH_DOMAIN"
fun getAuthIntentSenderForResponse(context: Context,
searchInfo: SearchInfo? = null): IntentSender {
return PendingIntent.getActivity(context, 0,
intent, PendingIntent.FLAG_CANCEL_CURRENT).intentSender
// Doesn't work with Parcelable (don't know why?)
Intent(context, AutofillLauncherActivity::class.java).apply {
searchInfo?.let {
putExtra(KEY_SEARCH_APPLICATION_ID, it.applicationId)
putExtra(KEY_SEARCH_DOMAIN, it.webDomain)
}
},
PendingIntent.FLAG_CANCEL_CURRENT).intentSender
}
}
}

View File

@@ -20,12 +20,14 @@
package com.kunzisoft.keepass.autofill
import android.os.Build
import android.os.Bundle
import android.os.CancellationSignal
import android.service.autofill.*
import androidx.annotation.RequiresApi
import android.util.Log
import android.widget.RemoteViews
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.model.SearchInfo
@RequiresApi(api = Build.VERSION_CODES.O)
class KeeAutofillService : AutofillService() {
@@ -44,9 +46,17 @@ class KeeAutofillService : AutofillService() {
if (autofillIds.isNotEmpty()) {
// If the entire Autofill Response is authenticated, AuthActivity is used
// to generate Response.
val sender = AutofillLauncherActivity.getAuthIntentSenderForResponse(this)
val searchInfo = SearchInfo().apply {
applicationId = parseResult.applicationId
webDomain = parseResult.domain
}
val sender = AutofillLauncherActivity.getAuthIntentSenderForResponse(this,
searchInfo)
val presentation = RemoteViews(packageName, R.layout.item_autofill_service_unlock)
responseBuilder.setAuthentication(autofillIds, sender, presentation)
responseBuilder.setClientState(Bundle().apply {
putParcelable("KEY_SEARCH_INFO", searchInfo)
})
callback.onSuccess(responseBuilder.build())
}
}

View File

@@ -397,9 +397,8 @@ class Database {
false
}
@JvmOverloads
fun search(str: String, max: Int = Integer.MAX_VALUE): Group? {
return mSearchHelper?.search(this, str, max)
fun createVirtualGroupWithSearchResult(str: String, max: Int = Integer.MAX_VALUE): Group? {
return mSearchHelper?.createVirtualGroupWithSearchResult(this, str, max)
}
fun searchEntries(context: Context, query: String): Cursor? {
@@ -412,10 +411,10 @@ class Database {
if (mDatabaseKDBX != null)
cursorKDBX = EntryCursorKDBX()
val searchResult = search(query, SearchHelper.MAX_SEARCH_ENTRY)
if (searchResult != null) {
val searchGroup = createVirtualGroupWithSearchResult(query, SearchHelper.MAX_SEARCH_ENTRY)
if (searchGroup != null) {
// Search in hide entries but not meta-stream
for (entry in searchResult.getFilteredChildEntries(*Group.ChildFilter.getDefaults(context))) {
for (entry in searchGroup.getFilteredChildEntries(*Group.ChildFilter.getDefaults(context))) {
entry.entryKDB?.let {
cursorKDB?.addEntry(it)
}

View File

@@ -36,7 +36,7 @@ class SearchHelper(private val isOmitBackup: Boolean) {
private var incrementEntry = 0
fun search(database: Database, qStr: String, max: Int): Group? {
fun createVirtualGroupWithSearchResult(database: Database, qStr: String, max: Int): Group? {
val searchGroup = database.createGroup()
searchGroup?.title = "\"" + qStr + "\""

View File

@@ -39,7 +39,7 @@ constructor(private val mEntry: EntryKDB,
title -> mEntry.title
url -> mEntry.url
username -> mEntry.username
comment -> mEntry.notes
notes -> mEntry.notes
else -> ""
}
}
@@ -73,7 +73,7 @@ constructor(private val mEntry: EntryKDB,
title -> mSearchParameters.searchInTitles
url -> mSearchParameters.searchInUrls
username -> mSearchParameters.searchInUserNames
comment -> mSearchParameters.searchInNotes
notes -> mSearchParameters.searchInNotes
else -> true
}
@@ -88,7 +88,7 @@ constructor(private val mEntry: EntryKDB,
private const val title = 0
private const val url = 1
private const val username = 2
private const val comment = 3
private const val notes = 3
private const val maxEntries = 4
}

View File

@@ -0,0 +1,42 @@
package com.kunzisoft.keepass.model
import android.os.Parcel
import android.os.Parcelable
class SearchInfo : Parcelable {
var applicationId: String? = null
var webDomain: String? = null
constructor()
private constructor(parcel: Parcel) {
val readAppId = parcel.readString()
applicationId = if (readAppId.isNullOrEmpty()) null else readAppId
val readDomain = parcel.readString()
webDomain = if (readDomain.isNullOrEmpty()) null else readDomain
}
override fun describeContents(): Int {
return 0
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(applicationId ?: "")
parcel.writeString(webDomain ?: "")
}
companion object {
@JvmField
val CREATOR: Parcelable.Creator<SearchInfo> = object : Parcelable.Creator<SearchInfo> {
override fun createFromParcel(parcel: Parcel): SearchInfo {
return SearchInfo(parcel)
}
override fun newArray(size: Int): Array<SearchInfo?> {
return arrayOfNulls(size)
}
}
}
}