Merge branch 'dev' of https://github.com/loki-project/session-android into refactor_clean_0

This commit is contained in:
Ryan ZHAO
2021-02-24 16:39:04 +11:00
21 changed files with 546 additions and 106 deletions

View File

@@ -48,7 +48,7 @@ import org.thoughtcrime.securesms.sskenvironment.ReadReceiptManager;
import org.thoughtcrime.securesms.sskenvironment.TypingStatusRepository;
import org.thoughtcrime.securesms.components.TypingStatusSender;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
import org.session.libsession.utilities.preferences.ProfileKeyUtil;
import org.session.libsession.messaging.threads.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;

View File

@@ -1,46 +0,0 @@
package org.thoughtcrime.securesms.crypto;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.session.libsignal.utilities.Base64;
import org.session.libsession.utilities.TextSecurePreferences;
import org.session.libsession.utilities.Util;
import java.io.IOException;
public class ProfileKeyUtil {
public static synchronized @NonNull byte[] getProfileKey(@NonNull Context context) {
try {
String encodedProfileKey = TextSecurePreferences.getProfileKey(context);
if (encodedProfileKey == null) {
encodedProfileKey = Util.getSecret(32);
TextSecurePreferences.setProfileKey(context, encodedProfileKey);
}
return Base64.decode(encodedProfileKey);
} catch (IOException e) {
throw new AssertionError(e);
}
}
public static synchronized @NonNull byte[] getProfileKeyFromEncodedString(String encodedProfileKey) {
try {
return Base64.decode(encodedProfileKey);
} catch (IOException e) {
throw new AssertionError(e);
}
}
public static synchronized @NonNull String generateEncodedProfileKey(@NonNull Context context) {
return Util.getSecret(32);
}
public static synchronized void setEncodedProfileKey(@NonNull Context context, @Nullable String key) {
TextSecurePreferences.setProfileKey(context, key);
}
}

View File

@@ -6,6 +6,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import org.session.libsession.utilities.preferences.ProfileKeyUtil;
import org.session.libsignal.metadata.SignalProtos;
import org.session.libsignal.utilities.logging.Log;
import org.session.libsession.messaging.threads.recipients.Recipient;

View File

@@ -122,7 +122,7 @@ public class MmsSmsDatabase extends Database {
}
public Cursor getConversation(long threadId, long offset, long limit) {
String order = MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " DESC";
String order = MmsSmsColumns.NORMALIZED_DATE_SENT + " DESC";
String selection = MmsSmsColumns.THREAD_ID + " = " + threadId;
String limitStr = limit > 0 || offset > 0 ? offset + ", " + limit : null;

View File

@@ -260,14 +260,14 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
if (userPublicKey == address.getNumber()) {
// Loki - Device link messages don't go through here
SendMessageResult result = messageSender.sendMessage(messageId, address, unidentifiedAccessPair, mediaMessage);
SendMessageResult result = messageSender.sendMessage(messageId, address, unidentifiedAccessPair, mediaMessage, true);
if (result.getLokiAPIError() != null) {
throw result.getLokiAPIError();
} else {
return result.getSuccess().isUnidentified();
}
} else {
SendMessageResult result = messageSender.sendMessage(messageId, address, unidentifiedAccessPair, mediaMessage);
SendMessageResult result = messageSender.sendMessage(messageId, address, unidentifiedAccessPair, mediaMessage, false);
if (result.getLokiAPIError() != null) {
throw result.getLokiAPIError();
} else {
@@ -276,7 +276,7 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
try {
// send to ourselves to sync multi-device
Optional<UnidentifiedAccess> syncAccess = UnidentifiedAccessUtil.getAccessForSync(context);
SendMessageResult selfSendResult = messageSender.sendMessage(messageId, localAddress, syncAccess, mediaSelfSendMessage);
SendMessageResult selfSendResult = messageSender.sendMessage(messageId, localAddress, syncAccess, mediaSelfSendMessage, true);
if (selfSendResult.getLokiAPIError() != null) {
throw selfSendResult.getLokiAPIError();
}

View File

@@ -17,7 +17,7 @@ import org.session.libsession.utilities.Util;
import org.greenrobot.eventbus.EventBus;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.contactshare.ContactModelMapper;
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
import org.session.libsession.utilities.preferences.ProfileKeyUtil;
import org.session.libsession.messaging.threads.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.events.PartProgressEvent;

View File

@@ -197,14 +197,14 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
if (userPublicKey.equals(address.getNumber())) {
// Loki - Device link messages don't go through here
SendMessageResult result = messageSender.sendMessage(messageId, address, unidentifiedAccess, textSecureMessage);
SendMessageResult result = messageSender.sendMessage(messageId, address, unidentifiedAccess, textSecureMessage, true);
if (result.getLokiAPIError() != null) {
throw result.getLokiAPIError();
} else {
return result.getSuccess().isUnidentified();
}
} else {
SendMessageResult result = messageSender.sendMessage(messageId, address, unidentifiedAccess, textSecureMessage);
SendMessageResult result = messageSender.sendMessage(messageId, address, unidentifiedAccess, textSecureMessage, false);
if (result.getLokiAPIError() != null) {
throw result.getLokiAPIError();
} else {
@@ -213,7 +213,7 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
try {
// send to ourselves to sync multi-device
Optional<UnidentifiedAccess> syncAccess = UnidentifiedAccessUtil.getAccessForSync(context);
SendMessageResult selfSendResult = messageSender.sendMessage(messageId, localAddress, syncAccess, textSecureSelfSendMessage);
SendMessageResult selfSendResult = messageSender.sendMessage(messageId, localAddress, syncAccess, textSecureSelfSendMessage, true);
if (selfSendResult.getLokiAPIError() != null) {
throw selfSendResult.getLokiAPIError();
}

View File

@@ -82,7 +82,7 @@ public class RequestGroupInfoJob extends BaseJob implements InjectableType {
messageSender.sendMessage(0, new SignalServiceAddress(source),
UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromExternal(context, source), false)),
message);
message, false);
}
@Override

View File

@@ -298,7 +298,7 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
}.failUi { exception ->
val message = if (exception is ClosedGroupsProtocolV2.Error) exception.description else "An error occurred"
Toast.makeText(this@EditClosedGroupActivity, message, Toast.LENGTH_LONG).show()
loader.fadeOut()
loaderContainer.fadeOut()
isLoading = false
}
} else {

View File

@@ -17,25 +17,26 @@ import android.view.MenuItem
import android.view.View
import android.view.inputmethod.InputMethodManager
import android.widget.Toast
import androidx.core.view.isVisible
import kotlinx.android.synthetic.main.activity_settings.*
import network.loki.messenger.BuildConfig
import network.loki.messenger.R
import nl.komponents.kovenant.Promise
import nl.komponents.kovenant.all
import nl.komponents.kovenant.deferred
import nl.komponents.kovenant.functional.bind
import nl.komponents.kovenant.task
import nl.komponents.kovenant.ui.alwaysUi
import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
import org.thoughtcrime.securesms.avatar.AvatarSelection
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil
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.utilities.UiModeUtilities
import org.thoughtcrime.securesms.loki.utilities.fadeIn
import org.thoughtcrime.securesms.loki.utilities.fadeOut
import org.thoughtcrime.securesms.loki.utilities.push
import org.thoughtcrime.securesms.mms.GlideApp
import org.thoughtcrime.securesms.mms.GlideRequests
@@ -48,6 +49,7 @@ 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
@@ -169,7 +171,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
}
private fun updateProfile(isUpdatingProfilePicture: Boolean) {
loader.fadeIn()
loader.isVisible = true
val promises = mutableListOf<Promise<*, Exception>>()
val displayName = displayNameToBeUploaded
if (displayName != null) {
@@ -196,7 +198,17 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
}
promises.add(deferred.promise)
}
all(promises).alwaysUi {
all(promises).bind {
// updating the profile name or picture
if (profilePicture != null || displayName != null) {
task {
MultiDeviceProtocol.forceSyncConfigurationNowIfNeeded(this@SettingsActivity)
}
} else {
Promise.of(Unit)
}
}.alwaysUi {
if (displayName != null) {
btnGroupNameDisplay.text = displayName
}
@@ -209,7 +221,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
profilePictureView.update()
}
profilePictureToBeUploaded = null
loader.fadeOut()
loader.isVisible = false
}
}
// endregion

View File

@@ -95,8 +95,12 @@ object ClosedGroupsProtocolV2 {
return deferred.promise
}
@JvmStatic
fun explicitLeave(context: Context, groupPublicKey: String): Promise<Unit, Exception> {
/**
* @param notifyUser Inserts an outgoing info message for the user's leave message, useful to set `false` if
* you are exiting asynchronously and deleting the thread from [HomeActivity][org.thoughtcrime.securesms.loki.activities.HomeActivity.deleteConversation]
*/
@JvmStatic @JvmOverloads
fun explicitLeave(context: Context, groupPublicKey: String, notifyUser: Boolean = true): Promise<Unit, Exception> {
val deferred = deferred<Unit, Exception>()
ThreadUtils.queue {
val userPublicKey = TextSecurePreferences.getLocalNumber(context)!!
@@ -120,7 +124,9 @@ object ClosedGroupsProtocolV2 {
// Notify the user
val infoType = GroupContext.Type.QUIT
val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(Recipient.from(context, Address.fromSerialized(groupID), false))
insertOutgoingInfoMessage(context, groupID, infoType, name, updatedMembers, admins, threadID, sentTime)
if (notifyUser) {
insertOutgoingInfoMessage(context, groupID, infoType, name, updatedMembers, admins, threadID, sentTime)
}
// Remove the group private key and unsubscribe from PNs
disableLocalGroupAndUnsubscribe(context, apiDB, groupPublicKey, groupDB, groupID, userPublicKey)
deferred.resolve(Unit)
@@ -144,9 +150,7 @@ object ClosedGroupsProtocolV2 {
val admins = group.admins.map { it.serialize() }
val adminsAsData = admins.map { Hex.fromStringCondensed(it) }
val sentTime = System.currentTimeMillis()
val encryptionKeyPair = pendingKeyPair.getOrElse(groupPublicKey) {
Optional.fromNullable(apiDB.getLatestClosedGroupEncryptionKeyPair(groupPublicKey))
}.orNull()
val encryptionKeyPair = pendingKeyPair[groupPublicKey]?.orNull() ?: Optional.fromNullable(apiDB.getLatestClosedGroupEncryptionKeyPair(groupPublicKey)).orNull()
if (encryptionKeyPair == null) {
Log.d("Loki", "Couldn't get encryption key pair for closed group.")
throw Error.NoKeyPair
@@ -359,7 +363,7 @@ object ClosedGroupsProtocolV2 {
apiDB.addClosedGroupEncryptionKeyPair(encryptionKeyPair, groupPublicKey)
// Notify the user (if we didn't make the group)
if (userPublicKey != senderPublicKey) {
insertIncomingInfoMessage(context, senderPublicKey, groupID, GroupContext.Type.UPDATE, SignalServiceGroup.Type.UPDATE, name, members, admins)
insertIncomingInfoMessage(context, senderPublicKey, groupID, GroupContext.Type.UPDATE, SignalServiceGroup.Type.UPDATE, name, members, admins, sentTimestamp)
} else if (prevGroup == null) {
// only notify if we created this group
val threadID = DatabaseFactory.getLokiThreadDatabase(context).getThreadID(groupID)
@@ -418,7 +422,7 @@ object ClosedGroupsProtocolV2 {
val threadID = DatabaseFactory.getLokiThreadDatabase(context).getThreadID(groupID)
insertOutgoingInfoMessage(context, groupID, contextType, name, members, admins, threadID, sentTimestamp)
} else {
insertIncomingInfoMessage(context, senderPublicKey, groupID, contextType, signalType, name, members, admins)
insertIncomingInfoMessage(context, senderPublicKey, groupID, contextType, signalType, name, members, admins, sentTimestamp)
}
}
@@ -450,7 +454,7 @@ object ClosedGroupsProtocolV2 {
val threadID = DatabaseFactory.getLokiThreadDatabase(context).getThreadID(groupID)
insertOutgoingInfoMessage(context, groupID, GroupContext.Type.UPDATE, name, members, admins, threadID, sentTimestamp)
} else {
insertIncomingInfoMessage(context, senderPublicKey, groupID, GroupContext.Type.UPDATE, SignalServiceGroup.Type.UPDATE, name, members, admins)
insertIncomingInfoMessage(context, senderPublicKey, groupID, GroupContext.Type.UPDATE, SignalServiceGroup.Type.UPDATE, name, members, admins, sentTimestamp)
}
if (userPublicKey in admins) {
// send current encryption key to the latest added members
@@ -489,7 +493,7 @@ object ClosedGroupsProtocolV2 {
val threadID = DatabaseFactory.getLokiThreadDatabase(context).getThreadID(groupID)
insertOutgoingInfoMessage(context, groupID, GroupContext.Type.UPDATE, name, members, admins, threadID, sentTimestamp)
} else {
insertIncomingInfoMessage(context, senderPublicKey, groupID, GroupContext.Type.UPDATE, SignalServiceGroup.Type.UPDATE, name, members, admins)
insertIncomingInfoMessage(context, senderPublicKey, groupID, GroupContext.Type.UPDATE, SignalServiceGroup.Type.UPDATE, name, members, admins, sentTimestamp)
}
}
@@ -517,7 +521,7 @@ object ClosedGroupsProtocolV2 {
val userLeft = userPublicKey == senderPublicKey
// if the admin left, we left, or we are the only remaining member: remove the group
if (didAdminLeave || userLeft || updatedMemberList.size == 1) {
if (didAdminLeave || userLeft) {
disableLocalGroupAndUnsubscribe(context, apiDB, groupPublicKey, groupDB, groupID, userPublicKey)
} else {
val isCurrentUserAdmin = admins.contains(userPublicKey)
@@ -531,7 +535,7 @@ object ClosedGroupsProtocolV2 {
val threadID = DatabaseFactory.getLokiThreadDatabase(context).getThreadID(groupID)
insertOutgoingInfoMessage(context, groupID, GroupContext.Type.QUIT, name, members, admins, threadID, sentTimestamp)
} else {
insertIncomingInfoMessage(context, senderPublicKey, groupID, GroupContext.Type.QUIT, SignalServiceGroup.Type.QUIT, name, members, admins)
insertIncomingInfoMessage(context, senderPublicKey, groupID, GroupContext.Type.QUIT, SignalServiceGroup.Type.QUIT, name, members, admins, sentTimestamp)
}
}
@@ -585,7 +589,7 @@ object ClosedGroupsProtocolV2 {
val threadID = DatabaseFactory.getLokiThreadDatabase(context).getThreadID(groupID)
insertOutgoingInfoMessage(context, groupID, type0, name, members, admins, threadID, sentTimestamp)
} else {
insertIncomingInfoMessage(context, senderPublicKey, groupID, type0, type1, name, members, admins)
insertIncomingInfoMessage(context, senderPublicKey, groupID, type0, type1, name, members, admins, sentTimestamp)
}
}
@@ -652,7 +656,7 @@ object ClosedGroupsProtocolV2 {
}
private fun insertIncomingInfoMessage(context: Context, senderPublicKey: String, groupID: String, type0: GroupContext.Type, type1: SignalServiceGroup.Type,
name: String, members: Collection<String>, admins: Collection<String>) {
name: String, members: Collection<String>, admins: Collection<String>, sentTimestamp: Long) {
val groupContextBuilder = GroupContext.newBuilder()
.setId(ByteString.copyFrom(GroupUtil.getDecodedGroupIDAsData(groupID)))
.setType(type0)
@@ -660,7 +664,7 @@ object ClosedGroupsProtocolV2 {
.addAllMembers(members)
.addAllAdmins(admins)
val group = SignalServiceGroup(type1, GroupUtil.getDecodedGroupIDAsData(groupID), SignalServiceGroup.GroupType.SIGNAL, name, members.toList(), null, admins.toList())
val m = IncomingTextMessage(Address.fromSerialized(senderPublicKey), 1, System.currentTimeMillis(), "", Optional.of(group), 0, true)
val m = IncomingTextMessage(Address.fromSerialized(senderPublicKey), 1, sentTimestamp, "", Optional.of(group), 0, true)
val infoMessage = IncomingGroupMessage(m, groupContextBuilder.build(), "")
val smsDB = DatabaseFactory.getSmsDatabase(context)
smsDB.insertMessageInbox(infoMessage)

View File

@@ -37,7 +37,7 @@ object MultiDeviceProtocol {
try {
messageSender.sendMessage(0, address, udAccess,
Date().time, serializedMessage, false, configurationMessage.ttl.toInt(),
true, false, true, Optional.absent())
true, false, false, Optional.absent())
TextSecurePreferences.setLastConfigurationSyncTime(context, now)
} catch (e: Exception) {
Log.d("Loki", "Failed to send configuration message due to error: $e.")
@@ -56,7 +56,7 @@ object MultiDeviceProtocol {
try {
messageSender.sendMessage(0, address, udAccess,
Date().time, serializedMessage, false, configurationMessage.ttl.toInt(),
true, false, true, Optional.absent())
true, false, false, Optional.absent())
} catch (e: Exception) {
Log.d("Loki", "Failed to send configuration message due to error: $e.")
}
@@ -92,6 +92,7 @@ object MultiDeviceProtocol {
if (allOpenGroups.contains(openGroup)) continue
OpenGroupUtilities.addGroup(context, openGroup, 1)
}
// TODO: handle new configuration message fields or handle in new pipeline
TextSecurePreferences.setConfigurationMessageSynced(context, true)
}
}

View File

@@ -4,7 +4,7 @@ import android.content.Context
import androidx.annotation.WorkerThread
import org.greenrobot.eventbus.EventBus
import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil
import org.session.libsession.utilities.preferences.ProfileKeyUtil
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.groups.GroupManager
import org.session.libsession.utilities.GroupUtil

View File

@@ -250,22 +250,26 @@
</ScrollView>
<RelativeLayout
android:id="@+id/loader"
<FrameLayout
android:animateLayoutChanges="true"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#A4000000"
android:visibility="gone"
android:alpha="0">
android:layout_height="match_parent">
<RelativeLayout
android:id="@+id/loader"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#A4000000"
android:visibility="gone">
<com.github.ybq.android.spinkit.SpinKitView
style="@style/SpinKitView.Large.ThreeBounce"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_centerInParent="true"
app:SpinKit_Color="@android:color/white" />
<com.github.ybq.android.spinkit.SpinKitView
style="@style/SpinKitView.Large.ThreeBounce"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_centerInParent="true"
app:SpinKit_Color="@android:color/white" />
</RelativeLayout>
</RelativeLayout>
</FrameLayout>
</RelativeLayout>

View File

@@ -3,6 +3,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:tools="http://schemas.android.com/tools"
android:background="@drawable/conversation_view_background"
android:gravity="center_vertical"
android:orientation="horizontal">
@@ -50,7 +51,7 @@
android:textSize="@dimen/medium_font_size"
android:textStyle="bold"
android:textColor="@color/text"
android:text="I'm a very long display name. What are you going to do about it?" />
tools:text="I'm a very long display name. What are you going to do about it?" />
<TextView
android:id="@+id/timestampTextView"
@@ -82,7 +83,8 @@
android:layout_marginEnd="6dp" />
<RelativeLayout
android:layout_width="wrap_content"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content">
<TextView
@@ -93,7 +95,7 @@
android:ellipsize="end"
android:textSize="@dimen/medium_font_size"
android:textColor="@color/text"
android:text="Sorry, gotta go fight crime again" />
tools:text="Sorry, gotta go fight crime again" />
<org.thoughtcrime.securesms.components.TypingIndicatorView
android:id="@+id/typingIndicatorView"
@@ -104,11 +106,6 @@
</RelativeLayout>
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1" />
<ImageView
android:id="@+id/statusIndicatorImageView"
android:layout_width="@dimen/conversation_view_status_indicator_size"