replace FlowLayout with constraint layout

Daniel Gultsch created

Change summary

build.gradle                                                                        |  2 
proguard-rules.pro                                                                  |  1 
src/main/java/eu/siacs/conversations/crypto/sasl/HashedToken.java                   |  6 
src/main/java/eu/siacs/conversations/persistance/UnifiedPushDatabase.java           |  6 
src/main/java/eu/siacs/conversations/services/AttachFileToConversationRunnable.java |  4 
src/main/java/eu/siacs/conversations/services/MessageArchiveService.java            |  4 
src/main/java/eu/siacs/conversations/services/NotificationService.java              | 42 
src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java                 | 16 
src/main/java/eu/siacs/conversations/ui/ConversationFragment.java                   |  4 
src/main/java/eu/siacs/conversations/ui/ShowLocationActivity.java                   |  9 
src/main/java/eu/siacs/conversations/ui/UriHandlerActivity.java                     |  8 
src/main/java/eu/siacs/conversations/ui/XmppActivity.java                           |  5 
src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java                | 23 
src/main/java/eu/siacs/conversations/ui/util/Attachment.java                        |  6 
src/main/java/eu/siacs/conversations/ui/util/SendButtonTool.java                    |  5 
src/main/java/eu/siacs/conversations/xml/Element.java                               |  6 
src/main/java/eu/siacs/conversations/xml/Tag.java                                   |  4 
src/main/res/layout/activity_contact_details.xml                                    | 21 
src/main/res/layout/item_contact.xml                                                | 20 
src/main/res/layout/list_item_tag.xml                                               |  2 
20 files changed, 118 insertions(+), 76 deletions(-)

Detailed changes

build.gradle 🔗

@@ -62,7 +62,7 @@ dependencies {
     implementation 'me.leolin:ShortcutBadger:1.1.22@aar'
     implementation 'org.whispersystems:signal-protocol-java:2.6.2'
     implementation 'com.makeramen:roundedimageview:2.3.0'
-    implementation "com.wefika:flowlayout:0.4.1"
+
     //noinspection GradleDependency
     implementation('com.github.natario1:Transcoder:v0.9.1') {
         exclude group: 'com.otaliastudios.opengl', module: 'egloo'

proguard-rules.pro 🔗

@@ -34,6 +34,7 @@
 -dontwarn org.openjsse.javax.net.ssl.SSLParameters
 -dontwarn org.openjsse.javax.net.ssl.SSLSocket
 -dontwarn org.openjsse.net.ssl.OpenJSSE
+-dontwarn org.jetbrains.annotations.**
 
 -keepclassmembers class eu.siacs.conversations.http.services.** {
   !transient <fields>;

src/main/java/eu/siacs/conversations/crypto/sasl/HashedToken.java 🔗

@@ -3,6 +3,8 @@ package eu.siacs.conversations.crypto.sasl;
 import android.util.Base64;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
+
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableMultimap;
@@ -10,8 +12,6 @@ import com.google.common.collect.Multimap;
 import com.google.common.hash.HashFunction;
 import com.google.common.primitives.Bytes;
 
-import org.jetbrains.annotations.NotNull;
-
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 import java.util.Collection;
@@ -168,7 +168,7 @@ public abstract class HashedToken extends SaslMechanism implements ChannelBindin
             return null;
         }
 
-        @NotNull
+        @NonNull
         @Override
         public String toString() {
             return MoreObjects.toStringHelper(this)

src/main/java/eu/siacs/conversations/persistance/UnifiedPushDatabase.java 🔗

@@ -7,6 +7,7 @@ import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteOpenHelper;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.google.common.base.MoreObjects;
@@ -14,9 +15,6 @@ import com.google.common.base.Objects;
 import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableList;
 
-import org.jetbrains.annotations.NotNull;
-
-import java.util.ArrayList;
 import java.util.List;
 
 import eu.siacs.conversations.Config;
@@ -254,7 +252,7 @@ public class UnifiedPushDatabase extends SQLiteOpenHelper {
             this.instance = instance;
         }
 
-        @NotNull
+        @NonNull
         @Override
         public String toString() {
             return MoreObjects.toStringHelper(this)

src/main/java/eu/siacs/conversations/services/AttachFileToConversationRunnable.java 🔗

@@ -11,8 +11,6 @@ import androidx.annotation.NonNull;
 import com.otaliastudios.transcoder.Transcoder;
 import com.otaliastudios.transcoder.TranscoderListener;
 
-import org.jetbrains.annotations.NotNull;
-
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.util.Objects;
@@ -167,7 +165,7 @@ public class AttachFileToConversationRunnable implements Runnable, TranscoderLis
     }
 
     @Override
-    public void onTranscodeFailed(@NonNull @NotNull Throwable exception) {
+    public void onTranscodeFailed(@NonNull final Throwable exception) {
         mXmppConnectionService.stopOngoingVideoTranscodingForegroundNotification();
         Log.d(Config.LOGTAG, "video transcoding failed", exception);
         processAsFile();

src/main/java/eu/siacs/conversations/services/MessageArchiveService.java 🔗

@@ -4,7 +4,7 @@ import static eu.siacs.conversations.utils.Random.SECURE_RANDOM;
 
 import android.util.Log;
 
-import org.jetbrains.annotations.NotNull;
+import androidx.annotation.NonNull;
 
 import java.math.BigInteger;
 import java.util.ArrayList;
@@ -640,7 +640,7 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
             }
         }
 
-        @NotNull
+        @NonNull
         @Override
         public String toString() {
             StringBuilder builder = new StringBuilder();

src/main/java/eu/siacs/conversations/services/NotificationService.java 🔗

@@ -17,6 +17,7 @@ import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Typeface;
 import android.media.AudioAttributes;
+import android.media.AudioManager;
 import android.media.Ringtone;
 import android.media.RingtoneManager;
 import android.net.Uri;
@@ -48,25 +49,6 @@ import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
 
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
 import eu.siacs.conversations.AppSettings;
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
@@ -79,7 +61,6 @@ import eu.siacs.conversations.persistance.FileBackend;
 import eu.siacs.conversations.ui.ConversationsActivity;
 import eu.siacs.conversations.ui.EditAccountActivity;
 import eu.siacs.conversations.ui.RtpSessionActivity;
-import eu.siacs.conversations.ui.TimePreference;
 import eu.siacs.conversations.utils.AccountUtils;
 import eu.siacs.conversations.utils.Compatibility;
 import eu.siacs.conversations.utils.GeoHelper;
@@ -89,6 +70,24 @@ import eu.siacs.conversations.xmpp.XmppConnection;
 import eu.siacs.conversations.xmpp.jingle.AbstractJingleConnection;
 import eu.siacs.conversations.xmpp.jingle.Media;
 
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
 public class NotificationService {
 
     private static final ScheduledExecutorService SCHEDULED_EXECUTOR_SERVICE =
@@ -518,6 +517,7 @@ public class NotificationService {
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
             this.currentlyPlayingRingtone.setLooping(true);
         }
+        Log.d(Config.LOGTAG,"start playing ringtone: "+uri);
         this.currentlyPlayingRingtone.play();
     }
 
@@ -651,7 +651,7 @@ public class NotificationService {
         int stopped = 0;
         if (this.currentlyPlayingRingtone != null) {
             if (this.currentlyPlayingRingtone.isPlaying()) {
-                Log.d(Config.LOGTAG, "stop playing ring tone");
+                Log.d(Config.LOGTAG, "stop playing ringtone");
                 ++stopped;
             }
             this.currentlyPlayingRingtone.stop();

src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java 🔗

@@ -30,10 +30,13 @@ import android.widget.Toast;
 import androidx.annotation.NonNull;
 import androidx.appcompat.app.AlertDialog;
 import androidx.core.content.ContextCompat;
+import androidx.core.view.ViewCompat;
 import androidx.databinding.DataBindingUtil;
 
 import com.google.android.material.color.MaterialColors;
 import com.google.android.material.dialog.MaterialAlertDialogBuilder;
+import com.google.common.collect.ImmutableList;
+import com.google.common.primitives.Ints;
 
 import org.openintents.openpgp.util.OpenPgpUtils;
 
@@ -514,12 +517,16 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp
             binding.tags.setVisibility(View.GONE);
         } else {
             binding.tags.setVisibility(View.VISIBLE);
-            binding.tags.removeAllViewsInLayout();
+            binding.tags.removeViews(1, binding.tags.getChildCount() - 1);
+            final ImmutableList.Builder<Integer> viewIdBuilder = new ImmutableList.Builder<>();
             for (final ListItem.Tag tag : tagList) {
                 final String name = tag.getName();
                 final TextView tv = (TextView) inflater.inflate(R.layout.list_item_tag, binding.tags, false);
                 tv.setText(name);
                 tv.setBackgroundTintList(ColorStateList.valueOf(MaterialColors.harmonizeWithPrimary(this,XEP0392Helper.rgbFromNick(name))));
+                final int id = ViewCompat.generateViewId();
+                tv.setId(id);
+                viewIdBuilder.add(id);
                 binding.tags.addView(tv);
             }
             if (contact.isBlocked()) {
@@ -529,6 +536,9 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp
                                         R.layout.list_item_tag, binding.tags, false);
                 tv.setText(R.string.blocked);
                 tv.setBackgroundTintList(ColorStateList.valueOf(MaterialColors.harmonizeWithPrimary(tv.getContext(), ContextCompat.getColor(tv.getContext(),R.color.gray_800))));
+                final int id = ViewCompat.generateViewId();
+                tv.setId(id);
+                viewIdBuilder.add(id);
                 binding.tags.addView(tv);
             } else {
                 final Presence.Status status = contact.getShownStatus();
@@ -538,9 +548,13 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp
                                     inflater.inflate(
                                             R.layout.list_item_tag, binding.tags, false);
                     UIHelper.setStatus(tv, status);
+                    final int id = ViewCompat.generateViewId();
+                    tv.setId(id);
+                    viewIdBuilder.add(id);
                     binding.tags.addView(tv);
                 }
             }
+            binding.flowWidget.setReferencedIds(Ints.toArray(viewIdBuilder.build()));
         }
     }
 

src/main/java/eu/siacs/conversations/ui/ConversationFragment.java 🔗

@@ -59,7 +59,6 @@ import android.widget.Toast;
 import androidx.annotation.IdRes;
 import androidx.annotation.NonNull;
 import androidx.annotation.StringRes;
-import androidx.appcompat.app.AlertDialog;
 import androidx.core.view.inputmethod.InputConnectionCompat;
 import androidx.core.view.inputmethod.InputContentInfoCompat;
 import androidx.databinding.DataBindingUtil;
@@ -131,7 +130,6 @@ import eu.siacs.conversations.xmpp.jingle.OngoingRtpSession;
 import eu.siacs.conversations.xmpp.jingle.RtpCapability;
 import eu.siacs.conversations.xmpp.jingle.RtpEndUserState;
 
-import org.jetbrains.annotations.NotNull;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -2314,7 +2312,7 @@ public class ConversationFragment extends XmppFragment
     }
 
     @Override
-    public void onSaveInstanceState(@NotNull Bundle outState) {
+    public void onSaveInstanceState(@NonNull Bundle outState) {
         super.onSaveInstanceState(outState);
         if (conversation != null) {
             outState.putString(STATE_CONVERSATION_UUID, conversation.getUuid());

src/main/java/eu/siacs/conversations/ui/ShowLocationActivity.java 🔗

@@ -17,7 +17,6 @@ import android.widget.Toast;
 import androidx.annotation.NonNull;
 import androidx.databinding.DataBindingUtil;
 
-import org.jetbrains.annotations.NotNull;
 import org.osmdroid.util.GeoPoint;
 
 import java.util.HashMap;
@@ -151,7 +150,7 @@ public class ShowLocationActivity extends LocationActivity implements LocationLi
     }
 
     @Override
-    public boolean onCreateOptionsMenu(@NotNull final Menu menu) {
+    public boolean onCreateOptionsMenu(@NonNull final Menu menu) {
         // Inflate the menu; this adds items to the action bar if it is present.
         getMenuInflater().inflate(R.menu.menu_show_location, menu);
         updateUi();
@@ -230,7 +229,7 @@ public class ShowLocationActivity extends LocationActivity implements LocationLi
     }
 
     @Override
-    public void onLocationChanged(@NotNull final Location location) {
+    public void onLocationChanged(@NonNull final Location location) {
         if (LocationHelper.isBetterLocation(location, this.myLoc)) {
             this.myLoc = location;
             updateLocationMarkers();
@@ -241,8 +240,8 @@ public class ShowLocationActivity extends LocationActivity implements LocationLi
     public void onStatusChanged(final String provider, final int status, final Bundle extras) {}
 
     @Override
-    public void onProviderEnabled(@NotNull final String provider) {}
+    public void onProviderEnabled(@NonNull final String provider) {}
 
     @Override
-    public void onProviderDisabled(@NotNull final String provider) {}
+    public void onProviderDisabled(@NonNull final String provider) {}
 }

src/main/java/eu/siacs/conversations/ui/UriHandlerActivity.java 🔗

@@ -11,8 +11,8 @@ import android.util.Log;
 import android.view.View;
 import android.widget.Toast;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.StringRes;
-import androidx.appcompat.app.AppCompatActivity;
 import androidx.core.content.ContextCompat;
 import androidx.databinding.DataBindingUtil;
 
@@ -35,8 +35,6 @@ import okhttp3.HttpUrl;
 import okhttp3.Request;
 import okhttp3.Response;
 
-import org.jetbrains.annotations.NotNull;
-
 import java.io.IOException;
 import java.util.List;
 import java.util.regex.Matcher;
@@ -218,13 +216,13 @@ public class UriHandlerActivity extends BaseActivity {
         this.call.enqueue(
                 new Callback() {
                     @Override
-                    public void onFailure(@NotNull Call call, @NotNull IOException e) {
+                    public void onFailure(@NonNull Call call, @NonNull IOException e) {
                         Log.d(Config.LOGTAG, "unable to check HTTP url", e);
                         showError(R.string.no_xmpp_adddress_found);
                     }
 
                     @Override
-                    public void onResponse(@NotNull Call call, @NotNull Response response) {
+                    public void onResponse(@NonNull Call call, @NonNull Response response) {
                         if (response.isSuccessful()) {
                             final String linkHeader = response.header("Link");
                             if (linkHeader != null && processLinkHeader(linkHeader)) {

src/main/java/eu/siacs/conversations/ui/XmppActivity.java 🔗

@@ -899,12 +899,9 @@ public abstract class XmppActivity extends ActionBarActivity {
         final Point size = new Point();
         getWindowManager().getDefaultDisplay().getSize(size);
         final int width = Math.min(size.x, size.y);
-        final boolean nightMode = (this.getResources().getConfiguration().uiMode
-                & Configuration.UI_MODE_NIGHT_MASK)
-                == Configuration.UI_MODE_NIGHT_YES;
         final int black;
         final int white;
-        if (nightMode) {
+        if (Activities.isNightMode(this)) {
             black = MaterialColors.getColor(this, com.google.android.material.R.attr.colorSurfaceContainerHighest,"No surface color configured");
             white = MaterialColors.getColor(this, com.google.android.material.R.attr.colorSurfaceInverse,"No inverse surface color configured");
         } else {

src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java 🔗

@@ -12,11 +12,15 @@ import android.widget.ImageView;
 import android.widget.TextView;
 
 import androidx.annotation.NonNull;
+import androidx.constraintlayout.helper.widget.Flow;
+import androidx.constraintlayout.widget.ConstraintLayout;
 import androidx.core.content.ContextCompat;
+import androidx.core.view.ViewCompat;
 import androidx.databinding.DataBindingUtil;
 
 import com.google.android.material.color.MaterialColors;
-import com.wefika.flowlayout.FlowLayout;
+import com.google.common.collect.ImmutableList;
+import com.google.common.primitives.Ints;
 
 import eu.siacs.conversations.AppSettings;
 import eu.siacs.conversations.Config;
@@ -85,13 +89,17 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> {
 			viewHolder.tags.setVisibility(View.GONE);
 		} else {
 			viewHolder.tags.setVisibility(View.VISIBLE);
-			viewHolder.tags.removeAllViewsInLayout();
+			viewHolder.tags.removeViews(1, viewHolder.tags.getChildCount() - 1);
+			final ImmutableList.Builder<Integer> viewIdBuilder = new ImmutableList.Builder<>();
 			for (final ListItem.Tag tag : tags) {
 				final String name = tag.getName();
 				final TextView tv = (TextView) inflater.inflate(R.layout.list_item_tag, viewHolder.tags, false);
 				tv.setText(name);
 				tv.setBackgroundTintList(ColorStateList.valueOf(MaterialColors.harmonizeWithPrimary(getContext(),XEP0392Helper.rgbFromNick(name))));
 				tv.setOnClickListener(this.onTagTvClick);
+				final int id = ViewCompat.generateViewId();
+				tv.setId(id);
+				viewIdBuilder.add(id);
 				viewHolder.tags.addView(tv);
 			}
 			if (item instanceof Contact contact) {
@@ -102,6 +110,9 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> {
 											R.layout.list_item_tag, viewHolder.tags, false);
 					tv.setText(R.string.blocked);
 					tv.setBackgroundTintList(ColorStateList.valueOf(MaterialColors.harmonizeWithPrimary(tv.getContext(),ContextCompat.getColor(tv.getContext(),R.color.gray_800))));
+					final int id = ViewCompat.generateViewId();
+					tv.setId(id);
+					viewIdBuilder.add(id);
 					viewHolder.tags.addView(tv);
 				} else {
                     final Presence.Status status = contact.getShownStatus();
@@ -111,10 +122,14 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> {
                                         inflater.inflate(
                                                 R.layout.list_item_tag, viewHolder.tags, false);
 						UIHelper.setStatus(tv, status);
+						final int id = ViewCompat.generateViewId();
+						tv.setId(id);
+						viewIdBuilder.add(id);
 						viewHolder.tags.addView(tv);
                     }
                 }
 			}
+			viewHolder.flowWidget.setReferencedIds(Ints.toArray(viewIdBuilder.build()));
 		}
 		final Jid jid = item.getJid();
 		if (jid != null) {
@@ -141,7 +156,8 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> {
 		private TextView name;
 		private TextView jid;
 		private ImageView avatar;
-		private FlowLayout tags;
+		private ConstraintLayout tags;
+		private Flow flowWidget;
 
 		private ViewHolder() {
 
@@ -153,6 +169,7 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> {
 			viewHolder.jid = binding.contactJid;
 			viewHolder.avatar = binding.contactPhoto;
 			viewHolder.tags = binding.tags;
+			viewHolder.flowWidget = binding.flowWidget;
 			binding.getRoot().setTag(viewHolder);
 			return viewHolder;
 		}

src/main/java/eu/siacs/conversations/ui/util/Attachment.java 🔗

@@ -36,13 +36,13 @@ import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import androidx.annotation.NonNull;
+
 import com.google.common.base.MoreObjects;
 
 import eu.siacs.conversations.entities.Message;
 import eu.siacs.conversations.utils.MimeUtils;
 
-import org.jetbrains.annotations.NotNull;
-
 import java.io.File;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -92,7 +92,7 @@ public class Attachment implements Parcelable {
         return type;
     }
 
-    @NotNull
+    @NonNull
     @Override
     public String toString() {
         return MoreObjects.toStringHelper(this)

src/main/java/eu/siacs/conversations/ui/util/SendButtonTool.java 🔗

@@ -44,6 +44,7 @@ import com.google.android.material.color.MaterialColors;
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.entities.Conversation;
 import eu.siacs.conversations.entities.Presence;
+import eu.siacs.conversations.ui.Activities;
 import eu.siacs.conversations.ui.ConversationFragment;
 import eu.siacs.conversations.utils.UIHelper;
 
@@ -110,9 +111,7 @@ public class SendButtonTool {
     }
 
     public @ColorInt static int getSendButtonColor(final View view, final Presence.Status status) {
-        final boolean nightMode =
-                (view.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK)
-                        == Configuration.UI_MODE_NIGHT_YES;
+        final boolean nightMode = Activities.isNightMode(view.getContext());
         return switch (status) {
             case OFFLINE -> MaterialColors.getColor(
                     view, com.google.android.material.R.attr.colorOnSurface);

src/main/java/eu/siacs/conversations/xml/Element.java 🔗

@@ -1,10 +1,10 @@
 package eu.siacs.conversations.xml;
 
+import androidx.annotation.NonNull;
+
 import com.google.common.base.Optional;
 import com.google.common.primitives.Ints;
 
-import org.jetbrains.annotations.NotNull;
-
 import java.util.ArrayList;
 import java.util.Hashtable;
 import java.util.List;
@@ -177,7 +177,7 @@ public class Element {
         return this.attributes;
     }
 
-    @NotNull
+    @NonNull
     public String toString() {
         final StringBuilder elementOutput = new StringBuilder();
         if ((content == null) && (children.size() == 0)) {

src/main/java/eu/siacs/conversations/xml/Tag.java 🔗

@@ -1,6 +1,6 @@
 package eu.siacs.conversations.xml;
 
-import org.jetbrains.annotations.NotNull;
+import androidx.annotation.NonNull;
 
 import java.util.Hashtable;
 import java.util.Map.Entry;
@@ -80,7 +80,7 @@ public class Tag {
         return (this.type == NO);
     }
 
-    @NotNull
+    @NonNull
     public String toString() {
         final StringBuilder tagOutput = new StringBuilder();
         tagOutput.append('<');

src/main/res/layout/activity_contact_details.xml 🔗

@@ -66,14 +66,25 @@
                                 android:text="@string/account_settings_example_jabber_id"
                                 android:textAppearance="?textAppearanceTitleMedium" />
 
-                            <com.wefika.flowlayout.FlowLayout
+                            <androidx.constraintlayout.widget.ConstraintLayout
                                 android:id="@+id/tags"
                                 android:layout_width="wrap_content"
                                 android:layout_height="wrap_content"
-                                android:layout_marginLeft="-2dp"
-                                android:layout_marginTop="4dp"
-                                android:layout_marginBottom="4dp"
-                                android:orientation="horizontal" />
+                                android:layout_marginTop="8sp">
+
+                                <androidx.constraintlayout.helper.widget.Flow
+                                    android:id="@+id/flow_widget"
+                                    android:layout_width="0dp"
+                                    android:layout_height="wrap_content"
+                                    app:flow_horizontalBias="0"
+                                    app:flow_horizontalGap="8sp"
+                                    app:flow_horizontalStyle="packed"
+                                    app:flow_verticalGap="4sp"
+                                    app:flow_wrapMode="chain"
+                                    app:layout_constraintEnd_toEndOf="parent"
+                                    app:layout_constraintStart_toStartOf="parent"
+                                    app:layout_constraintTop_toTopOf="parent" />
+                            </androidx.constraintlayout.widget.ConstraintLayout>
 
                             <TextView
                                 android:id="@+id/details_lastseen"

src/main/res/layout/item_contact.xml 🔗

@@ -41,12 +41,26 @@
                 android:textAppearance="?textAppearanceBodyMedium"
                 tools:text="juliet@capulet.example" />
 
-            <com.wefika.flowlayout.FlowLayout
+            <androidx.constraintlayout.widget.ConstraintLayout
                 android:id="@+id/tags"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:layout_marginStart="-2dp"
-                android:orientation="horizontal"/>
+                android:layout_marginTop="4sp">
+
+                <androidx.constraintlayout.helper.widget.Flow
+                    android:id="@+id/flow_widget"
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    app:flow_horizontalBias="0"
+                    app:flow_horizontalGap="8sp"
+                    app:flow_horizontalStyle="packed"
+                    app:flow_verticalGap="4sp"
+                    app:flow_wrapMode="chain"
+                    app:layout_constraintEnd_toEndOf="parent"
+                    app:layout_constraintStart_toStartOf="parent"
+                    app:layout_constraintTop_toTopOf="parent" />
+            </androidx.constraintlayout.widget.ConstraintLayout>
+
 
             <TextView
                 android:id="@+id/key"

src/main/res/layout/list_item_tag.xml 🔗

@@ -3,8 +3,6 @@
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:layout_marginHorizontal="2dp"
-    android:layout_marginVertical="4dp"
     android:background="@drawable/background_label"
     android:backgroundTint="@color/green_700"
     android:maxLines="1"