Merge branch 'feature/Database_Notification' into develop
@@ -141,7 +141,10 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
|
||||
<service
|
||||
android:name="com.kunzisoft.keepass.notifications.DatabaseOpenNotificationService"
|
||||
android:enabled="true"
|
||||
android:exported="false" />
|
||||
<service
|
||||
android:name="com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService"
|
||||
android:enabled="true"
|
||||
|
||||
@@ -20,12 +20,14 @@
|
||||
package com.kunzisoft.keepass.database.action
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import com.kunzisoft.keepass.app.database.CipherDatabaseAction
|
||||
import com.kunzisoft.keepass.app.database.CipherDatabaseEntity
|
||||
import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.database.exception.LoadDatabaseException
|
||||
import com.kunzisoft.keepass.notifications.DatabaseOpenNotificationService
|
||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
|
||||
@@ -82,6 +84,9 @@ class LoadDatabaseRunnable(private val context: Context,
|
||||
catch (e: LoadDatabaseException) {
|
||||
finishRun(false, e)
|
||||
}
|
||||
|
||||
// Start the opening notification
|
||||
context.startService(Intent(context, DatabaseOpenNotificationService::class.java))
|
||||
}
|
||||
|
||||
override fun onFinishRun(result: Result) {
|
||||
|
||||
@@ -29,17 +29,16 @@ 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
|
||||
import com.kunzisoft.keepass.timeout.TimeoutHelper.NEVER
|
||||
import com.kunzisoft.keepass.utils.LOCK_ACTION
|
||||
import java.util.*
|
||||
|
||||
class ClipboardEntryNotificationService : EntryNotificationService() {
|
||||
|
||||
override var notificationId = 485
|
||||
private var cleanNotificationTimerTask: Thread? = null
|
||||
private var notificationTimeoutMilliSecs: Long = 0
|
||||
class ClipboardEntryNotificationService : LockNotificationService() {
|
||||
|
||||
override val notificationId = 485
|
||||
private var clipboardHelper: ClipboardHelper? = null
|
||||
private var notificationTimeoutMilliSecs: Long = 0
|
||||
private var cleanCopyNotificationTimerTask: Thread? = null
|
||||
|
||||
override fun onCreate() {
|
||||
@@ -59,10 +58,9 @@ class ClipboardEntryNotificationService : EntryNotificationService() {
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
//Get settings
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
val timeoutClipboardClear = prefs.getString(getString(R.string.clipboard_timeout_key),
|
||||
getString(R.string.clipboard_timeout_default)) ?: "6000"
|
||||
notificationTimeoutMilliSecs = java.lang.Long.parseLong(timeoutClipboardClear)
|
||||
notificationTimeoutMilliSecs = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
.getString(getString(R.string.clipboard_timeout_key),
|
||||
getString(R.string.clipboard_timeout_default))?.toLong() ?: TimeoutHelper.DEFAULT_TIMEOUT
|
||||
|
||||
when {
|
||||
intent == null -> Log.w(TAG, "null intent")
|
||||
@@ -72,11 +70,7 @@ class ClipboardEntryNotificationService : EntryNotificationService() {
|
||||
}
|
||||
ACTION_CLEAN_CLIPBOARD == intent.action -> {
|
||||
stopTask(cleanCopyNotificationTimerTask)
|
||||
try {
|
||||
clipboardHelper?.cleanClipboard()
|
||||
} catch (e: SamsungClipboardException) {
|
||||
Log.e(TAG, "Clipboard can't be cleaned", e)
|
||||
}
|
||||
cleanClipboard()
|
||||
stopNotificationAndSendLockIfNeeded()
|
||||
}
|
||||
else -> for (actionKey in ClipboardEntryNotificationField.allActionKeys) {
|
||||
@@ -135,29 +129,11 @@ class ClipboardEntryNotificationService : EntryNotificationService() {
|
||||
getCopyPendingIntent(fieldToAdd, fieldsToAdd))
|
||||
}
|
||||
}
|
||||
|
||||
notificationManager?.cancel(notificationId)
|
||||
notificationManager?.notify(++notificationId, builder.build())
|
||||
|
||||
val myNotificationId = notificationId
|
||||
stopTask(cleanNotificationTimerTask)
|
||||
// If timer
|
||||
if (notificationTimeoutMilliSecs != NEVER) {
|
||||
cleanNotificationTimerTask = Thread {
|
||||
try {
|
||||
Thread.sleep(notificationTimeoutMilliSecs)
|
||||
} catch (e: InterruptedException) {
|
||||
cleanNotificationTimerTask = null
|
||||
}
|
||||
notificationManager?.cancel(myNotificationId)
|
||||
}
|
||||
cleanNotificationTimerTask?.start()
|
||||
}
|
||||
notificationManager?.notify(notificationId, builder.build())
|
||||
}
|
||||
|
||||
private fun copyField(fieldToCopy: ClipboardEntryNotificationField, nextFields: ArrayList<ClipboardEntryNotificationField>) {
|
||||
stopTask(cleanCopyNotificationTimerTask)
|
||||
stopTask(cleanNotificationTimerTask)
|
||||
|
||||
try {
|
||||
clipboardHelper?.copyToClipboard(fieldToCopy.label, fieldToCopy.value)
|
||||
@@ -204,11 +180,7 @@ class ClipboardEntryNotificationService : EntryNotificationService() {
|
||||
notificationManager?.cancel(myNotificationId)
|
||||
// Clean password only if no next field
|
||||
if (nextFields.size <= 0)
|
||||
try {
|
||||
clipboardHelper?.cleanClipboard()
|
||||
} catch (e: SamsungClipboardException) {
|
||||
Log.e(TAG, "Clipboard can't be cleaned", e)
|
||||
}
|
||||
cleanClipboard()
|
||||
}
|
||||
cleanCopyNotificationTimerTask?.start()
|
||||
} else {
|
||||
@@ -222,12 +194,25 @@ class ClipboardEntryNotificationService : EntryNotificationService() {
|
||||
|
||||
}
|
||||
|
||||
private fun cleanClipboard() {
|
||||
try {
|
||||
clipboardHelper?.cleanClipboard()
|
||||
} catch (e: SamsungClipboardException) {
|
||||
Log.e(TAG, "Clipboard can't be cleaned", e)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTaskRemoved(rootIntent: Intent?) {
|
||||
cleanClipboard()
|
||||
|
||||
super.onTaskRemoved(rootIntent)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
cleanClipboard()
|
||||
|
||||
stopTask(cleanCopyNotificationTimerTask)
|
||||
stopTask(cleanNotificationTimerTask)
|
||||
cleanCopyNotificationTimerTask = null
|
||||
cleanNotificationTimerTask = null
|
||||
|
||||
super.onDestroy()
|
||||
}
|
||||
@@ -252,13 +237,15 @@ class ClipboardEntryNotificationService : EntryNotificationService() {
|
||||
(entry.containsCustomFieldsProtected() && PreferencesUtil.allowCopyPasswordAndProtectedFields(context))
|
||||
)
|
||||
|
||||
var startService = false
|
||||
val intent = Intent(context, ClipboardEntryNotificationService::class.java)
|
||||
|
||||
// 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, ClipboardEntryNotificationService::class.java)
|
||||
intent.action = ACTION_NEW_NOTIFICATION
|
||||
intent.putExtra(EXTRA_ENTRY_TITLE, entry.title)
|
||||
// Construct notification fields
|
||||
@@ -302,10 +289,14 @@ class ClipboardEntryNotificationService : EntryNotificationService() {
|
||||
|
||||
}
|
||||
// Add notifications
|
||||
startService = true
|
||||
intent.putParcelableArrayListExtra(EXTRA_FIELDS, notificationFields)
|
||||
context.startService(intent)
|
||||
}
|
||||
}
|
||||
|
||||
if (!startService)
|
||||
context.stopService(intent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.kunzisoft.keepass.notifications
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.activities.GroupActivity
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.utils.LOCK_ACTION
|
||||
|
||||
class DatabaseOpenNotificationService: LockNotificationService() {
|
||||
|
||||
override val notificationId: Int = 340
|
||||
|
||||
private fun stopNotificationAndSendLock() {
|
||||
// Send lock action
|
||||
sendBroadcast(Intent(LOCK_ACTION))
|
||||
// Stop the service
|
||||
stopSelf()
|
||||
}
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
|
||||
when(intent?.action) {
|
||||
ACTION_CLOSE_DATABASE -> {
|
||||
stopNotificationAndSendLock()
|
||||
}
|
||||
else -> {
|
||||
val databaseIntent = Intent(this, GroupActivity::class.java)
|
||||
var pendingDatabaseFlag = 0
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
pendingDatabaseFlag = PendingIntent.FLAG_IMMUTABLE
|
||||
}
|
||||
val pendingDatabaseIntent = PendingIntent.getActivity(this, 0, databaseIntent, pendingDatabaseFlag)
|
||||
val deleteIntent = Intent(this, DatabaseOpenNotificationService::class.java).apply {
|
||||
action = ACTION_CLOSE_DATABASE
|
||||
}
|
||||
val pendingDeleteIntent = PendingIntent.getService(this, 0, deleteIntent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
|
||||
val database = Database.getInstance()
|
||||
if (database.loaded) {
|
||||
notificationManager?.notify(notificationId, buildNewNotification().apply {
|
||||
setSmallIcon(R.drawable.notification_ic_database_open)
|
||||
setContentTitle(getString(R.string.database_opened))
|
||||
setContentText(database.name + " (" + database.version + ")")
|
||||
setAutoCancel(false)
|
||||
setContentIntent(pendingDatabaseIntent)
|
||||
setDeleteIntent(pendingDeleteIntent)
|
||||
}.build())
|
||||
} else {
|
||||
stopSelf()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return START_STICKY
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val ACTION_CLOSE_DATABASE = "ACTION_CLOSE_DATABASE"
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,7 +26,7 @@ import kotlin.collections.ArrayList
|
||||
|
||||
class DatabaseTaskNotificationService : NotificationService(), ProgressTaskUpdater {
|
||||
|
||||
override var notificationId: Int = 575
|
||||
override val notificationId: Int = 575
|
||||
|
||||
private var actionRunnableAsyncTask: ActionRunnableAsyncTask? = null
|
||||
|
||||
@@ -154,7 +154,7 @@ class DatabaseTaskNotificationService : NotificationService(), ProgressTaskUpdat
|
||||
private fun newNotification(title: Int) {
|
||||
|
||||
val builder = buildNewNotification()
|
||||
.setSmallIcon(R.drawable.notification_ic_data_usage_24dp)
|
||||
.setSmallIcon(R.drawable.notification_ic_database_load)
|
||||
.setContentTitle(getString(title))
|
||||
.setAutoCancel(false)
|
||||
.setContentIntent(null)
|
||||
|
||||
@@ -12,21 +12,17 @@ import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||
import com.kunzisoft.keepass.timeout.TimeoutHelper
|
||||
import com.kunzisoft.keepass.utils.LOCK_ACTION
|
||||
|
||||
class KeyboardEntryNotificationService : EntryNotificationService() {
|
||||
class KeyboardEntryNotificationService : LockNotificationService() {
|
||||
|
||||
override var notificationId = 486
|
||||
override val notificationId = 486
|
||||
private var cleanNotificationTimerTask: Thread? = null
|
||||
private var notificationTimeoutMilliSecs: Long = 0
|
||||
|
||||
private var pendingDeleteIntent: PendingIntent? = null
|
||||
|
||||
private fun stopNotificationAndSendLockIfNeeded() {
|
||||
// Remove the entry from the keyboard
|
||||
MagikIME.removeEntry(this)
|
||||
// Clear the entry if define in preferences
|
||||
val sharedPreferences = android.preference.PreferenceManager.getDefaultSharedPreferences(this)
|
||||
if (sharedPreferences.getBoolean(getString(R.string.keyboard_notification_entry_clear_close_key),
|
||||
resources.getBoolean(R.bool.keyboard_notification_entry_clear_close_default))) {
|
||||
if (PreferencesUtil.isClearKeyboardNotificationEnable(this)) {
|
||||
sendBroadcast(Intent(LOCK_ACTION))
|
||||
}
|
||||
// Stop the service
|
||||
@@ -34,6 +30,11 @@ class KeyboardEntryNotificationService : EntryNotificationService() {
|
||||
}
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
//Get settings
|
||||
notificationTimeoutMilliSecs = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
.getString(getString(R.string.keyboard_entry_timeout_key),
|
||||
getString(R.string.timeout_default))?.toLong() ?: TimeoutHelper.DEFAULT_TIMEOUT
|
||||
|
||||
when {
|
||||
intent == null -> Log.w(TAG, "null intent")
|
||||
ACTION_CLEAN_KEYBOARD_ENTRY == intent.action -> {
|
||||
@@ -74,22 +75,9 @@ class KeyboardEntryNotificationService : EntryNotificationService() {
|
||||
notificationManager?.cancel(notificationId)
|
||||
notificationManager?.notify(notificationId, builder.build())
|
||||
|
||||
|
||||
stopTask(cleanNotificationTimerTask)
|
||||
// Timeout only if notification clear is available
|
||||
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
if (sharedPreferences.getBoolean(getString(R.string.keyboard_notification_entry_clear_close_key),
|
||||
resources.getBoolean(R.bool.keyboard_notification_entry_clear_close_default))) {
|
||||
val keyboardTimeout = sharedPreferences.getString(getString(R.string.keyboard_entry_timeout_key),
|
||||
getString(R.string.timeout_default))
|
||||
notificationTimeoutMilliSecs = try {
|
||||
keyboardTimeout?.let {
|
||||
java.lang.Long.parseLong(keyboardTimeout)
|
||||
} ?: 0
|
||||
} catch (e: NumberFormatException) {
|
||||
TimeoutHelper.DEFAULT_TIMEOUT
|
||||
}
|
||||
|
||||
if (PreferencesUtil.isClearKeyboardNotificationEnable(this)) {
|
||||
if (notificationTimeoutMilliSecs != TimeoutHelper.NEVER) {
|
||||
cleanNotificationTimerTask = Thread {
|
||||
val maxPos = 100
|
||||
@@ -112,7 +100,15 @@ class KeyboardEntryNotificationService : EntryNotificationService() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTaskRemoved(rootIntent: Intent?) {
|
||||
MagikIME.removeEntry(this)
|
||||
|
||||
super.onTaskRemoved(rootIntent)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
// Remove the entry from the keyboard
|
||||
MagikIME.removeEntry(this)
|
||||
|
||||
stopTask(cleanNotificationTimerTask)
|
||||
cleanNotificationTimerTask = null
|
||||
@@ -130,12 +126,26 @@ class KeyboardEntryNotificationService : EntryNotificationService() {
|
||||
const val ACTION_CLEAN_KEYBOARD_ENTRY = "ACTION_CLEAN_KEYBOARD_ENTRY"
|
||||
|
||||
fun launchNotificationIfAllowed(context: Context, entry: EntryInfo) {
|
||||
|
||||
val containsUsernameToCopy = entry.username.isNotEmpty()
|
||||
val containsPasswordToCopy = entry.password.isNotEmpty()
|
||||
val containsExtraFieldToCopy = entry.customFields.isNotEmpty()
|
||||
|
||||
var startService = false
|
||||
val intent = Intent(context, KeyboardEntryNotificationService::class.java)
|
||||
|
||||
// Show the notification if allowed in Preferences
|
||||
if (PreferencesUtil.isKeyboardNotificationEntryEnable(context)) {
|
||||
context.startService(Intent(context, KeyboardEntryNotificationService::class.java).apply {
|
||||
putExtra(ENTRY_INFO_KEY, entry)
|
||||
})
|
||||
if (containsUsernameToCopy || containsPasswordToCopy || containsExtraFieldToCopy) {
|
||||
startService = true
|
||||
context.startService(intent.apply {
|
||||
putExtra(ENTRY_INFO_KEY, entry)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (!startService)
|
||||
context.stopService(intent)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import com.kunzisoft.keepass.utils.LOCK_ACTION
|
||||
|
||||
abstract class EntryNotificationService : NotificationService() {
|
||||
abstract class LockNotificationService : NotificationService() {
|
||||
|
||||
private var lockBroadcastReceiver: BroadcastReceiver? = null
|
||||
|
||||
@@ -3,22 +3,22 @@ package com.kunzisoft.keepass.notifications
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.app.Service
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.IBinder
|
||||
import androidx.core.app.NotificationCompat
|
||||
import android.util.TypedValue
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.activities.stylish.Stylish
|
||||
|
||||
|
||||
abstract class NotificationService : Service() {
|
||||
|
||||
protected var notificationManager: NotificationManager? = null
|
||||
protected var notificationManager: NotificationManagerCompat? = null
|
||||
private var colorNotificationAccent: Int = 0
|
||||
|
||||
protected abstract var notificationId: Int
|
||||
protected abstract val notificationId: Int
|
||||
|
||||
override fun onBind(intent: Intent): IBinder? {
|
||||
return null
|
||||
@@ -27,17 +27,19 @@ abstract class NotificationService : Service() {
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
notificationManager = NotificationManagerCompat.from(this)
|
||||
|
||||
// Create notification channel for Oreo+
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val channel = NotificationChannel(CHANNEL_ID_KEEPASS,
|
||||
CHANNEL_NAME_KEEPASS,
|
||||
NotificationManager.IMPORTANCE_DEFAULT).apply {
|
||||
enableVibration(false)
|
||||
setSound(null, null)
|
||||
if (notificationManager?.getNotificationChannel(CHANNEL_ID_KEEPASS) == null) {
|
||||
val channel = NotificationChannel(CHANNEL_ID_KEEPASS,
|
||||
CHANNEL_NAME_KEEPASS,
|
||||
NotificationManager.IMPORTANCE_DEFAULT).apply {
|
||||
enableVibration(false)
|
||||
setSound(null, null)
|
||||
}
|
||||
notificationManager?.createNotificationChannel(channel)
|
||||
}
|
||||
notificationManager?.createNotificationChannel(channel)
|
||||
}
|
||||
|
||||
// Get the color
|
||||
@@ -51,11 +53,19 @@ abstract class NotificationService : Service() {
|
||||
protected fun buildNewNotification(): NotificationCompat.Builder {
|
||||
return NotificationCompat.Builder(this, CHANNEL_ID_KEEPASS)
|
||||
.setColor(colorNotificationAccent)
|
||||
.setGroup(GROUP_KEEPASS)
|
||||
//TODO .setGroup(GROUP_KEEPASS)
|
||||
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_SECRET)
|
||||
}
|
||||
|
||||
// TODO only for > lollipop
|
||||
protected fun buildSummaryNotification(): NotificationCompat.Builder {
|
||||
return buildNewNotification().apply {
|
||||
// TODO Ic setSmallIcon(R.drawable.notification_ic_data_usage_24dp)
|
||||
setGroupSummary(true)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
notificationManager?.cancel(notificationId)
|
||||
|
||||
@@ -66,5 +76,6 @@ abstract class NotificationService : Service() {
|
||||
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 SUMMARY_ID = 0
|
||||
}
|
||||
}
|
||||
@@ -209,6 +209,12 @@ object PreferencesUtil {
|
||||
context.resources.getBoolean(R.bool.clear_clipboard_notification_default))
|
||||
}
|
||||
|
||||
fun isClearKeyboardNotificationEnable(context: Context): Boolean {
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
return prefs.getBoolean(context.getString(R.string.keyboard_notification_entry_clear_close_key),
|
||||
context.resources.getBoolean(R.bool.keyboard_notification_entry_clear_close_default))
|
||||
}
|
||||
|
||||
fun setAllowCopyPasswordAndProtectedFields(context: Context, allowCopy: Boolean) {
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
prefs.edit()
|
||||
|
||||
|
Before Width: | Height: | Size: 1012 B |
BIN
app/src/main/res/drawable-hdpi/notification_ic_database_load.png
Normal file
|
After Width: | Height: | Size: 897 B |
BIN
app/src/main/res/drawable-hdpi/notification_ic_database_open.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 752 B |
BIN
app/src/main/res/drawable-mdpi/notification_ic_database_load.png
Normal file
|
After Width: | Height: | Size: 657 B |
BIN
app/src/main/res/drawable-mdpi/notification_ic_database_open.png
Normal file
|
After Width: | Height: | Size: 619 B |
|
Before Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
@@ -257,6 +257,7 @@
|
||||
<string name="password_size_summary">Sets default size of the generated passwords</string>
|
||||
<string name="list_password_generator_options_title">Password characters</string>
|
||||
<string name="list_password_generator_options_summary">Set allowed password generator characters</string>
|
||||
<string name="database_opened">Database opened</string>
|
||||
<string name="clipboard">Clipboard</string>
|
||||
<string name="clipboard_notifications_title">Clipboard notifications</string>
|
||||
<string name="clipboard_notifications_summary">Enable clipboard notifications to copy fields when viewing an entry</string>
|
||||
|
||||
68
art/ic_database_loading_notification.svg
Normal file
@@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
inkscape:export-ydpi="22.5"
|
||||
inkscape:export-xdpi="22.5"
|
||||
inkscape:export-filename="/home/joker/Projects/Kunzisoft/Inkscape/Android Icon Template_low"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.92.1 r15371"
|
||||
xml:space="preserve"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
sodipodi:docname="ic_database_loading_notification.svg"><sodipodi:namedview
|
||||
pagecolor="#d7d7d7"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1043"
|
||||
id="namedview4"
|
||||
showgrid="true"
|
||||
inkscape:zoom="8.0000005"
|
||||
inkscape:cx="-6.6979566"
|
||||
inkscape:cy="13.688469"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="47"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer4"
|
||||
showguides="false"
|
||||
inkscape:pagecheckerboard="true"><inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid4818" /></sodipodi:namedview><metadata
|
||||
id="metadata8"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs6" /><g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer4"
|
||||
inkscape:label="Work"
|
||||
style="display:inline"
|
||||
transform="translate(0,-169.5)"><path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:3.60014629;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="M 12 3 A 9 9 0 0 0 8.21875 3.84375 L 9.2226562 5.5839844 A 7 7 0 0 1 12 5 A 7 7 0 0 1 19 12 A 7 7 0 0 1 18.728516 13.90625 L 20.785156 13.90625 A 9 9 0 0 0 21 12 A 9 9 0 0 0 12 3 z M 6.5097656 4.8828125 A 9 9 0 0 0 3 12 A 9 9 0 0 0 4.0820312 16.273438 C 4.2366214 15.324774 4.6246965 14.418539 5.2070312 13.654297 A 7 7 0 0 1 5 12 A 7 7 0 0 1 7.5195312 6.6308594 L 6.5097656 4.8828125 z "
|
||||
transform="translate(0,169.5)"
|
||||
id="path4524" /><g
|
||||
style="stroke-width:280.000084;stroke-miterlimit:4;stroke-dasharray:none;stroke:#0a0a0a;stroke-opacity:1;fill:none"
|
||||
transform="matrix(0.01428571,0,0,0.01428571,5.8571429,179.54429)"
|
||||
id="g4531" /><g
|
||||
id="g4824"
|
||||
transform="matrix(0.01428571,0,0,0.01428571,-14.142857,179.54429)"
|
||||
style="stroke:none;stroke-width:280.00009155;stroke-miterlimit:4;stroke-dasharray:none"><path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:280.00009155;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
inkscape:connector-curvature="0"
|
||||
d="m 1676.9004,589.6 c 23.6,0 44.5,-9 62.6,-27.1 18.1,-18.1 27.1,-38.9 27.1,-62.6 0,-23.6 -9,-44.5 -27.1,-62.6 -18.1,-18.1 -38.9,-27.1 -62.6,-27.1 -23.6,0 -44.1,9 -61.5,27.1 -17.4,18.1 -26.1,38.9 -26.1,62.6 0,23.6 8.7,44.5 26.1,62.6 17.4,18.1 37.9,27.1 61.5,27.1 z m 252.3,-179.3 h 460.8 v 179.3 h -89.7 v 177.2 h -177.2 V 589.7 h -193.9 c -18.1,52.8 -50,95.6 -95.9,128.2 -45.9,32.7 -98,49 -156.4,49 -73.7,0 -136.6,-26.1 -188.7,-78.2 -52.1,-52.1 -78.2,-115 -78.2,-188.7 0,-73.7 26.1,-136.6 78.2,-188.7 52.1,-52.1 115,-78.2 188.7,-78.2 58.4,0 110.5,16.3 156.4,49 45.9,32.7 77.8,75.4 95.9,128.2 z"
|
||||
id="path4836" /></g></g></svg>
|
||||
|
After Width: | Height: | Size: 3.7 KiB |
64
art/ic_database_open_notification.svg
Normal file
@@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
inkscape:export-ydpi="22.5"
|
||||
inkscape:export-xdpi="22.5"
|
||||
inkscape:export-filename="/home/joker/Projects/Kunzisoft/Inkscape/Android Icon Template_low"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.92.1 r15371"
|
||||
xml:space="preserve"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
sodipodi:docname="ic_database_open_notification.svg"><sodipodi:namedview
|
||||
pagecolor="#d7d7d7"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1043"
|
||||
id="namedview4"
|
||||
showgrid="true"
|
||||
inkscape:zoom="22.627418"
|
||||
inkscape:cx="13.649551"
|
||||
inkscape:cy="13.821457"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="47"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer4"
|
||||
showguides="false"
|
||||
inkscape:pagecheckerboard="true"><inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid4818" /></sodipodi:namedview><metadata
|
||||
id="metadata8"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs6" /><g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer4"
|
||||
inkscape:label="Work"
|
||||
style="display:inline"
|
||||
transform="translate(0,-169.5)"><g
|
||||
style="stroke-width:280.000084;stroke-miterlimit:4;stroke-dasharray:none;stroke:#0a0a0a;stroke-opacity:1;fill:none"
|
||||
transform="matrix(0.01428571,0,0,0.01428571,5.8571429,179.54429)"
|
||||
id="g4531" /><path
|
||||
style="stroke-width:0.02143062;fill:#ffffff;fill-opacity:1"
|
||||
d="M 12 2 C 9.592834 2 7.5684489 3.7254014 7.1015625 6 L 9.0839844 6 C 9.5058542 4.7867337 10.652056 3.8984375 12.001953 3.8984375 C 13.70139 3.8984375 15.103516 5.2984 15.103516 7 L 15.103516 9 L 6 9 C 4.9006164 9 4 9.8986566 4 10.998047 L 4 14 L 4.9589844 14 C 5.1749868 13.669015 5.4230196 13.358232 5.703125 13.078125 C 6.780379 12.000875 8.287459 11.375 9.8125 11.375 C 11.018931 11.375 12.224623 11.745422 13.207031 12.445312 C 13.763824 12.841985 14.247164 13.34112 14.630859 13.90625 L 20 13.90625 L 20 11 C 20 9.9006 19.099383 9.0019531 18 9.0019531 L 17 9.0019531 L 17 7.0019531 C 17 4.2502431 14.749531 2 12 2 z M 9.8125 13.375 C 8.7596432 13.375 7.861473 13.747902 7.1171875 14.492188 C 6.372902 15.236473 6 16.134643 6 17.1875 C 6 18.240357 6.372902 19.138527 7.1171875 19.882812 C 7.861473 20.627098 8.7596432 21 9.8125 21 C 10.646785 21 11.391161 20.767924 12.046875 20.300781 C 12.702589 19.835067 13.159397 19.223035 13.417969 18.46875 L 16.1875 18.46875 L 16.1875 20.998047 L 18.71875 20.998047 L 18.71875 18.466797 L 20 18.466797 L 20 15.90625 L 13.417969 15.90625 C 13.159397 15.151965 12.702589 14.541361 12.046875 14.074219 C 11.391161 13.607076 10.646785 13.375 9.8125 13.375 z M 9.8125 15.904297 C 10.151071 15.904297 10.44846 16.032444 10.707031 16.291016 C 10.965603 16.549587 11.09375 16.848404 11.09375 17.185547 C 11.09375 17.524118 10.965603 17.821507 10.707031 18.080078 C 10.44846 18.338649 10.149643 18.466797 9.8125 18.466797 C 9.4753572 18.466797 9.1821651 18.338649 8.9335938 18.080078 C 8.6850224 17.821507 8.5605469 17.52269 8.5605469 17.185547 C 8.5605469 16.846976 8.6850224 16.549587 8.9335938 16.291016 C 9.1821651 16.032444 9.4753572 15.904297 9.8125 15.904297 z "
|
||||
transform="translate(0,169.5)"
|
||||
id="path4566" /><g
|
||||
style="stroke:none;stroke-width:280.00009155;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
transform="matrix(0.01428571,0,0,0.01428571,5.857143,179.54429)"
|
||||
id="g4580" /></g></svg>
|
||||
|
After Width: | Height: | Size: 4.1 KiB |