mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-21 15:05:19 +00:00
feat: add contact syncing, UI improvements for profile syncing and conversation threads in the home screen
This commit is contained in:
parent
11c122e376
commit
3a09d23337
@ -1,5 +1,4 @@
|
||||
buildscript {
|
||||
ext.kotlin_version = "1.4.0"
|
||||
ext.kovenant_version = "3.3.0"
|
||||
|
||||
repositories {
|
||||
@ -124,8 +123,8 @@ dependencies {
|
||||
implementation "com.google.protobuf:protobuf-java:$protobufVersion"
|
||||
implementation "com.fasterxml.jackson.core:jackson-databind:$jacksonDatabindVersion"
|
||||
implementation "com.squareup.okhttp3:okhttp:$okhttpVersion"
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion"
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9'
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.2"
|
||||
implementation "nl.komponents.kovenant:kovenant:$kovenantVersion"
|
||||
implementation "nl.komponents.kovenant:kovenant-android:$kovenantVersion"
|
||||
implementation "com.github.lelloman:android-identicons:v11"
|
||||
|
@ -249,7 +249,14 @@ public class RecipientDatabase extends Database {
|
||||
recipient.resolve().setProfileAvatar(profileAvatar);
|
||||
}
|
||||
|
||||
public void setProfileSharing(@NonNull Recipient recipient, @SuppressWarnings("SameParameterValue") boolean enabled) {
|
||||
public void setProfileName(@NonNull Recipient recipient, @Nullable String profileName) {
|
||||
ContentValues contentValues = new ContentValues(1);
|
||||
contentValues.put(SYSTEM_DISPLAY_NAME, profileName);
|
||||
updateOrInsert(recipient.getAddress(), contentValues);
|
||||
recipient.resolve().setProfileName(profileName);
|
||||
}
|
||||
|
||||
public void setProfileSharing(@NonNull Recipient recipient, boolean enabled) {
|
||||
ContentValues contentValues = new ContentValues(1);
|
||||
contentValues.put(PROFILE_SHARING, enabled ? 1 : 0);
|
||||
updateOrInsert(recipient.getAddress(), contentValues);
|
||||
|
@ -506,6 +506,10 @@ public class ThreadDatabase extends Database {
|
||||
notifyConversationListeners(threadId);
|
||||
}
|
||||
|
||||
public void notifyUpdatedFromConfig() {
|
||||
notifyConversationListListeners();
|
||||
}
|
||||
|
||||
public boolean update(long threadId, boolean unarchive) {
|
||||
MmsSmsDatabase mmsSmsDatabase = DatabaseFactory.getMmsSmsDatabase(context);
|
||||
long count = mmsSmsDatabase.getConversationCount(threadId);
|
||||
|
@ -22,9 +22,11 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import kotlinx.android.synthetic.main.activity_home.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import network.loki.messenger.R
|
||||
import org.session.libsession.messaging.threads.recipients.RecipientModifiedListener
|
||||
import org.thoughtcrime.securesms.ApplicationContext
|
||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||
import org.thoughtcrime.securesms.conversation.ConversationActivity
|
||||
@ -46,7 +48,10 @@ import org.thoughtcrime.securesms.loki.dialogs.*
|
||||
import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocolV2
|
||||
import java.io.IOException
|
||||
|
||||
class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListener, SeedReminderViewDelegate, NewConversationButtonSetViewDelegate {
|
||||
class HomeActivity : PassphraseRequiredActionBarActivity,
|
||||
ConversationClickListener,
|
||||
SeedReminderViewDelegate,
|
||||
NewConversationButtonSetViewDelegate {
|
||||
|
||||
private lateinit var glide: GlideRequests
|
||||
private var broadcastReceiver: BroadcastReceiver? = null
|
||||
@ -71,9 +76,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
|
||||
glide = GlideApp.with(this)
|
||||
// Set up toolbar buttons
|
||||
profileButton.glide = glide
|
||||
profileButton.publicKey = publicKey
|
||||
profileButton.displayName = TextSecurePreferences.getProfileName(this)
|
||||
profileButton.update()
|
||||
updateProfileButton()
|
||||
profileButton.setOnClickListener { openSettings() }
|
||||
pathStatusViewContainer.disableClipping()
|
||||
pathStatusViewContainer.setOnClickListener { showPath() }
|
||||
@ -149,6 +152,12 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
|
||||
}
|
||||
this.broadcastReceiver = broadcastReceiver
|
||||
LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, IntentFilter("blockedContactsChanged"))
|
||||
lifecycleScope.launchWhenResumed {
|
||||
// update things based on TextSecurePrefs (profile info etc)
|
||||
TextSecurePreferences.events.filter { it == TextSecurePreferences.PROFILE_NAME_PREF }.collect {
|
||||
updateProfileButton()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
@ -198,6 +207,13 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
|
||||
val threadCount = (recyclerView.adapter as HomeAdapter).itemCount
|
||||
emptyStateContainer.visibility = if (threadCount == 0) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
private fun updateProfileButton() {
|
||||
profileButton.publicKey = publicKey
|
||||
profileButton.displayName = TextSecurePreferences.getProfileName(this)
|
||||
profileButton.recycle()
|
||||
profileButton.update()
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region Interaction
|
||||
@ -356,7 +372,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
|
||||
|
||||
private fun openSettings() {
|
||||
val intent = Intent(this, SettingsActivity::class.java)
|
||||
show(intent)
|
||||
show(intent, isForResult = true)
|
||||
}
|
||||
|
||||
private fun showPath() {
|
||||
|
@ -68,6 +68,10 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
|
||||
return TextSecurePreferences.getLocalNumber(this)!!
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val updatedProfileResultCode = 1234
|
||||
}
|
||||
|
||||
// region Lifecycle
|
||||
override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) {
|
||||
super.onCreate(savedInstanceState, isReady)
|
||||
|
@ -12,12 +12,18 @@ import org.session.libsignal.service.api.push.SignalServiceAddress
|
||||
import org.session.libsignal.service.internal.push.SignalServiceProtos
|
||||
import org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage
|
||||
import org.session.libsignal.service.loki.utilities.removing05PrefixIfNeeded
|
||||
import org.session.libsignal.utilities.Base64
|
||||
import org.session.libsignal.utilities.Hex
|
||||
import org.session.libsignal.utilities.logging.Log
|
||||
import org.thoughtcrime.securesms.ApplicationContext
|
||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase
|
||||
import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob
|
||||
import org.thoughtcrime.securesms.loki.utilities.ContactUtilities
|
||||
import org.thoughtcrime.securesms.loki.utilities.OpenGroupUtilities
|
||||
import org.thoughtcrime.securesms.sskenvironment.ProfileManager
|
||||
import java.util.*
|
||||
|
||||
object MultiDeviceProtocol {
|
||||
@ -76,7 +82,7 @@ object MultiDeviceProtocol {
|
||||
// TODO: remove this after we migrate to new message receiving pipeline
|
||||
@JvmStatic
|
||||
fun handleConfigurationMessage(context: Context, content: SignalServiceProtos.Content, senderPublicKey: String, timestamp: Long) {
|
||||
if (TextSecurePreferences.getConfigurationMessageSynced(context)) return
|
||||
// if (TextSecurePreferences.getConfigurationMessageSynced(context)) return
|
||||
val configurationMessage = ConfigurationMessage.fromProto(content) ?: return
|
||||
val userPublicKey = TextSecurePreferences.getLocalNumber(context) ?: return
|
||||
if (senderPublicKey != userPublicKey) return
|
||||
@ -103,6 +109,37 @@ object MultiDeviceProtocol {
|
||||
if (allOpenGroups.contains(openGroup)) continue
|
||||
OpenGroupUtilities.addGroup(context, openGroup, 1)
|
||||
}
|
||||
if (configurationMessage.profileKey.isNotEmpty()) {
|
||||
val profileKey = Base64.encodeBytes(configurationMessage.profileKey)
|
||||
TextSecurePreferences.setProfileKey(context, profileKey)
|
||||
}
|
||||
if (!configurationMessage.profilePicture.isNullOrEmpty()) {
|
||||
TextSecurePreferences.setProfilePictureURL(context, configurationMessage.profilePicture)
|
||||
}
|
||||
if (configurationMessage.displayName.isNotEmpty()) {
|
||||
TextSecurePreferences.setProfileName(context, configurationMessage.displayName)
|
||||
}
|
||||
val threadDatabase = DatabaseFactory.getThreadDatabase(context)
|
||||
val recipientDatabase = DatabaseFactory.getRecipientDatabase(context)
|
||||
for (contact in configurationMessage.contacts) {
|
||||
val address = Address.fromSerialized(contact.publicKey)
|
||||
val recipient = Recipient.from(context, address, true)
|
||||
if (!contact.profilePicture.isNullOrEmpty()) {
|
||||
recipientDatabase.setProfileAvatar(recipient, contact.profilePicture)
|
||||
}
|
||||
if (contact.profileKey?.isNotEmpty() == true) {
|
||||
recipientDatabase.setProfileKey(recipient, contact.profileKey)
|
||||
}
|
||||
if (contact.name.isNotEmpty()) {
|
||||
recipientDatabase.setProfileName(recipient, contact.name)
|
||||
}
|
||||
recipientDatabase.setProfileSharing(recipient, true)
|
||||
// create Thread if needed
|
||||
threadDatabase.getOrCreateThreadIdFor(recipient)
|
||||
}
|
||||
if (configurationMessage.contacts.isNotEmpty()) {
|
||||
threadDatabase.notifyUpdatedFromConfig()
|
||||
}
|
||||
// TODO: handle new configuration message fields or handle in new pipeline
|
||||
TextSecurePreferences.setConfigurationMessageSynced(context, true)
|
||||
}
|
||||
|
@ -58,6 +58,7 @@ allprojects {
|
||||
}
|
||||
|
||||
project.ext {
|
||||
kotlin_version = "1.4.31"
|
||||
androidBuildToolsVersion = '29.0.3'
|
||||
androidCompileSdkVersion = 29 // This is also our target SDK.
|
||||
androidMinSdkVersion = 21
|
||||
|
@ -38,7 +38,7 @@ dependencies {
|
||||
// Remote:
|
||||
implementation 'org.greenrobot:eventbus:3.0.0'
|
||||
implementation "com.goterl.lazycode:lazysodium-android:4.2.0@aar"
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||
implementation 'androidx.core:core-ktx:1.3.2'
|
||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||
implementation 'com.google.android.material:material:1.2.1'
|
||||
@ -61,10 +61,10 @@ dependencies {
|
||||
implementation "com.squareup.okhttp3:okhttp:$okhttpVersion"
|
||||
implementation "org.threeten:threetenbp:1.3.6"
|
||||
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion"
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
|
||||
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.2"
|
||||
implementation "nl.komponents.kovenant:kovenant:$kovenantVersion"
|
||||
|
||||
testImplementation "junit:junit:3.8.2"
|
||||
|
@ -123,7 +123,8 @@ class ConfigurationMessage(val closedGroups: List<ClosedGroup>, val openGroups:
|
||||
val displayName = configurationProto.displayName
|
||||
val profilePicture = configurationProto.profilePicture
|
||||
val profileKey = configurationProto.profileKey
|
||||
return ConfigurationMessage(closedGroups, openGroups, listOf(), displayName, profilePicture, profileKey.toByteArray())
|
||||
val contacts = configurationProto.contactsList.mapNotNull { Contact.fromProto(it) }
|
||||
return ConfigurationMessage(closedGroups, openGroups, contacts, displayName, profilePicture, profileKey.toByteArray())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,9 @@ import android.preference.PreferenceManager.getDefaultSharedPreferences
|
||||
import android.provider.Settings
|
||||
import androidx.annotation.ArrayRes
|
||||
import androidx.core.app.NotificationCompat
|
||||
import kotlinx.coroutines.channels.BufferOverflow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.asSharedFlow
|
||||
import org.session.libsession.R
|
||||
import org.session.libsession.utilities.preferences.NotificationPrivacyPreference
|
||||
import org.session.libsignal.utilities.logging.Log
|
||||
@ -16,6 +19,9 @@ import java.util.*
|
||||
object TextSecurePreferences {
|
||||
private val TAG = TextSecurePreferences::class.simpleName
|
||||
|
||||
private val _events = MutableSharedFlow<String>(0, 64, BufferOverflow.DROP_OLDEST)
|
||||
val events get() = _events.asSharedFlow()
|
||||
|
||||
const val DISABLE_PASSPHRASE_PREF = "pref_disable_passphrase"
|
||||
const val THEME_PREF = "pref_theme"
|
||||
const val LANGUAGE_PREF = "pref_language"
|
||||
@ -321,6 +327,7 @@ object TextSecurePreferences {
|
||||
@JvmStatic
|
||||
fun setProfileName(context: Context, name: String?) {
|
||||
setStringPreference(context, PROFILE_NAME_PREF, name)
|
||||
_events.tryEmit(PROFILE_NAME_PREF)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
|
Loading…
Reference in New Issue
Block a user