mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Save search info settings
This commit is contained in:
@@ -183,29 +183,7 @@ class EntryEditActivity : LockingActivity(),
|
|||||||
tempEntryInfo?.password = it
|
tempEntryInfo?.password = it
|
||||||
}
|
}
|
||||||
registerInfo?.searchInfo?.let { searchInfo ->
|
registerInfo?.searchInfo?.let { searchInfo ->
|
||||||
searchInfo.webDomain?.let { webDomain ->
|
tempEntryInfo?.saveSearchInfo(mDatabase, searchInfo)
|
||||||
// If unable to save web domain in custom field or URL not populate, save in URL
|
|
||||||
if (mDatabase?.allowEntryCustomFields() != true) {
|
|
||||||
//|| tempEntryInfo?.url?.isEmpty() == true) {
|
|
||||||
val scheme = "http"
|
|
||||||
// TODO Retrieve scheme
|
|
||||||
tempEntryInfo?.url = "$scheme://$webDomain"
|
|
||||||
} else {
|
|
||||||
// Save web domain in custom field
|
|
||||||
tempEntryInfo?.addUniqueField(Field(EntryInfo.WEB_DOMAIN_FIELD_NAME,
|
|
||||||
ProtectedString(false, webDomain))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} ?: run {
|
|
||||||
// Save application id in custom field
|
|
||||||
if (mDatabase?.allowEntryCustomFields() == true) {
|
|
||||||
searchInfo.applicationId?.let { applicationId ->
|
|
||||||
tempEntryInfo?.addUniqueField(Field(EntryInfo.APPLICATION_ID_FIELD_NAME,
|
|
||||||
ProtectedString(false, applicationId))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build fragment to manage entry modification
|
// Build fragment to manage entry modification
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Compa
|
|||||||
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_CREATE_GROUP_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
|
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_DELETE_NODES_TASK
|
||||||
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_MOVE_NODES_TASK
|
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_MOVE_NODES_TASK
|
||||||
|
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_ENTRY_TASK
|
||||||
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_GROUP_TASK
|
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_GROUP_TASK
|
||||||
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.NEW_NODES_KEY
|
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.NEW_NODES_KEY
|
||||||
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.OLD_NODES_KEY
|
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.OLD_NODES_KEY
|
||||||
@@ -252,6 +253,35 @@ class GroupActivity : LockingActivity(),
|
|||||||
refreshSearchGroup()
|
refreshSearchGroup()
|
||||||
|
|
||||||
when (actionTask) {
|
when (actionTask) {
|
||||||
|
ACTION_DATABASE_UPDATE_ENTRY_TASK -> {
|
||||||
|
if (result.isSuccess) {
|
||||||
|
mListNodesFragment?.updateNodes(oldNodes, newNodes)
|
||||||
|
EntrySelectionHelper.doSpecialAction(intent,
|
||||||
|
{
|
||||||
|
// Not use
|
||||||
|
},
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
val entry = newNodes[0] as Entry
|
||||||
|
entrySelectedForKeyboardSelection(entry)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Unable to perform action for keyboard selection after entry update", e)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ _, _ ->
|
||||||
|
try {
|
||||||
|
val entry = newNodes[0] as Entry
|
||||||
|
entrySelectedForAutofillSelection(entry)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Unable to perform action for autofill selection after entry update", e)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Not use
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
ACTION_DATABASE_UPDATE_GROUP_TASK -> {
|
ACTION_DATABASE_UPDATE_GROUP_TASK -> {
|
||||||
if (result.isSuccess) {
|
if (result.isSuccess) {
|
||||||
mListNodesFragment?.updateNodes(oldNodes, newNodes)
|
mListNodesFragment?.updateNodes(oldNodes, newNodes)
|
||||||
@@ -533,24 +563,19 @@ class GroupActivity : LockingActivity(),
|
|||||||
{
|
{
|
||||||
EntryActivity.launch(this@GroupActivity, entryVersioned, mReadOnly)
|
EntryActivity.launch(this@GroupActivity, entryVersioned, mReadOnly)
|
||||||
},
|
},
|
||||||
{
|
{ searchInfo ->
|
||||||
rebuildListNodes()
|
if (PreferencesUtil.isKeyboardSaveSearchInfoEnable(this@GroupActivity)) {
|
||||||
// Populate Magikeyboard with entry
|
updateEntryWithSearchInfo(entryVersioned, searchInfo)
|
||||||
mDatabase?.let { database ->
|
} else {
|
||||||
populateKeyboardAndMoveAppToBackground(this@GroupActivity,
|
entrySelectedForKeyboardSelection(entryVersioned)
|
||||||
entryVersioned.getEntryInfo(database),
|
|
||||||
intent)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ _, _ ->
|
{ searchInfo, _ ->
|
||||||
// Build response with the entry selected
|
if (PreferencesUtil.isAutofillSaveSearchInfoEnable(this@GroupActivity)) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && mDatabase != null) {
|
updateEntryWithSearchInfo(entryVersioned, searchInfo)
|
||||||
mDatabase?.let { database ->
|
} else {
|
||||||
AutofillHelper.buildResponse(this@GroupActivity,
|
entrySelectedForAutofillSelection(entryVersioned)
|
||||||
entryVersioned.getEntryInfo(database))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
finish()
|
|
||||||
},
|
},
|
||||||
{ searchInfo ->
|
{ searchInfo ->
|
||||||
rebuildListNodes()
|
rebuildListNodes()
|
||||||
@@ -566,6 +591,39 @@ class GroupActivity : LockingActivity(),
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun entrySelectedForKeyboardSelection(entry: Entry) {
|
||||||
|
rebuildListNodes()
|
||||||
|
// Populate Magikeyboard with entry
|
||||||
|
mDatabase?.let { database ->
|
||||||
|
populateKeyboardAndMoveAppToBackground(this,
|
||||||
|
entry.getEntryInfo(database),
|
||||||
|
intent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun entrySelectedForAutofillSelection(entry: Entry) {
|
||||||
|
// Build response with the entry selected
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && mDatabase != null) {
|
||||||
|
mDatabase?.let { database ->
|
||||||
|
AutofillHelper.buildResponse(this,
|
||||||
|
entry.getEntryInfo(database))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateEntryWithSearchInfo(entry: Entry, searchInfo: SearchInfo?) {
|
||||||
|
val newEntry = Entry(entry)
|
||||||
|
newEntry.setEntryInfo(mDatabase, newEntry.getEntryInfo(mDatabase).apply {
|
||||||
|
saveSearchInfo(mDatabase, searchInfo)
|
||||||
|
})
|
||||||
|
mProgressDatabaseTaskProvider?.startDatabaseUpdateEntry(
|
||||||
|
entry,
|
||||||
|
newEntry,
|
||||||
|
!mReadOnly && mAutoSaveEnable
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private fun finishNodeAction() {
|
private fun finishNodeAction() {
|
||||||
actionNodeMode?.finish()
|
actionNodeMode?.finish()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,9 +22,11 @@ package com.kunzisoft.keepass.model
|
|||||||
import android.os.Parcel
|
import android.os.Parcel
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import com.kunzisoft.keepass.database.element.Attachment
|
import com.kunzisoft.keepass.database.element.Attachment
|
||||||
|
import com.kunzisoft.keepass.database.element.Database
|
||||||
import com.kunzisoft.keepass.database.element.DateInstant
|
import com.kunzisoft.keepass.database.element.DateInstant
|
||||||
import com.kunzisoft.keepass.database.element.icon.IconImage
|
import com.kunzisoft.keepass.database.element.icon.IconImage
|
||||||
import com.kunzisoft.keepass.database.element.icon.IconImageStandard
|
import com.kunzisoft.keepass.database.element.icon.IconImageStandard
|
||||||
|
import com.kunzisoft.keepass.database.element.security.ProtectedString
|
||||||
import com.kunzisoft.keepass.otp.OtpElement
|
import com.kunzisoft.keepass.otp.OtpElement
|
||||||
import com.kunzisoft.keepass.otp.OtpEntryFields.OTP_TOKEN_FIELD
|
import com.kunzisoft.keepass.otp.OtpEntryFields.OTP_TOKEN_FIELD
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
@@ -101,7 +103,7 @@ class EntryInfo : Parcelable {
|
|||||||
return customFields.lastOrNull { it.name == label }?.protectedValue?.toString() ?: ""
|
return customFields.lastOrNull { it.name == label }?.protectedValue?.toString() ?: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addUniqueField(field: Field, number: Int = 0) {
|
private fun addUniqueField(field: Field, number: Int = 0) {
|
||||||
var exists = false
|
var exists = false
|
||||||
var sameData = false
|
var sameData = false
|
||||||
val suffix = if (number > 0) number.toString() else ""
|
val suffix = if (number > 0) number.toString() else ""
|
||||||
@@ -121,6 +123,34 @@ class EntryInfo : Parcelable {
|
|||||||
(customFields as ArrayList<Field>).add(Field(field.name + suffix, field.protectedValue))
|
(customFields as ArrayList<Field>).add(Field(field.name + suffix, field.protectedValue))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun saveSearchInfo(database: Database?, searchInfo: SearchInfo?) {
|
||||||
|
searchInfo?.let { mSearchInfo ->
|
||||||
|
mSearchInfo.webDomain?.let { webDomain ->
|
||||||
|
// If unable to save web domain in custom field or URL not populate, save in URL
|
||||||
|
if (database?.allowEntryCustomFields() != true) {
|
||||||
|
//|| tempEntryInfo?.url?.isEmpty() == true) {
|
||||||
|
val scheme = "http"
|
||||||
|
// TODO Retrieve scheme
|
||||||
|
url = "$scheme://$webDomain"
|
||||||
|
} else {
|
||||||
|
// Save web domain in custom field
|
||||||
|
addUniqueField(Field(WEB_DOMAIN_FIELD_NAME,
|
||||||
|
ProtectedString(false, webDomain))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} ?: run {
|
||||||
|
// Save application id in custom field
|
||||||
|
if (database?.allowEntryCustomFields() == true) {
|
||||||
|
mSearchInfo.applicationId?.let { applicationId ->
|
||||||
|
addUniqueField(Field(APPLICATION_ID_FIELD_NAME,
|
||||||
|
ProtectedString(false, applicationId))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
const val WEB_DOMAIN_FIELD_NAME = "WebDomain"
|
const val WEB_DOMAIN_FIELD_NAME = "WebDomain"
|
||||||
|
|||||||
@@ -345,6 +345,14 @@ object PreferencesUtil {
|
|||||||
context.resources.getBoolean(R.bool.keyboard_search_share_default))
|
context.resources.getBoolean(R.bool.keyboard_search_share_default))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun isKeyboardSaveSearchInfoEnable(context: Context): Boolean {
|
||||||
|
if (!isKeyboardSearchShareEnable(context))
|
||||||
|
return false
|
||||||
|
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
return prefs.getBoolean(context.getString(R.string.keyboard_save_search_info_key),
|
||||||
|
context.resources.getBoolean(R.bool.keyboard_save_search_info_default))
|
||||||
|
}
|
||||||
|
|
||||||
fun isAutoGoActionEnable(context: Context): Boolean {
|
fun isAutoGoActionEnable(context: Context): Boolean {
|
||||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
return prefs.getBoolean(context.getString(R.string.keyboard_auto_go_action_key),
|
return prefs.getBoolean(context.getString(R.string.keyboard_auto_go_action_key),
|
||||||
@@ -381,6 +389,12 @@ object PreferencesUtil {
|
|||||||
context.resources.getBoolean(R.bool.autofill_auto_search_default))
|
context.resources.getBoolean(R.bool.autofill_auto_search_default))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun isAutofillSaveSearchInfoEnable(context: Context): Boolean {
|
||||||
|
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
return prefs.getBoolean(context.getString(R.string.autofill_save_search_info_key),
|
||||||
|
context.resources.getBoolean(R.bool.autofill_save_search_info_default))
|
||||||
|
}
|
||||||
|
|
||||||
fun askToSaveAutofillData(context: Context): Boolean {
|
fun askToSaveAutofillData(context: Context): Boolean {
|
||||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
return prefs.getBoolean(context.getString(R.string.autofill_ask_to_save_data_key),
|
return prefs.getBoolean(context.getString(R.string.autofill_ask_to_save_data_key),
|
||||||
|
|||||||
@@ -120,9 +120,11 @@
|
|||||||
<string name="keyboard_selection_entry_key" translatable="false">keyboard_selection_entry_key</string>
|
<string name="keyboard_selection_entry_key" translatable="false">keyboard_selection_entry_key</string>
|
||||||
<bool name="keyboard_selection_entry_default" translatable="false">false</bool>
|
<bool name="keyboard_selection_entry_default" translatable="false">false</bool>
|
||||||
<string name="keyboard_notification_entry_key" translatable="false">keyboard_notification_entry_key</string>
|
<string name="keyboard_notification_entry_key" translatable="false">keyboard_notification_entry_key</string>
|
||||||
<string name="keyboard_search_share_key" translatable="false">keyboard_search_share_key</string>
|
|
||||||
<bool name="keyboard_notification_entry_default" translatable="false">true</bool>
|
<bool name="keyboard_notification_entry_default" translatable="false">true</bool>
|
||||||
|
<string name="keyboard_search_share_key" translatable="false">keyboard_search_share_key</string>
|
||||||
<bool name="keyboard_search_share_default" translatable="false">false</bool>
|
<bool name="keyboard_search_share_default" translatable="false">false</bool>
|
||||||
|
<string name="keyboard_save_search_info_key" translatable="false">keyboard_save_search_info_key</string>
|
||||||
|
<bool name="keyboard_save_search_info_default" translatable="false">true</bool>
|
||||||
<string name="keyboard_notification_entry_clear_close_key" translatable="false">keyboard_notification_entry_clear_close_key</string>
|
<string name="keyboard_notification_entry_clear_close_key" translatable="false">keyboard_notification_entry_clear_close_key</string>
|
||||||
<bool name="keyboard_notification_entry_clear_close_default" translatable="false">true</bool>
|
<bool name="keyboard_notification_entry_clear_close_default" translatable="false">true</bool>
|
||||||
<string name="keyboard_entry_timeout_key" translatable="false">keyboard_entry_timeout_key</string>
|
<string name="keyboard_entry_timeout_key" translatable="false">keyboard_entry_timeout_key</string>
|
||||||
@@ -139,6 +141,8 @@
|
|||||||
<bool name="keyboard_previous_fill_in_default" translatable="false">false</bool>
|
<bool name="keyboard_previous_fill_in_default" translatable="false">false</bool>
|
||||||
<string name="autofill_auto_search_key" translatable="false">autofill_auto_search_key</string>
|
<string name="autofill_auto_search_key" translatable="false">autofill_auto_search_key</string>
|
||||||
<bool name="autofill_auto_search_default" translatable="false">true</bool>
|
<bool name="autofill_auto_search_default" translatable="false">true</bool>
|
||||||
|
<string name="autofill_save_search_info_key" translatable="false">autofill_save_search_info_key</string>
|
||||||
|
<bool name="autofill_save_search_info_default" translatable="false">true</bool>
|
||||||
<string name="autofill_ask_to_save_data_key" translatable="false">autofill_ask_to_save_data_key</string>
|
<string name="autofill_ask_to_save_data_key" translatable="false">autofill_ask_to_save_data_key</string>
|
||||||
<bool name="autofill_ask_to_save_data_default" translatable="false">true</bool>
|
<bool name="autofill_ask_to_save_data_default" translatable="false">true</bool>
|
||||||
<string name="autofill_application_id_blocklist_key" translatable="false">autofill_application_id_blocklist_key</string>
|
<string name="autofill_application_id_blocklist_key" translatable="false">autofill_application_id_blocklist_key</string>
|
||||||
|
|||||||
@@ -381,6 +381,8 @@
|
|||||||
<string name="keyboard_notification_entry_summary">Show a notification when an entry is available</string>
|
<string name="keyboard_notification_entry_summary">Show a notification when an entry is available</string>
|
||||||
<string name="keyboard_search_share_title">Search shared info</string>
|
<string name="keyboard_search_share_title">Search shared info</string>
|
||||||
<string name="keyboard_search_share_summary">Automatically search for shared information to populate the keyboard</string>
|
<string name="keyboard_search_share_summary">Automatically search for shared information to populate the keyboard</string>
|
||||||
|
<string name="keyboard_save_search_info_title">Save shared info</string>
|
||||||
|
<string name="keyboard_save_search_info_summary">Try to save shared information when making a manual entry selection</string>
|
||||||
<string name="keyboard_notification_entry_clear_close_title">Clear at closing</string>
|
<string name="keyboard_notification_entry_clear_close_title">Clear at closing</string>
|
||||||
<string name="keyboard_notification_entry_clear_close_summary">Close the database when closing the notification</string>
|
<string name="keyboard_notification_entry_clear_close_summary">Close the database when closing the notification</string>
|
||||||
<string name="keyboard_entry_timeout_title">Timeout</string>
|
<string name="keyboard_entry_timeout_title">Timeout</string>
|
||||||
@@ -402,6 +404,8 @@
|
|||||||
<string name="keyboard_previous_fill_in_summary">Automatically switch back to the previous keyboard after executing "Auto key action"</string>
|
<string name="keyboard_previous_fill_in_summary">Automatically switch back to the previous keyboard after executing "Auto key action"</string>
|
||||||
<string name="autofill_auto_search_title">Auto search</string>
|
<string name="autofill_auto_search_title">Auto search</string>
|
||||||
<string name="autofill_auto_search_summary">Automatically suggest search results from the web domain or application ID</string>
|
<string name="autofill_auto_search_summary">Automatically suggest search results from the web domain or application ID</string>
|
||||||
|
<string name="autofill_save_search_info_title">Save search info</string>
|
||||||
|
<string name="autofill_save_search_info_summary">Try to save search information when making a manual entry selection</string>
|
||||||
<string name="autofill_ask_to_save_data_title">Ask to save data</string>
|
<string name="autofill_ask_to_save_data_title">Ask to save data</string>
|
||||||
<string name="autofill_ask_to_save_data_summary">Ask to save data when a form is validated</string>
|
<string name="autofill_ask_to_save_data_summary">Ask to save data when a form is validated</string>
|
||||||
<string name="autofill_application_id_blocklist_title">Application blocklist</string>
|
<string name="autofill_application_id_blocklist_title">Application blocklist</string>
|
||||||
|
|||||||
@@ -25,6 +25,11 @@
|
|||||||
android:title="@string/autofill_auto_search_title"
|
android:title="@string/autofill_auto_search_title"
|
||||||
android:summary="@string/autofill_auto_search_summary"
|
android:summary="@string/autofill_auto_search_summary"
|
||||||
android:defaultValue="@bool/autofill_auto_search_default"/>
|
android:defaultValue="@bool/autofill_auto_search_default"/>
|
||||||
|
<SwitchPreference
|
||||||
|
android:key="@string/autofill_save_search_info_key"
|
||||||
|
android:title="@string/autofill_save_search_info_title"
|
||||||
|
android:summary="@string/autofill_save_search_info_summary"
|
||||||
|
android:defaultValue="@bool/autofill_save_search_info_default"/>
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
android:title="@string/save">
|
android:title="@string/save">
|
||||||
|
|||||||
@@ -35,6 +35,12 @@
|
|||||||
android:title="@string/keyboard_search_share_title"
|
android:title="@string/keyboard_search_share_title"
|
||||||
android:summary="@string/keyboard_search_share_summary"
|
android:summary="@string/keyboard_search_share_summary"
|
||||||
android:defaultValue="@bool/keyboard_search_share_default"/>
|
android:defaultValue="@bool/keyboard_search_share_default"/>
|
||||||
|
<SwitchPreference
|
||||||
|
android:key="@string/keyboard_save_search_info_key"
|
||||||
|
android:title="@string/keyboard_save_search_info_title"
|
||||||
|
android:summary="@string/keyboard_save_search_info_summary"
|
||||||
|
android:dependency="@string/keyboard_search_share_key"
|
||||||
|
android:defaultValue="@bool/keyboard_save_search_info_default"/>
|
||||||
<SwitchPreference
|
<SwitchPreference
|
||||||
android:key="@string/keyboard_notification_entry_clear_close_key"
|
android:key="@string/keyboard_notification_entry_clear_close_key"
|
||||||
android:title="@string/keyboard_notification_entry_clear_close_title"
|
android:title="@string/keyboard_notification_entry_clear_close_title"
|
||||||
|
|||||||
Reference in New Issue
Block a user