]> git.ktnx.net Git - mobile-ledger.git/commitdiff
bump androidx.constraintlayout library version master
authorDamyan Ivanov <dam+mobileledger@ktnx.net>
Sun, 9 Jun 2024 19:19:26 +0000 (22:19 +0300)
committerDamyan Ivanov <dam+mobileledger@ktnx.net>
Sun, 9 Jun 2024 19:19:26 +0000 (22:19 +0300)
12 files changed:
app/build.gradle
app/src/main/AndroidManifest.xml
app/src/main/java/net/ktnx/mobileledger/App.java
app/src/main/java/net/ktnx/mobileledger/ui/account_summary/AccountSummaryFragment.java
app/src/main/java/net/ktnx/mobileledger/ui/transaction_list/TransactionListDelimiterRowHolder.java
app/src/main/java/net/ktnx/mobileledger/ui/transaction_list/TransactionRowHolder.java
app/src/main/java/net/ktnx/mobileledger/utils/ObservableAtomicInteger.java [deleted file]
app/src/main/java/net/ktnx/mobileledger/utils/ObservableList.java [deleted file]
app/src/main/java/net/ktnx/mobileledger/utils/ObservableValue.java [deleted file]
app/src/main/res/drawable-anydpi-v26/app_icon.xml
app/src/main/res/drawable/launcher_foreground.xml
art/app-icon-adaptive.svg [new file with mode: 0644]

index 71673f6eb8dca03508bc0f12f40580f9893fda37..2d2659e1831a75876d58a0543075282a175473b8 100644 (file)
 apply plugin: 'com.android.application'
 
 android {
 apply plugin: 'com.android.application'
 
 android {
-    compileSdkVersion 31
+    compileSdkVersion 33
     defaultConfig {
         applicationId "net.ktnx.mobileledger"
         minSdkVersion 22
     defaultConfig {
         applicationId "net.ktnx.mobileledger"
         minSdkVersion 22
-        targetSdkVersion 31
+        targetSdkVersion 33
         vectorDrawables.useSupportLibrary true
         versionCode 56
         versionName '0.21.7'
         vectorDrawables.useSupportLibrary true
         versionCode 56
         versionName '0.21.7'
@@ -75,17 +75,17 @@ dependencies {
     implementation fileTree(include: ['*.jar'], dir: 'libs')
     implementation 'androidx.legacy:legacy-support-v4:1.0.0'
     implementation 'com.google.android.material:material:1.5.0'
     implementation fileTree(include: ['*.jar'], dir: 'libs')
     implementation 'androidx.legacy:legacy-support-v4:1.0.0'
     implementation 'com.google.android.material:material:1.5.0'
-    implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
+    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
     implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
     implementation 'androidx.recyclerview:recyclerview:1.2.1'
     testImplementation 'junit:junit:4.13.2'
     androidTestImplementation 'androidx.test:runner:1.5.0-alpha02'
     androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0-alpha05'
     implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
     implementation 'androidx.recyclerview:recyclerview:1.2.1'
     testImplementation 'junit:junit:4.13.2'
     androidTestImplementation 'androidx.test:runner:1.5.0-alpha02'
     androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0-alpha05'
-    implementation 'org.jetbrains:annotations:23.0.0'
-    implementation 'com.fasterxml.jackson.module:jackson-modules-java8:2.13.2'
+    implementation 'org.jetbrains:annotations:24.1.0'
+    implementation 'com.fasterxml.jackson.module:jackson-modules-java8:2.17.1'
     implementation "androidx.navigation:navigation-fragment:$nav_version"
     implementation "androidx.navigation:navigation-ui:$nav_version"
     implementation "androidx.navigation:navigation-fragment:$nav_version"
     implementation "androidx.navigation:navigation-ui:$nav_version"
-    implementation 'androidx.appcompat:appcompat:1.6.0-alpha01'
+    implementation 'androidx.appcompat:appcompat:1.6.1'
 }
 
 allprojects {
 }
 
 allprojects {
index b17a1ecfcacfaec410eee37f6be145c34c967ac7..1674610ed87c06e2d8e203c989c0452dabfa8b07 100644 (file)
@@ -29,6 +29,7 @@
         android:roundIcon="@drawable/app_icon_round"
         android:supportsRtl="true"
         android:backupAgent=".backup.MobileLedgerBackupAgent"
         android:roundIcon="@drawable/app_icon_round"
         android:supportsRtl="true"
         android:backupAgent=".backup.MobileLedgerBackupAgent"
+        android:enableOnBackInvokedCallback="true"
         tools:ignore="GoogleAppIndexingWarning">
         <activity
             android:name=".BackupsActivity"
         tools:ignore="GoogleAppIndexingWarning">
         <activity
             android:name=".BackupsActivity"
index a9a416e7bb3c15a8e49c51f1aedb45f1307d0bee..b0c8e1f7574cc9f8048774dbd67000df0b99de15 100644 (file)
@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright © 2021 Damyan Ivanov.
+ * Copyright © 2024 Damyan Ivanov.
  * This file is part of MoLe.
  * MoLe is free software: you can distribute it and/or modify it
  * under the term of the GNU General Public License as published by
  * This file is part of MoLe.
  * MoLe is free software: you can distribute it and/or modify it
  * under the term of the GNU General Public License as published by
@@ -41,6 +41,7 @@ public class App extends Application {
     public static final String PREF_NAME = "MoLe";
     public static final String PREF_THEME_HUE = "theme-hue";
     public static final String PREF_PROFILE_ID = "profile-id";
     public static final String PREF_NAME = "MoLe";
     public static final String PREF_THEME_HUE = "theme-hue";
     public static final String PREF_PROFILE_ID = "profile-id";
+    public static final String PREF_SHOW_ZERO_BALANCE_ACCOUNTS = "show-zero-balance-accounts";
     public static App instance;
     private static ProfileDetailModel profileModel;
     private boolean monthNamesPrepared = false;
     public static App instance;
     private static ProfileDetailModel profileModel;
     private boolean monthNamesPrepared = false;
@@ -68,6 +69,16 @@ public class App extends Application {
         SharedPreferences prefs = instance.getSharedPreferences(PREF_NAME, MODE_PRIVATE);
         return prefs.getInt(PREF_THEME_HUE, Colors.DEFAULT_HUE_DEG);
     }
         SharedPreferences prefs = instance.getSharedPreferences(PREF_NAME, MODE_PRIVATE);
         return prefs.getInt(PREF_THEME_HUE, Colors.DEFAULT_HUE_DEG);
     }
+    public static boolean getShowZeroBalanceAccounts() {
+        SharedPreferences prefs = instance.getSharedPreferences(PREF_NAME, MODE_PRIVATE);
+        return prefs.getBoolean(PREF_SHOW_ZERO_BALANCE_ACCOUNTS, true);
+    }
+    public static void storeShowZeroBalanceAccounts(boolean value) {
+        SharedPreferences prefs = instance.getSharedPreferences(PREF_NAME, MODE_PRIVATE);
+        SharedPreferences.Editor editor = prefs.edit();
+        editor.putBoolean(PREF_SHOW_ZERO_BALANCE_ACCOUNTS, value);
+        editor.apply();
+    }
     private String getAuthURL() {
         if (profileModel != null)
             return profileModel.getUrl();
     private String getAuthURL() {
         if (profileModel != null)
             return profileModel.getUrl();
index 807c16daf8540af877827e2729d0e848f733d3c3..02d685ce8138b54cd3fb5fa7e132713ba12fd30c 100644 (file)
@@ -36,6 +36,7 @@ import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
 import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
 
 import androidx.recyclerview.widget.RecyclerView;
 import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
 
+import net.ktnx.mobileledger.App;
 import net.ktnx.mobileledger.R;
 import net.ktnx.mobileledger.async.GeneralBackgroundTasks;
 import net.ktnx.mobileledger.databinding.AccountSummaryFragmentBinding;
 import net.ktnx.mobileledger.R;
 import net.ktnx.mobileledger.async.GeneralBackgroundTasks;
 import net.ktnx.mobileledger.databinding.AccountSummaryFragmentBinding;
@@ -106,8 +107,7 @@ public class AccountSummaryFragment extends MobileLedgerListFragment {
 
         mainActivity.fabShouldShow();
 
 
         mainActivity.fabShouldShow();
 
-        if (mainActivity instanceof FabManager.FabHandler)
-            FabManager.handle(mainActivity, b.accountRoot);
+        FabManager.handle(mainActivity, b.accountRoot);
 
         Colors.themeWatch.observe(getViewLifecycleOwner(), this::themeChanged);
         b.accountSwipeRefreshLayout.setOnRefreshListener(() -> {
 
         Colors.themeWatch.observe(getViewLifecycleOwner(), this::themeChanged);
         b.accountSwipeRefreshLayout.setOnRefreshListener(() -> {
@@ -118,6 +118,8 @@ public class AccountSummaryFragment extends MobileLedgerListFragment {
         Data.observeProfile(this, profile -> onProfileChanged(profile, Boolean.TRUE.equals(
                 model.getShowZeroBalanceAccounts()
                      .getValue())));
         Data.observeProfile(this, profile -> onProfileChanged(profile, Boolean.TRUE.equals(
                 model.getShowZeroBalanceAccounts()
                      .getValue())));
+        model.getShowZeroBalanceAccounts()
+             .setValue(App.getShowZeroBalanceAccounts());
     }
     @Override
     public void onCreateOptionsMenu(@NotNull Menu menu, @NotNull MenuInflater inflater) {
     }
     @Override
     public void onCreateOptionsMenu(@NotNull Menu menu, @NotNull MenuInflater inflater) {
@@ -138,6 +140,7 @@ public class AccountSummaryFragment extends MobileLedgerListFragment {
              .observe(this, v -> {
                  menuShowZeroBalances.setChecked(v);
                  onProfileChanged(Data.getProfile(), v);
              .observe(this, v -> {
                  menuShowZeroBalances.setChecked(v);
                  onProfileChanged(Data.getProfile(), v);
+                 App.storeShowZeroBalanceAccounts(v);
              });
 
         super.onCreateOptionsMenu(menu, inflater);
              });
 
         super.onCreateOptionsMenu(menu, inflater);
index 390b4941719c2eca9b5d4c4e992086131ac3525e..b5fe883d14a74cc2d92cf308d6382838155b3bb0 100644 (file)
@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright © 2021 Damyan Ivanov.
+ * Copyright © 2024 Damyan Ivanov.
  * This file is part of MoLe.
  * MoLe is free software: you can distribute it and/or modify it
  * under the term of the GNU General Public License as published by
  * This file is part of MoLe.
  * MoLe is free software: you can distribute it and/or modify it
  * under the term of the GNU General Public License as published by
@@ -19,9 +19,12 @@ package net.ktnx.mobileledger.ui.transaction_list;
 
 import android.view.View;
 
 
 import android.view.View;
 
+import androidx.constraintlayout.widget.ConstraintLayout;
+
 import net.ktnx.mobileledger.App;
 import net.ktnx.mobileledger.databinding.TransactionDelimiterBinding;
 import net.ktnx.mobileledger.model.TransactionListItem;
 import net.ktnx.mobileledger.App;
 import net.ktnx.mobileledger.databinding.TransactionDelimiterBinding;
 import net.ktnx.mobileledger.model.TransactionListItem;
+import net.ktnx.mobileledger.utils.DimensionUtils;
 import net.ktnx.mobileledger.utils.Globals;
 import net.ktnx.mobileledger.utils.SimpleDate;
 
 import net.ktnx.mobileledger.utils.Globals;
 import net.ktnx.mobileledger.utils.SimpleDate;
 
@@ -46,15 +49,21 @@ class TransactionListDelimiterRowHolder extends TransactionRowHolderBase {
             b.transactionDelimiterMonth.setText(
                     Globals.monthNames[cal.get(GregorianCalendar.MONTH)]);
             b.transactionDelimiterMonth.setVisibility(View.VISIBLE);
             b.transactionDelimiterMonth.setText(
                     Globals.monthNames[cal.get(GregorianCalendar.MONTH)]);
             b.transactionDelimiterMonth.setVisibility(View.VISIBLE);
-            //                holder.vDelimiterLine.setBackgroundResource(R.drawable
-            //                .dashed_border_8dp);
             b.transactionDelimiterThick.setVisibility(View.VISIBLE);
             b.transactionDelimiterThick.setVisibility(View.VISIBLE);
+            ConstraintLayout.LayoutParams lp =
+                    (ConstraintLayout.LayoutParams) b.transactionDelimiterThick.getLayoutParams();
+            lp.height = DimensionUtils.dp2px(b.getRoot()
+                                              .getContext(), 4);
+            b.transactionDelimiterThick.setLayoutParams(lp);
         }
         else {
             b.transactionDelimiterMonth.setVisibility(View.GONE);
         }
         else {
             b.transactionDelimiterMonth.setVisibility(View.GONE);
-            //                holder.vDelimiterLine.setBackgroundResource(R.drawable
-            //                .dashed_border_1dp);
-            b.transactionDelimiterThick.setVisibility(View.GONE);
+            ConstraintLayout.LayoutParams lp =
+                    (ConstraintLayout.LayoutParams) b.transactionDelimiterThick.getLayoutParams();
+            lp.height = DimensionUtils.dp2px(b.getRoot()
+                                              .getContext(), 1.3f);
+            b.transactionDelimiterThick.setLayoutParams(lp);
+            b.transactionDelimiterThick.setVisibility(View.VISIBLE);
         }
 
     }
         }
 
     }
index 876430f5de06e4c1b4b25a9061a5f9d74abfe94b..e516115074bd3f9f6a03bb6d63b4747600ce75af 100644 (file)
@@ -40,12 +40,9 @@ import net.ktnx.mobileledger.model.TransactionListItem;
 import net.ktnx.mobileledger.utils.Colors;
 import net.ktnx.mobileledger.utils.Misc;
 
 import net.ktnx.mobileledger.utils.Colors;
 import net.ktnx.mobileledger.utils.Misc;
 
-import java.util.Observer;
-
 class TransactionRowHolder extends TransactionRowHolderBase {
     private final TransactionListRowBinding b;
     TransactionListItem.Type lastType;
 class TransactionRowHolder extends TransactionRowHolderBase {
     private final TransactionListRowBinding b;
     TransactionListItem.Type lastType;
-    private Observer lastUpdateObserver;
     public TransactionRowHolder(@NonNull TransactionListRowBinding binding) {
         super(binding.getRoot());
         b = binding;
     public TransactionRowHolder(@NonNull TransactionListRowBinding binding) {
         super(binding.getRoot());
         b = binding;
diff --git a/app/src/main/java/net/ktnx/mobileledger/utils/ObservableAtomicInteger.java b/app/src/main/java/net/ktnx/mobileledger/utils/ObservableAtomicInteger.java
deleted file mode 100644 (file)
index f804464..0000000
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright © 2020 Damyan Ivanov.
- * This file is part of MoLe.
- * MoLe is free software: you can distribute it and/or modify it
- * under the term of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your opinion), any later version.
- *
- * MoLe is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License terms for details.
- *
- * You should have received a copy of the GNU General Public License
- * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
- */
-
-package net.ktnx.mobileledger.utils;
-
-import android.os.Build;
-
-import androidx.annotation.RequiresApi;
-
-import java.util.Observable;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.IntBinaryOperator;
-import java.util.function.IntUnaryOperator;
-
-public class ObservableAtomicInteger extends Observable {
-    private final AtomicInteger holder;
-    ObservableAtomicInteger() {
-        super();
-        holder = new AtomicInteger();
-    }
-    public ObservableAtomicInteger(int initialValue) {
-        this();
-        holder.set(initialValue);
-    }
-    public int get() {
-        return holder.get();
-    }
-    public void set(int newValue) {
-//        debug("atomic", "set");
-        holder.set(newValue);
-        forceNotify();
-    }
-    private void forceNotify() {
-        setChanged();
-//        debug("atomic", String.format("notifying %d observers", countObservers()));
-        notifyObservers();
-    }
-//    public void lazySet(int newValue) {
-//        holder.lazySet(newValue);
-//        forceNotify();
-//    }
-    public int getAndSet(int newValue) {
-        int result = holder.getAndSet(newValue);
-        forceNotify();
-        return result;
-    }
-    public boolean compareAndSet(int expect, int update) {
-        boolean result = holder.compareAndSet(expect, update);
-        if (result) forceNotify();
-        return result;
-    }
-//    public boolean weakCompareAndSet(int expect, int update) {
-//        boolean result = holder.weakCompareAndSet(expect, update);
-//        if (result) forceNotify();
-//        return result;
-//    }
-    public int getAndIncrement() {
-        int result = holder.getAndIncrement();
-        forceNotify();
-        return result;
-    }
-    public int getAndDecrement() {
-        int result = holder.getAndDecrement();
-        forceNotify();
-        return result;
-    }
-    public int getAndAdd(int delta) {
-        int result = holder.getAndAdd(delta);
-        forceNotify();
-        return result;
-    }
-    public int incrementAndGet() {
-//        debug("atomic", "incrementAndGet");
-        int result = holder.incrementAndGet();
-        forceNotify();
-        return result;
-    }
-    public int decrementAndGet() {
-//        debug("atomic", "decrementAndGet");
-        int result = holder.decrementAndGet();
-        forceNotify();
-        return result;
-    }
-    public int addAndGet(int delta) {
-        int result = holder.addAndGet(delta);
-        forceNotify();
-        return result;
-    }
-    @RequiresApi(Build.VERSION_CODES.N)
-    public int getAndUpdate(IntUnaryOperator updateFunction) {
-        int result = holder.getAndUpdate(updateFunction);
-        forceNotify();
-        return result;
-    }
-    @RequiresApi(api = Build.VERSION_CODES.N)
-    public int updateAndGet(IntUnaryOperator updateFunction) {
-        int result = holder.updateAndGet(updateFunction);
-        forceNotify();
-        return result;
-    }
-    @RequiresApi(api = Build.VERSION_CODES.N)
-    public int getAndAccumulate(int x, IntBinaryOperator accumulatorFunction) {
-        int result = holder.getAndAccumulate(x, accumulatorFunction);
-        forceNotify();
-        return result;
-    }
-    @RequiresApi(api = Build.VERSION_CODES.N)
-    public int accumulateAndGet(int x, IntBinaryOperator accumulatorFunction) {
-        int result = holder.accumulateAndGet(x, accumulatorFunction);
-        forceNotify();
-        return result;
-    }
-    public int intValue() {
-        return holder.intValue();
-    }
-    public long longValue() {
-        return holder.longValue();
-    }
-    public float floatValue() {
-        return holder.floatValue();
-    }
-    public double doubleValue() {
-        return holder.doubleValue();
-    }
-    public byte byteValue() {
-        return holder.byteValue();
-    }
-    public short shortValue() {
-        return holder.shortValue();
-    }
-}
diff --git a/app/src/main/java/net/ktnx/mobileledger/utils/ObservableList.java b/app/src/main/java/net/ktnx/mobileledger/utils/ObservableList.java
deleted file mode 100644 (file)
index 4ca3e69..0000000
+++ /dev/null
@@ -1,325 +0,0 @@
-/*
- * Copyright © 2020 Damyan Ivanov.
- * This file is part of MoLe.
- * MoLe is free software: you can distribute it and/or modify it
- * under the term of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your opinion), any later version.
- *
- * MoLe is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License terms for details.
- *
- * You should have received a copy of the GNU General Public License
- * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
- */
-
-package net.ktnx.mobileledger.utils;
-
-import android.os.Build;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RequiresApi;
-
-import org.jetbrains.annotations.NotNull;
-
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.List;
-import java.util.ListIterator;
-import java.util.Observable;
-import java.util.Spliterator;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-import java.util.function.Consumer;
-import java.util.function.Predicate;
-import java.util.function.UnaryOperator;
-import java.util.stream.Stream;
-
-import static net.ktnx.mobileledger.utils.Logger.debug;
-
-public class ObservableList<T> extends Observable implements List<T> {
-    private List<T> list;
-    private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
-    private int notificationBlocks = 0;
-    private boolean notificationWasBlocked = false;
-    public ObservableList(List<T> list) {
-        this.list = list;
-    }
-    private void forceNotify() {
-        if (notificationBlocked()) return;
-        setChanged();
-        notifyObservers();
-    }
-    private boolean notificationBlocked() {
-        return notificationWasBlocked = (notificationBlocks > 0);
-    }
-    private void forceNotify(int index) {
-        if (notificationBlocked()) return;
-        setChanged();
-        notifyObservers(index);
-    }
-    public int size() {
-        try (LockHolder lh = lockForReading()) {
-            return list.size();
-        }
-    }
-    public boolean isEmpty() {
-        try (LockHolder lh = lockForReading()) {
-            return list.isEmpty();
-        }
-    }
-    public boolean contains(@Nullable Object o) {
-        try (LockHolder lh = lockForReading()) {
-            return list.contains(o);
-        }
-    }
-    @NonNull
-    public Iterator<T> iterator() {
-        throw new RuntimeException("Iterators break encapsulation and ignore locking");
-//        return list.iterator();
-    }
-    @NotNull
-    public Object[] toArray() {
-        try (LockHolder lh = lockForReading()) {
-            return list.toArray();
-        }
-    }
-    @Override
-    @NotNull
-    public <T1> T1[] toArray(@Nullable T1[] a) {
-        try (LockHolder lh = lockForReading()) {
-            return list.toArray(a);
-        }
-    }
-    public boolean add(T t) {
-        try (LockHolder lh = lockForWriting()) {
-            boolean result = list.add(t);
-            lh.downgrade();
-            if (result)
-                forceNotify();
-            return result;
-        }
-    }
-    public boolean remove(@Nullable Object o) {
-        try (LockHolder lh = lockForWriting()) {
-            boolean result = list.remove(o);
-            lh.downgrade();
-            if (result) forceNotify();
-            return result;
-        }
-    }
-    public T removeQuietly(int index) {
-        return list.remove(index);
-    }
-    public boolean containsAll(@NonNull Collection<?> c) {
-        try (LockHolder lh = lockForReading()) {
-            return list.containsAll(c);
-        }
-    }
-    public boolean addAll(@NonNull Collection<? extends T> c) {
-        try (LockHolder lh = lockForWriting()) {
-            boolean result = list.addAll(c);
-            lh.downgrade();
-            if (result) forceNotify();
-            return result;
-        }
-    }
-    public boolean addAll(int index, @NonNull Collection<? extends T> c) {
-        try (LockHolder lh = lockForWriting()) {
-            boolean result = list.addAll(index, c);
-            lh.downgrade();
-            if (result) forceNotify();
-            return result;
-        }
-    }
-    public boolean addAllQuietly(int index, Collection<? extends T> c) {
-        return list.addAll(index, c);
-    }
-    public boolean removeAll(@NonNull Collection<?> c) {
-        try (LockHolder lh = lockForWriting()) {
-            boolean result = list.removeAll(c);
-            lh.downgrade();
-            if (result) forceNotify();
-            return result;
-        }
-    }
-    public boolean retainAll(@NonNull Collection<?> c) {
-        try (LockHolder lh = lockForWriting()) {
-            boolean result = list.retainAll(c);
-            lh.downgrade();
-            if (result) forceNotify();
-            return result;
-        }
-    }
-    @RequiresApi(api = Build.VERSION_CODES.N)
-    public void replaceAll(@NonNull UnaryOperator<T> operator) {
-        try (LockHolder lh = lockForWriting()) {
-            list.replaceAll(operator);
-            lh.downgrade();
-            forceNotify();
-        }
-    }
-    @RequiresApi(api = Build.VERSION_CODES.N)
-    public void sort(@Nullable Comparator<? super T> c) {
-        try (LockHolder lh = lockForWriting()) {
-            lock.writeLock().lock();
-            list.sort(c);
-            lh.downgrade();
-            forceNotify();
-        }
-    }
-    public void clear() {
-        try (LockHolder lh = lockForWriting()) {
-            boolean wasEmpty = list.isEmpty();
-            list.clear();
-            lh.downgrade();
-            if (!wasEmpty) forceNotify();
-        }
-    }
-    public T get(int index) {
-        try (LockHolder lh = lockForReading()) {
-            return list.get(index);
-        }
-    }
-    public T set(int index, T element) {
-        try (LockHolder lh = lockForWriting()) {
-            T result = list.set(index, element);
-            lh.downgrade();
-            forceNotify();
-            return result;
-        }
-    }
-    public void add(int index, T element) {
-        try (LockHolder lh = lockForWriting()) {
-            list.add(index, element);
-            lh.downgrade();
-            forceNotify();
-        }
-    }
-    public T remove(int index) {
-        try (LockHolder lh = lockForWriting()) {
-            T result = list.remove(index);
-            lh.downgrade();
-            forceNotify();
-            return result;
-        }
-    }
-    public int indexOf(@Nullable Object o) {
-        try (LockHolder lh = lockForReading()) {
-            return list.indexOf(o);
-        }
-    }
-    public int lastIndexOf(@Nullable Object o) {
-        try (LockHolder lh = lockForReading()) {
-            return list.lastIndexOf(o);
-        }
-    }
-    @NotNull
-    public ListIterator<T> listIterator() {
-        if (!lock.isWriteLockedByCurrentThread()) throw new RuntimeException(
-                "Iterators break encapsulation and ignore locking. Write-lock first");
-        return list.listIterator();
-    }
-    @NotNull
-    public ListIterator<T> listIterator(int index) {
-        if (!lock.isWriteLockedByCurrentThread()) throw new RuntimeException(
-                "Iterators break encapsulation and ignore locking. Write-lock first");
-        return list.listIterator(index);
-    }
-    @NotNull
-    public List<T> subList(int fromIndex, int toIndex) {
-        try (LockHolder lh = lockForReading()) {
-            return list.subList(fromIndex, toIndex);
-        }
-    }
-    @NotNull
-    @RequiresApi(api = Build.VERSION_CODES.N)
-    public Spliterator<T> spliterator() {
-        if (!lock.isWriteLockedByCurrentThread())
-            throw new RuntimeException(
-                    "Iterators break encapsulation and ignore locking. Write-lock first");
-        return list.spliterator();
-    }
-    @RequiresApi(api = Build.VERSION_CODES.N)
-    public boolean removeIf(@NotNull Predicate<? super T> filter) {
-        try (LockHolder lh = lockForWriting()) {
-            boolean result = list.removeIf(filter);
-            lh.downgrade();
-            if (result)
-                forceNotify();
-            return result;
-        }
-    }
-    @NotNull
-    @RequiresApi(api = Build.VERSION_CODES.N)
-    public Stream<T> stream() {
-        if (!lock.isWriteLockedByCurrentThread()) throw new RuntimeException(
-                "Iterators break encapsulation and ignore locking. Write-lock first");
-        return list.stream();
-    }
-    @NotNull
-    @RequiresApi(api = Build.VERSION_CODES.N)
-    public Stream<T> parallelStream() {
-        if (!lock.isWriteLockedByCurrentThread())
-            throw new RuntimeException(
-                    "Iterators break encapsulation and ignore locking. Write-lock first");
-        return list.parallelStream();
-    }
-    @RequiresApi(api = Build.VERSION_CODES.N)
-    public void forEach(@NotNull Consumer<? super T> action) {
-        try (LockHolder lh = lockForReading()) {
-            list.forEach(action);
-        }
-    }
-    public List<T> getList() {
-        if (!lock.isWriteLockedByCurrentThread())
-            throw new RuntimeException(
-                    "Direct list access breaks encapsulation and ignore locking. Write-lock first");
-        return list;
-    }
-    public void setList(List<T> aList) {
-        try (LockHolder lh = lockForWriting()) {
-            list = aList;
-            lh.downgrade();
-            forceNotify();
-        }
-    }
-    public void triggerItemChangedNotification(T item) {
-        try (LockHolder lh = lockForReading()) {
-            int index = list.indexOf(item);
-            if (index == -1) {
-                debug("ObList", "??? not sending notifications for item not found in the list");
-                return;
-            }
-            debug("ObList", "Notifying item change observers");
-            triggerItemChangedNotification(index);
-        }
-    }
-    public void triggerItemChangedNotification(int index) {
-        forceNotify(index);
-    }
-    public LockHolder lockForWriting() {
-        ReentrantReadWriteLock.WriteLock wLock = lock.writeLock();
-        wLock.lock();
-
-        ReentrantReadWriteLock.ReadLock rLock = lock.readLock();
-        rLock.lock();
-
-        return new LockHolder(rLock, wLock);
-    }
-    public LockHolder lockForReading() {
-        ReentrantReadWriteLock.ReadLock rLock = lock.readLock();
-        rLock.lock();
-        return new LockHolder(rLock);
-    }
-    public void blockNotifications() {
-        notificationBlocks++;
-    }
-    public void unblockNotifications() {
-        notificationBlocks--;
-        if ((notificationBlocks == 0) && notificationWasBlocked) notifyObservers();
-    }
-}
\ No newline at end of file
diff --git a/app/src/main/java/net/ktnx/mobileledger/utils/ObservableValue.java b/app/src/main/java/net/ktnx/mobileledger/utils/ObservableValue.java
deleted file mode 100644 (file)
index 326be07..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright © 2020 Damyan Ivanov.
- * This file is part of MoLe.
- * MoLe is free software: you can distribute it and/or modify it
- * under the term of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your opinion), any later version.
- *
- * MoLe is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License terms for details.
- *
- * You should have received a copy of the GNU General Public License
- * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
- */
-
-package net.ktnx.mobileledger.utils;
-
-import java.util.Observable;
-import java.util.Observer;
-
-public class ObservableValue<T> {
-    private final ObservableValueImpl<T> impl = new ObservableValueImpl<>();
-    public ObservableValue() {}
-    public ObservableValue(T initialValue) {
-        impl.setValue(initialValue, false);
-    }
-    public void set(T newValue) {
-        impl.setValue(newValue);
-    }
-    public T get() {
-        return impl.getValue();
-    }
-    public void addObserver(Observer o) {
-        impl.addObserver(o);
-    }
-    public void deleteObserver(Observer o) {
-        impl.deleteObserver(o);
-    }
-    public void notifyObservers() {
-        impl.notifyObservers();
-    }
-    public void notifyObservers(T arg) {
-        impl.notifyObservers(arg);
-    }
-    public void deleteObservers() {
-        impl.deleteObservers();
-    }
-    public boolean hasChanged() {
-        return impl.hasChanged();
-    }
-    public int countObservers() {
-        return impl.countObservers();
-    }
-    public void forceNotifyObservers() {
-        impl.setChanged();
-        impl.notifyObservers();
-    }
-    private static class ObservableValueImpl<T> extends Observable {
-        protected T value;
-        public void setValue(T newValue) {
-            setValue(newValue, true);
-        }
-        protected void setChanged() {
-            super.setChanged();
-        }
-        private synchronized void setValue(T newValue, boolean notify) {
-            if ((newValue == null) && (value == null))
-                return;
-
-            if ((newValue != null) && newValue.equals(value)) return;
-
-            T oldValue = value;
-            value = newValue;
-            setChanged();
-            if (notify) notifyObservers(oldValue);
-        }
-        public T getValue() {
-            return value;
-        }
-    }
-}
\ No newline at end of file
index b82ac92d9abc5e9fedea840ca7a250a9194da7fc..cd0b7c71f24a78c1b793383dda73d64cf5769db5 100644 (file)
@@ -19,4 +19,5 @@
 <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
     <background android:drawable="@color/ic_launcher_background"/>
     <foreground android:drawable="@drawable/launcher_foreground" />
 <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
     <background android:drawable="@color/ic_launcher_background"/>
     <foreground android:drawable="@drawable/launcher_foreground" />
+    <monochrome android:drawable="@drawable/launcher_foreground" />
 </adaptive-icon>
\ No newline at end of file
 </adaptive-icon>
\ No newline at end of file
index af159e2bf0effd213f56ab3e9c22cd3cca0a18aa..b78a67edd0cfc056ca974caeb4df8f9a9115a330 100644 (file)
@@ -1,5 +1,5 @@
 <!--
 <!--
-  ~ Copyright © 2021 Damyan Ivanov.
+  ~ Copyright © 2024 Damyan Ivanov.
   ~ This file is part of MoLe.
   ~ MoLe is free software: you can distribute it and/or modify it
   ~ under the term of the GNU General Public License as published by
   ~ This file is part of MoLe.
   ~ MoLe is free software: you can distribute it and/or modify it
   ~ under the term of the GNU General Public License as published by
     android:viewportHeight="108"
     >
     <group
     android:viewportHeight="108"
     >
     <group
-        android:scaleX="0.73"
-        android:scaleY="0.73"
-        android:translateX="14.58"
-        android:translateY="14.58"
+        android:scaleX="0.8"
+        android:scaleY="0.8"
+        android:translateX="10.8"
+        android:translateY="10.8"
         >
         <path
         >
         <path
-            android:pathData="m42.06,32.596c-1.331,1.331 -1.33,3.49 0.002,4.82 1.331,1.33 3.488,1.329 4.818,-0.002 3.962,-3.962 10.28,-3.962 14.242,0 1.33,1.331 3.487,1.332 4.818,0.002 1.332,-1.33 1.333,-3.488 0.002,-4.82 -7.523,-6.703 -17.464,-6.433 -23.881,0z"
+            android:fillColor="#00000000"
+            android:pathData="m46.42,36.41a10.72,10.72 89.98,0 1,15.16 0"
+            android:strokeWidth="4"
+            android:strokeColor="#ffffff"
+            android:strokeLineCap="round"
             android:strokeLineJoin="round"
             android:strokeLineJoin="round"
-            android:strokeWidth="7.1924"
-            android:fillColor="#ffffff"
-            android:strokeColor="#00000000"
-            android:fillType="nonZero"
-            android:strokeLineCap="round"/>
+            />
         <path
         <path
-            android:pathData="m32.531,23.62c-1.33,1.33 -1.33,3.487 0,4.818 1.33,1.33 3.487,1.33 4.818,0 9.225,-9.227 24.078,-9.227 33.304,0 1.33,1.33 3.487,1.33 4.818,0 1.33,-1.33 1.33,-3.487 0,-4.818 -12.952,-12.351 -31.975,-11.302 -42.939,0z"
+            android:fillColor="#00000000"
+            android:pathData="m38.84,29.27a21.44,21.44 89.98,0 1,30.32 0"
+            android:strokeWidth="4"
+            android:strokeColor="#ffffff"
+            android:strokeLineCap="round"
             android:strokeLineJoin="round"
             android:strokeLineJoin="round"
-            android:strokeWidth="7.1924"
-            android:fillColor="#ffffff"
-            android:strokeColor="#00000000"
-            android:fillType="nonZero"
-            android:strokeLineCap="round"/>
+            />
         <path
         <path
-            android:pathData="m17.52,41.916c-3.229,0.666 -3.355,3.178 -3.355,3.827L14.165,89.41c0,2.088 1.746,3.837 3.835,3.837 13.048,-2.199 19.956,-2.121 36,0.007C68.79,91.212 79.587,90.998 90,93.247c2.089,0 3.835,-1.749 3.835,-3.837L93.835,45.743c0,-2.094 -1.814,-3.538 -3.636,-3.827 -9.491,-1.502 -13.338,-2.216 -27.678,-0.945 0.484,1.475 0.72,2.785 0.442,4.184 10.721,-1.146 19.131,-0.668 26.469,0.915l-0.045,42.916C77.902,86.92 70.894,86.96 56.213,88.799L56.213,52.774c-1.422,0.348 -3.104,0.332 -4.426,0L51.787,88.799C40.094,86.929 28.873,86.93 18.591,88.987L18.591,46.07c11.035,-1.397 15.892,-2.046 26.444,-0.915 -0.18,-1.441 0.022,-2.978 0.451,-4.184 -9.086,-1.124 -17.014,-0.646 -27.966,0.945z"
+            android:fillColor="#00000000"
+            android:pathData="m61.2,42.96c0,0 6.34,-0.66 9.51,-0.61 4.38,0.07 13.08,1.25 13.08,1.25 0.79,0.07 1.44,0.62 1.44,1.38l0,34.67c0,0.77 -0.64,1.38 -1.44,1.38 0,0 -8.7,-1.24 -13.08,-1.29 -5.59,-0.06 -16.71,1.31 -16.71,1.31 0,0 -10.73,-1.34 -16.13,-1.31 -4.58,0.03 -13.67,1.29 -13.67,1.29 -0.8,0 -1.44,-0.62 -1.44,-1.38L22.77,44.97c0,-0.77 0.64,-1.38 1.44,-1.38 0,0 9.09,-1.2 13.67,-1.25 2.99,-0.03 8.96,0.62 8.96,0.62"
+            android:strokeWidth="3.54"
+            android:strokeColor="#ffffff"
+            android:strokeLineCap="butt"
             android:strokeLineJoin="round"
             android:strokeLineJoin="round"
-            android:strokeWidth="4.42609"
-            android:fillColor="#ffffff"
-            android:strokeColor="#00000000"
-            android:fillType="nonZero"
-            android:strokeLineCap="round"/>
+            />
         <path
         <path
-            android:pathData="M32.773,55.429L32.773,65.965L22.238,65.965v5.137h10.534v10.534h5.137L37.91,71.102L48.446,71.102L48.446,65.965L37.91,65.965L37.91,55.429Z"
+            android:fillColor="#00000000"
+            android:fillType="evenOdd"
+            android:pathData="M54,80.11L54,50.74"
+            android:strokeWidth="3.47"
+            android:strokeColor="#ffffff"
+            android:strokeLineCap="butt"
             android:strokeLineJoin="miter"
             android:strokeLineJoin="miter"
-            android:strokeWidth="5.42196"
-            android:fillColor="#ffffff"
-            android:strokeColor="#00000000"
+            />
+        <path
+            android:fillColor="#00000000"
             android:fillType="evenOdd"
             android:fillType="evenOdd"
-            android:strokeLineCap="butt"/>
+            android:pathData="M27.95,63.29L48.79,63.29"
+            android:strokeWidth="3.47"
+            android:strokeColor="#ffffff"
+            android:strokeLineCap="butt"
+            android:strokeLineJoin="miter"
+            />
         <path
         <path
-            android:pathData="M59.352,65.965L59.352,71.102L85.559,71.102v-5.137z"
+            android:fillColor="#00000000"
+            android:fillType="evenOdd"
+            android:pathData="M38.37,73.72L38.37,52.87"
+            android:strokeWidth="3.47"
+            android:strokeColor="#ffffff"
+            android:strokeLineCap="butt"
             android:strokeLineJoin="miter"
             android:strokeLineJoin="miter"
-            android:strokeWidth="5.42196"
-            android:fillColor="#ffffff"
-            android:strokeColor="#00000000"
+            />
+        <path
+            android:fillColor="#00000000"
             android:fillType="evenOdd"
             android:fillType="evenOdd"
-            android:strokeLineCap="butt"/>
+            android:pathData="M59.21,63.29L80.05,63.29"
+            android:strokeWidth="3.47"
+            android:strokeColor="#ffffff"
+            android:strokeLineCap="butt"
+            android:strokeLineJoin="miter"
+            />
         <path
         <path
-            android:pathData="m54,40.578c1.764,0 3.407,1.645 3.407,3.407 0,1.762 -1.643,3.403 -3.407,3.403 -1.764,0 -3.407,-1.641 -3.407,-3.403 0,-1.762 1.643,-3.407 3.407,-3.407z"
-            android:strokeLineJoin="round"
-            android:strokeWidth="11.5078"
             android:fillColor="#ffffff"
             android:fillColor="#ffffff"
-            android:strokeColor="#00000000"
             android:fillType="nonZero"
             android:fillType="nonZero"
-            android:strokeLineCap="round"/>
+            android:pathData="m54,40.84c1.4,0 2.71,1.31 2.71,2.71 0,1.4 -1.31,2.71 -2.71,2.71 -1.4,0 -2.71,-1.31 -2.71,-2.71 0,-1.4 1.31,-2.71 2.71,-2.71z"
+            android:strokeWidth="8.68"
+            android:strokeColor="#00000000"
+            android:strokeLineCap="round"
+            android:strokeLineJoin="round"
+            />
     </group>
 </vector>
     </group>
 </vector>
diff --git a/art/app-icon-adaptive.svg b/art/app-icon-adaptive.svg
new file mode 100644 (file)
index 0000000..b0f4b05
--- /dev/null
@@ -0,0 +1,191 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   sodipodi:docname="app-icon-adaptive.svg"
+   inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
+   id="svg4620"
+   version="1.1"
+   viewBox="0 0 108 108"
+   height="108"
+   width="108"
+   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"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:dc="http://purl.org/dc/elements/1.1/">
+  <title
+     id="title921">MoLe app icon</title>
+  <defs
+     id="defs4614" />
+  <sodipodi:namedview
+     units="px"
+     inkscape:document-rotation="0"
+     inkscape:snap-to-guides="true"
+     inkscape:bbox-paths="true"
+     inkscape:bbox-nodes="true"
+     inkscape:snap-bbox="true"
+     inkscape:pagecheckerboard="true"
+     inkscape:window-maximized="0"
+     inkscape:window-y="10"
+     inkscape:window-x="12"
+     inkscape:window-height="1014"
+     inkscape:window-width="1890"
+     showborder="true"
+     inkscape:snap-object-midpoints="true"
+     inkscape:guide-bbox="true"
+     showguides="true"
+     inkscape:snap-page="true"
+     showgrid="false"
+     inkscape:current-layer="layer1"
+     inkscape:document-units="px"
+     inkscape:cy="46.080652"
+     inkscape:cx="47.195507"
+     inkscape:zoom="5.3818683"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     borderopacity="1.0"
+     bordercolor="#666666"
+     pagecolor="#ffffff"
+     id="base"
+     inkscape:showpageshadow="0"
+     inkscape:deskcolor="#d1d1d1">
+    <sodipodi:guide
+       inkscape:color="rgb(0,0,255)"
+       inkscape:label=""
+       inkscape:locked="false"
+       id="guide1407"
+       orientation="-1,0"
+       position="21,-37.820936" />
+    <sodipodi:guide
+       inkscape:color="rgb(0,0,255)"
+       inkscape:locked="false"
+       inkscape:label=""
+       id="guide31"
+       orientation="0,1"
+       position="-10.658475,87" />
+    <sodipodi:guide
+       position="-11.893959,21"
+       orientation="0,1"
+       id="guide8469"
+       inkscape:locked="false"
+       inkscape:label=""
+       inkscape:color="rgb(0,134,229)" />
+    <sodipodi:guide
+       position="87,-37.820936"
+       orientation="-1,0"
+       id="guide11759"
+       inkscape:label=""
+       inkscape:locked="false"
+       inkscape:color="rgb(0,0,255)" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata4617">
+    <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>MoLe app icon</dc:title>
+        <cc:license
+           rdf:resource="https://spdx.org/licenses/GPL-3.0-or-later.html" />
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Damyan Ivanov &lt;dam+mole@ktnx.net&gt;</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:rights>
+          <cc:Agent>
+            <dc:title>Copyright © 2019 Damyan Ivanov &lt;dam+mole@ktnx.net&gt;. All rights reserved.</dc:title>
+          </cc:Agent>
+        </dc:rights>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="фон"
+     id="layer3"
+     inkscape:groupmode="layer">
+    <rect
+       ry="8.6399994"
+       y="0"
+       x="0"
+       height="108"
+       width="108"
+       id="rect5267"
+       style="opacity:1;fill:#935ff2;fill-opacity:1;stroke:none;stroke-width:7.02;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.0885827;stroke-opacity:1;paint-order:markers stroke fill" />
+  </g>
+  <g
+     style="opacity:1"
+     transform="translate(0,-197)"
+     id="layer1"
+     inkscape:groupmode="layer"
+     inkscape:label="Layer 1">
+    <g
+       transform="matrix(1.7368422,0,0,1.7368422,12.315788,-135.70585)"
+       id="g892">
+      <path
+         style="opacity:1;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:2.30303;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.0885827;stroke-opacity:1"
+         id="path5171"
+         sodipodi:type="arc"
+         sodipodi:cx="24"
+         sodipodi:cy="216.88385"
+         sodipodi:rx="6.1715264"
+         sodipodi:ry="6.1723976"
+         sodipodi:start="3.9269908"
+         sodipodi:end="5.4977871"
+         d="m 19.636072,212.51931 a 6.1715264,6.1723976 0 0 1 8.727856,0"
+         sodipodi:arc-type="arc"
+         sodipodi:open="true" />
+      <path
+         sodipodi:open="true"
+         d="m 15.272143,208.40876 a 12.343053,12.344795 0 0 1 17.455713,0"
+         sodipodi:end="5.4977871"
+         sodipodi:start="3.9269908"
+         sodipodi:ry="12.344795"
+         sodipodi:rx="12.343053"
+         sodipodi:cy="217.13785"
+         sodipodi:cx="24"
+         sodipodi:type="arc"
+         id="path5179"
+         style="opacity:1;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:2.30303;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.0885827;stroke-opacity:1"
+         sodipodi:arc-type="arc" />
+      <path
+         style="opacity:1;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:2.03818;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.0885827;stroke-opacity:1"
+         d="m 28.145643,216.2905 c 0,0 3.647627,-0.38031 5.476763,-0.35202 2.521183,0.039 7.530262,0.71841 7.530262,0.71841 0.456729,0.0418 0.82814,0.35465 0.82814,0.79521 v 19.96076 c 0,0.44055 -0.369351,0.79522 -0.82814,0.79522 0,0 -5.007349,-0.71629 -7.530262,-0.74156 -3.216849,-0.0323 -9.619664,0.75293 -9.619664,0.75293 0,0 -6.180328,-0.7732 -9.286648,-0.75293 -2.635263,0.0172 -7.8687611,0.74156 -7.8687611,0.74156 -0.458791,0 -0.828142,-0.35467 -0.828142,-0.79522 V 217.4521 c 0,-0.44056 0.369351,-0.79521 0.828142,-0.79521 0,0 5.2350911,-0.68943 7.8687611,-0.71841 1.722646,-0.019 5.155938,0.35654 5.155938,0.35654"
+         id="rect5165"
+         inkscape:connector-curvature="0"
+         sodipodi:nodetypes="cassssacassssac" />
+      <path
+         style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="M 24,237.67988 V 220.77219"
+         id="path5167"
+         inkscape:connector-curvature="0"
+         sodipodi:nodetypes="cc" />
+      <path
+         style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="M 9,228 H 21"
+         id="path5216"
+         inkscape:connector-curvature="0"
+         sodipodi:nodetypes="cc" />
+      <path
+         sodipodi:nodetypes="cc"
+         inkscape:connector-curvature="0"
+         id="path5218"
+         d="M 15,234 V 222"
+         style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cc"
+         inkscape:connector-curvature="0"
+         id="path5220"
+         d="M 27,228 H 39"
+         style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <path
+         style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.0885827;stroke-opacity:1;paint-order:markers stroke fill;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+         d="m 24,215.0707 c 0.807623,0 1.559999,0.75332 1.56,1.56 2e-6,0.80669 -0.752376,1.55829 -1.56,1.55829 -0.807624,0 -1.560002,-0.7516 -1.56,-1.55829 10e-7,-0.80668 0.752377,-1.56 1.56,-1.56 z"
+         id="path5169"
+         inkscape:connector-curvature="0" />
+    </g>
+  </g>
+</svg>