mirror of
https://github.com/oxen-io/session-android.git
synced 2025-08-12 04:57:43 +00:00
Merge remote-tracking branch 'upstream/dev' into open_groups_V2
# Conflicts: # app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java # app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java # app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiMessageDatabase.kt # app/src/main/java/org/thoughtcrime/securesms/loki/protocol/MultiDeviceProtocol.kt # app/src/main/java/org/thoughtcrime/securesms/loki/views/MentionCandidateSelectionView.kt # app/src/main/java/org/thoughtcrime/securesms/loki/views/MentionCandidateView.kt # libsession/src/main/java/org/session/libsession/messaging/mentions/MentionsManager.kt # libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt # libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/ClosedGroupPoller.kt # libsession/src/main/java/org/session/libsession/messaging/utilities/DotNetAPI.kt # libsession/src/main/java/org/session/libsession/utilities/mentions/Mention.kt # libsignal/src/main/java/org/session/libsignal/service/loki/Mention.kt # libsignal/src/main/java/org/session/libsignal/service/loki/utilities/mentions/Mention.kt
This commit is contained in:
20
app/src/main/java/org/thoughtcrime/securesms/AppContext.kt
Normal file
20
app/src/main/java/org/thoughtcrime/securesms/AppContext.kt
Normal file
@@ -0,0 +1,20 @@
|
||||
package org.thoughtcrime.securesms
|
||||
|
||||
import android.util.Log
|
||||
import nl.komponents.kovenant.Kovenant
|
||||
import nl.komponents.kovenant.jvm.asDispatcher
|
||||
import org.session.libsignal.utilities.ThreadUtils
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
object AppContext {
|
||||
|
||||
fun configureKovenant() {
|
||||
Kovenant.context {
|
||||
callbackContext.dispatcher = Executors.newSingleThreadExecutor().asDispatcher()
|
||||
workerContext.dispatcher = ThreadUtils.executorPool.asDispatcher()
|
||||
multipleCompletion = { v1, v2 ->
|
||||
Log.d("Loki", "Promise resolved more than once (first with $v1, then with $v2); ignoring $v2.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -49,7 +49,7 @@ import org.session.libsession.utilities.dynamiclanguage.DynamicLanguageContextWr
|
||||
import org.session.libsession.utilities.dynamiclanguage.LocaleParser;
|
||||
import org.session.libsession.utilities.preferences.ProfileKeyUtil;
|
||||
import org.session.libsignal.service.api.util.StreamDetails;
|
||||
import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol;
|
||||
import org.session.libsignal.service.loki.LokiAPIDatabaseProtocol;
|
||||
import org.session.libsignal.utilities.logging.Log;
|
||||
import org.signal.aesgcmprovider.AesGcmProvider;
|
||||
import org.thoughtcrime.securesms.components.TypingStatusSender;
|
||||
@@ -93,7 +93,6 @@ import org.webrtc.voiceengine.WebRtcAudioUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.Security;
|
||||
import java.util.Date;
|
||||
@@ -104,6 +103,7 @@ import dagger.ObjectGraph;
|
||||
import kotlin.Unit;
|
||||
import kotlinx.coroutines.Job;
|
||||
import network.loki.messenger.BuildConfig;
|
||||
import nl.komponents.kovenant.Kovenant;
|
||||
|
||||
import static nl.komponents.kovenant.android.KovenantAndroid.startKovenant;
|
||||
import static nl.komponents.kovenant.android.KovenantAndroid.stopKovenant;
|
||||
@@ -164,6 +164,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
||||
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
|
||||
// Loki
|
||||
// ========
|
||||
AppContext.INSTANCE.configureKovenant();
|
||||
messageNotifier = new OptimizedMessageNotifier(new DefaultMessageNotifier());
|
||||
broadcaster = new Broadcaster(this);
|
||||
threadNotificationHandler = new Handler(Looper.getMainLooper());
|
||||
|
@@ -84,6 +84,7 @@ public class PassphrasePromptActivity extends BaseActionBarActivity {
|
||||
}
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
keyCachingService.setMasterSecret(new Object());
|
||||
keyCachingService = null;
|
||||
}
|
||||
}, Context.BIND_AUTO_CREATE);
|
||||
@@ -133,7 +134,9 @@ public class PassphrasePromptActivity extends BaseActionBarActivity {
|
||||
private void handleAuthenticated() {
|
||||
authenticated = true;
|
||||
//TODO Replace with a proper call.
|
||||
keyCachingService.setMasterSecret(new Object());
|
||||
if (keyCachingService != null) {
|
||||
keyCachingService.setMasterSecret(new Object());
|
||||
}
|
||||
|
||||
// Finish and proceed with the next intent.
|
||||
Intent nextIntent = getIntent().getParcelableExtra("next_intent");
|
||||
@@ -188,6 +191,8 @@ public class PassphrasePromptActivity extends BaseActionBarActivity {
|
||||
|
||||
if (!keyguardManager.isKeyguardSecure()) {
|
||||
Log.w(TAG ,"Keyguard not secure...");
|
||||
TextSecurePreferences.setScreenLockEnabled(getApplicationContext(), false);
|
||||
TextSecurePreferences.setScreenLockTimeout(getApplicationContext(), 0);
|
||||
handleAuthenticated();
|
||||
return;
|
||||
}
|
||||
|
@@ -109,10 +109,10 @@ import org.session.libsession.utilities.TextSecurePreferences;
|
||||
import org.session.libsession.utilities.Util;
|
||||
import org.session.libsession.utilities.ViewUtil;
|
||||
import org.session.libsession.utilities.concurrent.AssertedSuccessListener;
|
||||
import org.session.libsession.utilities.mentions.Mention;
|
||||
import org.session.libsession.utilities.views.Stub;
|
||||
import org.session.libsignal.libsignal.InvalidMessageException;
|
||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
||||
import org.session.libsignal.service.loki.Mention;
|
||||
import org.session.libsignal.service.loki.utilities.HexEncodingKt;
|
||||
import org.session.libsignal.service.loki.utilities.PublicKeyValidation;
|
||||
import org.session.libsignal.utilities.concurrent.ListenableFuture;
|
||||
|
@@ -25,10 +25,9 @@ import org.session.libsession.utilities.Util;
|
||||
|
||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
||||
import org.session.libsignal.service.api.messages.SignalServiceAttachmentPointer;
|
||||
import org.session.libsignal.service.loki.database.LokiOpenGroupDatabaseProtocol;
|
||||
import org.session.libsignal.service.loki.LokiOpenGroupDatabaseProtocol;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
@@ -44,6 +43,7 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
|
||||
static final String GROUP_ID = "group_id";
|
||||
private static final String TITLE = "title";
|
||||
private static final String MEMBERS = "members";
|
||||
private static final String ZOMBIE_MEMBERS = "zombie_members";
|
||||
private static final String AVATAR = "avatar";
|
||||
private static final String AVATAR_ID = "avatar_id";
|
||||
private static final String AVATAR_KEY = "avatar_key";
|
||||
@@ -64,6 +64,7 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
|
||||
GROUP_ID + " TEXT, " +
|
||||
TITLE + " TEXT, " +
|
||||
MEMBERS + " TEXT, " +
|
||||
ZOMBIE_MEMBERS + " TEXT, " +
|
||||
AVATAR + " BLOB, " +
|
||||
AVATAR_ID + " INTEGER, " +
|
||||
AVATAR_KEY + " BLOB, " +
|
||||
@@ -81,7 +82,7 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
|
||||
};
|
||||
|
||||
private static final String[] GROUP_PROJECTION = {
|
||||
GROUP_ID, TITLE, MEMBERS, AVATAR, AVATAR_ID, AVATAR_KEY, AVATAR_CONTENT_TYPE, AVATAR_RELAY, AVATAR_DIGEST,
|
||||
GROUP_ID, TITLE, MEMBERS, ZOMBIE_MEMBERS, AVATAR, AVATAR_ID, AVATAR_KEY, AVATAR_CONTENT_TYPE, AVATAR_RELAY, AVATAR_DIGEST,
|
||||
TIMESTAMP, ACTIVE, MMS, AVATAR_URL, ADMINS
|
||||
};
|
||||
|
||||
@@ -162,7 +163,7 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
|
||||
}
|
||||
|
||||
public @NonNull List<Recipient> getGroupMembers(String groupId, boolean includeSelf) {
|
||||
List<Address> members = getCurrentMembers(groupId);
|
||||
List<Address> members = getCurrentMembers(groupId, false);
|
||||
List<Recipient> recipients = new LinkedList<>();
|
||||
|
||||
for (Address member : members) {
|
||||
@@ -177,6 +178,17 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
|
||||
return recipients;
|
||||
}
|
||||
|
||||
public @NonNull List<Recipient> getGroupZombieMembers(String groupId) {
|
||||
List<Address> members = getCurrentZombieMembers(groupId);
|
||||
List<Recipient> recipients = new LinkedList<>();
|
||||
|
||||
for (Address member : members) {
|
||||
recipients.add(Recipient.from(context, member, false));
|
||||
}
|
||||
|
||||
return recipients;
|
||||
}
|
||||
|
||||
public long create(@NonNull String groupId, @Nullable String title, @NonNull List<Address> members,
|
||||
@Nullable SignalServiceAttachmentPointer avatar, @Nullable String relay, @Nullable List<Address> admins, @NonNull Long formationTimestamp)
|
||||
{
|
||||
@@ -300,6 +312,16 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
|
||||
});
|
||||
}
|
||||
|
||||
public void updateZombieMembers(String groupId, List<Address> members) {
|
||||
Collections.sort(members);
|
||||
|
||||
ContentValues contents = new ContentValues();
|
||||
contents.put(ZOMBIE_MEMBERS, Address.toSerializedList(members, ','));
|
||||
contents.put(ACTIVE, 1);
|
||||
databaseHelper.getWritableDatabase().update(TABLE_NAME, contents, GROUP_ID + " = ?",
|
||||
new String[] {groupId});
|
||||
}
|
||||
|
||||
public void updateAdmins(String groupId, List<Address> admins) {
|
||||
Collections.sort(admins);
|
||||
|
||||
@@ -311,7 +333,7 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
|
||||
}
|
||||
|
||||
public void removeMember(String groupId, Address source) {
|
||||
List<Address> currentMembers = getCurrentMembers(groupId);
|
||||
List<Address> currentMembers = getCurrentMembers(groupId, false);
|
||||
currentMembers.remove(source);
|
||||
|
||||
ContentValues contents = new ContentValues();
|
||||
@@ -329,18 +351,22 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
|
||||
});
|
||||
}
|
||||
|
||||
private List<Address> getCurrentMembers(String groupId) {
|
||||
private List<Address> getCurrentMembers(String groupId, boolean zombieMembers) {
|
||||
Cursor cursor = null;
|
||||
|
||||
String membersColumn = MEMBERS;
|
||||
if (zombieMembers) membersColumn = ZOMBIE_MEMBERS;
|
||||
|
||||
try {
|
||||
cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, new String[] {MEMBERS},
|
||||
cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, new String[] {membersColumn},
|
||||
GROUP_ID + " = ?",
|
||||
new String[] {groupId},
|
||||
null, null, null);
|
||||
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
String serializedMembers = cursor.getString(cursor.getColumnIndexOrThrow(MEMBERS));
|
||||
return Address.fromSerializedList(serializedMembers, ',');
|
||||
String serializedMembers = cursor.getString(cursor.getColumnIndexOrThrow(membersColumn));
|
||||
if (serializedMembers != null && !serializedMembers.isEmpty())
|
||||
return Address.fromSerializedList(serializedMembers, ',');
|
||||
}
|
||||
|
||||
return new LinkedList<>();
|
||||
@@ -350,6 +376,10 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
|
||||
}
|
||||
}
|
||||
|
||||
private List<Address> getCurrentZombieMembers(String groupId) {
|
||||
return getCurrentMembers(groupId, true);
|
||||
}
|
||||
|
||||
public boolean isActive(String groupId) {
|
||||
Optional<GroupRecord> record = getGroup(groupId);
|
||||
return record.isPresent() && record.get().isActive();
|
||||
|
@@ -455,6 +455,10 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
|
||||
DatabaseFactory.getGroupDatabase(context).setActive(groupID, value)
|
||||
}
|
||||
|
||||
override fun getZombieMember(groupID: String): Set<String> {
|
||||
return DatabaseFactory.getGroupDatabase(context).getGroupZombieMembers(groupID).map { it.address.serialize() }.toHashSet()
|
||||
}
|
||||
|
||||
override fun removeMember(groupID: String, member: Address) {
|
||||
DatabaseFactory.getGroupDatabase(context).removeMember(groupID, member)
|
||||
}
|
||||
@@ -463,6 +467,10 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
|
||||
DatabaseFactory.getGroupDatabase(context).updateMembers(groupID, members)
|
||||
}
|
||||
|
||||
override fun updateZombieMembers(groupID: String, members: List<Address>) {
|
||||
DatabaseFactory.getGroupDatabase(context).updateZombieMembers(groupID, members)
|
||||
}
|
||||
|
||||
override fun insertIncomingInfoMessage(context: Context, senderPublicKey: String, groupID: String, type: SignalServiceGroup.Type, name: String, members: Collection<String>, admins: Collection<String>, sentTimestamp: Long) {
|
||||
val group = SignalServiceGroup(type, GroupUtil.getDecodedGroupIDAsData(groupID), SignalServiceGroup.GroupType.SIGNAL, name, members.toList(), null, admins.toList())
|
||||
val m = IncomingTextMessage(Address.fromSerialized(senderPublicKey), 1, sentTimestamp, "", Optional.of(group), 0, true)
|
||||
|
@@ -276,6 +276,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
||||
}
|
||||
|
||||
if (oldVersion < lokiV23) {
|
||||
db.execSQL("ALTER TABLE groups ADD COLUMN zombie_members TEXT");
|
||||
db.execSQL(LokiMessageDatabase.getUpdateMessageIDTableForType());
|
||||
db.execSQL(LokiMessageDatabase.getUpdateMessageMappingTable());
|
||||
}
|
||||
|
@@ -37,8 +37,14 @@ import java.io.IOException
|
||||
|
||||
class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
|
||||
private val originalMembers = HashSet<String>()
|
||||
private val zombies = HashSet<String>()
|
||||
private val members = HashSet<String>()
|
||||
private val allMembers: Set<String>
|
||||
get() {
|
||||
return members + zombies
|
||||
}
|
||||
private var hasNameChanged = false
|
||||
private var isSelfAdmin = false
|
||||
private var isLoading = false
|
||||
set(newValue) { field = newValue; invalidateOptionsMenu() }
|
||||
|
||||
@@ -54,7 +60,10 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
|
||||
}
|
||||
|
||||
private val memberListAdapter by lazy {
|
||||
EditClosedGroupMembersAdapter(this, GlideApp.with(this), this::onMemberClick)
|
||||
if (isSelfAdmin)
|
||||
EditClosedGroupMembersAdapter(this, GlideApp.with(this), isSelfAdmin, this::onMemberClick)
|
||||
else
|
||||
EditClosedGroupMembersAdapter(this, GlideApp.with(this), isSelfAdmin)
|
||||
}
|
||||
|
||||
private lateinit var mainContentContainer: LinearLayout
|
||||
@@ -81,7 +90,10 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
|
||||
ThemeUtil.getThemedDrawableResId(this, R.attr.actionModeCloseDrawable))
|
||||
|
||||
groupID = intent.getStringExtra(groupIDKey)!!
|
||||
originalName = DatabaseFactory.getGroupDatabase(this).getGroup(groupID).get().title
|
||||
val groupInfo = DatabaseFactory.getGroupDatabase(this).getGroup(groupID).get()
|
||||
originalName = groupInfo.title
|
||||
isSelfAdmin = groupInfo.admins.any{ it.serialize() == TextSecurePreferences.getLocalNumber(this) }
|
||||
|
||||
name = originalName
|
||||
|
||||
mainContentContainer = findViewById(R.id.mainContentContainer)
|
||||
@@ -116,31 +128,35 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
LoaderManager.getInstance(this).initLoader(loaderID, null, object : LoaderManager.LoaderCallbacks<List<String>> {
|
||||
LoaderManager.getInstance(this).initLoader(loaderID, null, object : LoaderManager.LoaderCallbacks<GroupMembers> {
|
||||
|
||||
override fun onCreateLoader(id: Int, bundle: Bundle?): Loader<List<String>> {
|
||||
override fun onCreateLoader(id: Int, bundle: Bundle?): Loader<GroupMembers> {
|
||||
return EditClosedGroupLoader(this@EditClosedGroupActivity, groupID)
|
||||
}
|
||||
|
||||
override fun onLoadFinished(loader: Loader<List<String>>, members: List<String>) {
|
||||
override fun onLoadFinished(loader: Loader<GroupMembers>, groupMembers: GroupMembers) {
|
||||
// We no longer need any subsequent loading events
|
||||
// (they will occur on every activity resume).
|
||||
LoaderManager.getInstance(this@EditClosedGroupActivity).destroyLoader(loaderID)
|
||||
|
||||
members.clear()
|
||||
members.addAll(groupMembers.members.toHashSet())
|
||||
zombies.clear()
|
||||
zombies.addAll(groupMembers.zombieMembers.toHashSet())
|
||||
originalMembers.clear()
|
||||
originalMembers.addAll(members.toHashSet())
|
||||
updateMembers(originalMembers)
|
||||
originalMembers.addAll(members + zombies)
|
||||
updateMembers()
|
||||
}
|
||||
|
||||
override fun onLoaderReset(loader: Loader<List<String>>) {
|
||||
updateMembers(setOf())
|
||||
override fun onLoaderReset(loader: Loader<GroupMembers>) {
|
||||
updateMembers()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
menuInflater.inflate(R.menu.menu_edit_closed_group, menu)
|
||||
return members.isNotEmpty() && !isLoading
|
||||
return allMembers.isNotEmpty() && !isLoading
|
||||
}
|
||||
// endregion
|
||||
|
||||
@@ -153,8 +169,8 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
|
||||
if (data == null || data.extras == null || !data.hasExtra(SelectContactsActivity.selectedContactsKey)) return
|
||||
|
||||
val selectedContacts = data.extras!!.getStringArray(SelectContactsActivity.selectedContactsKey)!!.toSet()
|
||||
val changedMembers = members + selectedContacts
|
||||
updateMembers(changedMembers)
|
||||
members.addAll(selectedContacts)
|
||||
updateMembers()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -173,17 +189,12 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateMembers(members: Set<String>) {
|
||||
this.members.clear()
|
||||
this.members.addAll(members)
|
||||
memberListAdapter.setMembers(members)
|
||||
private fun updateMembers() {
|
||||
memberListAdapter.setMembers(allMembers)
|
||||
memberListAdapter.setZombieMembers(zombies)
|
||||
|
||||
val admins = DatabaseFactory.getGroupDatabase(this).getGroup(groupID).get().admins.map { it.toString() }.toMutableSet()
|
||||
admins.remove(TextSecurePreferences.getLocalNumber(this))
|
||||
memberListAdapter.setLockedMembers(admins)
|
||||
|
||||
mainContentContainer.visibility = if (members.isEmpty()) View.GONE else View.VISIBLE
|
||||
emptyStateContainer.visibility = if (members.isEmpty()) View.VISIBLE else View.GONE
|
||||
mainContentContainer.visibility = if (allMembers.isEmpty()) View.GONE else View.VISIBLE
|
||||
emptyStateContainer.visibility = if (allMembers.isEmpty()) View.VISIBLE else View.GONE
|
||||
|
||||
invalidateOptionsMenu()
|
||||
}
|
||||
@@ -200,8 +211,9 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
|
||||
private fun onMemberClick(member: String) {
|
||||
val bottomSheet = ClosedGroupEditingOptionsBottomSheet()
|
||||
bottomSheet.onRemoveTapped = {
|
||||
val changedMembers = members - member
|
||||
updateMembers(changedMembers)
|
||||
if (zombies.contains(member)) zombies.remove(member)
|
||||
else members.remove(member)
|
||||
updateMembers()
|
||||
bottomSheet.dismiss()
|
||||
}
|
||||
bottomSheet.show(supportFragmentManager, "GroupEditingOptionsBottomSheet")
|
||||
@@ -209,7 +221,7 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
|
||||
|
||||
private fun onAddMembersClick() {
|
||||
val intent = Intent(this@EditClosedGroupActivity, SelectContactsActivity::class.java)
|
||||
intent.putExtra(SelectContactsActivity.usersToExcludeKey, members.toTypedArray())
|
||||
intent.putExtra(SelectContactsActivity.usersToExcludeKey, allMembers.toTypedArray())
|
||||
intent.putExtra(SelectContactsActivity.emptyStateTextKey, "No contacts to add")
|
||||
startActivityForResult(intent, addUsersRequestCode)
|
||||
}
|
||||
@@ -229,7 +241,7 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
|
||||
}
|
||||
|
||||
private fun commitChanges() {
|
||||
val hasMemberListChanges = (members != originalMembers)
|
||||
val hasMemberListChanges = (allMembers != originalMembers)
|
||||
|
||||
if (!hasNameChanged && !hasMemberListChanges) {
|
||||
return finish()
|
||||
@@ -237,15 +249,13 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
|
||||
|
||||
val name = if (hasNameChanged) this.name else originalName
|
||||
|
||||
val members = this.members.map {
|
||||
val members = this.allMembers.map {
|
||||
Recipient.from(this, Address.fromSerialized(it), false)
|
||||
}.toSet()
|
||||
val originalMembers = this.originalMembers.map {
|
||||
Recipient.from(this, Address.fromSerialized(it), false)
|
||||
}.toSet()
|
||||
|
||||
val admins = members.toSet() //TODO For now, consider all the users to be admins.
|
||||
|
||||
var isClosedGroup: Boolean
|
||||
var groupPublicKey: String?
|
||||
try {
|
||||
@@ -303,4 +313,6 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class GroupMembers(val members: List<String>, val zombieMembers: List<String>) { }
|
||||
}
|
@@ -4,12 +4,19 @@ import android.content.Context
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
import org.thoughtcrime.securesms.util.AsyncLoader
|
||||
|
||||
class EditClosedGroupLoader(context: Context, val groupID: String) : AsyncLoader<List<String>>(context) {
|
||||
class EditClosedGroupLoader(context: Context, val groupID: String) : AsyncLoader<EditClosedGroupActivity.GroupMembers>(context) {
|
||||
|
||||
override fun loadInBackground(): List<String> {
|
||||
val members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupID, true)
|
||||
return members.map {
|
||||
it.address.toString()
|
||||
}
|
||||
override fun loadInBackground(): EditClosedGroupActivity.GroupMembers {
|
||||
val groupDatabase = DatabaseFactory.getGroupDatabase(context)
|
||||
val members = groupDatabase.getGroupMembers(groupID, true)
|
||||
val zombieMembers = groupDatabase.getGroupZombieMembers(groupID)
|
||||
return EditClosedGroupActivity.GroupMembers(
|
||||
members.map {
|
||||
it.address.toString()
|
||||
},
|
||||
zombieMembers.map {
|
||||
it.address.toString()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
@@ -7,15 +7,17 @@ import org.session.libsession.messaging.threads.Address
|
||||
import org.thoughtcrime.securesms.loki.views.UserView
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||
import org.session.libsession.messaging.threads.recipients.Recipient
|
||||
import org.session.libsession.utilities.TextSecurePreferences
|
||||
|
||||
class EditClosedGroupMembersAdapter(
|
||||
private val context: Context,
|
||||
private val glide: GlideRequests,
|
||||
private val admin: Boolean,
|
||||
private val memberClickListener: ((String) -> Unit)? = null
|
||||
) : RecyclerView.Adapter<EditClosedGroupMembersAdapter.ViewHolder>() {
|
||||
|
||||
private val members = ArrayList<String>()
|
||||
private val lockedMembers = HashSet<String>()
|
||||
private val zombieMembers = ArrayList<String>()
|
||||
|
||||
fun setMembers(members: Collection<String>) {
|
||||
this.members.clear()
|
||||
@@ -23,9 +25,9 @@ class EditClosedGroupMembersAdapter(
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
fun setLockedMembers(members: Collection<String>) {
|
||||
this.lockedMembers.clear()
|
||||
this.lockedMembers.addAll(members)
|
||||
fun setZombieMembers(members: Collection<String>) {
|
||||
this.zombieMembers.clear()
|
||||
this.zombieMembers.addAll(members)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
@@ -39,15 +41,20 @@ class EditClosedGroupMembersAdapter(
|
||||
override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
|
||||
val member = members[position]
|
||||
|
||||
val lockedMember = lockedMembers.contains(member)
|
||||
val unlocked = admin && member != TextSecurePreferences.getLocalNumber(context)
|
||||
|
||||
viewHolder.view.bind(Recipient.from(
|
||||
context,
|
||||
Address.fromSerialized(member), false),
|
||||
glide,
|
||||
if (lockedMember) UserView.ActionIndicator.None else UserView.ActionIndicator.Menu)
|
||||
if (unlocked) UserView.ActionIndicator.Menu else UserView.ActionIndicator.None)
|
||||
|
||||
if (!lockedMember) {
|
||||
if (zombieMembers.contains(member))
|
||||
viewHolder.view.alpha = 0.5F
|
||||
else
|
||||
viewHolder.view.alpha = 1F
|
||||
|
||||
if (unlocked) {
|
||||
viewHolder.view.setOnClickListener { this.memberClickListener?.invoke(member) }
|
||||
}
|
||||
}
|
||||
|
@@ -27,7 +27,7 @@ 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.MnemonicCodec
|
||||
import org.session.libsignal.service.loki.utilities.hexEncodedPublicKey
|
||||
import org.session.libsignal.utilities.Hex
|
||||
import org.session.libsignal.utilities.logging.Log
|
||||
@@ -45,7 +45,7 @@ class LinkDeviceActivity : BaseActionBarActivity(), ScanQRCodeWrapperFragmentDel
|
||||
private var restoreJob: Job? = null
|
||||
|
||||
override fun onBackPressed() {
|
||||
if (restoreJob?.isActive == true) return // don't allow going back with pending job
|
||||
if (restoreJob?.isActive == true) return // Don't allow going back with a pending job
|
||||
super.onBackPressed()
|
||||
}
|
||||
|
||||
@@ -53,14 +53,12 @@ class LinkDeviceActivity : BaseActionBarActivity(), ScanQRCodeWrapperFragmentDel
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setUpActionBarSessionLogo()
|
||||
// Set the registration sync variables
|
||||
TextSecurePreferences.apply {
|
||||
setHasViewedSeed(this@LinkDeviceActivity, true)
|
||||
setConfigurationMessageSynced(this@LinkDeviceActivity, false)
|
||||
setRestorationTime(this@LinkDeviceActivity, System.currentTimeMillis())
|
||||
setLastProfileUpdateTime(this@LinkDeviceActivity, 0)
|
||||
}
|
||||
// registration variables are synced
|
||||
setContentView(R.layout.activity_link_device)
|
||||
viewPager.adapter = adapter
|
||||
tabLayout.setupWithViewPager(viewPager)
|
||||
|
@@ -15,7 +15,7 @@ import kotlinx.android.synthetic.main.activity_recovery_phrase_restore.*
|
||||
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.MnemonicCodec
|
||||
import org.session.libsignal.service.loki.utilities.hexEncodedPublicKey
|
||||
import org.session.libsignal.utilities.Hex
|
||||
import org.thoughtcrime.securesms.BaseActionBarActivity
|
||||
@@ -30,14 +30,12 @@ class RecoveryPhraseRestoreActivity : BaseActionBarActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setUpActionBarSessionLogo()
|
||||
// Set the registration sync variables
|
||||
TextSecurePreferences.apply {
|
||||
setHasViewedSeed(this@RecoveryPhraseRestoreActivity, true)
|
||||
setConfigurationMessageSynced(this@RecoveryPhraseRestoreActivity, false)
|
||||
setRestorationTime(this@RecoveryPhraseRestoreActivity, System.currentTimeMillis())
|
||||
setLastProfileUpdateTime(this@RecoveryPhraseRestoreActivity, System.currentTimeMillis())
|
||||
}
|
||||
// registration variables are synced
|
||||
setContentView(R.layout.activity_recovery_phrase_restore)
|
||||
mnemonicEditText.imeOptions = mnemonicEditText.imeOptions or 16777216 // Always use incognito keyboard
|
||||
restoreButton.setOnClickListener { restore() }
|
||||
|
@@ -39,14 +39,12 @@ class RegisterActivity : BaseActionBarActivity() {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_register)
|
||||
setUpActionBarSessionLogo()
|
||||
// Set the registration sync variables
|
||||
TextSecurePreferences.apply {
|
||||
setHasViewedSeed(this@RegisterActivity, false)
|
||||
setConfigurationMessageSynced(this@RegisterActivity, true)
|
||||
setRestorationTime(this@RegisterActivity, 0)
|
||||
setLastProfileUpdateTime(this@RegisterActivity, System.currentTimeMillis())
|
||||
}
|
||||
// registration variables are synced
|
||||
registerButton.setOnClickListener { register() }
|
||||
copyButton.setOnClickListener { copyPublicKey() }
|
||||
val termsExplanation = SpannableStringBuilder("By using this service, you agree to our Terms of Service and Privacy Policy")
|
||||
|
@@ -16,7 +16,7 @@ import org.session.libsession.utilities.IdentityKeyUtil
|
||||
import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities
|
||||
import org.thoughtcrime.securesms.loki.utilities.getColorWithID
|
||||
import org.session.libsession.utilities.TextSecurePreferences
|
||||
import org.session.libsignal.service.loki.crypto.MnemonicCodec
|
||||
import org.session.libsignal.service.loki.MnemonicCodec
|
||||
import org.session.libsignal.service.loki.utilities.hexEncodedPrivateKey
|
||||
|
||||
class SeedActivity : BaseActionBarActivity() {
|
||||
|
@@ -8,7 +8,7 @@ import org.session.libsignal.libsignal.ecc.DjbECPrivateKey
|
||||
import org.session.libsignal.libsignal.ecc.DjbECPublicKey
|
||||
import org.session.libsignal.libsignal.ecc.ECKeyPair
|
||||
import org.session.libsignal.service.loki.Snode
|
||||
import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol
|
||||
import org.session.libsignal.service.loki.LokiAPIDatabaseProtocol
|
||||
import org.session.libsignal.service.loki.utilities.PublicKeyValidation
|
||||
import org.session.libsignal.service.loki.utilities.removing05PrefixIfNeeded
|
||||
import org.session.libsignal.service.loki.utilities.toHexString
|
||||
|
@@ -2,12 +2,11 @@ package org.thoughtcrime.securesms.loki.database
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import org.session.libsignal.service.loki.database.LokiMessageDatabaseProtocol
|
||||
import org.session.libsignal.utilities.logging.Log
|
||||
import org.thoughtcrime.securesms.database.Database
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
||||
import org.thoughtcrime.securesms.loki.utilities.*
|
||||
import org.session.libsignal.service.loki.LokiMessageDatabaseProtocol
|
||||
|
||||
class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiMessageDatabaseProtocol {
|
||||
|
||||
|
@@ -11,7 +11,7 @@ import org.thoughtcrime.securesms.loki.utilities.get
|
||||
import org.thoughtcrime.securesms.loki.utilities.insertOrUpdate
|
||||
import org.session.libsession.messaging.threads.recipients.Recipient
|
||||
import org.session.libsession.utilities.TextSecurePreferences
|
||||
import org.session.libsignal.service.loki.database.LokiUserDatabaseProtocol
|
||||
import org.session.libsignal.service.loki.LokiUserDatabaseProtocol
|
||||
|
||||
class LokiUserDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiUserDatabaseProtocol {
|
||||
|
||||
|
@@ -15,7 +15,7 @@ import kotlinx.android.synthetic.main.dialog_seed.view.*
|
||||
import network.loki.messenger.R
|
||||
import org.session.libsession.utilities.IdentityKeyUtil
|
||||
import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities
|
||||
import org.session.libsignal.service.loki.crypto.MnemonicCodec
|
||||
import org.session.libsignal.service.loki.MnemonicCodec
|
||||
import org.session.libsignal.service.loki.utilities.hexEncodedPrivateKey
|
||||
|
||||
|
||||
|
@@ -1,6 +1,8 @@
|
||||
package org.thoughtcrime.securesms.loki.protocol
|
||||
|
||||
import android.content.Context
|
||||
import com.google.protobuf.ByteString
|
||||
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||
import org.session.libsession.messaging.messages.Destination
|
||||
import org.session.libsession.messaging.messages.control.ConfigurationMessage
|
||||
import org.session.libsession.messaging.sending_receiving.MessageSender
|
||||
|
@@ -3,8 +3,9 @@ package org.thoughtcrime.securesms.loki.utilities
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import org.session.libsignal.service.loki.Broadcaster
|
||||
|
||||
class Broadcaster(private val context: Context) : org.session.libsignal.service.loki.utilities.Broadcaster {
|
||||
class Broadcaster(private val context: Context) : Broadcaster {
|
||||
|
||||
override fun broadcast(event: String) {
|
||||
val intent = Intent(event)
|
||||
|
@@ -1,10 +1,6 @@
|
||||
package org.thoughtcrime.securesms.loki.utilities
|
||||
|
||||
import android.content.Context
|
||||
import org.session.libsignal.service.loki.crypto.MnemonicCodec
|
||||
import org.session.libsignal.service.loki.utilities.removing05PrefixIfNeeded
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
|
||||
object MnemonicUtilities {
|
||||
|
||||
|
@@ -7,10 +7,10 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.BaseAdapter
|
||||
import android.widget.ListView
|
||||
import org.session.libsession.utilities.mentions.Mention
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
import org.thoughtcrime.securesms.loki.utilities.toPx
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||
import org.session.libsignal.service.loki.Mention
|
||||
|
||||
class MentionCandidateSelectionView(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : ListView(context, attrs, defStyleAttr) {
|
||||
private var mentionCandidates = listOf<Mention>()
|
||||
|
@@ -9,7 +9,7 @@ import android.widget.LinearLayout
|
||||
import kotlinx.android.synthetic.main.view_mention_candidate.view.*
|
||||
import network.loki.messenger.R
|
||||
import org.session.libsession.messaging.open_groups.OpenGroupAPI
|
||||
import org.session.libsession.utilities.mentions.Mention
|
||||
import org.session.libsignal.service.loki.Mention
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||
|
||||
class MentionCandidateView(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : LinearLayout(context, attrs, defStyleAttr) {
|
||||
|
@@ -146,13 +146,13 @@ class ProfilePictureView : RelativeLayout {
|
||||
private fun setProfilePictureIfNeeded(imageView: ImageView, publicKey: String, displayName: String?, @DimenRes sizeResId: Int) {
|
||||
if (publicKey.isNotEmpty()) {
|
||||
val recipient = Recipient.from(context, Address.fromSerialized(publicKey), false)
|
||||
if (imagesCached.contains(recipient.profileAvatar.orEmpty())) return
|
||||
if (imagesCached.contains(publicKey)) return
|
||||
val signalProfilePicture = recipient.contactPhoto
|
||||
if (signalProfilePicture != null && (signalProfilePicture as? ProfileContactPhoto)?.avatarObject != "0"
|
||||
&& (signalProfilePicture as? ProfileContactPhoto)?.avatarObject != "") {
|
||||
glide.clear(imageView)
|
||||
glide.load(signalProfilePicture).diskCacheStrategy(DiskCacheStrategy.AUTOMATIC).circleCrop().into(imageView)
|
||||
imagesCached.add(recipient.profileAvatar.orEmpty())
|
||||
imagesCached.add(publicKey)
|
||||
} else {
|
||||
val sizeInPX = resources.getDimensionPixelSize(sizeResId)
|
||||
glide.clear(imageView)
|
||||
@@ -162,7 +162,7 @@ class ProfilePictureView : RelativeLayout {
|
||||
publicKey,
|
||||
displayName
|
||||
)).diskCacheStrategy(DiskCacheStrategy.ALL).circleCrop().into(imageView)
|
||||
imagesCached.add(recipient.profileAvatar.orEmpty())
|
||||
imagesCached.add(publicKey)
|
||||
}
|
||||
} else {
|
||||
imageView.setImageDrawable(null)
|
||||
|
@@ -102,7 +102,8 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment
|
||||
if (duration == 0) {
|
||||
TextSecurePreferences.setScreenLockTimeout(getContext(), 0);
|
||||
} else {
|
||||
long timeoutSeconds = Math.max(TimeUnit.MILLISECONDS.toSeconds(duration), 60);
|
||||
long timeoutSeconds = TimeUnit.MILLISECONDS.toSeconds(duration);
|
||||
// long timeoutSeconds = Math.max(TimeUnit.MILLISECONDS.toSeconds(duration), 60);
|
||||
TextSecurePreferences.setScreenLockTimeout(getContext(), timeoutSeconds);
|
||||
}
|
||||
|
||||
|
@@ -119,7 +119,6 @@ public class KeyCachingService extends Service {
|
||||
KeyCachingService.masterSecret = masterSecret;
|
||||
|
||||
foregroundService();
|
||||
startTimeoutIfAppropriate(this);
|
||||
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
@@ -210,7 +209,7 @@ public class KeyCachingService extends Service {
|
||||
boolean passLockActive = timeoutEnabled && !TextSecurePreferences.isPasswordDisabled(context);
|
||||
|
||||
long screenTimeout = TextSecurePreferences.getScreenLockTimeout(context);
|
||||
boolean screenLockActive = screenTimeout >= 60 && TextSecurePreferences.isScreenLockEnabled(context);
|
||||
boolean screenLockActive = screenTimeout >= 0 && TextSecurePreferences.isScreenLockEnabled(context);
|
||||
|
||||
if (!appVisible && secretSet && (passLockActive || screenLockActive)) {
|
||||
long passphraseTimeoutMinutes = TextSecurePreferences.getPassphraseTimeoutInterval(context);
|
||||
|
@@ -98,13 +98,6 @@
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/home_activity_gradient" />
|
||||
|
||||
<org.thoughtcrime.securesms.loki.views.NewConversationButtonSetView
|
||||
android:id="@+id/newConversationButtonSet"
|
||||
android:layout_width="276dp"
|
||||
android:layout_height="236dp"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_alignParentBottom="true" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/emptyStateContainer"
|
||||
android:layout_width="wrap_content"
|
||||
@@ -131,6 +124,13 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<org.thoughtcrime.securesms.loki.views.NewConversationButtonSetView
|
||||
android:id="@+id/newConversationButtonSet"
|
||||
android:layout_width="276dp"
|
||||
android:layout_height="236dp"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_alignParentBottom="true" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</LinearLayout>
|
Reference in New Issue
Block a user