fix: Add user verification device credential setting #2283

This commit is contained in:
J-Jamet
2025-12-02 16:55:42 +01:00
parent 762ac8f77b
commit 09ef69e6ae
13 changed files with 173 additions and 60 deletions

View File

@@ -15,6 +15,7 @@ import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.dialogs.CheckDatabaseCredentialDialogFragment
import com.kunzisoft.keepass.credentialprovider.passkey.data.UserVerificationRequirement
import com.kunzisoft.keepass.model.EntryInfo
import com.kunzisoft.keepass.settings.PreferencesUtil.isUserVerificationDeviceCredential
import com.kunzisoft.keepass.utils.getEnumExtra
import com.kunzisoft.keepass.utils.putEnumExtra
import com.kunzisoft.keepass.view.toastError
@@ -96,11 +97,7 @@ class UserVerificationHelper {
userVerificationViewModel: UserVerificationViewModel,
dataToVerify: UserVerificationData
) {
if (context?.isAuthenticatorsAllowed() == true) {
activity?.showUserVerificationDeviceCredential(userVerificationViewModel, dataToVerify)
} else {
activity?.showUserVerificationDatabaseCredential(userVerificationViewModel, dataToVerify)
}
activity?.checkUserVerification(userVerificationViewModel, dataToVerify)
}
/**
@@ -110,7 +107,7 @@ class UserVerificationHelper {
userVerificationViewModel: UserVerificationViewModel,
dataToVerify: UserVerificationData
) {
if (isAuthenticatorsAllowed()) {
if (isAuthenticatorsAllowed() && isUserVerificationDeviceCredential(this)) {
showUserVerificationDeviceCredential(userVerificationViewModel, dataToVerify)
} else {
showUserVerificationDatabaseCredential(userVerificationViewModel, dataToVerify)

View File

@@ -60,7 +60,7 @@ import com.kunzisoft.keepass.database.ContextualDatabase
import com.kunzisoft.keepass.model.AppOrigin
import com.kunzisoft.keepass.model.SearchInfo
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_ENTRY_TASK
import com.kunzisoft.keepass.settings.PreferencesUtil.isPasskeyUserVerificationPreferred
import com.kunzisoft.keepass.settings.PreferencesUtil.isUserVerificationPreferred
import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.utils.AppUtil.randomRequestCode
import com.kunzisoft.keepass.view.toastError
@@ -202,7 +202,7 @@ class PasskeyLauncherActivity : DatabaseLockActivity() {
super.onUnknownDatabaseRetrieved(database)
// To manage https://github.com/Kunzisoft/KeePassDX/issues/2283
val userVerificationNeeded = intent.isUserVerificationNeeded(
userVerificationPreferred = isPasskeyUserVerificationPreferred(this)
userVerificationPreferred = isUserVerificationPreferred(this)
) && intent.getUserVerifiedWithAuth().not()
if (userVerificationNeeded) {
checkUserVerification(

View File

@@ -302,7 +302,7 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
biometricUnlockEnablePreference.isChecked = false
warningMessage(activity, keystoreWarning = true, deleteKeys = true) {
biometricUnlockEnablePreference.isChecked = true
deviceCredentialUnlockEnablePreference?.isChecked = false
deviceCredentialUnlockEnablePreference.isChecked = false
}
} else {
biometricUnlockEnablePreference.isChecked = false
@@ -349,7 +349,7 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
deviceCredentialUnlockEnablePreference.isChecked = false
warningMessage(activity, keystoreWarning = true, deleteKeys = true) {
deviceCredentialUnlockEnablePreference.isChecked = true
biometricUnlockEnablePreference?.isChecked = false
biometricUnlockEnablePreference.isChecked = false
}
} else {
deviceCredentialUnlockEnablePreference.isChecked = false
@@ -523,27 +523,23 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
}
override fun onDisplayPreferenceDialog(preference: Preference) {
var otherDialogFragment = false
var dialogFragment: DialogFragment? = null
// Main Preferences
when (preference.key) {
getString(R.string.app_timeout_key),
getString(R.string.clipboard_timeout_key),
getString(R.string.temp_device_unlock_timeout_key) -> {
dialogFragment = DurationDialogFragmentCompat.newInstance(preference.key)
}
else -> otherDialogFragment = true
else -> {}
}
if (dialogFragment != null) {
@Suppress("DEPRECATION")
dialogFragment.setTargetFragment(this, 0)
dialogFragment.show(parentFragmentManager, TAG_PREF_FRAGMENT)
}
// Could not be handled here. Try with the super method.
else if (otherDialogFragment) {
} else {
// Could not be handled here. Try with the super method.
super.onDisplayPreferenceDialog(preference)
}
}

View File

@@ -30,11 +30,17 @@ import com.kunzisoft.keepass.activities.dialogs.UnderDevelopmentFeatureDialogFra
abstract class NestedSettingsFragment : PreferenceFragmentCompat() {
enum class Screen {
APPLICATION, FORM_FILLING, DEVICE_UNLOCK, APPEARANCE, DATABASE, DATABASE_SECURITY, DATABASE_MASTER_KEY
APPLICATION,
FORM_FILLING,
DEVICE_UNLOCK,
APPEARANCE,
DATABASE,
DATABASE_SECURITY,
DATABASE_MASTER_KEY
}
fun getScreen(): Screen {
return Screen.values()[requireArguments().getInt(TAG_KEY)]
return Screen.entries[requireArguments().getInt(TAG_KEY)]
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
@@ -50,8 +56,7 @@ abstract class NestedSettingsFragment : PreferenceFragmentCompat() {
preferenceInDev.setOnPreferenceClickListener { preference ->
try { // don't check if we can
(preference as TwoStatePreference).isChecked = false
} catch (ignored: Exception) {
}
} catch (_: Exception) {}
UnderDevelopmentFeatureDialogFragment().show(parentFragmentManager, "underDevFeatureDialog")
false
}

View File

@@ -690,10 +690,16 @@ object PreferencesUtil {
context.resources.getBoolean(R.bool.passkeys_close_database_default))
}
fun isPasskeyUserVerificationPreferred(context: Context): Boolean {
fun isUserVerificationDeviceCredential(context: Context): Boolean {
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
return prefs.getBoolean(context.getString(R.string.passkeys_user_verification_preferred_key),
context.resources.getBoolean(R.bool.passkeys_user_verification_preferred_default))
return prefs.getBoolean(context.getString(R.string.user_verification_device_credential_key),
context.resources.getBoolean(R.bool.user_verification_device_credential_default))
}
fun isUserVerificationPreferred(context: Context): Boolean {
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
return prefs.getBoolean(context.getString(R.string.user_verification_preferred_key),
context.resources.getBoolean(R.bool.user_verification_preferred_default))
}
fun isPasskeyBackupEligibilityEnable(context: Context): Boolean {

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M11,4C9.95,4 9.063,4.363 8.338,5.088C7.613,5.813 7.25,6.7 7.25,7.75C7.25,8.8 7.613,9.687 8.338,10.412C9.063,11.137 9.95,11.5 11,11.5C12.05,11.5 12.937,11.137 13.662,10.412C14.387,9.687 14.75,8.8 14.75,7.75C14.75,6.7 14.387,5.813 13.662,5.088C12.937,4.363 12.05,4 11,4zM11,13.5C10.567,13.5 10.147,13.521 9.738,13.563C9.33,13.604 8.908,13.667 8.475,13.75C8.47,13.754 8.049,13.845 7.912,13.875C6.887,14.125 5.816,14.5 4.699,15C4.199,15.233 3.791,15.575 3.475,16.025C3.158,16.475 3,17.017 3,17.65L3,20L6,20L13.682,20C13.429,19.394 13.287,18.729 13.287,18.031C13.287,16.501 13.971,15.144 15.033,14.205C14.724,14.11 14.389,13.949 14.088,13.875C14.08,13.877 13.525,13.75 13.525,13.75C13.301,13.707 13.089,13.684 12.871,13.652C12.721,13.63 12.57,13.603 12.42,13.586C12.368,13.58 12.313,13.568 12.262,13.563C11.853,13.521 11.433,13.5 11,13.5zM18.414,14.445C16.433,14.445 14.826,16.05 14.826,18.031C14.826,20.012 16.433,21.619 18.414,21.619C20.395,21.619 22,20.012 22,18.031C22,16.05 20.395,14.445 18.414,14.445zM19.848,16.314C19.956,16.314 20.064,16.355 20.146,16.438L20.443,16.736C20.608,16.9 20.607,17.168 20.443,17.332L18.145,19.633C17.98,19.797 17.711,19.797 17.547,19.633L16.391,18.475C16.226,18.31 16.226,18.043 16.391,17.879L16.689,17.58C16.854,17.416 17.121,17.416 17.285,17.58L17.846,18.139L19.549,16.438C19.631,16.355 19.739,16.314 19.848,16.314z"
android:fillColor="#7D7D7D"/>
</vector>

View File

@@ -69,6 +69,8 @@
<bool name="allow_no_password_default" translatable="false">false</bool>
<string name="delete_entered_password_key" translatable="false">delete_entered_password_key</string>
<bool name="delete_entered_password_default" translatable="false">true</bool>
<string name="user_verification_device_credential_key" translatable="false">user_verification_device_credential_key</string>
<bool name="user_verification_device_credential_default" translatable="false">true</bool>
<string name="enable_auto_save_database_key" translatable="false">enable_auto_save_database_key</string>
<bool name="enable_auto_save_database_default" translatable="false">true</bool>
<string name="enable_keep_screen_on_key" translatable="false">enable_keep_screen_on_key</string>
@@ -77,8 +79,6 @@
<bool name="enable_screenshot_mode_key_default" translatable="false">false</bool>
<string name="auto_focus_search_key" translatable="false">auto_focus_search_key</string>
<bool name="auto_focus_search_default" translatable="false">false</bool>
<string name="subdomain_search_key" translatable="false">subdomain_search_key</string>
<bool name="subdomain_search_default" translatable="false">false</bool>
<string name="app_timeout_key" translatable="false">app_timeout_key</string>
<string name="lock_database_screen_off_key" translatable="false">lock_database_screen_off_key</string>
<bool name="lock_database_screen_off_default" translatable="false">true</bool>
@@ -140,12 +140,14 @@
<string name="passkeys_privileged_apps_key" translatable="false">passkeys_privileged_apps_key</string>
<string name="passkeys_auto_select_key" translatable="false">passkeys_auto_select_key</string>
<bool name="passkeys_auto_select_default" translatable="false">true</bool>
<string name="passkeys_user_verification_preferred_key" translatable="false">passkeys_user_verification_preferred_key</string>
<bool name="passkeys_user_verification_preferred_default" translatable="false">false</bool>
<string name="passkeys_backup_eligibility_key" translatable="false">passkeys_backup_eligibility_key</string>
<bool name="passkeys_backup_eligibility_default" translatable="false">true</bool>
<string name="passkeys_backup_state_key" translatable="false">passkeys_backup_state_key</string>
<bool name="passkeys_backup_state_default" translatable="false">true</bool>
<string name="user_verification_preferred_key" translatable="false">user_verification_preferred_key</string>
<bool name="user_verification_preferred_default" translatable="false">false</bool>
<string name="subdomain_search_key" translatable="false">subdomain_search_key</string>
<bool name="subdomain_search_default" translatable="false">false</bool>
<string name="keyboard_notification_entry_key" translatable="false">keyboard_notification_entry_key</string>
<bool name="keyboard_notification_entry_default" translatable="false">true</bool>
<string name="keyboard_notification_entry_clear_close_key" translatable="false">keyboard_notification_entry_clear_close_key</string>

View File

@@ -434,8 +434,6 @@
<string name="passkeys_missing_signature_app_ask_question">Add app signature to passkey entry?</string>
<string name="passkeys_auto_select_title">Auto select</string>
<string name="passkeys_auto_select_summary">Auto select if only one entry and the database is open, only if the requesting app is compatible</string>
<string name="passkeys_user_verification_preferred_title">Preferred User Verification</string>
<string name="passkeys_user_verification_preferred_summary">Perform a user verification to access sensitive data when the relying party requests \"preferred\".</string>
<string name="passkeys_backup_eligibility_title">Backup Eligibility</string>
<string name="passkeys_backup_eligibility_summary">Determine at creation time whether the public key credential source is allowed to be backed up</string>
<string name="passkeys_backup_state_title">Backup State</string>
@@ -446,6 +444,10 @@
<string name="autofill_explanation_summary">Configure autofilling to quickly fill out forms in other apps</string>
<string name="autofill_select_entry">Select entry…</string>
<string name="autofill_preference_title">Autofill settings</string>
<string name="user_verification_device_credential_title">Device credential</string>
<string name="user_verification_device_credential_summary">Use the device credential as user verification if available</string>
<string name="user_verification_preferred_title">Preferred User Verification</string>
<string name="user_verification_preferred_summary">Perform a user verification when the relying party requests \"preferred\"</string>
<string name="password_size_title">Generated password size</string>
<string name="password_size_summary">Sets default size of the generated passwords</string>
<string name="list_password_generator_options_title">Password characters</string>

View File

@@ -32,6 +32,11 @@
android:title="@string/delete_entered_password_title"
android:summary="@string/delete_entered_password_summary"
android:defaultValue="@bool/delete_entered_password_default"/>
<SwitchPreferenceCompat
android:key="@string/user_verification_device_credential_key"
android:title="@string/user_verification_device_credential_title"
android:summary="@string/user_verification_device_credential_summary"
android:defaultValue="@bool/user_verification_device_credential_default"/>
<SwitchPreferenceCompat
android:key="@string/enable_auto_save_database_key"
android:title="@string/enable_auto_save_database_title"

View File

@@ -20,12 +20,31 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:title="@string/general">
android:key="@string/credential_provider_key"
android:title="@string/credential_provider">
<SwitchPreferenceCompat
android:key="@string/subdomain_search_key"
android:title="@string/subdomain_search_title"
android:summary="@string/subdomain_search_summary"
android:defaultValue="@bool/subdomain_search_default"/>
android:key="@string/settings_credential_provider_enable_key"
android:title="@string/set_credential_provider_service_title"
android:defaultValue="@bool/settings_credential_provider_enable_default"/>
<SwitchPreferenceCompat
android:key="@string/user_verification_preferred_key"
android:title="@string/user_verification_preferred_title"
android:summary="@string/user_verification_preferred_summary"
android:defaultValue="@bool/user_verification_preferred_default"/>
<Preference
android:key="@string/passkeys_explanation_key"
android:icon="@drawable/prefs_info_24dp"
android:summary="@string/passkeys_explanation_summary"/>
<Preference
android:key="@string/settings_passkeys_key"
android:title="@string/passkeys_preference_title" />
<Preference
android:key="@string/autofill_explanation_key"
android:icon="@drawable/prefs_info_24dp"
android:summary="@string/autofill_explanation_summary"/>
<Preference
android:key="@string/settings_autofill_key"
android:title="@string/autofill_preference_title" />
</PreferenceCategory>
<PreferenceCategory
@@ -43,26 +62,12 @@
</PreferenceCategory>
<PreferenceCategory
android:key="@string/credential_provider_key"
android:title="@string/credential_provider">
android:title="@string/general">
<SwitchPreferenceCompat
android:key="@string/settings_credential_provider_enable_key"
android:title="@string/set_credential_provider_service_title"
android:defaultValue="@bool/settings_credential_provider_enable_default"/>
<Preference
android:key="@string/passkeys_explanation_key"
android:icon="@drawable/prefs_info_24dp"
android:summary="@string/passkeys_explanation_summary"/>
<Preference
android:key="@string/settings_passkeys_key"
android:title="@string/passkeys_preference_title" />
<Preference
android:key="@string/autofill_explanation_key"
android:icon="@drawable/prefs_info_24dp"
android:summary="@string/autofill_explanation_summary"/>
<Preference
android:key="@string/settings_autofill_key"
android:title="@string/autofill_preference_title" />
android:key="@string/subdomain_search_key"
android:title="@string/subdomain_search_title"
android:summary="@string/subdomain_search_summary"
android:defaultValue="@bool/subdomain_search_default"/>
</PreferenceCategory>
<PreferenceCategory

View File

@@ -37,11 +37,6 @@
</PreferenceCategory>
<PreferenceCategory
android:title="@string/database">
<SwitchPreferenceCompat
android:key="@string/passkeys_user_verification_preferred_key"
android:title="@string/passkeys_user_verification_preferred_title"
android:summary="@string/passkeys_user_verification_preferred_summary"
android:defaultValue="@bool/passkeys_user_verification_preferred_default"/>
<SwitchPreferenceCompat
android:key="@string/passkeys_backup_eligibility_key"
android:title="@string/passkeys_backup_eligibility_title"

46
art/ic_passkey.svg Normal file
View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
fill="none"
viewBox="0 0 24 24"
id="Passkey--Streamline-Outlined-Material"
height="24"
width="24"
version="1.1"
sodipodi:docname="ic_passkey.svg"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs9" />
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="25.721009"
inkscape:cx="10.886043"
inkscape:cy="11.372027"
inkscape:window-width="1865"
inkscape:window-height="1005"
inkscape:window-x="849"
inkscape:window-y="1469"
inkscape:window-maximized="1"
inkscape:current-layer="Passkey--Streamline-Outlined-Material" />
<desc
id="desc2">
Passkey Streamline Icon: https://streamlinehq.com
</desc>
<path
fill="#000000"
d="M3 20v-2.35c0 -0.63335 0.158335 -1.175 0.475 -1.625 0.316665 -0.45 0.725 -0.79165 1.225 -1.025 1.11665 -0.5 2.1875 -0.875 3.2125 -1.125S9.96665 13.5 11 13.5c0.43335 0 0.85415 0.02085 1.2625 0.0625s0.82915 0.10415 1.2625 0.1875c-0.08335 0.96665 0.09585 1.87915 0.5375 2.7375C14.50415 17.34585 15.15 18.01665 16 18.5v1.5H3Zm16 3.675 -1.5 -1.5v-4.65c-0.73335 -0.21665 -1.33335 -0.62915 -1.8 -1.2375 -0.46665 -0.60835 -0.7 -1.3125 -0.7 -2.1125 0 -0.96665 0.34165 -1.79165 1.025 -2.475 0.68335 -0.68335 1.50835 -1.025 2.475 -1.025s1.79165 0.34165 2.475 1.025c0.68335 0.68335 1.025 1.50835 1.025 2.475 0 0.75 -0.2125 1.41665 -0.6375 2 -0.425 0.58335 -0.9625 1 -1.6125 1.25l1.25 1.25 -1.5 1.5 1.5 1.5 -2 2ZM11 11.5c-1.05 0 -1.9375 -0.3625 -2.6625 -1.0875 -0.725 -0.725 -1.0875 -1.6125 -1.0875 -2.6625s0.3625 -1.9375 1.0875 -2.6625C9.0625 4.3625 9.95 4 11 4s1.9375 0.3625 2.6625 1.0875c0.725 0.725 1.0875 1.6125 1.0875 2.6625s-0.3625 1.9375 -1.0875 2.6625C12.9375 11.1375 12.05 11.5 11 11.5Zm7.5 3.175c0.28335 0 0.52085 -0.09585 0.7125 -0.2875S19.5 13.95835 19.5 13.675c0 -0.28335 -0.09585 -0.52085 -0.2875 -0.7125s-0.42915 -0.2875 -0.7125 -0.2875c-0.28335 0 -0.52085 0.09585 -0.7125 0.2875S17.5 13.39165 17.5 13.675c0 0.28335 0.09585 0.52085 0.2875 0.7125s0.42915 0.2875 0.7125 0.2875Z"
stroke-width="0.5"
id="path4"
style="fill:#ffffff;fill-opacity:1" />
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg
fill="#000000"
version="1.1"
id="Capa_1"
width="24"
height="24"
viewBox="0 0 24 24"
xml:space="preserve"
sodipodi:docname="ic_user_verification.svg"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs9" /><sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="11.313709"
inkscape:cx="14.672466"
inkscape:cy="29.742679"
inkscape:window-width="1865"
inkscape:window-height="1005"
inkscape:window-x="849"
inkscape:window-y="1469"
inkscape:window-maximized="1"
inkscape:current-layer="g4" />
<g
id="g4"
transform="matrix(0.04926836,0,0,0.04926836,2,2)">
<path
id="rect6102"
style="fill:#ffffff;fill-opacity:1;stroke-width:19.2718"
d="M 182.67302 40.594004 C 161.36123 40.594004 143.35545 47.959647 128.64018 62.674922 C 113.92489 77.390207 106.55926 95.395978 106.55926 116.70776 C 106.55926 138.01955 113.92489 156.02533 128.64018 170.7406 C 143.35545 185.4559 161.36123 192.82152 182.67302 192.82152 C 203.98481 192.82152 221.99056 185.4559 236.70586 170.7406 C 251.42113 156.02533 258.78678 138.01955 258.78678 116.70776 C 258.78678 95.395978 251.42113 77.390207 236.70586 62.674922 C 221.99056 47.959647 203.98481 40.594004 182.67302 40.594004 z M 182.67302 233.41552 C 173.87734 233.41552 165.35216 233.83872 157.06391 234.68409 C 148.77566 235.52943 140.21083 236.79801 131.41516 238.48977 C 131.32956 238.57537 122.77898 240.40892 119.9981 241.0269 C 99.19372 246.10112 77.450632 253.71256 54.786048 263.86103 C 44.637578 268.59731 36.357481 275.53976 29.930149 284.67338 C 23.50282 293.807 20.297002 304.80093 20.297002 317.65601 L 20.297002 365.34604 L 81.188008 365.34604 L 237.10228 365.34604 C 231.97732 353.03923 229.09448 339.55171 229.09448 325.38631 C 229.09448 294.33677 242.97366 266.78778 264.53495 247.72649 C 258.25368 245.79499 251.46723 242.51941 245.34794 241.0269 C 245.18295 241.0669 233.93088 238.48977 233.93088 238.48977 C 229.37611 237.61371 225.06904 237.15674 220.65061 236.50764 C 217.59924 236.0587 214.54817 235.50028 211.49317 235.1598 C 210.44767 235.0407 209.31971 234.78992 208.28213 234.68409 C 199.99385 233.83872 191.4687 233.41552 182.67302 233.41552 z M 333.15626 252.60253 C 292.94556 252.60253 260.33284 285.17466 260.33284 325.38631 C 260.33284 365.59794 292.95126 398.20974 333.15626 398.20974 C 373.36125 398.20974 405.94004 365.59129 405.94004 325.38631 C 405.93909 285.17466 373.36694 252.60253 333.15626 252.60253 z M 362.25391 290.54048 C 364.45101 290.54048 366.65335 291.37209 368.31923 293.03797 L 374.3449 299.10328 C 377.68235 302.43504 377.6757 307.86254 374.3449 311.19427 L 327.68558 357.89323 C 324.35383 361.22498 318.8867 361.22498 315.55495 357.89323 L 292.08654 334.38518 C 288.7548 331.05344 288.7548 325.62594 292.08654 322.29419 L 298.15186 316.22888 C 301.48361 312.89712 306.9111 312.89712 310.24285 316.22888 L 321.62027 327.56665 L 356.1886 293.03797 C 357.85446 291.37209 360.05682 290.54048 362.25391 290.54048 z " />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.5 KiB