From 9e3a6ce97707f185ba183db346614a03de35cb04 Mon Sep 17 00:00:00 2001
From: Mikunj <mikunj@live.com.au>
Date: Fri, 6 Sep 2019 11:49:02 +1000
Subject: [PATCH 1/6] Added Identicon

---
 .../securesms/ConversationListActivity.java   |   3 +-
 .../securesms/components/AvatarImageView.java |   3 +-
 .../loki/identicon/IdenticonDrawable.kt       |  58 ++++++++
 .../loki/identicon/JazzIdenticonDrawable.kt   | 130 ++++++++++++++++++
 .../securesms/loki/identicon/RNG.kt           |  29 ++++
 .../widgets/ProfilePreference.java            |   3 +-
 6 files changed, 223 insertions(+), 3 deletions(-)
 create mode 100644 src/org/thoughtcrime/securesms/loki/identicon/IdenticonDrawable.kt
 create mode 100644 src/org/thoughtcrime/securesms/loki/identicon/JazzIdenticonDrawable.kt
 create mode 100644 src/org/thoughtcrime/securesms/loki/identicon/RNG.kt

diff --git a/src/org/thoughtcrime/securesms/ConversationListActivity.java b/src/org/thoughtcrime/securesms/ConversationListActivity.java
index 42c4d4bbdd..1b70956137 100644
--- a/src/org/thoughtcrime/securesms/ConversationListActivity.java
+++ b/src/org/thoughtcrime/securesms/ConversationListActivity.java
@@ -46,6 +46,7 @@ import org.thoughtcrime.securesms.database.Address;
 import org.thoughtcrime.securesms.database.DatabaseFactory;
 import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
 import org.thoughtcrime.securesms.lock.RegistrationLockDialog;
+import org.thoughtcrime.securesms.loki.identicon.JazzIdenticonDrawable;
 import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
 import org.thoughtcrime.securesms.notifications.MessageNotifier;
 import org.thoughtcrime.securesms.permissions.Permissions;
@@ -204,7 +205,7 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
         int height = profilePictureImageView.getHeight();
         if (width == 0 || height == 0) return true;
         profilePictureImageView.getViewTreeObserver().removeOnPreDrawListener(this);
-        ClassicIdenticonDrawable identicon = new ClassicIdenticonDrawable(width, height, recipient.getAddress().serialize().hashCode());
+        JazzIdenticonDrawable identicon = new JazzIdenticonDrawable(width, height, recipient.getAddress().serialize());
         profilePictureImageView.setImageDrawable(identicon);
         return true;
       }
diff --git a/src/org/thoughtcrime/securesms/components/AvatarImageView.java b/src/org/thoughtcrime/securesms/components/AvatarImageView.java
index ce0fffb72a..b9a7627269 100644
--- a/src/org/thoughtcrime/securesms/components/AvatarImageView.java
+++ b/src/org/thoughtcrime/securesms/components/AvatarImageView.java
@@ -17,6 +17,7 @@ import android.view.ViewOutlineProvider;
 import com.lelloman.identicon.drawable.ClassicIdenticonDrawable;
 
 import network.loki.messenger.R;
+import org.thoughtcrime.securesms.loki.identicon.JazzIdenticonDrawable;
 import org.thoughtcrime.securesms.mms.GlideRequests;
 import org.thoughtcrime.securesms.recipients.Recipient;
 import org.thoughtcrime.securesms.recipients.RecipientExporter;
@@ -97,7 +98,7 @@ public class AvatarImageView extends AppCompatImageView {
   protected void onSizeChanged(int w, int h, int oldw, int oldh) {
     super.onSizeChanged(w, h, oldw, oldh);
     if (w == 0 || h == 0 || recipient == null) { return; }
-    ClassicIdenticonDrawable identicon = new ClassicIdenticonDrawable(w, h, recipient.getAddress().serialize().hashCode());
+    JazzIdenticonDrawable identicon = new JazzIdenticonDrawable(w, h, recipient.getAddress().serialize());
     setImageDrawable(identicon);
   }
 
diff --git a/src/org/thoughtcrime/securesms/loki/identicon/IdenticonDrawable.kt b/src/org/thoughtcrime/securesms/loki/identicon/IdenticonDrawable.kt
new file mode 100644
index 0000000000..257b4bf40c
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/loki/identicon/IdenticonDrawable.kt
@@ -0,0 +1,58 @@
+package org.thoughtcrime.securesms.loki.identicon
+
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.ColorFilter
+import android.graphics.Paint
+import android.graphics.Rect
+import android.graphics.drawable.Drawable
+
+/**
+ * Basically a [Bitmap] wrapper, the [Bitmap] size must be known when instantiating it
+ * but when drawing it will draw the [Bitmap] to fit the canvas.
+ */
+abstract class IdenticonDrawable(
+        width: Int,
+        height: Int,
+        hash: Long
+) : Drawable() {
+
+  private val bitmapRect: Rect = Rect(0, 0, width, height)
+  private val destinationRect: Rect = Rect(0, 0, width, height)
+  private val bitmap: Bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
+  private val canvas: Canvas = Canvas(bitmap)
+  private val bitmapPaint = Paint(Paint.ANTI_ALIAS_FLAG)
+
+  var hash: Long = hash
+    set(value) {
+      field = value
+      onSetHash(value)
+      invalidateBitmap()
+    }
+
+  protected fun invalidateBitmap() {
+    drawBitmap(canvas)
+    invalidateSelf()
+  }
+
+  protected abstract fun drawBitmap(canvas: Canvas)
+
+  protected open fun onSetHash(newHash: Long) = Unit
+
+  override fun draw(canvas: Canvas) {
+    destinationRect.set(0, 0, canvas.width, canvas.height)
+    canvas.drawBitmap(bitmap, bitmapRect, destinationRect, bitmapPaint)
+  }
+
+  override fun setAlpha(i: Int) {
+    bitmapPaint.alpha = i
+  }
+
+  override fun setColorFilter(colorFilter: ColorFilter?) {
+    bitmapPaint.colorFilter = colorFilter
+  }
+
+  override fun getOpacity(): Int {
+    return bitmapPaint.alpha
+  }
+}
\ No newline at end of file
diff --git a/src/org/thoughtcrime/securesms/loki/identicon/JazzIdenticonDrawable.kt b/src/org/thoughtcrime/securesms/loki/identicon/JazzIdenticonDrawable.kt
new file mode 100644
index 0000000000..06c5fc618d
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/loki/identicon/JazzIdenticonDrawable.kt
@@ -0,0 +1,130 @@
+package org.thoughtcrime.securesms.loki.identicon
+
+import android.graphics.*
+import kotlin.math.*
+
+class JazzIdenticonDrawable(width: Int, height: Int, hash: Long) : IdenticonDrawable(width, height, hash) {
+
+  constructor(width: Int, height: Int, hashString: String): this(width, height, 0) {
+    val hexRegex = Regex("^[0-9A-Fa-f]+\$")
+    if (hashString.length >= 12 && hashString.matches(hexRegex)) {
+      hash = hashString.substring(0 until 12).toLong(16)
+    }
+  }
+
+  companion object {
+    var colors = listOf(
+            "#01888c", // teal
+            "#fc7500", // bright orange
+            "#034f5d", // dark teal
+            "#E784BA", // light pink
+            "#81C8B6", // bright green
+            "#c7144c", // raspberry
+            "#f3c100", // goldenrod
+            "#1598f2", // lightning blue
+            "#2465e1", // sail blue
+            "#f19e02"  // gold
+    ).map{ Color.parseColor(it) }
+  }
+
+  private var generator: RNG = RNG(hash)
+  private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply { style = Paint.Style.FILL }
+
+  // Settings
+  private val wobble: Float = 30f
+  private val shapeCount = 4
+
+  init {
+    invalidateBitmap()
+  }
+
+  override fun onSetHash(newHash: Long) {
+    super.onSetHash(newHash)
+    generator = RNG(newHash)
+    invalidateBitmap()
+  }
+
+  override fun drawBitmap(canvas: Canvas) {
+    generator.reset()
+
+    val newColors = hueShift(colors)
+    val shuffled = shuffleList(newColors)
+
+    canvas.drawColor(shuffled[0])
+    for (i in 0 until shapeCount) {
+      drawSquare(canvas, shuffled[i + 1], i, shapeCount - 1)
+    }
+  }
+
+  private fun drawSquare(canvas: Canvas, color: Int, index: Int, total: Int) {
+    val size = min(canvas.width, canvas.height)
+    val center = (size / 2).toFloat()
+    val firstRotation = generator.nextFloat()
+    val angle = PI * 2 * firstRotation
+
+    val a = size / total.toFloat()
+    val b = generator.nextFloat()
+    val c = index.toFloat() * a
+    val velocity = a * b + c
+
+    val tx = cos(angle) * velocity
+    val ty = sin(angle) * velocity
+
+    // Third random is a shape rotation on top of all that
+    val secondRotation = generator.nextFloat()
+    val rotation = (firstRotation * 360f) + (secondRotation * 180f)
+
+    // Paint it!
+    canvas.save()
+
+    paint.color = color
+    canvas.translate(tx.toFloat(), ty.toFloat())
+    canvas.rotate(rotation.round(1), center, center)
+    canvas.drawRect(0f, 0f, canvas.width.toFloat(), canvas.height.toFloat(), paint)
+
+    canvas.restore()
+  }
+
+  private fun hueShift(colors: List<Int>): List<Int> {
+    val amount = generator.nextFloat() * 30 - wobble / 2
+
+    return colors.map { color ->
+      val red = Color.red(color)
+      val green = Color.green(color)
+      val blue = Color.blue(color)
+
+      val hsv = FloatArray(3)
+      Color.RGBToHSV(red, green, blue, hsv)
+
+      // Normalise between 0 and 360
+      var newHue = hsv[0] + round(amount)
+      if (newHue < 0) { newHue += 360 }
+      if (newHue > 360) { newHue -= 360 }
+
+      hsv[0] = newHue
+      Color.HSVToColor(hsv)
+    }
+  }
+
+  private fun <T> shuffleList(list: List<T>): List<T> {
+    var currentIndex = list.count()
+    val newList = list.toMutableList()
+    while (currentIndex > 0) {
+      val randomIndex = generator.next().toInt() % currentIndex
+      currentIndex -= 1
+
+      // Swap
+      val temp = newList[currentIndex]
+      newList[currentIndex] = newList[randomIndex]
+      newList[randomIndex] = temp
+    }
+
+    return newList
+  }
+}
+
+private fun Float.round(decimals: Int): Float {
+  var multiplier = 1f
+  repeat(decimals) { multiplier *= 10 }
+  return round(this * multiplier) / multiplier
+}
\ No newline at end of file
diff --git a/src/org/thoughtcrime/securesms/loki/identicon/RNG.kt b/src/org/thoughtcrime/securesms/loki/identicon/RNG.kt
new file mode 100644
index 0000000000..6e43ecaeb5
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/loki/identicon/RNG.kt
@@ -0,0 +1,29 @@
+package org.thoughtcrime.securesms.loki.identicon
+
+class RNG(hash: Long) {
+
+  private var seed: Long
+  private val initial: Long
+
+  init {
+    seed = hash % 2147483647
+    if (seed <= 0) {
+      seed = 2147483646
+    }
+    initial = seed
+  }
+
+  public fun next(): Long {
+    val newSeed = (seed * 16807) % 2147483647
+    seed = newSeed
+    return seed
+  }
+
+  public fun nextFloat(): Float {
+    return (next() - 1).toFloat() / 2147483646
+  }
+
+  public fun reset() {
+    seed = initial
+  }
+}
\ No newline at end of file
diff --git a/src/org/thoughtcrime/securesms/preferences/widgets/ProfilePreference.java b/src/org/thoughtcrime/securesms/preferences/widgets/ProfilePreference.java
index c67d6c0d1e..2838bfc0b3 100644
--- a/src/org/thoughtcrime/securesms/preferences/widgets/ProfilePreference.java
+++ b/src/org/thoughtcrime/securesms/preferences/widgets/ProfilePreference.java
@@ -21,6 +21,7 @@ import android.widget.Toast;
 import com.lelloman.identicon.drawable.ClassicIdenticonDrawable;
 
 import org.thoughtcrime.securesms.database.Address;
+import org.thoughtcrime.securesms.loki.identicon.JazzIdenticonDrawable;
 import org.thoughtcrime.securesms.util.TextSecurePreferences;
 
 import network.loki.messenger.R;
@@ -101,7 +102,7 @@ public class ProfilePreference extends Preference {
         int height = avatarView.getHeight();
         if (width == 0 || height == 0) return true;
         avatarView.getViewTreeObserver().removeOnPreDrawListener(this);
-        ClassicIdenticonDrawable identicon = new ClassicIdenticonDrawable(width, height, userHexEncodedPublicKey.hashCode());
+        JazzIdenticonDrawable identicon = new JazzIdenticonDrawable(width, height, userHexEncodedPublicKey);
         avatarView.setImageDrawable(identicon);
         return true;
       }

From e054740ce0f15021d20b03bc20e1ea998e627486 Mon Sep 17 00:00:00 2001
From: Mikunj <mikunj@live.com.au>
Date: Fri, 6 Sep 2019 14:08:46 +1000
Subject: [PATCH 2/6] Disable identicon generation for groups.

---
 .../securesms/components/AvatarImageView.java | 26 +++++++++++++++++--
 1 file changed, 24 insertions(+), 2 deletions(-)

diff --git a/src/org/thoughtcrime/securesms/components/AvatarImageView.java b/src/org/thoughtcrime/securesms/components/AvatarImageView.java
index b9a7627269..dfaf053a0a 100644
--- a/src/org/thoughtcrime/securesms/components/AvatarImageView.java
+++ b/src/org/thoughtcrime/securesms/components/AvatarImageView.java
@@ -6,10 +6,12 @@ import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Outline;
 import android.graphics.Paint;
+import android.graphics.drawable.Drawable;
 import android.provider.ContactsContract;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.v7.widget.AppCompatImageView;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewOutlineProvider;
@@ -17,11 +19,16 @@ import android.view.ViewOutlineProvider;
 import com.lelloman.identicon.drawable.ClassicIdenticonDrawable;
 
 import network.loki.messenger.R;
+import org.thoughtcrime.securesms.color.MaterialColor;
+import org.thoughtcrime.securesms.contacts.avatars.ContactColors;
+import org.thoughtcrime.securesms.contacts.avatars.GeneratedContactPhoto;
 import org.thoughtcrime.securesms.loki.identicon.JazzIdenticonDrawable;
 import org.thoughtcrime.securesms.mms.GlideRequests;
 import org.thoughtcrime.securesms.recipients.Recipient;
 import org.thoughtcrime.securesms.recipients.RecipientExporter;
+import org.thoughtcrime.securesms.util.TextSecurePreferences;
 import org.thoughtcrime.securesms.util.ThemeUtil;
+import org.whispersystems.libsignal.util.guava.Optional;
 
 public class AvatarImageView extends AppCompatImageView {
 
@@ -98,8 +105,23 @@ public class AvatarImageView extends AppCompatImageView {
   protected void onSizeChanged(int w, int h, int oldw, int oldh) {
     super.onSizeChanged(w, h, oldw, oldh);
     if (w == 0 || h == 0 || recipient == null) { return; }
-    JazzIdenticonDrawable identicon = new JazzIdenticonDrawable(w, h, recipient.getAddress().serialize());
-    setImageDrawable(identicon);
+
+    Drawable image;
+    if (recipient.isGroupRecipient()) {
+      Context context = this.getContext();
+
+      String name = Optional.fromNullable(recipient.getName()).or(Optional.fromNullable(TextSecurePreferences.getProfileName(context))).or("");
+      MaterialColor fallbackColor = recipient.getColor();
+
+      if (fallbackColor == ContactColors.UNKNOWN_COLOR && !TextUtils.isEmpty(name)) {
+        fallbackColor = ContactColors.generateFor(name);
+      }
+
+      image = new GeneratedContactPhoto(name, R.drawable.ic_profile_default).asDrawable(context, fallbackColor.toAvatarColor(context));
+    } else {
+      image = new JazzIdenticonDrawable(w, h, recipient.getAddress().serialize());
+    }
+    setImageDrawable(image);
   }
 
   public void setAvatar(@NonNull GlideRequests requestManager, @Nullable Recipient recipient, boolean quickContactEnabled) {

From 7ff27b572f34d72e1d7c0d7aa49de7a1798ae102 Mon Sep 17 00:00:00 2001
From: Mikunj <mikunj@live.com.au>
Date: Fri, 6 Sep 2019 16:18:26 +1000
Subject: [PATCH 3/6] Fix identicons for public chats

---
 src/org/thoughtcrime/securesms/loki/LokiGroupChatPoller.kt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/org/thoughtcrime/securesms/loki/LokiGroupChatPoller.kt b/src/org/thoughtcrime/securesms/loki/LokiGroupChatPoller.kt
index a5c57e31f9..e1053d38bc 100644
--- a/src/org/thoughtcrime/securesms/loki/LokiGroupChatPoller.kt
+++ b/src/org/thoughtcrime/securesms/loki/LokiGroupChatPoller.kt
@@ -94,7 +94,7 @@ class LokiGroupChatPoller(private val context: Context, private val group: LokiG
             val x1 = SignalServiceGroup(SignalServiceGroup.Type.UPDATE, id, null, null, null)
             val x2 = SignalServiceDataMessage(message.timestamp, x1, null, message.body)
             val senderDisplayName = "${message.displayName} (...${message.hexEncodedPublicKey.takeLast(8)})"
-            val x3 = SignalServiceContent(x2, senderDisplayName, SignalServiceAddress.DEFAULT_DEVICE_ID, message.timestamp, false)
+            val x3 = SignalServiceContent(x2, message.hexEncodedPublicKey, SignalServiceAddress.DEFAULT_DEVICE_ID, message.timestamp, false)
             PushDecryptJob(context).handleTextMessage(x3, x2, Optional.absent(), Optional.of(message.serverID))
         }
         fun processOutgoingMessage(message: LokiGroupMessage) {

From 842be4378a81cf5b32ea0b66a99e64491d4beb88 Mon Sep 17 00:00:00 2001
From: Mikunj <mikunj@live.com.au>
Date: Mon, 9 Sep 2019 12:53:31 +1000
Subject: [PATCH 4/6] Show correct names on public chats.

---
 .../conversation/ConversationItem.java        | 20 +++++++-------
 .../database/helpers/SQLCipherOpenHelper.java | 10 +++++--
 .../securesms/loki/LokiGroupChatPoller.kt     |  5 +++-
 .../securesms/loki/LokiUserDatabase.kt        | 26 ++++++++++++++++++-
 .../securesms/util/GroupUtil.java             |  6 ++++-
 5 files changed, 52 insertions(+), 15 deletions(-)

diff --git a/src/org/thoughtcrime/securesms/conversation/ConversationItem.java b/src/org/thoughtcrime/securesms/conversation/ConversationItem.java
index c089c50f19..35b1be25d1 100644
--- a/src/org/thoughtcrime/securesms/conversation/ConversationItem.java
+++ b/src/org/thoughtcrime/securesms/conversation/ConversationItem.java
@@ -99,15 +99,7 @@ import org.thoughtcrime.securesms.mms.TextSlide;
 import org.thoughtcrime.securesms.recipients.Recipient;
 import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
 import org.thoughtcrime.securesms.stickers.StickerUrl;
-import org.thoughtcrime.securesms.util.DateUtils;
-import org.thoughtcrime.securesms.util.DynamicTheme;
-import org.thoughtcrime.securesms.util.LongClickCopySpan;
-import org.thoughtcrime.securesms.util.LongClickMovementMethod;
-import org.thoughtcrime.securesms.util.SearchUtil;
-import org.thoughtcrime.securesms.util.TextSecurePreferences;
-import org.thoughtcrime.securesms.util.ThemeUtil;
-import org.thoughtcrime.securesms.util.Util;
-import org.thoughtcrime.securesms.util.ViewUtil;
+import org.thoughtcrime.securesms.util.*;
 import org.thoughtcrime.securesms.util.views.Stub;
 import org.whispersystems.libsignal.util.guava.Optional;
 
@@ -883,7 +875,15 @@ public class ConversationItem extends LinearLayout
   @SuppressLint("SetTextI18n")
   private void setGroupMessageStatus(MessageRecord messageRecord, Recipient recipient) {
     if (groupThread && !messageRecord.isOutgoing()) {
-      this.groupSender.setText(recipient.toShortString());
+      // Show custom display names for group chats
+      String displayName = recipient.toShortString();
+      try {
+        String serverId = GroupUtil.getDecodedStringId(conversationRecipient.getAddress().serialize());
+        String senderDisplayName = DatabaseFactory.getLokiUserDatabase(context).getServerDisplayName(serverId, recipient.getAddress().serialize());
+        if (senderDisplayName != null) { displayName = senderDisplayName; }
+      } catch (Exception e) {}
+
+      this.groupSender.setText(displayName);
 
       if (recipient.getName() == null && !TextUtils.isEmpty(recipient.getProfileName())) {
         this.groupSenderProfileName.setText("~" + recipient.getProfileName());
diff --git a/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java b/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java
index 5c54f7d13b..31ca0310ec 100644
--- a/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java
+++ b/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java
@@ -73,8 +73,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
   private static final int JOBMANAGER_STRIKES_BACK          = 20;
   private static final int STICKERS                         = 21;
   private static final int lokiV1                           = 22;
+  private static final int lokiV2                           = 23;
 
-  private static final int    DATABASE_VERSION = lokiV1; // Loki - onUpgrade(...) must be updated to use Loki version numbers if Signal makes any database changes
+  private static final int    DATABASE_VERSION = lokiV2; // Loki - onUpgrade(...) must be updated to use Loki version numbers if Signal makes any database changes
   private static final String DATABASE_NAME    = "signal.db";
 
   private final Context        context;
@@ -134,7 +135,8 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
     db.execSQL(LokiMessageDatabase.getCreateTableCommand());
     db.execSQL(LokiThreadDatabase.getCreateFriendRequestTableCommand());
     db.execSQL(LokiThreadDatabase.getCreateSessionResetTableCommand());
-    db.execSQL(LokiUserDatabase.getCreateTableCommand());
+    db.execSQL(LokiUserDatabase.getCreateDisplayNameTableCommand());
+    db.execSQL(LokiUserDatabase.getCreateServerDisplayNameTableCommand());
 
     executeStatements(db, SmsDatabase.CREATE_INDEXS);
     executeStatements(db, MmsDatabase.CREATE_INDEXS);
@@ -493,6 +495,10 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
         db.execSQL(LokiAPIDatabase.getCreateLastDeletionServerIDTableCommand());
       }
 
+      if (oldVersion < lokiV2) {
+        db.execSQL(LokiUserDatabase.getCreateServerDisplayNameTableCommand());
+      }
+
       db.setTransactionSuccessful();
     } finally {
       db.endTransaction();
diff --git a/src/org/thoughtcrime/securesms/loki/LokiGroupChatPoller.kt b/src/org/thoughtcrime/securesms/loki/LokiGroupChatPoller.kt
index e1053d38bc..b0e6fcb59f 100644
--- a/src/org/thoughtcrime/securesms/loki/LokiGroupChatPoller.kt
+++ b/src/org/thoughtcrime/securesms/loki/LokiGroupChatPoller.kt
@@ -93,9 +93,12 @@ class LokiGroupChatPoller(private val context: Context, private val group: LokiG
             val id = group.id.toByteArray()
             val x1 = SignalServiceGroup(SignalServiceGroup.Type.UPDATE, id, null, null, null)
             val x2 = SignalServiceDataMessage(message.timestamp, x1, null, message.body)
-            val senderDisplayName = "${message.displayName} (...${message.hexEncodedPublicKey.takeLast(8)})"
             val x3 = SignalServiceContent(x2, message.hexEncodedPublicKey, SignalServiceAddress.DEFAULT_DEVICE_ID, message.timestamp, false)
+
             PushDecryptJob(context).handleTextMessage(x3, x2, Optional.absent(), Optional.of(message.serverID))
+
+            val senderDisplayName = "${message.displayName} (...${message.hexEncodedPublicKey.takeLast(8)})"
+            DatabaseFactory.getLokiUserDatabase(context).setServerDisplayName(group.id, message.hexEncodedPublicKey, senderDisplayName)
         }
         fun processOutgoingMessage(message: LokiGroupMessage) {
             val messageServerID = message.serverID ?: return
diff --git a/src/org/thoughtcrime/securesms/loki/LokiUserDatabase.kt b/src/org/thoughtcrime/securesms/loki/LokiUserDatabase.kt
index 50a2faee8f..fd71896a09 100644
--- a/src/org/thoughtcrime/securesms/loki/LokiUserDatabase.kt
+++ b/src/org/thoughtcrime/securesms/loki/LokiUserDatabase.kt
@@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.loki
 
 import android.content.ContentValues
 import android.content.Context
+import android.database.sqlite.SQLiteDatabase
 import org.thoughtcrime.securesms.database.Address
 import org.thoughtcrime.securesms.database.Database
 import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
@@ -13,9 +14,12 @@ class LokiUserDatabase(context: Context, helper: SQLCipherOpenHelper) : Database
 
     companion object {
         private val displayNameTable = "loki_user_display_name_database"
+        private val serverDisplayNameTable = "loki_user_server_display_name_database"
         private val hexEncodedPublicKey = "hex_encoded_public_key"
         private val displayName = "display_name"
-        @JvmStatic val createTableCommand = "CREATE TABLE $displayNameTable ($hexEncodedPublicKey TEXT PRIMARY KEY, $displayName TEXT);"
+        private val serverId = "server_id"
+        @JvmStatic val createDisplayNameTableCommand = "CREATE TABLE $displayNameTable ($hexEncodedPublicKey TEXT PRIMARY KEY, $displayName TEXT);"
+        @JvmStatic val createServerDisplayNameTableCommand = "CREATE TABLE $serverDisplayNameTable ($hexEncodedPublicKey TEXT, $serverId TEXT, $displayName TEXT, PRIMARY KEY ($hexEncodedPublicKey, $serverId));"
     }
 
     override fun getDisplayName(hexEncodedPublicKey: String): String? {
@@ -37,4 +41,24 @@ class LokiUserDatabase(context: Context, helper: SQLCipherOpenHelper) : Database
         database.insertOrUpdate(displayNameTable, row, "${Companion.hexEncodedPublicKey} = ?", arrayOf( hexEncodedPublicKey ))
         Recipient.from(context, Address.fromSerialized(hexEncodedPublicKey), false).notifyListeners()
     }
+
+    fun getServerDisplayName(serverId: String, hexEncodedPublicKey: String): String? {
+        val database = databaseHelper.readableDatabase
+        return database.get(serverDisplayNameTable, "${Companion.hexEncodedPublicKey} = ? AND ${Companion.serverId} = ?", arrayOf(hexEncodedPublicKey, serverId)) { cursor ->
+            cursor.getString(cursor.getColumnIndexOrThrow(displayName))
+        }
+    }
+
+    fun setServerDisplayName(serverId: String, hexEncodedPublicKey: String, displayName: String) {
+        val database = databaseHelper.writableDatabase
+        val values = ContentValues(3)
+        values.put(Companion.serverId, serverId)
+        values.put(Companion.hexEncodedPublicKey, hexEncodedPublicKey)
+        values.put(Companion.displayName, displayName)
+        try {
+            database.insertWithOnConflict(serverDisplayNameTable, null, values, SQLiteDatabase.CONFLICT_REPLACE)
+        } catch (e: Exception) {
+            print(e)
+        }
+    }
 }
\ No newline at end of file
diff --git a/src/org/thoughtcrime/securesms/util/GroupUtil.java b/src/org/thoughtcrime/securesms/util/GroupUtil.java
index 6b83304ee1..224887fb0a 100644
--- a/src/org/thoughtcrime/securesms/util/GroupUtil.java
+++ b/src/org/thoughtcrime/securesms/util/GroupUtil.java
@@ -15,7 +15,6 @@ import org.thoughtcrime.securesms.logging.Log;
 import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
 import org.thoughtcrime.securesms.recipients.Recipient;
 import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
-import org.thoughtcrime.securesms.sms.MessageSender;
 import org.whispersystems.libsignal.util.guava.Optional;
 
 import java.io.IOException;
@@ -43,6 +42,11 @@ public class GroupUtil {
     return Hex.fromStringCondensed(groupId.split("!", 2)[1]);
   }
 
+  public static String getDecodedStringId(String groupId) throws IOException {
+    byte[] id = getDecodedId(groupId);
+    return new String(id);
+  }
+
   public static boolean isEncodedGroup(@NonNull String groupId) {
     return groupId.startsWith(ENCODED_SIGNAL_GROUP_PREFIX) || groupId.startsWith(ENCODED_MMS_GROUP_PREFIX);
   }

From 7c6c551b32df42f68168c64d3e9dca5c73d3f9e1 Mon Sep 17 00:00:00 2001
From: Mikunj <mikunj@live.com.au>
Date: Mon, 9 Sep 2019 13:20:59 +1000
Subject: [PATCH 5/6] Fix order of execution.

---
 src/org/thoughtcrime/securesms/loki/LokiGroupChatPoller.kt | 4 ++--
 src/org/thoughtcrime/securesms/loki/LokiUserDatabase.kt    | 1 +
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/org/thoughtcrime/securesms/loki/LokiGroupChatPoller.kt b/src/org/thoughtcrime/securesms/loki/LokiGroupChatPoller.kt
index b0e6fcb59f..3a5c94f36b 100644
--- a/src/org/thoughtcrime/securesms/loki/LokiGroupChatPoller.kt
+++ b/src/org/thoughtcrime/securesms/loki/LokiGroupChatPoller.kt
@@ -95,10 +95,10 @@ class LokiGroupChatPoller(private val context: Context, private val group: LokiG
             val x2 = SignalServiceDataMessage(message.timestamp, x1, null, message.body)
             val x3 = SignalServiceContent(x2, message.hexEncodedPublicKey, SignalServiceAddress.DEFAULT_DEVICE_ID, message.timestamp, false)
 
-            PushDecryptJob(context).handleTextMessage(x3, x2, Optional.absent(), Optional.of(message.serverID))
-
             val senderDisplayName = "${message.displayName} (...${message.hexEncodedPublicKey.takeLast(8)})"
             DatabaseFactory.getLokiUserDatabase(context).setServerDisplayName(group.id, message.hexEncodedPublicKey, senderDisplayName)
+
+            PushDecryptJob(context).handleTextMessage(x3, x2, Optional.absent(), Optional.of(message.serverID))
         }
         fun processOutgoingMessage(message: LokiGroupMessage) {
             val messageServerID = message.serverID ?: return
diff --git a/src/org/thoughtcrime/securesms/loki/LokiUserDatabase.kt b/src/org/thoughtcrime/securesms/loki/LokiUserDatabase.kt
index fd71896a09..4b16d8432a 100644
--- a/src/org/thoughtcrime/securesms/loki/LokiUserDatabase.kt
+++ b/src/org/thoughtcrime/securesms/loki/LokiUserDatabase.kt
@@ -57,6 +57,7 @@ class LokiUserDatabase(context: Context, helper: SQLCipherOpenHelper) : Database
         values.put(Companion.displayName, displayName)
         try {
             database.insertWithOnConflict(serverDisplayNameTable, null, values, SQLiteDatabase.CONFLICT_REPLACE)
+            Recipient.from(context, Address.fromSerialized(hexEncodedPublicKey), false).notifyListeners()
         } catch (e: Exception) {
             print(e)
         }

From fd389a637a8f7e607b789695eb1e6a06bfb81ea8 Mon Sep 17 00:00:00 2001
From: Niels Andriesse <andriesseniels@gmail.com>
Date: Tue, 10 Sep 2019 13:50:59 +1000
Subject: [PATCH 6/6] Clean

---
 .../securesms/ConversationListActivity.java   |  4 +---
 .../securesms/components/AvatarImageView.java |  7 +++---
 .../conversation/ConversationItem.java        |  4 +++-
 .../loki/{identicon => }/IdenticonDrawable.kt | 15 +++----------
 .../{identicon => }/JazzIdenticonDrawable.kt  |  6 +++--
 .../securesms/loki/LokiGroupChatPoller.kt     |  2 --
 .../securesms/loki/LokiUserDatabase.kt        | 22 +++++++++++--------
 .../securesms/loki/{identicon => }/RNG.kt     |  9 ++++----
 .../widgets/ProfilePreference.java            |  4 +---
 9 files changed, 32 insertions(+), 41 deletions(-)
 rename src/org/thoughtcrime/securesms/loki/{identicon => }/IdenticonDrawable.kt (79%)
 rename src/org/thoughtcrime/securesms/loki/{identicon => }/JazzIdenticonDrawable.kt (96%)
 rename src/org/thoughtcrime/securesms/loki/{identicon => }/RNG.kt (72%)

diff --git a/src/org/thoughtcrime/securesms/ConversationListActivity.java b/src/org/thoughtcrime/securesms/ConversationListActivity.java
index 1b70956137..ddd3ad3dff 100644
--- a/src/org/thoughtcrime/securesms/ConversationListActivity.java
+++ b/src/org/thoughtcrime/securesms/ConversationListActivity.java
@@ -37,8 +37,6 @@ import android.view.ViewTreeObserver;
 import android.widget.ImageView;
 import android.widget.Toast;
 
-import com.lelloman.identicon.drawable.ClassicIdenticonDrawable;
-
 import org.thoughtcrime.securesms.components.RatingManager;
 import org.thoughtcrime.securesms.components.SearchToolbar;
 import org.thoughtcrime.securesms.conversation.ConversationActivity;
@@ -46,7 +44,7 @@ import org.thoughtcrime.securesms.database.Address;
 import org.thoughtcrime.securesms.database.DatabaseFactory;
 import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
 import org.thoughtcrime.securesms.lock.RegistrationLockDialog;
-import org.thoughtcrime.securesms.loki.identicon.JazzIdenticonDrawable;
+import org.thoughtcrime.securesms.loki.JazzIdenticonDrawable;
 import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
 import org.thoughtcrime.securesms.notifications.MessageNotifier;
 import org.thoughtcrime.securesms.permissions.Permissions;
diff --git a/src/org/thoughtcrime/securesms/components/AvatarImageView.java b/src/org/thoughtcrime/securesms/components/AvatarImageView.java
index dfaf053a0a..5a3db4bbce 100644
--- a/src/org/thoughtcrime/securesms/components/AvatarImageView.java
+++ b/src/org/thoughtcrime/securesms/components/AvatarImageView.java
@@ -16,13 +16,10 @@ import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewOutlineProvider;
 
-import com.lelloman.identicon.drawable.ClassicIdenticonDrawable;
-
-import network.loki.messenger.R;
 import org.thoughtcrime.securesms.color.MaterialColor;
 import org.thoughtcrime.securesms.contacts.avatars.ContactColors;
 import org.thoughtcrime.securesms.contacts.avatars.GeneratedContactPhoto;
-import org.thoughtcrime.securesms.loki.identicon.JazzIdenticonDrawable;
+import org.thoughtcrime.securesms.loki.JazzIdenticonDrawable;
 import org.thoughtcrime.securesms.mms.GlideRequests;
 import org.thoughtcrime.securesms.recipients.Recipient;
 import org.thoughtcrime.securesms.recipients.RecipientExporter;
@@ -30,6 +27,8 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences;
 import org.thoughtcrime.securesms.util.ThemeUtil;
 import org.whispersystems.libsignal.util.guava.Optional;
 
+import network.loki.messenger.R;
+
 public class AvatarImageView extends AppCompatImageView {
 
   private static final String TAG = AvatarImageView.class.getSimpleName();
diff --git a/src/org/thoughtcrime/securesms/conversation/ConversationItem.java b/src/org/thoughtcrime/securesms/conversation/ConversationItem.java
index 35b1be25d1..6dc4cf67c5 100644
--- a/src/org/thoughtcrime/securesms/conversation/ConversationItem.java
+++ b/src/org/thoughtcrime/securesms/conversation/ConversationItem.java
@@ -881,7 +881,9 @@ public class ConversationItem extends LinearLayout
         String serverId = GroupUtil.getDecodedStringId(conversationRecipient.getAddress().serialize());
         String senderDisplayName = DatabaseFactory.getLokiUserDatabase(context).getServerDisplayName(serverId, recipient.getAddress().serialize());
         if (senderDisplayName != null) { displayName = senderDisplayName; }
-      } catch (Exception e) {}
+      } catch (Exception e) {
+        // Do nothing
+      }
 
       this.groupSender.setText(displayName);
 
diff --git a/src/org/thoughtcrime/securesms/loki/identicon/IdenticonDrawable.kt b/src/org/thoughtcrime/securesms/loki/IdenticonDrawable.kt
similarity index 79%
rename from src/org/thoughtcrime/securesms/loki/identicon/IdenticonDrawable.kt
rename to src/org/thoughtcrime/securesms/loki/IdenticonDrawable.kt
index 257b4bf40c..86d69ce990 100644
--- a/src/org/thoughtcrime/securesms/loki/identicon/IdenticonDrawable.kt
+++ b/src/org/thoughtcrime/securesms/loki/IdenticonDrawable.kt
@@ -1,22 +1,13 @@
-package org.thoughtcrime.securesms.loki.identicon
+package org.thoughtcrime.securesms.loki
 
-import android.graphics.Bitmap
-import android.graphics.Canvas
-import android.graphics.ColorFilter
-import android.graphics.Paint
-import android.graphics.Rect
+import android.graphics.*
 import android.graphics.drawable.Drawable
 
 /**
  * Basically a [Bitmap] wrapper, the [Bitmap] size must be known when instantiating it
  * but when drawing it will draw the [Bitmap] to fit the canvas.
  */
-abstract class IdenticonDrawable(
-        width: Int,
-        height: Int,
-        hash: Long
-) : Drawable() {
-
+abstract class IdenticonDrawable(width: Int, height: Int, hash: Long) : Drawable() {
   private val bitmapRect: Rect = Rect(0, 0, width, height)
   private val destinationRect: Rect = Rect(0, 0, width, height)
   private val bitmap: Bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
diff --git a/src/org/thoughtcrime/securesms/loki/identicon/JazzIdenticonDrawable.kt b/src/org/thoughtcrime/securesms/loki/JazzIdenticonDrawable.kt
similarity index 96%
rename from src/org/thoughtcrime/securesms/loki/identicon/JazzIdenticonDrawable.kt
rename to src/org/thoughtcrime/securesms/loki/JazzIdenticonDrawable.kt
index 06c5fc618d..ff678db54c 100644
--- a/src/org/thoughtcrime/securesms/loki/identicon/JazzIdenticonDrawable.kt
+++ b/src/org/thoughtcrime/securesms/loki/JazzIdenticonDrawable.kt
@@ -1,6 +1,8 @@
-package org.thoughtcrime.securesms.loki.identicon
+package org.thoughtcrime.securesms.loki
 
-import android.graphics.*
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.Paint
 import kotlin.math.*
 
 class JazzIdenticonDrawable(width: Int, height: Int, hash: Long) : IdenticonDrawable(width, height, hash) {
diff --git a/src/org/thoughtcrime/securesms/loki/LokiGroupChatPoller.kt b/src/org/thoughtcrime/securesms/loki/LokiGroupChatPoller.kt
index 3a5c94f36b..bf8c86458a 100644
--- a/src/org/thoughtcrime/securesms/loki/LokiGroupChatPoller.kt
+++ b/src/org/thoughtcrime/securesms/loki/LokiGroupChatPoller.kt
@@ -94,10 +94,8 @@ class LokiGroupChatPoller(private val context: Context, private val group: LokiG
             val x1 = SignalServiceGroup(SignalServiceGroup.Type.UPDATE, id, null, null, null)
             val x2 = SignalServiceDataMessage(message.timestamp, x1, null, message.body)
             val x3 = SignalServiceContent(x2, message.hexEncodedPublicKey, SignalServiceAddress.DEFAULT_DEVICE_ID, message.timestamp, false)
-
             val senderDisplayName = "${message.displayName} (...${message.hexEncodedPublicKey.takeLast(8)})"
             DatabaseFactory.getLokiUserDatabase(context).setServerDisplayName(group.id, message.hexEncodedPublicKey, senderDisplayName)
-
             PushDecryptJob(context).handleTextMessage(x3, x2, Optional.absent(), Optional.of(message.serverID))
         }
         fun processOutgoingMessage(message: LokiGroupMessage) {
diff --git a/src/org/thoughtcrime/securesms/loki/LokiUserDatabase.kt b/src/org/thoughtcrime/securesms/loki/LokiUserDatabase.kt
index 4b16d8432a..5b907f8be7 100644
--- a/src/org/thoughtcrime/securesms/loki/LokiUserDatabase.kt
+++ b/src/org/thoughtcrime/securesms/loki/LokiUserDatabase.kt
@@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.loki
 import android.content.ContentValues
 import android.content.Context
 import android.database.sqlite.SQLiteDatabase
+import android.util.Log
 import org.thoughtcrime.securesms.database.Address
 import org.thoughtcrime.securesms.database.Database
 import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
@@ -13,13 +14,16 @@ import org.whispersystems.signalservice.loki.messaging.LokiUserDatabaseProtocol
 class LokiUserDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiUserDatabaseProtocol {
 
     companion object {
+        // Shared
+        private val displayName = "display_name"
+        // Display name cache
         private val displayNameTable = "loki_user_display_name_database"
-        private val serverDisplayNameTable = "loki_user_server_display_name_database"
         private val hexEncodedPublicKey = "hex_encoded_public_key"
-        private val displayName = "display_name"
-        private val serverId = "server_id"
         @JvmStatic val createDisplayNameTableCommand = "CREATE TABLE $displayNameTable ($hexEncodedPublicKey TEXT PRIMARY KEY, $displayName TEXT);"
-        @JvmStatic val createServerDisplayNameTableCommand = "CREATE TABLE $serverDisplayNameTable ($hexEncodedPublicKey TEXT, $serverId TEXT, $displayName TEXT, PRIMARY KEY ($hexEncodedPublicKey, $serverId));"
+        // Server display name cache
+        private val serverDisplayNameTable = "loki_user_server_display_name_database"
+        private val serverID = "server_id"
+        @JvmStatic val createServerDisplayNameTableCommand = "CREATE TABLE $serverDisplayNameTable ($hexEncodedPublicKey TEXT, $serverID TEXT, $displayName TEXT, PRIMARY KEY ($hexEncodedPublicKey, $serverID));"
     }
 
     override fun getDisplayName(hexEncodedPublicKey: String): String? {
@@ -42,24 +46,24 @@ class LokiUserDatabase(context: Context, helper: SQLCipherOpenHelper) : Database
         Recipient.from(context, Address.fromSerialized(hexEncodedPublicKey), false).notifyListeners()
     }
 
-    fun getServerDisplayName(serverId: String, hexEncodedPublicKey: String): String? {
+    fun getServerDisplayName(serverID: String, hexEncodedPublicKey: String): String? {
         val database = databaseHelper.readableDatabase
-        return database.get(serverDisplayNameTable, "${Companion.hexEncodedPublicKey} = ? AND ${Companion.serverId} = ?", arrayOf(hexEncodedPublicKey, serverId)) { cursor ->
+        return database.get(serverDisplayNameTable, "${Companion.hexEncodedPublicKey} = ? AND ${Companion.serverID} = ?", arrayOf(hexEncodedPublicKey, serverID)) { cursor ->
             cursor.getString(cursor.getColumnIndexOrThrow(displayName))
         }
     }
 
-    fun setServerDisplayName(serverId: String, hexEncodedPublicKey: String, displayName: String) {
+    fun setServerDisplayName(serverID: String, hexEncodedPublicKey: String, displayName: String) {
         val database = databaseHelper.writableDatabase
         val values = ContentValues(3)
-        values.put(Companion.serverId, serverId)
+        values.put(Companion.serverID, serverID)
         values.put(Companion.hexEncodedPublicKey, hexEncodedPublicKey)
         values.put(Companion.displayName, displayName)
         try {
             database.insertWithOnConflict(serverDisplayNameTable, null, values, SQLiteDatabase.CONFLICT_REPLACE)
             Recipient.from(context, Address.fromSerialized(hexEncodedPublicKey), false).notifyListeners()
         } catch (e: Exception) {
-            print(e)
+            Log.d("Loki", "Couldn't save server display name due to exception: $e.")
         }
     }
 }
\ No newline at end of file
diff --git a/src/org/thoughtcrime/securesms/loki/identicon/RNG.kt b/src/org/thoughtcrime/securesms/loki/RNG.kt
similarity index 72%
rename from src/org/thoughtcrime/securesms/loki/identicon/RNG.kt
rename to src/org/thoughtcrime/securesms/loki/RNG.kt
index 6e43ecaeb5..208414785a 100644
--- a/src/org/thoughtcrime/securesms/loki/identicon/RNG.kt
+++ b/src/org/thoughtcrime/securesms/loki/RNG.kt
@@ -1,7 +1,6 @@
-package org.thoughtcrime.securesms.loki.identicon
+package org.thoughtcrime.securesms.loki
 
 class RNG(hash: Long) {
-
   private var seed: Long
   private val initial: Long
 
@@ -13,17 +12,17 @@ class RNG(hash: Long) {
     initial = seed
   }
 
-  public fun next(): Long {
+  fun next(): Long {
     val newSeed = (seed * 16807) % 2147483647
     seed = newSeed
     return seed
   }
 
-  public fun nextFloat(): Float {
+  fun nextFloat(): Float {
     return (next() - 1).toFloat() / 2147483646
   }
 
-  public fun reset() {
+  fun reset() {
     seed = initial
   }
 }
\ No newline at end of file
diff --git a/src/org/thoughtcrime/securesms/preferences/widgets/ProfilePreference.java b/src/org/thoughtcrime/securesms/preferences/widgets/ProfilePreference.java
index 2838bfc0b3..49e60e1f97 100644
--- a/src/org/thoughtcrime/securesms/preferences/widgets/ProfilePreference.java
+++ b/src/org/thoughtcrime/securesms/preferences/widgets/ProfilePreference.java
@@ -18,10 +18,8 @@ import android.widget.ImageView;
 import android.widget.TextView;
 import android.widget.Toast;
 
-import com.lelloman.identicon.drawable.ClassicIdenticonDrawable;
-
 import org.thoughtcrime.securesms.database.Address;
-import org.thoughtcrime.securesms.loki.identicon.JazzIdenticonDrawable;
+import org.thoughtcrime.securesms.loki.JazzIdenticonDrawable;
 import org.thoughtcrime.securesms.util.TextSecurePreferences;
 
 import network.loki.messenger.R;