Empty Magikeyboard memory when the main service is killed #1261

This commit is contained in:
J-Jamet
2022-04-08 16:53:03 +02:00
parent 2042c85b22
commit 3e56521ea8
6 changed files with 145 additions and 133 deletions

View File

@@ -8,7 +8,7 @@ KeePassDX(3.4.0)
* Fix small bugs #1282 * Fix small bugs #1282
* Better search implementation #175 #1254 * Better search implementation #175 #1254
* Setting to change keyboard during a search #1267 * Setting to change keyboard during a search #1267
* Manage package name from Magikeyboard #1010 * Manage package name from Magikeyboard #1010 #1261
* Ask confirmation to lock if changes without save #970 * Ask confirmation to lock if changes without save #970
KeePassDX(3.3.3) KeePassDX(3.3.3)

View File

@@ -24,6 +24,7 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.widget.Toast import android.widget.Toast
import com.kunzisoft.keepass.R import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper
@@ -60,7 +61,7 @@ class EntrySelectionLauncherActivity : DatabaseModeActivity() {
keySelectionBundle.getParcelable<SearchInfo>(KEY_SEARCH_INFO)?.let { mSearchInfo -> keySelectionBundle.getParcelable<SearchInfo>(KEY_SEARCH_INFO)?.let { mSearchInfo ->
searchInfo = mSearchInfo searchInfo = mSearchInfo
} }
launch(database, searchInfo) launch(database, searchInfo, true)
} else { } else {
// To manage share // To manage share
var sharedWebDomain: String? = null var sharedWebDomain: String? = null
@@ -102,112 +103,95 @@ class EntrySelectionLauncherActivity : DatabaseModeActivity() {
} }
private fun launch(database: Database?, private fun launch(database: Database?,
searchInfo: SearchInfo) { searchInfo: SearchInfo,
forceSelection: Boolean = false) {
if (!searchInfo.containsOnlyNullValues()) { // Setting to integrate Magikeyboard
// Setting to integrate Magikeyboard val searchShareForMagikeyboard = PreferencesUtil.isKeyboardSearchShareEnable(this)
val searchShareForMagikeyboard = PreferencesUtil.isKeyboardSearchShareEnable(this)
// If database is open // If database is open
val readOnly = database?.isReadOnly != false val readOnly = database?.isReadOnly != false
SearchHelper.checkAutoSearchInfo(this, SearchHelper.checkAutoSearchInfo(this,
database, database,
searchInfo, searchInfo,
{ openedDatabase, items -> { openedDatabase, items ->
// Items found // Items found
if (searchInfo.otpString != null) { if (searchInfo.otpString != null) {
if (!readOnly) { if (!readOnly) {
GroupActivity.launchForSaveResult( GroupActivity.launchForSaveResult(
this, this,
openedDatabase, openedDatabase,
searchInfo, searchInfo,
false) false)
} else {
Toast.makeText(applicationContext,
R.string.autofill_read_only_save,
Toast.LENGTH_LONG)
.show()
}
} else if (searchShareForMagikeyboard) {
if (items.size == 1) {
// Automatically populate keyboard
val entryPopulate = items[0]
populateKeyboardAndMoveAppToBackground(
this,
entryPopulate,
intent)
} else {
// Select the one we want
GroupActivity.launchForKeyboardSelectionResult(this,
openedDatabase,
searchInfo,
true)
}
} else { } else {
GroupActivity.launchForSearchResult(this, Toast.makeText(applicationContext,
R.string.autofill_read_only_save,
Toast.LENGTH_LONG)
.show()
}
} else if (searchShareForMagikeyboard) {
if (items.size == 1 && !forceSelection) {
// Automatically populate keyboard
val entryPopulate = items[0]
populateKeyboardAndMoveAppToBackground(
this,
entryPopulate,
intent)
Log.e("TEST", "One item activity")
} else {
// Select the one we want
GroupActivity.launchForKeyboardSelectionResult(this,
openedDatabase, openedDatabase,
searchInfo, searchInfo,
true) true)
} }
}, } else {
{ openedDatabase -> GroupActivity.launchForSearchResult(this,
// Show the database UI to select the entry openedDatabase,
if (searchInfo.otpString != null) { searchInfo,
if (!readOnly) { true)
GroupActivity.launchForSaveResult(this, }
openedDatabase, },
searchInfo, { openedDatabase ->
false) // Show the database UI to select the entry
} else { if (searchInfo.otpString != null) {
Toast.makeText(applicationContext, if (!readOnly) {
R.string.autofill_read_only_save,
Toast.LENGTH_LONG)
.show()
}
} else if (readOnly || searchShareForMagikeyboard) {
GroupActivity.launchForKeyboardSelectionResult(this,
openedDatabase,
searchInfo,
false)
} else {
GroupActivity.launchForSaveResult(this, GroupActivity.launchForSaveResult(this,
openedDatabase, openedDatabase,
searchInfo, searchInfo,
false) false)
}
},
{
// If database not open
if (searchInfo.otpString != null) {
FileDatabaseSelectActivity.launchForSaveResult(this,
searchInfo)
} else if (searchShareForMagikeyboard) {
FileDatabaseSelectActivity.launchForKeyboardSelectionResult(this,
searchInfo)
} else { } else {
FileDatabaseSelectActivity.launchForSearchResult(this, Toast.makeText(applicationContext,
searchInfo) R.string.autofill_read_only_save,
Toast.LENGTH_LONG)
.show()
} }
} else if (readOnly || searchShareForMagikeyboard) {
GroupActivity.launchForKeyboardSelectionResult(this,
openedDatabase,
searchInfo,
false)
} else {
GroupActivity.launchForSaveResult(this,
openedDatabase,
searchInfo,
false)
} }
)
} else {
SearchHelper.checkAutoSearchInfo(this,
database,
null,
{ _, _ ->
// Not called
// if items found directly returns before calling this activity
},
{ openedDatabase ->
// Select if not found
GroupActivity.launchForKeyboardSelectionResult(this, openedDatabase)
}, },
{ {
// Pass extra to get entry // If database not open
FileDatabaseSelectActivity.launchForKeyboardSelectionResult(this) if (searchInfo.otpString != null) {
FileDatabaseSelectActivity.launchForSaveResult(this,
searchInfo)
} else if (searchShareForMagikeyboard) {
FileDatabaseSelectActivity.launchForKeyboardSelectionResult(this,
searchInfo)
} else {
FileDatabaseSelectActivity.launchForSearchResult(this,
searchInfo)
}
} }
) )
}
finish() finish()
} }
@@ -225,7 +209,7 @@ class EntrySelectionLauncherActivity : DatabaseModeActivity() {
} }
// New task needed because don't launch from an Activity context // New task needed because don't launch from an Activity context
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or
Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS Intent.FLAG_ACTIVITY_CLEAR_TASK
context.startActivity(intent) context.startActivity(intent)
} }
} }

View File

@@ -96,15 +96,9 @@ abstract class DatabaseModeActivity : DatabaseActivity() {
private fun backToTheMainAppAndFinish() { private fun backToTheMainAppAndFinish() {
// To move the app in background and return to the main app // To move the app in background and return to the main app
// Not visible as opened with FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
moveTaskToBack(true) moveTaskToBack(true)
// To remove this instance in the OS app selector // Not finish() to prevent service kill
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
finishAndRemoveTask()
} else {
Handler(Looper.getMainLooper()).postDelayed({
finish()
}, 500)
}
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {

View File

@@ -39,12 +39,15 @@ import com.kunzisoft.keepass.adapters.FieldsAdapter
import com.kunzisoft.keepass.database.action.DatabaseTaskProvider import com.kunzisoft.keepass.database.action.DatabaseTaskProvider
import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.Field import com.kunzisoft.keepass.database.element.Field
import com.kunzisoft.keepass.database.element.node.NodeIdUUID
import com.kunzisoft.keepass.database.search.SearchHelper
import com.kunzisoft.keepass.model.EntryInfo import com.kunzisoft.keepass.model.EntryInfo
import com.kunzisoft.keepass.model.SearchInfo import com.kunzisoft.keepass.model.SearchInfo
import com.kunzisoft.keepass.otp.OtpEntryFields.OTP_TOKEN_FIELD import com.kunzisoft.keepass.otp.OtpEntryFields.OTP_TOKEN_FIELD
import com.kunzisoft.keepass.services.KeyboardEntryNotificationService import com.kunzisoft.keepass.services.KeyboardEntryNotificationService
import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.utils.* import com.kunzisoft.keepass.utils.*
import java.util.*
class MagikeyboardService : InputMethodService(), KeyboardView.OnKeyboardActionListener { class MagikeyboardService : InputMethodService(), KeyboardView.OnKeyboardActionListener {
@@ -71,6 +74,7 @@ class MagikeyboardService : InputMethodService(), KeyboardView.OnKeyboardActionL
mDatabaseTaskProvider?.registerProgressTask() mDatabaseTaskProvider?.registerProgressTask()
mDatabaseTaskProvider?.onDatabaseRetrieved = { database -> mDatabaseTaskProvider?.onDatabaseRetrieved = { database ->
this.mDatabase = database this.mDatabase = database
assignKeyboardView()
} }
// Remove the entry and lock the keyboard when the lock signal is receive // Remove the entry and lock the keyboard when the lock signal is receive
lockReceiver = LockReceiver { lockReceiver = LockReceiver {
@@ -110,7 +114,7 @@ class MagikeyboardService : InputMethodService(), KeyboardView.OnKeyboardActionL
fieldsAdapter = FieldsAdapter(this) fieldsAdapter = FieldsAdapter(this)
fieldsAdapter?.onItemClickListener = object : FieldsAdapter.OnItemClickListener { fieldsAdapter?.onItemClickListener = object : FieldsAdapter.OnItemClickListener {
override fun onItemClick(item: Field) { override fun onItemClick(item: Field) {
currentInputConnection.commitText(entryInfoKey?.getGeneratedFieldValue(item.name) , 1) currentInputConnection.commitText(getEntryInfo()?.getGeneratedFieldValue(item.name) , 1)
actionTabAutomatically() actionTabAutomatically()
} }
} }
@@ -120,17 +124,6 @@ class MagikeyboardService : InputMethodService(), KeyboardView.OnKeyboardActionL
val closeView = popupFieldsView.findViewById<View>(R.id.keyboard_popup_close) val closeView = popupFieldsView.findViewById<View>(R.id.keyboard_popup_close)
closeView.setOnClickListener { popupCustomKeys?.dismiss() } closeView.setOnClickListener { popupCustomKeys?.dismiss() }
// Remove entry info if the database is not loaded
// or if entry info timestamp is before database loaded timestamp
val databaseTime = mDatabase?.loadTimestamp
val entryTime = entryInfoTimestamp
if (mDatabase == null
|| mDatabase?.loaded != true
|| databaseTime == null
|| entryTime == null
|| entryTime < databaseTime) {
removeEntryInfo()
}
assignKeyboardView() assignKeyboardView()
keyboardView?.onKeyboardActionListener = this keyboardView?.onKeyboardActionListener = this
@@ -140,12 +133,23 @@ class MagikeyboardService : InputMethodService(), KeyboardView.OnKeyboardActionL
return super.onCreateInputView() return super.onCreateInputView()
} }
private fun getEntryInfo(): EntryInfo? {
var entryInfoRetrieved: EntryInfo? = null
entryUUID?.let { entryId ->
entryInfoRetrieved = mDatabase
?.getEntryById(NodeIdUUID(entryId))
?.getEntryInfo(mDatabase)
}
return entryInfoRetrieved
}
private fun assignKeyboardView() { private fun assignKeyboardView() {
dismissCustomKeys() dismissCustomKeys()
if (keyboardView != null) { if (keyboardView != null) {
if (entryInfoKey != null) { val entryInfo = getEntryInfo()
if (entryInfo != null) {
if (keyboardEntry != null) { if (keyboardEntry != null) {
populateEntryInfoInView() populateEntryInfoInView(entryInfo)
keyboardView?.keyboard = keyboardEntry keyboardView?.keyboard = keyboardEntry
} }
} else { } else {
@@ -161,10 +165,10 @@ class MagikeyboardService : InputMethodService(), KeyboardView.OnKeyboardActionL
} }
} }
private fun populateEntryInfoInView() { private fun populateEntryInfoInView(entryInfo: EntryInfo) {
entryText?.visibility = View.VISIBLE entryText?.visibility = View.VISIBLE
if (entryInfoKey?.title?.isNotEmpty() == true) { if (entryInfo.title.isNotEmpty()) {
entryText?.text = entryInfoKey?.title entryText?.text = entryInfo.title
} else { } else {
hideEntryInfo() hideEntryInfo()
} }
@@ -259,12 +263,13 @@ class MagikeyboardService : InputMethodService(), KeyboardView.OnKeyboardActionL
dismissCustomKeys() dismissCustomKeys()
} }
KEY_USERNAME -> { KEY_USERNAME -> {
entryInfoKey?.username?.let { username -> getEntryInfo()?.username?.let { username ->
currentInputConnection.commitText(username, 1) currentInputConnection.commitText(username, 1)
} }
actionTabAutomatically() actionTabAutomatically()
} }
KEY_PASSWORD -> { KEY_PASSWORD -> {
val entryInfoKey = getEntryInfo()
entryInfoKey?.password?.let { password -> entryInfoKey?.password?.let { password ->
currentInputConnection.commitText(password, 1) currentInputConnection.commitText(password, 1)
} }
@@ -272,14 +277,14 @@ class MagikeyboardService : InputMethodService(), KeyboardView.OnKeyboardActionL
actionGoAutomatically(!otpFieldExists) actionGoAutomatically(!otpFieldExists)
} }
KEY_OTP -> { KEY_OTP -> {
entryInfoKey?.let { entryInfo -> getEntryInfo()?.let { entryInfo ->
currentInputConnection.commitText( currentInputConnection.commitText(
entryInfo.getGeneratedFieldValue(OTP_TOKEN_FIELD), 1) entryInfo.getGeneratedFieldValue(OTP_TOKEN_FIELD), 1)
} }
actionGoAutomatically() actionGoAutomatically()
} }
KEY_OTP_ALT -> { KEY_OTP_ALT -> {
entryInfoKey?.let { entryInfo -> getEntryInfo()?.let { entryInfo ->
val otpToken = entryInfo.getGeneratedFieldValue(OTP_TOKEN_FIELD) val otpToken = entryInfo.getGeneratedFieldValue(OTP_TOKEN_FIELD)
if (otpToken.isNotEmpty()) { if (otpToken.isNotEmpty()) {
// Cut to fill each digit separatelyKeyEvent.KEYCODE_TAB // Cut to fill each digit separatelyKeyEvent.KEYCODE_TAB
@@ -296,13 +301,13 @@ class MagikeyboardService : InputMethodService(), KeyboardView.OnKeyboardActionL
actionGoAutomatically() actionGoAutomatically()
} }
KEY_URL -> { KEY_URL -> {
entryInfoKey?.url?.let { url -> getEntryInfo()?.url?.let { url ->
currentInputConnection.commitText(url, 1) currentInputConnection.commitText(url, 1)
} }
actionGoAutomatically() actionGoAutomatically()
} }
KEY_FIELDS -> { KEY_FIELDS -> {
entryInfoKey?.customFields?.let { customFields -> getEntryInfo()?.customFields?.let { customFields ->
fieldsAdapter?.apply { fieldsAdapter?.apply {
setFields(customFields.filter { it.name != OTP_TOKEN_FIELD}) setFields(customFields.filter { it.name != OTP_TOKEN_FIELD})
notifyDataSetChanged() notifyDataSetChanged()
@@ -318,8 +323,41 @@ class MagikeyboardService : InputMethodService(), KeyboardView.OnKeyboardActionL
} }
private fun actionKeyEntry(searchInfo: SearchInfo? = null) { private fun actionKeyEntry(searchInfo: SearchInfo? = null) {
// Stop current service and reinit entry SearchHelper.checkAutoSearchInfo(this,
stopService(Intent(this, KeyboardEntryNotificationService::class.java)) mDatabase,
searchInfo,
{ _, items ->
if (items.size == 1) {
if (entryUUID == null) {
// Automatically populate keyboard
removeEntryInfo()
addEntryAndLaunchNotificationIfAllowed(
this,
items[0],
true
)
assignKeyboardView()
} else {
// Choose another one
launchEntrySelection(null)
}
} else {
// Select if multiple
launchEntrySelection(searchInfo)
}
},
{ _ ->
// Select if not found
launchEntrySelection(searchInfo)
},
{
// Select if database not opened
launchEntrySelection(searchInfo)
}
)
}
private fun launchEntrySelection(searchInfo: SearchInfo?) {
removeEntryInfo() removeEntryInfo()
EntrySelectionLauncherActivity.launch(this, searchInfo) EntrySelectionLauncherActivity.launch(this, searchInfo)
} }
@@ -390,13 +428,10 @@ class MagikeyboardService : InputMethodService(), KeyboardView.OnKeyboardActionL
const val KEY_URL = 520 const val KEY_URL = 520
const val KEY_FIELDS = 530 const val KEY_FIELDS = 530
// TODO Retrieve entry info from id and service when database is open private var entryUUID: UUID? = null
private var entryInfoKey: EntryInfo? = null
private var entryInfoTimestamp: Long? = null
private fun removeEntryInfo() { private fun removeEntryInfo() {
entryInfoKey = null entryUUID = null
entryInfoTimestamp = null
} }
fun removeEntry(context: Context) { fun removeEntry(context: Context) {
@@ -405,8 +440,7 @@ class MagikeyboardService : InputMethodService(), KeyboardView.OnKeyboardActionL
fun addEntryAndLaunchNotificationIfAllowed(context: Context, entry: EntryInfo, toast: Boolean = false) { fun addEntryAndLaunchNotificationIfAllowed(context: Context, entry: EntryInfo, toast: Boolean = false) {
// Add a new entry // Add a new entry
entryInfoKey = entry entryUUID = entry.id
entryInfoTimestamp = System.currentTimeMillis()
// Launch notification if allowed // Launch notification if allowed
KeyboardEntryNotificationService.launchNotificationIfAllowed(context, entry, toast) KeyboardEntryNotificationService.launchNotificationIfAllowed(context, entry, toast)
} }

View File

@@ -7,5 +7,5 @@
* Fix small bugs #1282 * Fix small bugs #1282
* Better search implementation #175 #1254 * Better search implementation #175 #1254
* Setting to change keyboard during a search #1267 * Setting to change keyboard during a search #1267
* Manage package name from Magikeyboard #1010 * Manage package name from Magikeyboard #1010 #1261
* Ask confirmation to lock if changes without save #970 * Ask confirmation to lock if changes without save #970

View File

@@ -7,5 +7,5 @@
* Correction de petits bugs #1282 * Correction de petits bugs #1282
* Meilleure implémentation de la recherche #175 #1254 * Meilleure implémentation de la recherche #175 #1254
* Paramètre pour changer de clavier lors d'une recherche #1267 * Paramètre pour changer de clavier lors d'une recherche #1267
* Gestion du nom de package du Magiclavier #1010 * Gestion du nom de package du Magiclavier #1010 #1261
* Demande de confirmation de verouiller si chanegement sans sauvegarde #970 * Demande de confirmation de verouiller si chanegement sans sauvegarde #970