diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt index 2848895708..b6254a5154 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt @@ -26,29 +26,32 @@ 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.greenrobot.eventbus.EventBus +import org.greenrobot.eventbus.Subscribe +import org.greenrobot.eventbus.ThreadMode +import org.session.libsession.utilities.GroupUtil +import org.session.libsession.utilities.ProfilePictureModifiedEvent +import org.session.libsession.utilities.TextSecurePreferences +import org.session.libsession.utilities.Util +import org.session.libsignal.service.loki.utilities.mentions.MentionsManager +import org.session.libsignal.service.loki.utilities.toHexString +import org.session.libsignal.utilities.ThreadUtils import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity import org.thoughtcrime.securesms.conversation.ConversationActivity -import org.session.libsession.utilities.GroupUtil import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.database.model.ThreadRecord +import org.thoughtcrime.securesms.loki.dialogs.* +import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocolV2 import org.thoughtcrime.securesms.loki.utilities.* import org.thoughtcrime.securesms.loki.views.ConversationView import org.thoughtcrime.securesms.loki.views.NewConversationButtonSetViewDelegate import org.thoughtcrime.securesms.loki.views.SeedReminderViewDelegate import org.thoughtcrime.securesms.mms.GlideApp import org.thoughtcrime.securesms.mms.GlideRequests -import org.session.libsession.utilities.TextSecurePreferences -import org.session.libsession.utilities.Util -import org.session.libsignal.service.loki.utilities.mentions.MentionsManager -import org.session.libsignal.utilities.ThreadUtils -import org.session.libsignal.service.loki.utilities.toHexString -import org.thoughtcrime.securesms.loki.dialogs.* -import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocolV2 import java.io.IOException -class HomeActivity : PassphraseRequiredActionBarActivity, +class HomeActivity : PassphraseRequiredActionBarActivity(), ConversationClickListener, SeedReminderViewDelegate, NewConversationButtonSetViewDelegate { @@ -62,8 +65,6 @@ class HomeActivity : PassphraseRequiredActionBarActivity, } // region Lifecycle - constructor() : super() - override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) { super.onCreate(savedInstanceState, isReady) // Double check that the long poller is up @@ -152,17 +153,19 @@ class HomeActivity : PassphraseRequiredActionBarActivity, } this.broadcastReceiver = broadcastReceiver LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, IntentFilter("blockedContactsChanged")) - lifecycleScope.launchWhenResumed { + lifecycleScope.launch { // update things based on TextSecurePrefs (profile info etc) TextSecurePreferences.events.filter { it == TextSecurePreferences.PROFILE_NAME_PREF }.collect { updateProfileButton() } } + EventBus.getDefault().register(this@HomeActivity) } override fun onResume() { super.onResume() - if (TextSecurePreferences.getLocalNumber(this) == null) { return; } // This can be the case after a secondary device is auto-cleared + if (TextSecurePreferences.getLocalNumber(this) == null) { + return; } // This can be the case after a secondary device is auto-cleared profileButton.recycle() // clear cached image before update tje profilePictureView profileButton.update() val hasViewedSeed = TextSecurePreferences.getHasViewedSeed(this) @@ -174,13 +177,17 @@ class HomeActivity : PassphraseRequiredActionBarActivity, } private fun showKeyPairMigrationSheetIfNeeded() { - if (KeyPairUtilities.hasV2KeyPair(this)) { return } + if (KeyPairUtilities.hasV2KeyPair(this)) { + return + } val keyPairMigrationSheet = KeyPairMigrationBottomSheet() keyPairMigrationSheet.show(supportFragmentManager, keyPairMigrationSheet.tag) } private fun showKeyPairMigrationSuccessSheetIfNeeded() { - if (!KeyPairUtilities.hasV2KeyPair(this) || !TextSecurePreferences.getIsMigratingKeyPair(this)) { return } + if (!KeyPairUtilities.hasV2KeyPair(this) || !TextSecurePreferences.getIsMigratingKeyPair(this)) { + return + } val keyPairMigrationSuccessSheet = KeyPairMigrationSuccessBottomSheet() keyPairMigrationSuccessSheet.show(supportFragmentManager, keyPairMigrationSuccessSheet.tag) TextSecurePreferences.setIsMigratingKeyPair(this, false) @@ -199,6 +206,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity, LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver) } super.onDestroy() + EventBus.getDefault().unregister(this) } // endregion @@ -208,6 +216,13 @@ class HomeActivity : PassphraseRequiredActionBarActivity, emptyStateContainer.visibility = if (threadCount == 0) View.VISIBLE else View.GONE } + @Subscribe(threadMode = ThreadMode.MAIN) + fun onUpdateProfileEvent(event: ProfilePictureModifiedEvent) { + if (event.recipient.isLocalNumber) { + updateProfileButton() + } + } + private fun updateProfileButton() { profileButton.publicKey = publicKey profileButton.displayName = TextSecurePreferences.getProfileName(this) @@ -260,34 +275,34 @@ class HomeActivity : PassphraseRequiredActionBarActivity, private fun blockConversation(thread: ThreadRecord) { AlertDialog.Builder(this) - .setTitle(R.string.RecipientPreferenceActivity_block_this_contact_question) - .setMessage(R.string.RecipientPreferenceActivity_you_will_no_longer_receive_messages_and_calls_from_this_contact) - .setNegativeButton(android.R.string.cancel, null) - .setPositiveButton(R.string.RecipientPreferenceActivity_block) { dialog, _ -> - ThreadUtils.queue { - DatabaseFactory.getRecipientDatabase(this).setBlocked(thread.recipient, true) - Util.runOnMain { - recyclerView.adapter!!.notifyDataSetChanged() - dialog.dismiss() + .setTitle(R.string.RecipientPreferenceActivity_block_this_contact_question) + .setMessage(R.string.RecipientPreferenceActivity_you_will_no_longer_receive_messages_and_calls_from_this_contact) + .setNegativeButton(android.R.string.cancel, null) + .setPositiveButton(R.string.RecipientPreferenceActivity_block) { dialog, _ -> + ThreadUtils.queue { + DatabaseFactory.getRecipientDatabase(this).setBlocked(thread.recipient, true) + Util.runOnMain { + recyclerView.adapter!!.notifyDataSetChanged() + dialog.dismiss() + } } - } - }.show() + }.show() } private fun unblockConversation(thread: ThreadRecord) { AlertDialog.Builder(this) - .setTitle(R.string.RecipientPreferenceActivity_unblock_this_contact_question) - .setMessage(R.string.RecipientPreferenceActivity_you_will_once_again_be_able_to_receive_messages_and_calls_from_this_contact) - .setNegativeButton(android.R.string.cancel, null) - .setPositiveButton(R.string.RecipientPreferenceActivity_unblock) { dialog, _ -> - ThreadUtils.queue { - DatabaseFactory.getRecipientDatabase(this).setBlocked(thread.recipient, false) - Util.runOnMain { - recyclerView.adapter!!.notifyDataSetChanged() - dialog.dismiss() + .setTitle(R.string.RecipientPreferenceActivity_unblock_this_contact_question) + .setMessage(R.string.RecipientPreferenceActivity_you_will_once_again_be_able_to_receive_messages_and_calls_from_this_contact) + .setNegativeButton(android.R.string.cancel, null) + .setPositiveButton(R.string.RecipientPreferenceActivity_unblock) { dialog, _ -> + ThreadUtils.queue { + DatabaseFactory.getRecipientDatabase(this).setBlocked(thread.recipient, false) + Util.runOnMain { + recyclerView.adapter!!.notifyDataSetChanged() + dialog.dismiss() + } } - } - }.show() + }.show() } private fun deleteConversation(thread: ThreadRecord) { @@ -307,52 +322,54 @@ class HomeActivity : PassphraseRequiredActionBarActivity, } val dialog = AlertDialog.Builder(this) dialog.setMessage(dialogMessage) - dialog.setPositiveButton(R.string.yes) { _, _ -> lifecycleScope.launch(Dispatchers.Main) { - val context = this@HomeActivity as Context + dialog.setPositiveButton(R.string.yes) { _, _ -> + lifecycleScope.launch(Dispatchers.Main) { + val context = this@HomeActivity as Context - // Send a leave group message if this is an active closed group - if (recipient.address.isClosedGroup && DatabaseFactory.getGroupDatabase(context).isActive(recipient.address.toGroupString())) { - var isClosedGroup: Boolean - var groupPublicKey: String? - try { - groupPublicKey = GroupUtil.doubleDecodeGroupID(recipient.address.toString()).toHexString() - isClosedGroup = DatabaseFactory.getLokiAPIDatabase(context).isClosedGroup(groupPublicKey) - } catch (e: IOException) { - groupPublicKey = null - isClosedGroup = false + // Send a leave group message if this is an active closed group + if (recipient.address.isClosedGroup && DatabaseFactory.getGroupDatabase(context).isActive(recipient.address.toGroupString())) { + var isClosedGroup: Boolean + var groupPublicKey: String? + try { + groupPublicKey = GroupUtil.doubleDecodeGroupID(recipient.address.toString()).toHexString() + isClosedGroup = DatabaseFactory.getLokiAPIDatabase(context).isClosedGroup(groupPublicKey) + } catch (e: IOException) { + groupPublicKey = null + isClosedGroup = false + } + if (isClosedGroup) { + ClosedGroupsProtocolV2.explicitLeave(context, groupPublicKey!!) + } else { + Toast.makeText(context, R.string.activity_home_leaving_group_failed_message, Toast.LENGTH_LONG).show() + return@launch + } } - if (isClosedGroup) { - ClosedGroupsProtocolV2.explicitLeave(context, groupPublicKey!!) - } else { - Toast.makeText(context, R.string.activity_home_leaving_group_failed_message, Toast.LENGTH_LONG).show() - return@launch + + withContext(Dispatchers.IO) { + val publicChat = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID) + //TODO Move open group related logic to OpenGroupUtilities / PublicChatManager / GroupManager + if (publicChat != null) { + val apiDB = DatabaseFactory.getLokiAPIDatabase(context) + apiDB.removeLastMessageServerID(publicChat.channel, publicChat.server) + apiDB.removeLastDeletionServerID(publicChat.channel, publicChat.server) + apiDB.clearOpenGroupProfilePictureURL(publicChat.channel, publicChat.server) + + ApplicationContext.getInstance(context).publicChatAPI!! + .leave(publicChat.channel, publicChat.server) + + ApplicationContext.getInstance(context).publicChatManager + .removeChat(publicChat.server, publicChat.channel) + } else { + threadDB.deleteConversation(threadID) + } + ApplicationContext.getInstance(context).messageNotifier.updateNotification(context) } + + // Notify the user + val toastMessage = if (recipient.isGroupRecipient) R.string.MessageRecord_left_group else R.string.activity_home_conversation_deleted_message + Toast.makeText(context, toastMessage, Toast.LENGTH_LONG).show() } - - withContext(Dispatchers.IO) { - val publicChat = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID) - //TODO Move open group related logic to OpenGroupUtilities / PublicChatManager / GroupManager - if (publicChat != null) { - val apiDB = DatabaseFactory.getLokiAPIDatabase(context) - apiDB.removeLastMessageServerID(publicChat.channel, publicChat.server) - apiDB.removeLastDeletionServerID(publicChat.channel, publicChat.server) - apiDB.clearOpenGroupProfilePictureURL(publicChat.channel, publicChat.server) - - ApplicationContext.getInstance(context).publicChatAPI!! - .leave(publicChat.channel, publicChat.server) - - ApplicationContext.getInstance(context).publicChatManager - .removeChat(publicChat.server, publicChat.channel) - } else { - threadDB.deleteConversation(threadID) - } - ApplicationContext.getInstance(context).messageNotifier.updateNotification(context) - } - - // Notify the user - val toastMessage = if (recipient.isGroupRecipient) R.string.MessageRecord_left_group else R.string.activity_home_conversation_deleted_message - Toast.makeText(context, toastMessage, Toast.LENGTH_LONG).show() - }} + } dialog.setNegativeButton(R.string.no) { _, _ -> // Do nothing } diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkDeviceActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkDeviceActivity.kt index 4969e70dde..6233a98f99 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkDeviceActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkDeviceActivity.kt @@ -1,29 +1,48 @@ package org.thoughtcrime.securesms.loki.activities import android.content.Context +import android.content.Intent import android.os.Bundle -import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentPagerAdapter import android.text.InputType import android.view.* import android.view.inputmethod.EditorInfo import android.view.inputmethod.InputMethodManager import android.widget.Toast import androidx.core.view.isVisible +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentPagerAdapter +import androidx.lifecycle.lifecycleScope +import com.google.android.material.snackbar.Snackbar import kotlinx.android.synthetic.main.activity_create_private_chat.* +import kotlinx.android.synthetic.main.activity_create_private_chat.tabLayout +import kotlinx.android.synthetic.main.activity_create_private_chat.viewPager +import kotlinx.android.synthetic.main.activity_link_device.* import kotlinx.android.synthetic.main.activity_settings.* +import kotlinx.android.synthetic.main.activity_settings.loader import kotlinx.android.synthetic.main.fragment_recovery_phrase.* +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.launch import network.loki.messenger.R +import org.session.libsession.utilities.TextSecurePreferences +import org.session.libsignal.libsignal.util.KeyHelper import org.session.libsignal.service.loki.crypto.MnemonicCodec +import org.session.libsignal.service.loki.utilities.hexEncodedPublicKey import org.session.libsignal.utilities.Hex +import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.BaseActionBarActivity import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragment import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragmentDelegate +import org.thoughtcrime.securesms.loki.utilities.KeyPairUtilities import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities +import org.thoughtcrime.securesms.loki.utilities.push import org.thoughtcrime.securesms.loki.utilities.setUpActionBarSessionLogo class LinkDeviceActivity : BaseActionBarActivity(), ScanQRCodeWrapperFragmentDelegate { private val adapter = LinkDeviceActivityAdapter(this) + private var restoreJob: Job? = null // region Lifecycle override fun onCreate(savedInstanceState: Bundle?) { @@ -59,8 +78,45 @@ class LinkDeviceActivity : BaseActionBarActivity(), ScanQRCodeWrapperFragmentDel } private fun continueWithSeed(seed: ByteArray) { - loader.isVisible = true - // TODO: Implement + restoreJob?.cancel() + restoreJob = lifecycleScope.launch { + // RestoreActivity handles seed this way + val keyPairGenerationResult = KeyPairUtilities.generate(seed) + val x25519KeyPair = keyPairGenerationResult.x25519KeyPair + KeyPairUtilities.store(this@LinkDeviceActivity, seed, keyPairGenerationResult.ed25519KeyPair, x25519KeyPair) + val userHexEncodedPublicKey = x25519KeyPair.hexEncodedPublicKey + val registrationID = KeyHelper.generateRegistrationId(false) + TextSecurePreferences.setLocalRegistrationId(this@LinkDeviceActivity, registrationID) + TextSecurePreferences.setLocalNumber(this@LinkDeviceActivity, userHexEncodedPublicKey) + TextSecurePreferences.setRestorationTime(this@LinkDeviceActivity, System.currentTimeMillis()) + TextSecurePreferences.setHasViewedSeed(this@LinkDeviceActivity, true) + + loader.isVisible = true + val snackBar = Snackbar.make(containerLayout, R.string.activity_link_device_skip_prompt,Snackbar.LENGTH_INDEFINITE) + .setAction(R.string.registration_activity__skip) { register() } + + val skipJob = launch { + delay(30_000L) + snackBar.show() + // show a dialog or something saying do you want to skip this bit? + } + // start polling and wait for updated message + ApplicationContext.getInstance(this@LinkDeviceActivity).startPollingIfNeeded() + TextSecurePreferences.events.filter { it == TextSecurePreferences.CONFIGURATION_SYNCED }.collect { + // handle we've synced + snackBar.dismiss() + skipJob.cancel() + register() + } + + loader.isVisible = false + } + } + + private fun register() { + restoreJob?.cancel() + val intent = Intent(this@LinkDeviceActivity, PNModeActivity::class.java) + push(intent) } // endregion } @@ -86,7 +142,7 @@ private class LinkDeviceActivityAdapter(private val activity: LinkDeviceActivity } } - override fun getPageTitle(index: Int): CharSequence? { + override fun getPageTitle(index: Int): CharSequence { return when (index) { 0 -> "Recovery Phrase" 1 -> "Scan QR Code" diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SettingsActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SettingsActivity.kt index a9296e9180..cacc35a975 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SettingsActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SettingsActivity.kt @@ -27,29 +27,29 @@ import nl.komponents.kovenant.deferred import nl.komponents.kovenant.functional.bind import nl.komponents.kovenant.task import nl.komponents.kovenant.ui.alwaysUi +import org.session.libsession.messaging.avatars.AvatarHelper +import org.session.libsession.messaging.threads.Address +import org.session.libsession.utilities.SSKEnvironment.ProfileManagerProtocol +import org.session.libsession.utilities.TextSecurePreferences +import org.session.libsession.utilities.preferences.ProfileKeyUtil +import org.session.libsignal.service.api.util.StreamDetails +import org.session.libsignal.service.loki.api.fileserver.FileServerAPI import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity import org.thoughtcrime.securesms.avatar.AvatarSelection -import org.session.libsession.utilities.preferences.ProfileKeyUtil -import org.session.libsession.messaging.threads.Address import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.loki.dialogs.ChangeUiModeDialog import org.thoughtcrime.securesms.loki.dialogs.ClearAllDataDialog import org.thoughtcrime.securesms.loki.dialogs.SeedDialog +import org.thoughtcrime.securesms.loki.protocol.MultiDeviceProtocol import org.thoughtcrime.securesms.loki.utilities.UiModeUtilities import org.thoughtcrime.securesms.loki.utilities.push import org.thoughtcrime.securesms.mms.GlideApp import org.thoughtcrime.securesms.mms.GlideRequests import org.thoughtcrime.securesms.permissions.Permissions -import org.session.libsession.messaging.avatars.AvatarHelper -import org.session.libsession.utilities.SSKEnvironment.ProfileManagerProtocol import org.thoughtcrime.securesms.profiles.ProfileMediaConstraints import org.thoughtcrime.securesms.util.BitmapDecodingException import org.thoughtcrime.securesms.util.BitmapUtil -import org.session.libsession.utilities.TextSecurePreferences -import org.session.libsignal.service.api.util.StreamDetails -import org.session.libsignal.service.loki.api.fileserver.FileServerAPI -import org.thoughtcrime.securesms.loki.protocol.MultiDeviceProtocol import java.io.ByteArrayInputStream import java.io.File import java.security.SecureRandom @@ -207,6 +207,12 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() { // updating the profile name or picture if (profilePicture != null || displayName != null) { task { + if (isUpdatingProfilePicture && profilePicture != null) { + AvatarHelper.setAvatar(this, Address.fromSerialized(TextSecurePreferences.getLocalNumber(this)!!), profilePicture) + TextSecurePreferences.setProfileAvatarId(this, SecureRandom().nextInt()) + ProfileKeyUtil.setEncodedProfileKey(this, encodedProfileKey) + ApplicationContext.getInstance(this).updateOpenGroupProfilePicturesIfNeeded() + } MultiDeviceProtocol.forceSyncConfigurationNowIfNeeded(this@SettingsActivity) } } else { @@ -216,15 +222,11 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() { if (displayName != null) { btnGroupNameDisplay.text = displayName } - displayNameToBeUploaded = null if (isUpdatingProfilePicture && profilePicture != null) { - AvatarHelper.setAvatar(this, Address.fromSerialized(TextSecurePreferences.getLocalNumber(this)!!), profilePicture) - TextSecurePreferences.setProfileAvatarId(this, SecureRandom().nextInt()) - ProfileKeyUtil.setEncodedProfileKey(this, encodedProfileKey) - ApplicationContext.getInstance(this).updateOpenGroupProfilePicturesIfNeeded() profilePictureView.recycle() // clear cached image before update tje profilePictureView profilePictureView.update() } + displayNameToBeUploaded = null profilePictureToBeUploaded = null loader.isVisible = false } diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/MultiDeviceProtocol.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/MultiDeviceProtocol.kt index f7f8be88e8..a05ddd0737 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/MultiDeviceProtocol.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/MultiDeviceProtocol.kt @@ -7,6 +7,7 @@ import org.session.libsession.messaging.messages.control.ConfigurationMessage import org.session.libsession.messaging.threads.Address import org.session.libsession.messaging.threads.recipients.Recipient import org.session.libsession.utilities.TextSecurePreferences +import org.session.libsession.utilities.preferences.ProfileKeyUtil import org.session.libsignal.libsignal.util.guava.Optional import org.session.libsignal.service.api.push.SignalServiceAddress import org.session.libsignal.service.internal.push.SignalServiceProtos @@ -18,12 +19,10 @@ 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.security.SecureRandom import java.util.* object MultiDeviceProtocol { @@ -82,12 +81,18 @@ 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) && !TextSecurePreferences.shouldUpdateProfile(context, timestamp)) return val configurationMessage = ConfigurationMessage.fromProto(content) ?: return val userPublicKey = TextSecurePreferences.getLocalNumber(context) ?: return if (senderPublicKey != userPublicKey) return val storage = MessagingConfiguration.shared.storage val allClosedGroupPublicKeys = storage.getAllClosedGroupPublicKeys() + + val threadDatabase = DatabaseFactory.getThreadDatabase(context) + val recipientDatabase = DatabaseFactory.getRecipientDatabase(context) + + val ourRecipient = Recipient.from(context, Address.fromSerialized(userPublicKey),false) + for (closedGroup in configurationMessage.closedGroups) { if (allClosedGroupPublicKeys.contains(closedGroup.publicKey)) continue @@ -109,18 +114,20 @@ 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) + recipientDatabase.setProfileName(ourRecipient, configurationMessage.displayName) + } + if (configurationMessage.profileKey.isNotEmpty()) { + val profileKey = Base64.encodeBytes(configurationMessage.profileKey) + ProfileKeyUtil.setEncodedProfileKey(context, profileKey) + recipientDatabase.setProfileKey(ourRecipient, configurationMessage.profileKey) + if (!configurationMessage.profilePicture.isNullOrEmpty()) { + TextSecurePreferences.setProfilePictureURL(context, configurationMessage.profilePicture) + TextSecurePreferences.setProfileAvatarId(context, SecureRandom().nextInt()) + ApplicationContext.getInstance(context).jobManager.add(RetrieveProfileAvatarJob(ourRecipient, configurationMessage.profilePicture)) + } } - 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) @@ -142,5 +149,6 @@ object MultiDeviceProtocol { } // TODO: handle new configuration message fields or handle in new pipeline TextSecurePreferences.setConfigurationMessageSynced(context, true) + TextSecurePreferences.setLastProfileUpdateTime(context, timestamp) } } \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/views/ProfilePictureView.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/views/ProfilePictureView.kt index 741f62f4ab..1e5ceca61d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/views/ProfilePictureView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/views/ProfilePictureView.kt @@ -12,12 +12,12 @@ import kotlinx.android.synthetic.main.view_profile_picture.view.* import network.loki.messenger.R import org.session.libsession.messaging.avatars.ProfileContactPhoto import org.session.libsession.messaging.threads.Address -import org.thoughtcrime.securesms.database.DatabaseFactory -import org.thoughtcrime.securesms.loki.utilities.AvatarPlaceholderGenerator -import org.thoughtcrime.securesms.mms.GlideRequests import org.session.libsession.messaging.threads.recipients.Recipient import org.session.libsession.utilities.TextSecurePreferences import org.session.libsignal.service.loki.utilities.mentions.MentionsManager +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.loki.utilities.AvatarPlaceholderGenerator +import org.thoughtcrime.securesms.mms.GlideRequests // TODO: Look into a better way of handling different sizes. Maybe an enum (with associated values) encapsulating the different modes? @@ -151,7 +151,7 @@ class ProfilePictureView : RelativeLayout { if (signalProfilePicture != null && (signalProfilePicture as? ProfileContactPhoto)?.avatarObject != "0" && (signalProfilePicture as? ProfileContactPhoto)?.avatarObject != "") { glide.clear(imageView) - glide.load(signalProfilePicture).diskCacheStrategy(DiskCacheStrategy.ALL).circleCrop().into(imageView) + glide.load(signalProfilePicture).diskCacheStrategy(DiskCacheStrategy.AUTOMATIC).circleCrop().into(imageView) imagesCached.add(publicKey) } else { val sizeInPX = resources.getDimensionPixelSize(sizeResId) diff --git a/app/src/main/res/layout/activity_link_device.xml b/app/src/main/res/layout/activity_link_device.xml index c15ccf4ec3..947d2c1c29 100644 --- a/app/src/main/res/layout/activity_link_device.xml +++ b/app/src/main/res/layout/activity_link_device.xml @@ -2,6 +2,7 @@ diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0ff4e7f611..0e3d163421 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1881,5 +1881,7 @@ Select a file Select a backup file and enter the passphrase it was created with. 30-digit passphrase + + This is taking a while, would you like to skip? diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/notifications/PushNotificationAPI.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/notifications/PushNotificationAPI.kt index c30ed32181..d1b2310207 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/notifications/PushNotificationAPI.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/notifications/PushNotificationAPI.kt @@ -2,14 +2,17 @@ package org.session.libsession.messaging.sending_receiving.notifications import android.annotation.SuppressLint import nl.komponents.kovenant.functional.map -import okhttp3.* +import okhttp3.MediaType +import okhttp3.Request +import okhttp3.RequestBody import org.session.libsession.messaging.MessagingConfiguration import org.session.libsession.utilities.TextSecurePreferences -import org.session.libsignal.utilities.logging.Log -import org.session.libsignal.utilities.JsonUtil import org.session.libsignal.service.loki.api.onionrequests.OnionRequestAPI import org.session.libsignal.service.loki.utilities.retryIfNeeded +import org.session.libsignal.utilities.JsonUtil +import org.session.libsignal.utilities.logging.Log +@SuppressLint("StaticFieldLeak") object PushNotificationAPI { val context = MessagingConfiguration.shared.context val server = "https://live.apns.getsession.org" diff --git a/libsession/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt b/libsession/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt index 1bf74da070..ea7de544be 100644 --- a/libsession/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt +++ b/libsession/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt @@ -107,7 +107,8 @@ object TextSecurePreferences { // region Multi Device private const val LAST_CONFIGURATION_SYNC_TIME = "pref_last_configuration_sync_time" - private const val CONFIGURATION_SYNCED = "pref_configuration_synced" + const val CONFIGURATION_SYNCED = "pref_configuration_synced" + private const val LAST_PROFILE_UPDATE_TIME = "pref_last_profile_update_time" @JvmStatic fun getLastConfigurationSyncTime(context: Context): Long { @@ -127,6 +128,7 @@ object TextSecurePreferences { @JvmStatic fun setConfigurationMessageSynced(context: Context, value: Boolean) { setBooleanPreference(context, CONFIGURATION_SYNCED, value) + _events.tryEmit(CONFIGURATION_SYNCED) } @JvmStatic @@ -753,5 +755,14 @@ object TextSecurePreferences { fun setIsMigratingKeyPair(context: Context?, newValue: Boolean) { setBooleanPreference(context!!, "is_migrating_key_pair", newValue) } + + @JvmStatic + fun setLastProfileUpdateTime(context: Context, profileUpdateTime: Long) { + setLongPreference(context, LAST_PROFILE_UPDATE_TIME, profileUpdateTime) + } + + @JvmStatic + fun shouldUpdateProfile(context: Context, profileUpdateTime: Long) = + profileUpdateTime > getLongPreference(context, LAST_PROFILE_UPDATE_TIME, 0) // endregion } \ No newline at end of file