Merge branch 'feature/Database_Notification' into develop

This commit is contained in:
J-Jamet
2019-10-29 10:13:23 +01:00
27 changed files with 303 additions and 81 deletions

View File

@@ -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"

View File

@@ -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) {

View File

@@ -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)
}
}
}

View File

@@ -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"
}
}

View File

@@ -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)

View File

@@ -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)
}
}

View File

@@ -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

View File

@@ -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
}
}

View File

@@ -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()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1012 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 897 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 752 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 657 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 619 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -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>

View 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

View 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