Better notification implementation

This commit is contained in:
J-Jamet
2019-08-13 12:36:58 +02:00
parent 17eadcee2b
commit 935d3e1c4b
9 changed files with 126 additions and 124 deletions

View File

@@ -43,7 +43,7 @@ import com.kunzisoft.keepass.database.element.PwNodeId
import com.kunzisoft.keepass.education.EntryActivityEducation
import com.kunzisoft.keepass.icons.assignDatabaseIcon
import com.kunzisoft.keepass.magikeyboard.MagikIME
import com.kunzisoft.keepass.notifications.NotificationEntryCopyManager
import com.kunzisoft.keepass.notifications.CopyingEntryNotificationService
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.settings.PreferencesUtil.isFirstTimeAskAllowCopyPasswordAndProtectedFields
import com.kunzisoft.keepass.settings.SettingsAutofillActivity
@@ -127,13 +127,17 @@ class EntryActivity : LockingHideActivity() {
fillEntryDataInContentsView(entry)
// Refresh Menu
invalidateOptionsMenu()
val entryInfo = entry.getEntryInfo(Database.getInstance())
// Manage entry copy to start notification if allowed
NotificationEntryCopyManager.launchNotificationIfAllowed(this,
firstLaunchOfActivity,
entry)
// Manage entry to init Magikeyboard
if (PreferencesUtil.enableKeyboardEntrySelection(this)) {
MagikIME.initMagikeyboardForEntry(this, entry.getEntryInfo(Database.getInstance()))
if (firstLaunchOfActivity) {
// Manage entry to launch copying notification if allowed
CopyingEntryNotificationService.launchNotificationIfAllowed(this, entryInfo)
// Manage entry to populate Magikeyboard and launch keyboard notification if allowed
if (PreferencesUtil.isKeyboardEntrySelectionEnable(this)) {
MagikIME.addEntryAndLaunchNotificationIfAllowed(this, entryInfo)
}
}
}

View File

@@ -403,9 +403,10 @@ class GroupActivity : LockingActivity(),
EntryActivity.launch(this@GroupActivity, entryVersioned, readOnly)
},
{
// Init Magikeyboard with entry
// Populate Magikeyboard with entry
mDatabase?.let { database ->
MagikIME.initMagikeyboardForEntry(this@GroupActivity, entryVersioned.getEntryInfo(database))
MagikIME.addEntryAndLaunchNotificationIfAllowed(this@GroupActivity,
entryVersioned.getEntryInfo(database))
}
// Consume the selection mode
EntrySelectionHelper.removeEntrySelectionModeFromIntent(intent)

View File

@@ -10,12 +10,13 @@ import android.util.Log
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.model.EntryInfo
import com.kunzisoft.keepass.notifications.NotificationService
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.timeout.TimeoutHelper
import com.kunzisoft.keepass.utils.LOCK_ACTION
class KeyboardEntryNotificationService : NotificationService() {
private val notificationId = 2
private val notificationId = 486
private var cleanNotificationTimer: Thread? = null
private var notificationTimeoutMilliSecs: Long = 0
@@ -157,6 +158,15 @@ class KeyboardEntryNotificationService : NotificationService() {
const val ENTRY_INFO_KEY = "ENTRY_INFO_KEY"
const val ACTION_CLEAN_KEYBOARD_ENTRY = "ACTION_CLEAN_KEYBOARD_ENTRY"
fun launchNotificationIfAllowed(context: Context, entry: EntryInfo) {
// Show the notification if allowed in Preferences
if (PreferencesUtil.isKeyboardNotificationEntryEnable(context)) {
context.startService(Intent(context, KeyboardEntryNotificationService::class.java).apply {
putExtra(ENTRY_INFO_KEY, entry)
})
}
}
}
}

View File

@@ -37,7 +37,6 @@ import android.widget.FrameLayout
import android.widget.PopupWindow
import android.widget.TextView
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.magikeyboard.KeyboardEntryNotificationService.Companion.ENTRY_INFO_KEY
import com.kunzisoft.keepass.magikeyboard.adapter.FieldsAdapter
import com.kunzisoft.keepass.model.EntryInfo
import com.kunzisoft.keepass.model.Field
@@ -63,10 +62,14 @@ class MagikIME : InputMethodService(), KeyboardView.OnKeyboardActionListener {
// Remove the entry and lock the keyboard when the lock signal is receive
lockBroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
when (intent?.action) {
REMOVE_ENTRY_MAGIKEYBOARD_ACTION, LOCK_ACTION -> {
removeEntryInfo()
assignKeyboardView()
}
}
}
}
registerReceiver(lockBroadcastReceiver,
IntentFilter().apply {
@@ -138,8 +141,8 @@ class MagikIME : InputMethodService(), KeyboardView.OnKeyboardActionListener {
}
// Define preferences
keyboardView?.isHapticFeedbackEnabled = PreferencesUtil.enableKeyboardVibration(this)
playSoundDuringCLick = PreferencesUtil.enableKeyboardSound(this)
keyboardView?.isHapticFeedbackEnabled = PreferencesUtil.isKeyboardVibrationEnable(this)
playSoundDuringCLick = PreferencesUtil.isKeyboardSoundEnable(this)
}
}
@@ -301,14 +304,11 @@ class MagikIME : InputMethodService(), KeyboardView.OnKeyboardActionListener {
context.sendBroadcast(Intent(REMOVE_ENTRY_MAGIKEYBOARD_ACTION))
}
fun initMagikeyboardForEntry(context: Context, entry: EntryInfo) {
fun addEntryAndLaunchNotificationIfAllowed(context: Context, entry: EntryInfo) {
// Add a new entry
entryInfoKey = entry
// Show the notification if allowed in Preferences
if (PreferencesUtil.enableKeyboardNotificationEntry(context)) {
context.startService(Intent(context, KeyboardEntryNotificationService::class.java).apply {
putExtra(ENTRY_INFO_KEY, entry)
})
}
// Launch notification if allowed
KeyboardEntryNotificationService.launchNotificationIfAllowed(context, entry)
}
}
}

View File

@@ -41,6 +41,14 @@ class EntryInfo : Parcelable {
parcel.writeArray(customFields.toTypedArray())
}
fun containsCustomFieldsProtected(): Boolean {
return customFields.any { it.protectedValue.isProtected }
}
fun containsCustomFieldsNotProtected(): Boolean {
return customFields.any { !it.protectedValue.isProtected }
}
companion object {
@JvmField

View File

@@ -20,18 +20,21 @@
package com.kunzisoft.keepass.notifications
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.preference.PreferenceManager
import android.util.Log
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.database.exception.SamsungClipboardException
import com.kunzisoft.keepass.model.EntryInfo
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.timeout.ClipboardHelper
import com.kunzisoft.keepass.timeout.TimeoutHelper.NEVER
import java.util.*
class CopyingEntryNotificationService : NotificationService() {
private var notificationId = 1
private var notificationId = 485
private var cleanNotificationTimer: Thread? = null
private var notificationTimeoutMilliSecs: Long = 0
@@ -219,6 +222,72 @@ class CopyingEntryNotificationService : NotificationService() {
const val EXTRA_ENTRY_TITLE = "EXTRA_ENTRY_TITLE"
const val EXTRA_FIELDS = "EXTRA_FIELDS"
const val ACTION_CLEAN_CLIPBOARD = "ACTION_CLEAN_CLIPBOARD"
fun launchNotificationIfAllowed(context: Context, entry: EntryInfo) {
val containsUsernameToCopy = entry.username.isNotEmpty()
val containsPasswordToCopy = entry.password.isNotEmpty()
&& PreferencesUtil.allowCopyPasswordAndProtectedFields(context)
val containsExtraFieldToCopy = entry.customFields.isNotEmpty()
&& (entry.containsCustomFieldsNotProtected()
||
(entry.containsCustomFieldsProtected() && PreferencesUtil.allowCopyPasswordAndProtectedFields(context))
)
// If notifications enabled in settings
// Don't if application timeout
if (PreferencesUtil.isClipboardNotificationsEnable(context)) {
if (containsUsernameToCopy || containsPasswordToCopy || containsExtraFieldToCopy) {
// username already copied, waiting for user's action before copy password.
val intent = Intent(context, CopyingEntryNotificationService::class.java)
intent.action = ACTION_NEW_NOTIFICATION
intent.putExtra(EXTRA_ENTRY_TITLE, entry.title)
// Construct notification fields
val notificationFields = ArrayList<NotificationCopyingField>()
// Add username if exists to notifications
if (containsUsernameToCopy)
notificationFields.add(
NotificationCopyingField(
NotificationCopyingField.NotificationFieldId.USERNAME,
entry.username,
context.resources))
// Add password to notifications
if (containsPasswordToCopy) {
notificationFields.add(
NotificationCopyingField(
NotificationCopyingField.NotificationFieldId.PASSWORD,
entry.password,
context.resources))
}
// Add extra fields
if (containsExtraFieldToCopy) {
try {
var anonymousFieldNumber = 0
entry.customFields.forEach { field ->
//If value is not protected or allowed
if (!field.protectedValue.isProtected
|| PreferencesUtil.allowCopyPasswordAndProtectedFields(context)) {
notificationFields.add(
NotificationCopyingField(
NotificationCopyingField.NotificationFieldId.anonymousFieldId[anonymousFieldNumber],
field.protectedValue.toString(),
field.name,
context.resources))
anonymousFieldNumber++
}
}
} catch (e: ArrayIndexOutOfBoundsException) {
Log.w("NotificationEntryCopyMg", "Only " + NotificationCopyingField.NotificationFieldId.anonymousFieldId.size +
" anonymous notifications are available")
}
}
// Add notifications
intent.putParcelableArrayListExtra(EXTRA_FIELDS, notificationFields)
context.startService(intent)
}
}
}
}
}

View File

@@ -1,90 +0,0 @@
package com.kunzisoft.keepass.notifications
import android.content.Context
import android.content.Intent
import android.util.Log
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.EntryVersioned
import com.kunzisoft.keepass.database.element.security.ProtectedString
import com.kunzisoft.keepass.settings.PreferencesUtil
import java.util.*
object NotificationEntryCopyManager {
fun launchNotificationIfAllowed(context: Context, firstLaunch: Boolean, entry: EntryVersioned) {
// Start to manage field reference to copy a value from ref
val database = Database.getInstance()
database.startManageEntry(entry)
val containsUsernameToCopy = entry.username.isNotEmpty()
val containsPasswordToCopy = entry.password.isNotEmpty()
&& PreferencesUtil.allowCopyPasswordAndProtectedFields(context)
val containsExtraFieldToCopy = entry.allowExtraFields()
&& (entry.containsCustomFields()
&& entry.containsCustomFieldsNotProtected()
|| (entry.containsCustomFields()
&& entry.containsCustomFieldsProtected()
&& PreferencesUtil.allowCopyPasswordAndProtectedFields(context))
)
// If notifications enabled in settings
// Don't if application timeout
if (firstLaunch
&& PreferencesUtil.isClipboardNotificationsEnable(context.applicationContext)) {
if (containsUsernameToCopy || containsPasswordToCopy || containsExtraFieldToCopy) {
// username already copied, waiting for user's action before copy password.
val intent = Intent(context, CopyingEntryNotificationService::class.java)
intent.action = CopyingEntryNotificationService.ACTION_NEW_NOTIFICATION
intent.putExtra(CopyingEntryNotificationService.EXTRA_ENTRY_TITLE, entry.title)
// Construct notification fields
val notificationFields = ArrayList<NotificationCopyingField>()
// Add username if exists to notifications
if (containsUsernameToCopy)
notificationFields.add(
NotificationCopyingField(
NotificationCopyingField.NotificationFieldId.USERNAME,
entry.username,
context.resources))
// Add password to notifications
if (containsPasswordToCopy) {
notificationFields.add(
NotificationCopyingField(
NotificationCopyingField.NotificationFieldId.PASSWORD,
entry.password,
context.resources))
}
// Add extra fields
if (containsExtraFieldToCopy) {
try {
entry.fields.doActionToAllCustomProtectedField(object : (String, ProtectedString) -> Unit {
private var anonymousFieldNumber = 0
override fun invoke(key: String, value: ProtectedString) {
//If value is not protected or allowed
if (!value.isProtected
|| PreferencesUtil.allowCopyPasswordAndProtectedFields(context)) {
notificationFields.add(
NotificationCopyingField(
NotificationCopyingField.NotificationFieldId.anonymousFieldId[anonymousFieldNumber],
value.toString(),
key,
context.resources))
anonymousFieldNumber++
}
}
})
} catch (e: ArrayIndexOutOfBoundsException) {
Log.w("NotificationEntryCopyMg", "Only " + NotificationCopyingField.NotificationFieldId.anonymousFieldId.size +
" anonymous notifications are available")
}
}
// Add notifications
intent.putParcelableArrayListExtra(CopyingEntryNotificationService.EXTRA_FIELDS, notificationFields)
context.startService(intent)
}
}
database.stopManageEntry(entry)
}
}

View File

@@ -51,8 +51,9 @@ abstract class NotificationService : Service() {
.setVisibility(NotificationCompat.VISIBILITY_SECRET)
}
companion object {
const val CHANNEL_ID_KEEPASS = "com.kunzisoft.keepass.notification.channel"
const val CHANNEL_NAME_KEEPASS = "KeePass DX notification"
const val GROUP_KEEPASS = "GROUP_KEEPASS"
}
}
const val CHANNEL_ID_KEEPASS = "com.kunzisoft.keepass.notification.channel"
const val CHANNEL_NAME_KEEPASS = "KeePass DX notification"
const val GROUP_KEEPASS = "GROUP_KEEPASS"

View File

@@ -20,7 +20,6 @@
package com.kunzisoft.keepass.settings
import android.content.Context
import android.content.SharedPreferences
import android.preference.PreferenceManager
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.database.SortNodeEnum
@@ -218,25 +217,25 @@ object PreferencesUtil {
context.resources.getBoolean(R.bool.delete_entered_password_default))
}
fun enableKeyboardEntrySelection(context: Context): Boolean {
fun isKeyboardEntrySelectionEnable(context: Context): Boolean {
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
return prefs.getBoolean(context.getString(R.string.keyboard_selection_entry_key),
context.resources.getBoolean(R.bool.keyboard_selection_entry_default))
}
fun enableKeyboardNotificationEntry(context: Context): Boolean {
fun isKeyboardNotificationEntryEnable(context: Context): Boolean {
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
return prefs.getBoolean(context.getString(R.string.keyboard_notification_entry_key),
context.resources.getBoolean(R.bool.keyboard_notification_entry_default))
}
fun enableKeyboardVibration(context: Context): Boolean {
fun isKeyboardVibrationEnable(context: Context): Boolean {
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
return prefs.getBoolean(context.getString(R.string.keyboard_key_vibrate_key),
context.resources.getBoolean(R.bool.keyboard_key_vibrate_default))
}
fun enableKeyboardSound(context: Context): Boolean {
fun isKeyboardSoundEnable(context: Context): Boolean {
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
return prefs.getBoolean(context.getString(R.string.keyboard_key_sound_key),
context.resources.getBoolean(R.bool.keyboard_key_sound_default))