From d3c55fad606111f20aa12bffc4746ceeebb57cfa Mon Sep 17 00:00:00 2001
From: 0x330a <92654767+0x330a@users.noreply.github.com>
Date: Mon, 27 Mar 2023 11:07:14 +1100
Subject: [PATCH] feat: support avatar removal from shared library

---
 .../securesms/database/Storage.kt             | 74 +++++++++++--------
 .../jobs/RetrieveProfileAvatarJob.java        |  2 +-
 .../securesms/preferences/SettingsActivity.kt | 13 +++-
 .../sskenvironment/ProfileManager.kt          |  2 +-
 .../libsession/database/StorageProtocol.kt    |  1 +
 .../libsession/utilities/SSKEnvironment.kt    |  2 +-
 6 files changed, 59 insertions(+), 35 deletions(-)

diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt
index 2cad2c4aa3..f25ed9b934 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt
@@ -338,11 +338,7 @@ class Storage(context: Context, helper: SQLCipherOpenHelper, private val configF
 
         // update pfp
         if (userPic == UserPic.DEFAULT) {
-            // clear picture if userPic is null
-            TextSecurePreferences.setProfileKey(context, null)
-            ProfileKeyUtil.setEncodedProfileKey(context, null)
-            profileManager.setProfileKey(context, recipient, null)
-            setUserProfilePictureURL(null)
+            clearUserPic()
         } else if (userPic.key.isNotEmpty() && userPic.url.isNotEmpty()
             && TextSecurePreferences.getProfilePictureURL(context) != userPic.url) {
             val profileKey = Base64.encodeBytes(userPic.key)
@@ -354,25 +350,25 @@ class Storage(context: Context, helper: SQLCipherOpenHelper, private val configF
 
     private fun updateContacts(contacts: Contacts) {
         val extracted = contacts.all().toList()
-        val profileManager = SSKEnvironment.shared.profileManager
-        extracted.forEach { contact ->
-            val address = fromSerialized(contact.id)
-            val recipient = Recipient.from(context, address, false)
-            setBlocked(listOf(recipient), contact.blocked, fromConfigUpdate = true)
-            setRecipientApproved(recipient, contact.approved)
-            setRecipientApprovedMe(recipient, contact.approvedMe)
-            profileManager.setName(context, recipient, contact.name)
-            profileManager.setNickname(context, recipient, contact.nickname)
+        addLibSessionContacts(extracted)
+    }
 
-            if (contact.profilePicture != UserPic.DEFAULT) {
-                val (url, key) = contact.profilePicture
-                if (key.size != ProfileKeyUtil.PROFILE_KEY_BYTES) return@forEach
-                profileManager.setProfileKey(context, recipient, key)
-                profileManager.setUnidentifiedAccessMode(context, recipient, Recipient.UnidentifiedAccessMode.UNKNOWN)
-                profileManager.setProfilePictureURL(context, recipient, url)
-            }
-            Log.d("Loki-DBG", "Updated contact $contact")
-        }
+    override fun clearUserPic() {
+        val userPublicKey = getUserPublicKey() ?: return
+        val recipientDatabase = DatabaseComponent.get(context).recipientDatabase()
+        // would love to get rid of recipient and context from this
+        val recipient = Recipient.from(context, fromSerialized(userPublicKey), false)
+        // clear picture if userPic is null
+        TextSecurePreferences.setProfileKey(context, null)
+        TextSecurePreferences.setProfileAvatarId(context, 0)
+        ProfileKeyUtil.setEncodedProfileKey(context, null)
+        SSKEnvironment.shared.profileManager.setProfileKey(context, recipient, null)
+        recipientDatabase.setProfileAvatar(recipient, null)
+
+        setUserProfilePictureURL(null)
+        Recipient.removeCached(fromSerialized(userPublicKey))
+        configFactory.user?.setPic(UserPic.DEFAULT)
+        ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(context)
     }
 
     private fun updateConvoVolatile(convos: ConversationVolatileConfig) {
@@ -972,21 +968,37 @@ class Storage(context: Context, helper: SQLCipherOpenHelper, private val configF
     }
 
     override fun addLibSessionContacts(contacts: List<LibSessionContact>) {
-        val recipientDatabase = DatabaseComponent.get(context).recipientDatabase()
-        val threadDatabase = DatabaseComponent.get(context).threadDatabase()
         val mappingDb = DatabaseComponent.get(context).blindedIdMappingDatabase()
         val moreContacts = contacts.filter { contact ->
             val id = SessionId(contact.id)
             id.prefix != IdPrefix.BLINDED || mappingDb.getBlindedIdMapping(contact.id).none { it.sessionId != null }
         }
-        for (contact in moreContacts) {
+        val profileManager = SSKEnvironment.shared.profileManager
+        moreContacts.forEach { contact ->
             val address = fromSerialized(contact.id)
-            val recipient = Recipient.from(context, address, true)
-            val (url, key) = contact.profilePicture.let { it.url to it.key }
-            // set or clear the avatar
-            recipientDatabase.setProfileAvatar(recipient, url)
-            recipientDatabase.setProfileKey(recipient, key)
+            val recipient = Recipient.from(context, address, false)
+            setBlocked(listOf(recipient), contact.blocked, fromConfigUpdate = true)
+            setRecipientApproved(recipient, contact.approved)
+            setRecipientApprovedMe(recipient, contact.approvedMe)
+            if (contact.name.isNotEmpty()) {
+                profileManager.setName(context, recipient, contact.name)
+            } else {
+                profileManager.setName(context, recipient, null)
+            }
+            if (contact.nickname.isNotEmpty()) {
+                profileManager.setNickname(context, recipient, contact.nickname)
+            } else {
+                profileManager.setNickname(context, recipient, null)
+            }
 
+            if (contact.profilePicture != UserPic.DEFAULT) {
+                val (url, key) = contact.profilePicture
+                if (key.size != ProfileKeyUtil.PROFILE_KEY_BYTES) return@forEach
+                profileManager.setProfileKey(context, recipient, key)
+                profileManager.setUnidentifiedAccessMode(context, recipient, Recipient.UnidentifiedAccessMode.UNKNOWN)
+                profileManager.setProfilePictureURL(context, recipient, url)
+            }
+            Log.d("Loki-DBG", "Updated contact $contact")
         }
     }
 
diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileAvatarJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileAvatarJob.java
index 39b7753035..7331f879cb 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileAvatarJob.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileAvatarJob.java
@@ -93,7 +93,7 @@ public class RetrieveProfileAvatarJob extends BaseJob {
     if (TextUtils.isEmpty(profileAvatar)) {
       Log.w(TAG, "Removing profile avatar for: " + recipient.getAddress().serialize());
       AvatarHelper.delete(context, recipient.getAddress());
-      database.setProfileAvatar(recipient, profileAvatar);
+      database.setProfileAvatar(recipient, null);
       return;
     }
 
diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/SettingsActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/SettingsActivity.kt
index c7390683c1..c0c963b59f 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/preferences/SettingsActivity.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/SettingsActivity.kt
@@ -30,6 +30,7 @@ import nl.komponents.kovenant.all
 import nl.komponents.kovenant.ui.alwaysUi
 import nl.komponents.kovenant.ui.successUi
 import org.session.libsession.avatars.AvatarHelper
+import org.session.libsession.messaging.MessagingModuleConfiguration
 import org.session.libsession.utilities.Address
 import org.session.libsession.utilities.ProfileKeyUtil
 import org.session.libsession.utilities.ProfilePictureUtilities
@@ -92,7 +93,9 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
             profilePictureView.root.displayName = displayName
             profilePictureView.root.isLarge = true
             profilePictureView.root.update()
-            profilePictureView.root.setOnClickListener { showEditProfilePictureUI() }
+            profilePictureView.root.setOnClickListener {
+                showEditProfilePictureUI()
+            }
             ctnGroupNameSection.setOnClickListener { startActionMode(DisplayNameEditActionModeCallback()) }
             btnGroupNameDisplay.text = displayName
             publicKeyTextView.text = hexEncodedPublicKey
@@ -270,6 +273,14 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
         push(intent)
     }
 
+    private fun deleteProfilePicture() {
+        MessagingModuleConfiguration.shared.storage.clearUserPic()
+        with (binding.profilePictureView.root) {
+            recycle()
+            update()
+        }
+    }
+
     private fun showEditProfilePictureUI() {
         // Ask for an optional camera permission.
         Permissions.with(this)
diff --git a/app/src/main/java/org/thoughtcrime/securesms/sskenvironment/ProfileManager.kt b/app/src/main/java/org/thoughtcrime/securesms/sskenvironment/ProfileManager.kt
index 5772b5d4ae..66aa1eaf81 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/sskenvironment/ProfileManager.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/sskenvironment/ProfileManager.kt
@@ -29,7 +29,7 @@ class ProfileManager(private val context: Context, private val configFactory: Co
         contactUpdatedInternal(contact)
     }
 
-    override fun setName(context: Context, recipient: Recipient, name: String) {
+    override fun setName(context: Context, recipient: Recipient, name: String?) {
         // New API
         val sessionID = recipient.address.serialize()
         val contactDatabase = DatabaseComponent.get(context).sessionContactDatabase()
diff --git a/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt b/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt
index 75f8b58718..67a7adcd00 100644
--- a/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt
+++ b/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt
@@ -41,6 +41,7 @@ interface StorageProtocol {
     fun getUserX25519KeyPair(): ECKeyPair
     fun getUserProfile(): Profile
     fun setUserProfilePictureURL(newProfilePicture: String?)
+    fun clearUserPic()
     // Signal
     fun getOrGenerateRegistrationID(): Int
 
diff --git a/libsession/src/main/java/org/session/libsession/utilities/SSKEnvironment.kt b/libsession/src/main/java/org/session/libsession/utilities/SSKEnvironment.kt
index e939655885..cba7b1f16d 100644
--- a/libsession/src/main/java/org/session/libsession/utilities/SSKEnvironment.kt
+++ b/libsession/src/main/java/org/session/libsession/utilities/SSKEnvironment.kt
@@ -30,7 +30,7 @@ class SSKEnvironment(
         }
 
         fun setNickname(context: Context, recipient: Recipient, nickname: String?)
-        fun setName(context: Context, recipient: Recipient, name: String)
+        fun setName(context: Context, recipient: Recipient, name: String?)
         fun setProfilePictureURL(context: Context, recipient: Recipient, profilePictureURL: String)
         fun setProfileKey(context: Context, recipient: Recipient, profileKey: ByteArray?)
         fun setUnidentifiedAccessMode(context: Context, recipient: Recipient, unidentifiedAccessMode: Recipient.UnidentifiedAccessMode)