mirror of
https://github.com/oxen-io/session-android.git
synced 2025-08-11 17:27:42 +00:00
SS-2168 - User profile warnings (#1531)
* Profile picture upload fail informs user * End of day push * Push before trying with okhttp library update * WIP * Further WIP * Add additional debug comments * Push before cleanup * Cleaned up * More cleanup * Minor adjustment * Final cleanup prior to PR review * Removed commented out old conscrypt version import * Addressed PR feeback from Fanchao --------- Co-authored-by: alansley <aclansley@gmail.com>
This commit is contained in:
@@ -271,7 +271,7 @@ dependencies {
|
||||
if (project.hasProperty('huawei')) huaweiImplementation 'com.huawei.hms:push:6.7.0.300'
|
||||
implementation 'com.google.android.exoplayer:exoplayer-core:2.9.1'
|
||||
implementation 'com.google.android.exoplayer:exoplayer-ui:2.9.1'
|
||||
implementation 'org.conscrypt:conscrypt-android:2.0.0'
|
||||
implementation 'org.conscrypt:conscrypt-android:2.5.2'
|
||||
implementation 'org.signal:aesgcmprovider:0.0.3'
|
||||
implementation 'org.webrtc:google-webrtc:1.0.32006'
|
||||
implementation "me.leolin:ShortcutBadger:1.1.16"
|
||||
|
@@ -22,6 +22,7 @@ import androidx.core.view.isVisible
|
||||
import network.loki.messenger.R
|
||||
import network.loki.messenger.databinding.ViewVisibleMessageContentBinding
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentTransferProgress
|
||||
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
|
||||
@@ -289,7 +290,7 @@ class VisibleMessageContentView : ConstraintLayout {
|
||||
|
||||
// replace URLSpans with ModalURLSpans
|
||||
body.getSpans<URLSpan>(0, body.length).toList().forEach { urlSpan ->
|
||||
val updatedUrl = urlSpan.url.let { HttpUrl.parse(it).toString() }
|
||||
val updatedUrl = urlSpan.url.let { it.toHttpUrlOrNull().toString() }
|
||||
val replacementSpan = ModalURLSpan(updatedUrl) { url ->
|
||||
val activity = context as AppCompatActivity
|
||||
ModalUrlBottomSheet(url).show(activity.supportFragmentManager, "Open URL Dialog")
|
||||
|
@@ -55,7 +55,7 @@ public class RecipientDatabase extends Database {
|
||||
private static final String SYSTEM_PHONE_LABEL = "system_phone_label";
|
||||
private static final String SYSTEM_CONTACT_URI = "system_contact_uri";
|
||||
private static final String SIGNAL_PROFILE_NAME = "signal_profile_name";
|
||||
private static final String SIGNAL_PROFILE_AVATAR = "signal_profile_avatar";
|
||||
private static final String SESSION_PROFILE_AVATAR = "signal_profile_avatar";
|
||||
private static final String PROFILE_SHARING = "profile_sharing_approval";
|
||||
private static final String CALL_RINGTONE = "call_ringtone";
|
||||
private static final String CALL_VIBRATE = "call_vibrate";
|
||||
@@ -69,7 +69,7 @@ public class RecipientDatabase extends Database {
|
||||
private static final String[] RECIPIENT_PROJECTION = new String[] {
|
||||
BLOCK, APPROVED, APPROVED_ME, NOTIFICATION, CALL_RINGTONE, VIBRATE, CALL_VIBRATE, MUTE_UNTIL, COLOR, SEEN_INVITE_REMINDER, DEFAULT_SUBSCRIPTION_ID, EXPIRE_MESSAGES, REGISTERED,
|
||||
PROFILE_KEY, SYSTEM_DISPLAY_NAME, SYSTEM_PHOTO_URI, SYSTEM_PHONE_LABEL, SYSTEM_CONTACT_URI,
|
||||
SIGNAL_PROFILE_NAME, SIGNAL_PROFILE_AVATAR, PROFILE_SHARING, NOTIFICATION_CHANNEL,
|
||||
SIGNAL_PROFILE_NAME, SESSION_PROFILE_AVATAR, PROFILE_SHARING, NOTIFICATION_CHANNEL,
|
||||
UNIDENTIFIED_ACCESS_MODE,
|
||||
FORCE_SMS_SELECTION, NOTIFY_TYPE, DISAPPEARING_STATE, WRAPPER_HASH, BLOCKS_COMMUNITY_MESSAGE_REQUESTS
|
||||
};
|
||||
@@ -97,7 +97,7 @@ public class RecipientDatabase extends Database {
|
||||
SYSTEM_CONTACT_URI + " TEXT DEFAULT NULL, " +
|
||||
PROFILE_KEY + " TEXT DEFAULT NULL, " +
|
||||
SIGNAL_PROFILE_NAME + " TEXT DEFAULT NULL, " +
|
||||
SIGNAL_PROFILE_AVATAR + " TEXT DEFAULT NULL, " +
|
||||
SESSION_PROFILE_AVATAR + " TEXT DEFAULT NULL, " +
|
||||
PROFILE_SHARING + " INTEGER DEFAULT 0, " +
|
||||
CALL_RINGTONE + " TEXT DEFAULT NULL, " +
|
||||
CALL_VIBRATE + " INTEGER DEFAULT " + Recipient.VibrateState.DEFAULT.getId() + ", " +
|
||||
@@ -204,7 +204,7 @@ public class RecipientDatabase extends Database {
|
||||
String systemPhoneLabel = cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_PHONE_LABEL));
|
||||
String systemContactUri = cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_CONTACT_URI));
|
||||
String signalProfileName = cursor.getString(cursor.getColumnIndexOrThrow(SIGNAL_PROFILE_NAME));
|
||||
String signalProfileAvatar = cursor.getString(cursor.getColumnIndexOrThrow(SIGNAL_PROFILE_AVATAR));
|
||||
String signalProfileAvatar = cursor.getString(cursor.getColumnIndexOrThrow(SESSION_PROFILE_AVATAR));
|
||||
boolean profileSharing = cursor.getInt(cursor.getColumnIndexOrThrow(PROFILE_SHARING)) == 1;
|
||||
String notificationChannel = cursor.getString(cursor.getColumnIndexOrThrow(NOTIFICATION_CHANNEL));
|
||||
int unidentifiedAccessMode = cursor.getInt(cursor.getColumnIndexOrThrow(UNIDENTIFIED_ACCESS_MODE));
|
||||
@@ -361,7 +361,7 @@ public class RecipientDatabase extends Database {
|
||||
|
||||
public void setProfileAvatar(@NonNull Recipient recipient, @Nullable String profileAvatar) {
|
||||
ContentValues contentValues = new ContentValues(1);
|
||||
contentValues.put(SIGNAL_PROFILE_AVATAR, profileAvatar);
|
||||
contentValues.put(SESSION_PROFILE_AVATAR, profileAvatar);
|
||||
updateOrInsert(recipient.getAddress(), contentValues);
|
||||
recipient.resolve().setProfileAvatar(profileAvatar);
|
||||
notifyRecipientListeners();
|
||||
|
@@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.database
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import java.security.MessageDigest
|
||||
import network.loki.messenger.libsession_util.ConfigBase
|
||||
import network.loki.messenger.libsession_util.ConfigBase.Companion.PRIORITY_HIDDEN
|
||||
import network.loki.messenger.libsession_util.ConfigBase.Companion.PRIORITY_PINNED
|
||||
@@ -10,6 +11,7 @@ import network.loki.messenger.libsession_util.ConversationVolatileConfig
|
||||
import network.loki.messenger.libsession_util.UserGroupsConfig
|
||||
import network.loki.messenger.libsession_util.UserProfile
|
||||
import network.loki.messenger.libsession_util.util.BaseCommunityInfo
|
||||
import network.loki.messenger.libsession_util.util.Contact as LibSessionContact
|
||||
import network.loki.messenger.libsession_util.util.Conversation
|
||||
import network.loki.messenger.libsession_util.util.ExpiryMode
|
||||
import network.loki.messenger.libsession_util.util.GroupInfo
|
||||
@@ -91,8 +93,6 @@ import org.thoughtcrime.securesms.groups.OpenGroupManager
|
||||
import org.thoughtcrime.securesms.mms.PartAuthority
|
||||
import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
|
||||
import org.thoughtcrime.securesms.util.SessionMetaProtocol
|
||||
import java.security.MessageDigest
|
||||
import network.loki.messenger.libsession_util.util.Contact as LibSessionContact
|
||||
|
||||
private const val TAG = "Storage"
|
||||
|
||||
@@ -471,7 +471,8 @@ open class Storage(
|
||||
val userPublicKey = getUserPublicKey() ?: return
|
||||
// would love to get rid of recipient and context from this
|
||||
val recipient = Recipient.from(context, fromSerialized(userPublicKey), false)
|
||||
// update name
|
||||
|
||||
// Update profile name
|
||||
val name = userProfile.getName() ?: return
|
||||
val userPic = userProfile.getPic()
|
||||
val profileManager = SSKEnvironment.shared.profileManager
|
||||
@@ -480,13 +481,14 @@ open class Storage(
|
||||
profileManager.setName(context, recipient, name)
|
||||
}
|
||||
|
||||
// update pfp
|
||||
// Update profile picture
|
||||
if (userPic == UserPic.DEFAULT) {
|
||||
clearUserPic()
|
||||
} else if (userPic.key.isNotEmpty() && userPic.url.isNotEmpty()
|
||||
&& TextSecurePreferences.getProfilePictureURL(context) != userPic.url) {
|
||||
setUserProfilePicture(userPic.url, userPic.key)
|
||||
}
|
||||
|
||||
if (userProfile.getNtsPriority() == PRIORITY_HIDDEN) {
|
||||
// delete nts thread if needed
|
||||
val ourThread = getThreadId(recipient) ?: return
|
||||
@@ -514,12 +516,13 @@ open class Storage(
|
||||
addLibSessionContacts(extracted, messageTimestamp)
|
||||
}
|
||||
|
||||
override fun clearUserPic() {
|
||||
val userPublicKey = getUserPublicKey() ?: return
|
||||
override fun clearUserPic() {
|
||||
val userPublicKey = getUserPublicKey() ?: return Log.w(TAG, "No user public key when trying to clear user pic")
|
||||
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
|
||||
|
||||
// Clear details related to the user's profile picture
|
||||
TextSecurePreferences.setProfileKey(context, null)
|
||||
ProfileKeyUtil.setEncodedProfileKey(context, null)
|
||||
recipientDatabase.setProfileAvatar(recipient, null)
|
||||
@@ -528,7 +531,6 @@ open class Storage(
|
||||
|
||||
Recipient.removeCached(fromSerialized(userPublicKey))
|
||||
configFactory.user?.setPic(UserPic.DEFAULT)
|
||||
ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(context)
|
||||
}
|
||||
|
||||
private fun updateConvoVolatile(convos: ConversationVolatileConfig, messageTimestamp: Long) {
|
||||
|
@@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.groups
|
||||
import android.content.Context
|
||||
import androidx.annotation.WorkerThread
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||
import org.session.libsession.messaging.open_groups.GroupMemberRole
|
||||
import org.session.libsession.messaging.open_groups.OpenGroup
|
||||
@@ -143,9 +144,9 @@ object OpenGroupManager {
|
||||
|
||||
@WorkerThread
|
||||
fun addOpenGroup(urlAsString: String, context: Context): OpenGroupApi.RoomInfo? {
|
||||
val url = HttpUrl.parse(urlAsString) ?: return null
|
||||
val url = urlAsString.toHttpUrlOrNull() ?: return null
|
||||
val server = OpenGroup.getServer(urlAsString)
|
||||
val room = url.pathSegments().firstOrNull() ?: return null
|
||||
val room = url.pathSegments.firstOrNull() ?: return null
|
||||
val publicKey = url.queryParameter("public_key") ?: return null
|
||||
|
||||
return add(server.toString().removeSuffix("/"), room, publicKey, context).second // assume migrated from calling function
|
||||
|
@@ -35,6 +35,5 @@ public class AndroidLogger extends Log.Logger {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void blockUntilAllWritesFinished() {
|
||||
}
|
||||
public void blockUntilAllWritesFinished() { }
|
||||
}
|
||||
|
@@ -10,6 +10,7 @@ import kotlinx.serialization.json.decodeFromStream
|
||||
import nl.komponents.kovenant.Promise
|
||||
import nl.komponents.kovenant.functional.map
|
||||
import okhttp3.MediaType
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody
|
||||
import org.session.libsession.messaging.sending_receiving.notifications.Response
|
||||
@@ -99,7 +100,7 @@ class PushRegistryV2 @Inject constructor(private val pushReceiver: PushReceiver)
|
||||
private inline fun <reified T: Response> getResponseBody(path: String, requestParameters: String): Promise<T, Exception> {
|
||||
val server = Server.LATEST
|
||||
val url = "${server.url}/$path"
|
||||
val body = RequestBody.create(MediaType.get("application/json"), requestParameters)
|
||||
val body = RequestBody.create("application/json".toMediaType(), requestParameters)
|
||||
val request = Request.Builder().url(url).post(body).build()
|
||||
|
||||
return OnionRequestAPI.sendOnionRequest(
|
||||
|
@@ -22,13 +22,16 @@ import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.Toast
|
||||
import androidx.core.view.isVisible
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import java.io.File
|
||||
import java.security.SecureRandom
|
||||
import javax.inject.Inject
|
||||
import network.loki.messenger.BuildConfig
|
||||
import network.loki.messenger.R
|
||||
import network.loki.messenger.databinding.ActivitySettingsBinding
|
||||
import network.loki.messenger.libsession_util.util.UserPic
|
||||
import nl.komponents.kovenant.Promise
|
||||
import nl.komponents.kovenant.all
|
||||
import nl.komponents.kovenant.ui.alwaysUi
|
||||
import nl.komponents.kovenant.ui.failUi
|
||||
import nl.komponents.kovenant.ui.successUi
|
||||
import org.session.libsession.avatars.AvatarHelper
|
||||
import org.session.libsession.avatars.ProfileContactPhoto
|
||||
@@ -37,7 +40,7 @@ import org.session.libsession.snode.SnodeAPI
|
||||
import org.session.libsession.utilities.*
|
||||
import org.session.libsession.utilities.SSKEnvironment.ProfileManagerProtocol
|
||||
import org.session.libsession.utilities.recipients.Recipient
|
||||
import org.session.libsignal.utilities.getProperty
|
||||
import org.session.libsignal.utilities.Log
|
||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||
import org.thoughtcrime.securesms.avatar.AvatarSelection
|
||||
import org.thoughtcrime.securesms.components.ProfilePictureView
|
||||
@@ -56,12 +59,10 @@ import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
|
||||
import org.thoughtcrime.securesms.util.disableClipping
|
||||
import org.thoughtcrime.securesms.util.push
|
||||
import org.thoughtcrime.securesms.util.show
|
||||
import java.io.File
|
||||
import java.security.SecureRandom
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class SettingsActivity : PassphraseRequiredActionBarActivity() {
|
||||
private val TAG = "SettingsActivity"
|
||||
|
||||
@Inject
|
||||
lateinit var configFactory: ConfigFactory
|
||||
@@ -233,41 +234,78 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
|
||||
displayName: String? = null
|
||||
) {
|
||||
binding.loader.isVisible = true
|
||||
val promises = mutableListOf<Promise<*, Exception>>()
|
||||
|
||||
if (displayName != null) {
|
||||
TextSecurePreferences.setProfileName(this, displayName)
|
||||
configFactory.user?.setName(displayName)
|
||||
}
|
||||
|
||||
// Bail if we're not updating the profile picture in any way
|
||||
if (!isUpdatingProfilePicture) return
|
||||
|
||||
val encodedProfileKey = ProfileKeyUtil.generateEncodedProfileKey(this)
|
||||
if (isUpdatingProfilePicture) {
|
||||
if (profilePicture != null) {
|
||||
promises.add(ProfilePictureUtilities.upload(profilePicture, encodedProfileKey, this))
|
||||
} else {
|
||||
|
||||
val uploadProfilePicturePromise: Promise<*, Exception>
|
||||
var removingProfilePic = false
|
||||
|
||||
// Adding a new profile picture?
|
||||
if (profilePicture != null) {
|
||||
uploadProfilePicturePromise = ProfilePictureUtilities.upload(profilePicture, encodedProfileKey, this)
|
||||
} else {
|
||||
// If not then we must be removing the existing one.
|
||||
// Note: To get a promise that will resolve / sync correctly we overwrite the existing profile picture with
|
||||
// a 0 byte image.
|
||||
removingProfilePic = true
|
||||
val emptyByteArray = ByteArray(0)
|
||||
uploadProfilePicturePromise = ProfilePictureUtilities.upload(emptyByteArray, encodedProfileKey, this)
|
||||
}
|
||||
|
||||
// If the upload picture promise succeeded then we hit this successUi block
|
||||
uploadProfilePicturePromise.successUi {
|
||||
|
||||
// If we successfully removed the profile picture on the network then we can clear the
|
||||
// local data - otherwise it's weird to fail the online section but it _looks_ like it
|
||||
// worked because we cleared the local image (also it denies them the chance to retry
|
||||
// removal if we do it locally, and may result in them having a visible profile picture
|
||||
// everywhere EXCEPT on their own device!).
|
||||
if (removingProfilePic) {
|
||||
MessagingModuleConfiguration.shared.storage.clearUserPic()
|
||||
}
|
||||
}
|
||||
val compoundPromise = all(promises)
|
||||
compoundPromise.successUi { // Do this on the UI thread so that it happens before the alwaysUi clause below
|
||||
|
||||
val userConfig = configFactory.user
|
||||
if (isUpdatingProfilePicture) {
|
||||
AvatarHelper.setAvatar(this, Address.fromSerialized(TextSecurePreferences.getLocalNumber(this)!!), profilePicture)
|
||||
TextSecurePreferences.setProfileAvatarId(this, profilePicture?.let { SecureRandom().nextInt() } ?: 0 )
|
||||
ProfileKeyUtil.setEncodedProfileKey(this, encodedProfileKey)
|
||||
// new config
|
||||
val url = TextSecurePreferences.getProfilePictureURL(this)
|
||||
val profileKey = ProfileKeyUtil.getProfileKey(this)
|
||||
if (profilePicture == null) {
|
||||
userConfig?.setPic(UserPic.DEFAULT)
|
||||
} else if (!url.isNullOrEmpty() && profileKey.isNotEmpty()) {
|
||||
userConfig?.setPic(UserPic(url, profileKey))
|
||||
}
|
||||
AvatarHelper.setAvatar(this, Address.fromSerialized(TextSecurePreferences.getLocalNumber(this)!!), profilePicture)
|
||||
TextSecurePreferences.setProfileAvatarId(this, profilePicture?.let { SecureRandom().nextInt() } ?: 0 )
|
||||
ProfileKeyUtil.setEncodedProfileKey(this, encodedProfileKey)
|
||||
|
||||
// new config
|
||||
val url = TextSecurePreferences.getProfilePictureURL(this)
|
||||
val profileKey = ProfileKeyUtil.getProfileKey(this)
|
||||
|
||||
// If we have a URL and a profile key then set the user's profile picture
|
||||
if (!url.isNullOrEmpty() && profileKey.isNotEmpty()) {
|
||||
userConfig?.setPic(UserPic(url, profileKey))
|
||||
}
|
||||
|
||||
if (userConfig != null && userConfig.needsDump()) {
|
||||
configFactory.persist(userConfig, SnodeAPI.nowWithOffset)
|
||||
}
|
||||
|
||||
ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(this@SettingsActivity)
|
||||
}
|
||||
compoundPromise.alwaysUi {
|
||||
|
||||
// Or if the promise failed to upload the new profile picture then we hit this failUi block
|
||||
uploadProfilePicturePromise.failUi {
|
||||
if (removingProfilePic) {
|
||||
Log.e(TAG, "Failed to remove profile picture")
|
||||
Toast.makeText(this@SettingsActivity, R.string.profileDisplayPictureRemoveError, Toast.LENGTH_LONG).show()
|
||||
} else {
|
||||
Log.e(TAG, "Failed to upload profile picture")
|
||||
Toast.makeText(this@SettingsActivity, R.string.profileErrorUpdate, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, regardless of whether the promise succeeded or failed, we always hit this `alwaysUi` block
|
||||
uploadProfilePicturePromise.alwaysUi {
|
||||
if (displayName != null) {
|
||||
binding.btnGroupNameDisplay.text = displayName
|
||||
}
|
||||
|
@@ -24,6 +24,7 @@ import org.session.libsession.utilities.WindowDebouncer
|
||||
import org.session.libsignal.crypto.ecc.DjbECPublicKey
|
||||
import org.session.libsignal.utilities.Hex
|
||||
import org.session.libsignal.utilities.IdPrefix
|
||||
import org.session.libsignal.utilities.Log
|
||||
import org.session.libsignal.utilities.toHexString
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase
|
||||
@@ -31,10 +32,12 @@ import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
||||
import java.util.Timer
|
||||
|
||||
object ConfigurationMessageUtilities {
|
||||
private const val TAG = "ConfigMessageUtils"
|
||||
|
||||
private val debouncer = WindowDebouncer(3000, Timer())
|
||||
|
||||
private fun scheduleConfigSync(userPublicKey: String) {
|
||||
|
||||
debouncer.publish {
|
||||
// don't schedule job if we already have one
|
||||
val storage = MessagingModuleConfiguration.shared.storage
|
||||
@@ -44,23 +47,20 @@ object ConfigurationMessageUtilities {
|
||||
(currentStorageJob as ConfigurationSyncJob).shouldRunAgain.set(true)
|
||||
return@publish
|
||||
}
|
||||
val newConfigSync = ConfigurationSyncJob(ourDestination)
|
||||
JobQueue.shared.add(newConfigSync)
|
||||
val newConfigSyncJob = ConfigurationSyncJob(ourDestination)
|
||||
JobQueue.shared.add(newConfigSyncJob)
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun syncConfigurationIfNeeded(context: Context) {
|
||||
// add if check here to schedule new config job process and return early
|
||||
val userPublicKey = TextSecurePreferences.getLocalNumber(context) ?: return
|
||||
val userPublicKey = TextSecurePreferences.getLocalNumber(context) ?: return Log.w(TAG, "User Public Key is null")
|
||||
scheduleConfigSync(userPublicKey)
|
||||
}
|
||||
|
||||
fun forceSyncConfigurationNowIfNeeded(context: Context): Promise<Unit, Exception> {
|
||||
// add if check here to schedule new config job process and return early
|
||||
val userPublicKey = TextSecurePreferences.getLocalNumber(context) ?: return Promise.ofFail(NullPointerException("User Public Key is null"))
|
||||
// schedule job if none exist
|
||||
// don't schedule job if we already have one
|
||||
// Schedule a new job if one doesn't already exist (only)
|
||||
scheduleConfigSync(userPublicKey)
|
||||
return Promise.ofSuccess(Unit)
|
||||
}
|
||||
|
Reference in New Issue
Block a user