mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-30 13:35:18 +00:00
Merge branch 'dev' into trusted_attachment_download
# Conflicts: # app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt # app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt
This commit is contained in:
commit
842cfc25a1
@ -143,8 +143,8 @@ dependencies {
|
|||||||
testImplementation 'org.robolectric:shadows-multidex:4.2'
|
testImplementation 'org.robolectric:shadows-multidex:4.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
def canonicalVersionCode = 193
|
def canonicalVersionCode = 198
|
||||||
def canonicalVersionName = "1.11.2"
|
def canonicalVersionName = "1.11.3"
|
||||||
|
|
||||||
def postFixSize = 10
|
def postFixSize = 10
|
||||||
def abiPostFix = ['armeabi-v7a' : 1,
|
def abiPostFix = ['armeabi-v7a' : 1,
|
||||||
|
@ -55,6 +55,12 @@
|
|||||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" tools:node="remove" />
|
<uses-permission android:name="android.permission.READ_PHONE_STATE" tools:node="remove" />
|
||||||
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" tools:node="remove"/>
|
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" tools:node="remove"/>
|
||||||
|
|
||||||
|
<queries>
|
||||||
|
<intent>
|
||||||
|
<action android:name="android.media.action.IMAGE_CAPTURE" />
|
||||||
|
</intent>
|
||||||
|
</queries>
|
||||||
|
|
||||||
<!-- The allowBackup="false" below is important to guard against potential malicious backups -->
|
<!-- The allowBackup="false" below is important to guard against potential malicious backups -->
|
||||||
|
|
||||||
<application
|
<application
|
||||||
@ -87,80 +93,80 @@
|
|||||||
android:value="false" />
|
android:value="false" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.loki.activities.LandingActivity"
|
android:name="org.thoughtcrime.securesms.onboarding.LandingActivity"
|
||||||
android:screenOrientation="portrait"
|
android:screenOrientation="portrait"
|
||||||
android:theme="@style/Theme.Session.DayNight.FlatActionBar" />
|
android:theme="@style/Theme.Session.DayNight.FlatActionBar" />
|
||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.loki.activities.RegisterActivity"
|
android:name="org.thoughtcrime.securesms.onboarding.RegisterActivity"
|
||||||
android:screenOrientation="portrait"
|
android:screenOrientation="portrait"
|
||||||
android:theme="@style/Theme.Session.DayNight.FlatActionBar" />
|
android:theme="@style/Theme.Session.DayNight.FlatActionBar" />
|
||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.loki.activities.RecoveryPhraseRestoreActivity"
|
android:name="org.thoughtcrime.securesms.onboarding.RecoveryPhraseRestoreActivity"
|
||||||
android:screenOrientation="portrait"
|
android:screenOrientation="portrait"
|
||||||
android:windowSoftInputMode="adjustResize"
|
android:windowSoftInputMode="adjustResize"
|
||||||
android:theme="@style/Theme.Session.DayNight.FlatActionBar" />
|
android:theme="@style/Theme.Session.DayNight.FlatActionBar" />
|
||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.loki.activities.LinkDeviceActivity"
|
android:name="org.thoughtcrime.securesms.onboarding.LinkDeviceActivity"
|
||||||
android:screenOrientation="portrait"
|
android:screenOrientation="portrait"
|
||||||
android:windowSoftInputMode="adjustResize"
|
android:windowSoftInputMode="adjustResize"
|
||||||
android:theme="@style/Theme.Session.DayNight.FlatActionBar" />
|
android:theme="@style/Theme.Session.DayNight.FlatActionBar" />
|
||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.loki.activities.DisplayNameActivity"
|
android:name="org.thoughtcrime.securesms.onboarding.DisplayNameActivity"
|
||||||
android:screenOrientation="portrait"
|
android:screenOrientation="portrait"
|
||||||
android:windowSoftInputMode="adjustResize"
|
android:windowSoftInputMode="adjustResize"
|
||||||
android:theme="@style/Theme.Session.DayNight.FlatActionBar" />
|
android:theme="@style/Theme.Session.DayNight.FlatActionBar" />
|
||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.loki.activities.PNModeActivity"
|
android:name="org.thoughtcrime.securesms.onboarding.PNModeActivity"
|
||||||
android:screenOrientation="portrait"
|
android:screenOrientation="portrait"
|
||||||
android:theme="@style/Theme.Session.DayNight.FlatActionBar" />
|
android:theme="@style/Theme.Session.DayNight.FlatActionBar" />
|
||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.loki.activities.HomeActivity"
|
android:name="org.thoughtcrime.securesms.home.HomeActivity"
|
||||||
android:screenOrientation="portrait"
|
android:screenOrientation="portrait"
|
||||||
android:launchMode="singleTask"
|
android:launchMode="singleTask"
|
||||||
android:theme="@style/Theme.Session.DayNight.NoActionBar" />
|
android:theme="@style/Theme.Session.DayNight.NoActionBar" />
|
||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.loki.activities.SettingsActivity"
|
android:name="org.thoughtcrime.securesms.preferences.SettingsActivity"
|
||||||
android:screenOrientation="portrait"
|
android:screenOrientation="portrait"
|
||||||
android:label="@string/activity_settings_title" />
|
android:label="@string/activity_settings_title" />
|
||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.loki.activities.PathActivity"
|
android:name="org.thoughtcrime.securesms.home.PathActivity"
|
||||||
android:screenOrientation="portrait" />
|
android:screenOrientation="portrait" />
|
||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.loki.activities.QRCodeActivity"
|
android:name="org.thoughtcrime.securesms.preferences.QRCodeActivity"
|
||||||
android:screenOrientation="portrait"
|
android:screenOrientation="portrait"
|
||||||
android:theme="@style/Theme.Session.DayNight.FlatActionBar" />
|
android:theme="@style/Theme.Session.DayNight.FlatActionBar" />
|
||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.loki.activities.CreatePrivateChatActivity"
|
android:name="org.thoughtcrime.securesms.dms.CreatePrivateChatActivity"
|
||||||
android:screenOrientation="portrait"
|
android:screenOrientation="portrait"
|
||||||
android:windowSoftInputMode="adjustResize"
|
android:windowSoftInputMode="adjustResize"
|
||||||
android:theme="@style/Theme.Session.DayNight.FlatActionBar" />
|
android:theme="@style/Theme.Session.DayNight.FlatActionBar" />
|
||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.loki.activities.CreateClosedGroupActivity"
|
android:name="org.thoughtcrime.securesms.groups.CreateClosedGroupActivity"
|
||||||
android:screenOrientation="portrait" />
|
android:screenOrientation="portrait" />
|
||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.loki.activities.EditClosedGroupActivity"
|
android:name="org.thoughtcrime.securesms.groups.EditClosedGroupActivity"
|
||||||
android:label="@string/activity_edit_closed_group_title"
|
android:label="@string/activity_edit_closed_group_title"
|
||||||
android:screenOrientation="portrait" />
|
android:screenOrientation="portrait" />
|
||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.loki.activities.JoinPublicChatActivity"
|
android:name="org.thoughtcrime.securesms.groups.JoinPublicChatActivity"
|
||||||
android:screenOrientation="portrait"
|
android:screenOrientation="portrait"
|
||||||
android:windowSoftInputMode="adjustResize"
|
android:windowSoftInputMode="adjustResize"
|
||||||
android:theme="@style/Theme.Session.DayNight.FlatActionBar" />
|
android:theme="@style/Theme.Session.DayNight.FlatActionBar" />
|
||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.loki.activities.SeedActivity"
|
android:name="org.thoughtcrime.securesms.onboarding.SeedActivity"
|
||||||
android:screenOrientation="portrait" />
|
android:screenOrientation="portrait" />
|
||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.loki.activities.SelectContactsActivity"
|
android:name="org.thoughtcrime.securesms.contacts.SelectContactsActivity"
|
||||||
android:screenOrientation="portrait" />
|
android:screenOrientation="portrait" />
|
||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.loki.activities.PrivacySettingsActivity"
|
android:name="org.thoughtcrime.securesms.preferences.PrivacySettingsActivity"
|
||||||
android:label="@string/activity_privacy_settings_title"
|
android:label="@string/activity_privacy_settings_title"
|
||||||
android:screenOrientation="portrait" />
|
android:screenOrientation="portrait" />
|
||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.loki.activities.NotificationSettingsActivity"
|
android:name="org.thoughtcrime.securesms.preferences.NotificationSettingsActivity"
|
||||||
android:screenOrientation="portrait" />
|
android:screenOrientation="portrait" />
|
||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.loki.activities.ChatSettingsActivity"
|
android:name="org.thoughtcrime.securesms.preferences.ChatSettingsActivity"
|
||||||
android:screenOrientation="portrait" />
|
android:screenOrientation="portrait" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
@ -192,7 +198,7 @@
|
|||||||
<activity-alias
|
<activity-alias
|
||||||
android:name=".RoutingActivity"
|
android:name=".RoutingActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:targetActivity="org.thoughtcrime.securesms.loki.activities.HomeActivity">
|
android:targetActivity="org.thoughtcrime.securesms.home.HomeActivity">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
@ -209,14 +215,14 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2"
|
android:name="org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2"
|
||||||
android:screenOrientation="portrait"
|
android:screenOrientation="portrait"
|
||||||
android:parentActivityName="org.thoughtcrime.securesms.loki.activities.HomeActivity"
|
android:parentActivityName="org.thoughtcrime.securesms.home.HomeActivity"
|
||||||
android:theme="@style/Theme.Session.DayNight.FlatActionBar">
|
android:theme="@style/Theme.Session.DayNight.FlatActionBar">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="org.thoughtcrime.securesms.loki.activities.HomeActivity" />
|
android:value="org.thoughtcrime.securesms.home.HomeActivity" />
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.loki.activities.OpenGroupGuidelinesActivity"
|
android:name="org.thoughtcrime.securesms.groups.OpenGroupGuidelinesActivity"
|
||||||
android:screenOrientation="portrait"
|
android:screenOrientation="portrait"
|
||||||
android:theme="@style/Theme.TextSecure.DayNight" />
|
android:theme="@style/Theme.TextSecure.DayNight" />
|
||||||
<activity
|
<activity
|
||||||
@ -285,7 +291,7 @@
|
|||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:theme="@style/Theme.Session.DayNight.NoActionBar" />
|
android:theme="@style/Theme.Session.DayNight.NoActionBar" />
|
||||||
<service
|
<service
|
||||||
android:name="org.thoughtcrime.securesms.loki.api.PushNotificationService"
|
android:name="org.thoughtcrime.securesms.notifications.PushNotificationService"
|
||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
android:exported="false">
|
android:exported="false">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
@ -405,7 +411,7 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
<receiver
|
<receiver
|
||||||
android:name="org.thoughtcrime.securesms.loki.api.BackgroundPollWorker$BootBroadcastReceiver"
|
android:name="org.thoughtcrime.securesms.notifications.BackgroundPollWorker$BootBroadcastReceiver"
|
||||||
android:enabled="true">
|
android:enabled="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||||
|
@ -27,10 +27,10 @@ import androidx.lifecycle.DefaultLifecycleObserver;
|
|||||||
import androidx.lifecycle.LifecycleOwner;
|
import androidx.lifecycle.LifecycleOwner;
|
||||||
import androidx.lifecycle.ProcessLifecycleOwner;
|
import androidx.lifecycle.ProcessLifecycleOwner;
|
||||||
import androidx.multidex.MultiDexApplication;
|
import androidx.multidex.MultiDexApplication;
|
||||||
|
|
||||||
import org.conscrypt.Conscrypt;
|
import org.conscrypt.Conscrypt;
|
||||||
import org.session.libsession.avatars.AvatarHelper;
|
import org.session.libsession.avatars.AvatarHelper;
|
||||||
import org.session.libsession.messaging.MessagingModuleConfiguration;
|
import org.session.libsession.messaging.MessagingModuleConfiguration;
|
||||||
import org.session.libsession.messaging.contacts.Contact;
|
|
||||||
import org.session.libsession.messaging.sending_receiving.notifications.MessageNotifier;
|
import org.session.libsession.messaging.sending_receiving.notifications.MessageNotifier;
|
||||||
import org.session.libsession.messaging.sending_receiving.pollers.ClosedGroupPollerV2;
|
import org.session.libsession.messaging.sending_receiving.pollers.ClosedGroupPollerV2;
|
||||||
import org.session.libsession.messaging.sending_receiving.pollers.Poller;
|
import org.session.libsession.messaging.sending_receiving.pollers.Poller;
|
||||||
@ -42,11 +42,11 @@ import org.session.libsession.utilities.TextSecurePreferences;
|
|||||||
import org.session.libsession.utilities.Util;
|
import org.session.libsession.utilities.Util;
|
||||||
import org.session.libsession.utilities.dynamiclanguage.DynamicLanguageContextWrapper;
|
import org.session.libsession.utilities.dynamiclanguage.DynamicLanguageContextWrapper;
|
||||||
import org.session.libsession.utilities.dynamiclanguage.LocaleParser;
|
import org.session.libsession.utilities.dynamiclanguage.LocaleParser;
|
||||||
import org.session.libsession.utilities.recipients.Recipient;
|
|
||||||
import org.session.libsignal.utilities.Log;
|
import org.session.libsignal.utilities.Log;
|
||||||
import org.session.libsignal.utilities.ThreadUtils;
|
import org.session.libsignal.utilities.ThreadUtils;
|
||||||
import org.signal.aesgcmprovider.AesGcmProvider;
|
import org.signal.aesgcmprovider.AesGcmProvider;
|
||||||
import org.thoughtcrime.securesms.components.TypingStatusSender;
|
import org.thoughtcrime.securesms.components.TypingStatusSender;
|
||||||
|
import org.thoughtcrime.securesms.crypto.KeyPairUtilities;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||||
import org.thoughtcrime.securesms.dependencies.SignalCommunicationModule;
|
import org.thoughtcrime.securesms.dependencies.SignalCommunicationModule;
|
||||||
@ -58,17 +58,14 @@ import org.thoughtcrime.securesms.jobs.JobManagerFactories;
|
|||||||
import org.thoughtcrime.securesms.logging.AndroidLogger;
|
import org.thoughtcrime.securesms.logging.AndroidLogger;
|
||||||
import org.thoughtcrime.securesms.logging.PersistentLogger;
|
import org.thoughtcrime.securesms.logging.PersistentLogger;
|
||||||
import org.thoughtcrime.securesms.logging.UncaughtExceptionLogger;
|
import org.thoughtcrime.securesms.logging.UncaughtExceptionLogger;
|
||||||
import org.thoughtcrime.securesms.loki.activities.HomeActivity;
|
import org.thoughtcrime.securesms.home.HomeActivity;
|
||||||
import org.thoughtcrime.securesms.loki.api.BackgroundPollWorker;
|
import org.thoughtcrime.securesms.notifications.BackgroundPollWorker;
|
||||||
import org.thoughtcrime.securesms.loki.api.LokiPushNotificationManager;
|
import org.thoughtcrime.securesms.notifications.LokiPushNotificationManager;
|
||||||
import org.thoughtcrime.securesms.loki.api.OpenGroupManager;
|
import org.thoughtcrime.securesms.groups.OpenGroupManager;
|
||||||
import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase;
|
import org.thoughtcrime.securesms.database.LokiAPIDatabase;
|
||||||
import org.thoughtcrime.securesms.loki.database.LokiUserDatabase;
|
import org.thoughtcrime.securesms.util.Broadcaster;
|
||||||
import org.thoughtcrime.securesms.loki.database.SessionContactDatabase;
|
import org.thoughtcrime.securesms.notifications.FcmUtils;
|
||||||
import org.thoughtcrime.securesms.loki.utilities.Broadcaster;
|
import org.thoughtcrime.securesms.util.UiModeUtilities;
|
||||||
import org.thoughtcrime.securesms.loki.utilities.ContactUtilities;
|
|
||||||
import org.thoughtcrime.securesms.loki.utilities.FcmUtils;
|
|
||||||
import org.thoughtcrime.securesms.loki.utilities.UiModeUtilities;
|
|
||||||
import org.thoughtcrime.securesms.notifications.DefaultMessageNotifier;
|
import org.thoughtcrime.securesms.notifications.DefaultMessageNotifier;
|
||||||
import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
||||||
import org.thoughtcrime.securesms.notifications.OptimizedMessageNotifier;
|
import org.thoughtcrime.securesms.notifications.OptimizedMessageNotifier;
|
||||||
@ -84,12 +81,14 @@ import org.webrtc.PeerConnectionFactory;
|
|||||||
import org.webrtc.PeerConnectionFactory.InitializationOptions;
|
import org.webrtc.PeerConnectionFactory.InitializationOptions;
|
||||||
import org.webrtc.voiceengine.WebRtcAudioManager;
|
import org.webrtc.voiceengine.WebRtcAudioManager;
|
||||||
import org.webrtc.voiceengine.WebRtcAudioUtils;
|
import org.webrtc.voiceengine.WebRtcAudioUtils;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.security.Security;
|
import java.security.Security;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import dagger.ObjectGraph;
|
import dagger.ObjectGraph;
|
||||||
import kotlin.Unit;
|
import kotlin.Unit;
|
||||||
import kotlinx.coroutines.Job;
|
import kotlinx.coroutines.Job;
|
||||||
@ -154,8 +153,10 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
|||||||
conversationListNotificationHandler = new Handler(Looper.getMainLooper());
|
conversationListNotificationHandler = new Handler(Looper.getMainLooper());
|
||||||
LokiAPIDatabase apiDB = DatabaseFactory.getLokiAPIDatabase(this);
|
LokiAPIDatabase apiDB = DatabaseFactory.getLokiAPIDatabase(this);
|
||||||
MessagingModuleConfiguration.Companion.configure(this,
|
MessagingModuleConfiguration.Companion.configure(this,
|
||||||
DatabaseFactory.getStorage(this),
|
DatabaseFactory.getStorage(this),
|
||||||
DatabaseFactory.getAttachmentProvider(this));
|
DatabaseFactory.getAttachmentProvider(this),
|
||||||
|
()-> KeyPairUtilities.INSTANCE.getUserED25519KeyPair(this)
|
||||||
|
);
|
||||||
SnodeModule.Companion.configure(apiDB, broadcaster);
|
SnodeModule.Companion.configure(apiDB, broadcaster);
|
||||||
String userPublicKey = TextSecurePreferences.getLocalNumber(this);
|
String userPublicKey = TextSecurePreferences.getLocalNumber(this);
|
||||||
if (userPublicKey != null) {
|
if (userPublicKey != null) {
|
||||||
@ -181,27 +182,6 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
|||||||
Log.i(TAG, "App is now visible.");
|
Log.i(TAG, "App is now visible.");
|
||||||
KeyCachingService.onAppForegrounded(this);
|
KeyCachingService.onAppForegrounded(this);
|
||||||
|
|
||||||
boolean hasPerformedContactMigration = TextSecurePreferences.INSTANCE.hasPerformedContactMigration(this);
|
|
||||||
if (!hasPerformedContactMigration) {
|
|
||||||
TextSecurePreferences.INSTANCE.setPerformedContactMigration(this);
|
|
||||||
Set<Recipient> allContacts = ContactUtilities.getAllContacts(this);
|
|
||||||
SessionContactDatabase contactDB = DatabaseFactory.getSessionContactDatabase(this);
|
|
||||||
LokiUserDatabase userDB = DatabaseFactory.getLokiUserDatabase(this);
|
|
||||||
for (Recipient recipient : allContacts) {
|
|
||||||
if (recipient.isGroupRecipient()) { continue; }
|
|
||||||
String sessionID = recipient.getAddress().serialize();
|
|
||||||
Contact contact = contactDB.getContactWithSessionID(sessionID);
|
|
||||||
if (contact == null) {
|
|
||||||
contact = new Contact(sessionID);
|
|
||||||
String name = userDB.getDisplayName(sessionID);
|
|
||||||
contact.setName(name);
|
|
||||||
contact.setProfilePictureURL(recipient.getProfileAvatar());
|
|
||||||
contact.setProfilePictureEncryptionKey(recipient.getProfileKey());
|
|
||||||
contact.setTrusted(true);
|
|
||||||
}
|
|
||||||
contactDB.setContact(contact);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (poller != null) {
|
if (poller != null) {
|
||||||
poller.setCaughtUp(false);
|
poller.setCaughtUp(false);
|
||||||
}
|
}
|
||||||
@ -487,7 +467,6 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
|||||||
boolean isUsingFCM = TextSecurePreferences.isUsingFCM(this);
|
boolean isUsingFCM = TextSecurePreferences.isUsingFCM(this);
|
||||||
TextSecurePreferences.clearAll(this);
|
TextSecurePreferences.clearAll(this);
|
||||||
if (isMigratingToV2KeyPair) {
|
if (isMigratingToV2KeyPair) {
|
||||||
TextSecurePreferences.setIsMigratingKeyPair(this, true);
|
|
||||||
TextSecurePreferences.setIsUsingFCM(this, isUsingFCM);
|
TextSecurePreferences.setIsUsingFCM(this, isUsingFCM);
|
||||||
TextSecurePreferences.setProfileName(this, displayName);
|
TextSecurePreferences.setProfileName(this, displayName);
|
||||||
}
|
}
|
||||||
|
@ -189,7 +189,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
|||||||
CharSequence relativeTimeSpan;
|
CharSequence relativeTimeSpan;
|
||||||
|
|
||||||
if (mediaItem.date > 0) {
|
if (mediaItem.date > 0) {
|
||||||
relativeTimeSpan = DateUtils.getExtendedRelativeTimeSpanString(this, Locale.getDefault(), mediaItem.date);
|
relativeTimeSpan = DateUtils.getDisplayFormattedTimeSpanString(this, Locale.getDefault(), mediaItem.date);
|
||||||
} else {
|
} else {
|
||||||
relativeTimeSpan = getString(R.string.MediaPreviewActivity_draft);
|
relativeTimeSpan = getString(R.string.MediaPreviewActivity_draft);
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import androidx.annotation.NonNull;
|
|||||||
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||||
import org.thoughtcrime.securesms.loki.views.UserView;
|
import org.thoughtcrime.securesms.contacts.UserView;
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||||
import org.session.libsession.utilities.recipients.Recipient;
|
import org.session.libsession.utilities.recipients.Recipient;
|
||||||
import org.session.libsession.utilities.Conversions;
|
import org.session.libsession.utilities.Conversions;
|
||||||
|
@ -39,7 +39,7 @@ import android.widget.ImageView;
|
|||||||
import androidx.core.hardware.fingerprint.FingerprintManagerCompat;
|
import androidx.core.hardware.fingerprint.FingerprintManagerCompat;
|
||||||
import androidx.core.os.CancellationSignal;
|
import androidx.core.os.CancellationSignal;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.animation.AnimationCompleteListener;
|
import org.thoughtcrime.securesms.util.AnimationCompleteListener;
|
||||||
import org.thoughtcrime.securesms.components.AnimatingToggle;
|
import org.thoughtcrime.securesms.components.AnimatingToggle;
|
||||||
import org.session.libsignal.utilities.Log;
|
import org.session.libsignal.utilities.Log;
|
||||||
import org.thoughtcrime.securesms.service.KeyCachingService;
|
import org.thoughtcrime.securesms.service.KeyCachingService;
|
||||||
|
@ -12,8 +12,8 @@ import androidx.annotation.Nullable;
|
|||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
import org.session.libsignal.utilities.Log;
|
import org.session.libsignal.utilities.Log;
|
||||||
import org.thoughtcrime.securesms.loki.activities.HomeActivity;
|
import org.thoughtcrime.securesms.home.HomeActivity;
|
||||||
import org.thoughtcrime.securesms.loki.activities.LandingActivity;
|
import org.thoughtcrime.securesms.onboarding.LandingActivity;
|
||||||
import org.thoughtcrime.securesms.service.KeyCachingService;
|
import org.thoughtcrime.securesms.service.KeyCachingService;
|
||||||
import org.session.libsession.utilities.TextSecurePreferences;
|
import org.session.libsession.utilities.TextSecurePreferences;
|
||||||
|
|
||||||
|
@ -42,9 +42,8 @@ import org.session.libsession.utilities.Address;
|
|||||||
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2;
|
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.session.libsignal.utilities.Log;
|
import org.session.libsignal.utilities.Log;
|
||||||
import org.thoughtcrime.securesms.loki.fragments.ContactSelectionListFragment;
|
import org.thoughtcrime.securesms.contacts.ContactSelectionListFragment;
|
||||||
import org.thoughtcrime.securesms.loki.fragments.ContactSelectionListLoader.DisplayMode;
|
import org.thoughtcrime.securesms.contacts.ContactSelectionListLoader.DisplayMode;
|
||||||
import org.thoughtcrime.securesms.mediasend.Media;
|
|
||||||
import org.thoughtcrime.securesms.mms.PartAuthority;
|
import org.thoughtcrime.securesms.mms.PartAuthority;
|
||||||
import org.thoughtcrime.securesms.providers.BlobProvider;
|
import org.thoughtcrime.securesms.providers.BlobProvider;
|
||||||
import org.session.libsession.utilities.recipients.Recipient;
|
import org.session.libsession.utilities.recipients.Recipient;
|
||||||
@ -54,7 +53,6 @@ import org.session.libsession.utilities.ViewUtil;
|
|||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
import network.loki.messenger.R;
|
import network.loki.messenger.R;
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ import androidx.appcompat.app.AppCompatActivity;
|
|||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.session.libsession.utilities.Address;
|
import org.session.libsession.utilities.Address;
|
||||||
import org.thoughtcrime.securesms.loki.activities.HomeActivity;
|
import org.thoughtcrime.securesms.home.HomeActivity;
|
||||||
import org.session.libsession.utilities.recipients.Recipient;
|
import org.session.libsession.utilities.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.util.CommunicationActions;
|
import org.thoughtcrime.securesms.util.CommunicationActions;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package org.thoughtcrime.securesms.loki.activities
|
package org.thoughtcrime.securesms.backup;
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
@ -9,17 +9,13 @@ import android.os.Bundle
|
|||||||
import android.provider.OpenableColumns
|
import android.provider.OpenableColumns
|
||||||
import android.text.Spannable
|
import android.text.Spannable
|
||||||
import android.text.SpannableStringBuilder
|
import android.text.SpannableStringBuilder
|
||||||
import android.text.method.LinkMovementMethod
|
|
||||||
import android.text.style.ClickableSpan
|
import android.text.style.ClickableSpan
|
||||||
import android.text.style.StyleSpan
|
import android.text.style.StyleSpan
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.inputmethod.InputMethodManager
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.result.ActivityResult
|
import androidx.activity.result.ActivityResult
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
import androidx.core.widget.addTextChangedListener
|
|
||||||
import androidx.databinding.DataBindingUtil
|
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
@ -28,18 +24,17 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.thoughtcrime.securesms.ApplicationContext
|
|
||||||
import org.thoughtcrime.securesms.BaseActionBarActivity
|
import org.thoughtcrime.securesms.BaseActionBarActivity
|
||||||
import org.thoughtcrime.securesms.backup.FullBackupImporter
|
|
||||||
import org.thoughtcrime.securesms.backup.FullBackupImporter.DatabaseDowngradeException
|
import org.thoughtcrime.securesms.backup.FullBackupImporter.DatabaseDowngradeException
|
||||||
import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider
|
import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
import org.session.libsignal.utilities.Log
|
import org.session.libsignal.utilities.Log
|
||||||
import org.thoughtcrime.securesms.loki.utilities.setUpActionBarSessionLogo
|
import org.thoughtcrime.securesms.util.setUpActionBarSessionLogo
|
||||||
import org.thoughtcrime.securesms.loki.utilities.show
|
import org.thoughtcrime.securesms.util.show
|
||||||
import org.thoughtcrime.securesms.notifications.NotificationChannels
|
import org.thoughtcrime.securesms.notifications.NotificationChannels
|
||||||
import org.thoughtcrime.securesms.util.BackupUtil
|
import org.thoughtcrime.securesms.util.BackupUtil
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
|
import org.thoughtcrime.securesms.home.HomeActivity
|
||||||
|
|
||||||
class BackupRestoreActivity : BaseActionBarActivity() {
|
class BackupRestoreActivity : BaseActionBarActivity() {
|
||||||
|
|
||||||
@ -188,7 +183,6 @@ class BackupRestoreViewModel(application: Application): AndroidViewModel(applica
|
|||||||
TextSecurePreferences.setRestorationTime(context, System.currentTimeMillis())
|
TextSecurePreferences.setRestorationTime(context, System.currentTimeMillis())
|
||||||
TextSecurePreferences.setHasViewedSeed(context, true)
|
TextSecurePreferences.setHasViewedSeed(context, true)
|
||||||
TextSecurePreferences.setHasSeenWelcomeScreen(context, true)
|
TextSecurePreferences.setHasSeenWelcomeScreen(context, true)
|
||||||
val application = ApplicationContext.getInstance(context)
|
|
||||||
|
|
||||||
BackupRestoreResult.SUCCESS
|
BackupRestoreResult.SUCCESS
|
||||||
} catch (e: DatabaseDowngradeException) {
|
} catch (e: DatabaseDowngradeException) {
|
@ -21,8 +21,7 @@ import org.thoughtcrime.securesms.crypto.ClassicDecryptingPartInputStream
|
|||||||
import org.thoughtcrime.securesms.crypto.ModernDecryptingPartInputStream
|
import org.thoughtcrime.securesms.crypto.ModernDecryptingPartInputStream
|
||||||
import org.thoughtcrime.securesms.database.*
|
import org.thoughtcrime.securesms.database.*
|
||||||
import org.session.libsignal.utilities.Log
|
import org.session.libsignal.utilities.Log
|
||||||
import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase
|
import org.thoughtcrime.securesms.database.LokiBackupFilesDatabase
|
||||||
import org.thoughtcrime.securesms.loki.database.LokiBackupFilesDatabase
|
|
||||||
import org.thoughtcrime.securesms.util.BackupUtil
|
import org.thoughtcrime.securesms.util.BackupUtil
|
||||||
import org.session.libsession.utilities.Util
|
import org.session.libsession.utilities.Util
|
||||||
import org.session.libsignal.crypto.kdf.HKDFv3
|
import org.session.libsignal.crypto.kdf.HKDFv3
|
||||||
|
@ -19,7 +19,7 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.loki.utilities.AvatarPlaceholderGenerator;
|
import org.thoughtcrime.securesms.util.AvatarPlaceholderGenerator;
|
||||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package org.thoughtcrime.securesms.loki.views
|
package org.thoughtcrime.securesms.components
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Canvas
|
import android.graphics.Canvas
|
||||||
@ -9,7 +9,7 @@ import android.view.LayoutInflater
|
|||||||
import android.widget.RelativeLayout
|
import android.widget.RelativeLayout
|
||||||
import kotlinx.android.synthetic.main.view_separator.view.*
|
import kotlinx.android.synthetic.main.view_separator.view.*
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.thoughtcrime.securesms.loki.utilities.toPx
|
import org.thoughtcrime.securesms.util.toPx
|
||||||
import org.session.libsession.utilities.ThemeUtil
|
import org.session.libsession.utilities.ThemeUtil
|
||||||
|
|
||||||
class LabeledSeparatorView : RelativeLayout {
|
class LabeledSeparatorView : RelativeLayout {
|
@ -1,4 +1,4 @@
|
|||||||
package org.thoughtcrime.securesms.loki.views
|
package org.thoughtcrime.securesms.components
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
@ -17,7 +17,7 @@ import org.session.libsession.utilities.Address
|
|||||||
import org.session.libsession.utilities.recipients.Recipient
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
import org.thoughtcrime.securesms.loki.utilities.AvatarPlaceholderGenerator
|
import org.thoughtcrime.securesms.util.AvatarPlaceholderGenerator
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||||
|
|
||||||
class ProfilePictureView : RelativeLayout {
|
class ProfilePictureView : RelativeLayout {
|
@ -22,8 +22,8 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
|||||||
import org.session.libsession.messaging.contacts.Contact;
|
import org.session.libsession.messaging.contacts.Contact;
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.Attachment;
|
import org.session.libsession.messaging.sending_receiving.attachments.Attachment;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.loki.database.SessionContactDatabase;
|
import org.thoughtcrime.securesms.database.SessionContactDatabase;
|
||||||
import org.thoughtcrime.securesms.loki.utilities.UiModeUtilities;
|
import org.thoughtcrime.securesms.util.UiModeUtilities;
|
||||||
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri;
|
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri;
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||||
import org.thoughtcrime.securesms.mms.Slide;
|
import org.thoughtcrime.securesms.mms.Slide;
|
||||||
|
@ -16,7 +16,7 @@ import androidx.annotation.Nullable;
|
|||||||
import androidx.appcompat.widget.SearchView;
|
import androidx.appcompat.widget.SearchView;
|
||||||
import androidx.appcompat.widget.Toolbar;
|
import androidx.appcompat.widget.Toolbar;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.animation.AnimationCompleteListener;
|
import org.thoughtcrime.securesms.util.AnimationCompleteListener;
|
||||||
|
|
||||||
import network.loki.messenger.R;
|
import network.loki.messenger.R;
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import org.session.libsession.messaging.messages.control.TypingIndicator;
|
|||||||
import org.session.libsession.messaging.sending_receiving.MessageSender;
|
import org.session.libsession.messaging.sending_receiving.MessageSender;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||||
import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol;
|
import org.thoughtcrime.securesms.util.SessionMetaProtocol;
|
||||||
import org.session.libsession.utilities.recipients.Recipient;
|
import org.session.libsession.utilities.recipients.Recipient;
|
||||||
import org.session.libsession.utilities.Util;
|
import org.session.libsession.utilities.Util;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package org.thoughtcrime.securesms.loki.fragments
|
package org.thoughtcrime.securesms.contacts
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
@ -7,7 +7,7 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import kotlinx.android.synthetic.main.contact_selection_list_divider.view.*
|
import kotlinx.android.synthetic.main.contact_selection_list_divider.view.*
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.thoughtcrime.securesms.loki.views.UserView
|
import org.thoughtcrime.securesms.contacts.UserView
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||||
import org.session.libsession.utilities.recipients.Recipient
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package org.thoughtcrime.securesms.loki.fragments
|
package org.thoughtcrime.securesms.contacts
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
@ -11,10 +11,11 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import kotlinx.android.synthetic.main.contact_selection_list_fragment.*
|
import kotlinx.android.synthetic.main.contact_selection_list_fragment.*
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader
|
|
||||||
import org.session.libsignal.utilities.Log
|
import org.session.libsignal.utilities.Log
|
||||||
import org.thoughtcrime.securesms.mms.GlideApp
|
import org.thoughtcrime.securesms.mms.GlideApp
|
||||||
import org.session.libsession.utilities.recipients.Recipient
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
|
import org.thoughtcrime.securesms.contacts.ContactSelectionListItem
|
||||||
|
import org.thoughtcrime.securesms.contacts.ContactSelectionListLoader
|
||||||
|
|
||||||
class ContactSelectionListFragment : Fragment(), LoaderManager.LoaderCallbacks<List<ContactSelectionListItem>>, ContactClickListener {
|
class ContactSelectionListFragment : Fragment(), LoaderManager.LoaderCallbacks<List<ContactSelectionListItem>>, ContactClickListener {
|
||||||
private var cursorFilter: String? = null
|
private var cursorFilter: String? = null
|
||||||
@ -98,7 +99,7 @@ class ContactSelectionListFragment : Fragment(), LoaderManager.LoaderCallbacks<L
|
|||||||
update(listOf())
|
update(listOf())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun update(items: List<ContactSelectionListItem>) {
|
private fun update(items: List<ContactSelectionListItem>) {
|
||||||
if (activity?.isDestroyed == true) {
|
if (activity?.isDestroyed == true) {
|
||||||
Log.e(ContactSelectionListFragment::class.java.name,
|
Log.e(ContactSelectionListFragment::class.java.name,
|
||||||
"Received a loader callback after the fragment was detached from the activity.",
|
"Received a loader callback after the fragment was detached from the activity.",
|
@ -1,8 +1,8 @@
|
|||||||
package org.thoughtcrime.securesms.loki.fragments
|
package org.thoughtcrime.securesms.contacts
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.thoughtcrime.securesms.loki.utilities.ContactUtilities
|
import org.thoughtcrime.securesms.util.ContactUtilities
|
||||||
import org.session.libsession.utilities.recipients.Recipient
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
import org.thoughtcrime.securesms.util.AsyncLoader
|
import org.thoughtcrime.securesms.util.AsyncLoader
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package org.thoughtcrime.securesms.loki.activities
|
package org.thoughtcrime.securesms.contacts
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
@ -40,8 +40,8 @@ class SelectContactsActivity : PassphraseRequiredActionBarActivity(), LoaderMana
|
|||||||
setContentView(R.layout.activity_select_contacts)
|
setContentView(R.layout.activity_select_contacts)
|
||||||
supportActionBar!!.title = resources.getString(R.string.activity_select_contacts_title)
|
supportActionBar!!.title = resources.getString(R.string.activity_select_contacts_title)
|
||||||
|
|
||||||
usersToExclude = intent.getStringArrayExtra(Companion.usersToExcludeKey)?.toSet() ?: setOf()
|
usersToExclude = intent.getStringArrayExtra(usersToExcludeKey)?.toSet() ?: setOf()
|
||||||
val emptyStateText = intent.getStringExtra(Companion.emptyStateTextKey)
|
val emptyStateText = intent.getStringExtra(emptyStateTextKey)
|
||||||
if (emptyStateText != null) {
|
if (emptyStateText != null) {
|
||||||
emptyStateMessageTextView.text = emptyStateText
|
emptyStateMessageTextView.text = emptyStateText
|
||||||
}
|
}
|
@ -1,10 +1,9 @@
|
|||||||
package org.thoughtcrime.securesms.loki.activities
|
package org.thoughtcrime.securesms.contacts
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import org.session.libsession.utilities.Address
|
import org.session.libsession.utilities.Address
|
||||||
import org.thoughtcrime.securesms.loki.views.UserView
|
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||||
import org.session.libsession.utilities.recipients.Recipient
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
|
|
@ -1,7 +1,7 @@
|
|||||||
package org.thoughtcrime.securesms.loki.activities
|
package org.thoughtcrime.securesms.contacts
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import org.thoughtcrime.securesms.loki.utilities.ContactUtilities
|
import org.thoughtcrime.securesms.util.ContactUtilities
|
||||||
import org.thoughtcrime.securesms.util.AsyncLoader
|
import org.thoughtcrime.securesms.util.AsyncLoader
|
||||||
|
|
||||||
class SelectContactsLoader(context: Context, val usersToExclude: Set<String>) : AsyncLoader<List<String>>(context) {
|
class SelectContactsLoader(context: Context, val usersToExclude: Set<String>) : AsyncLoader<List<String>>(context) {
|
@ -1,4 +1,4 @@
|
|||||||
package org.thoughtcrime.securesms.loki.views
|
package org.thoughtcrime.securesms.contacts
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
@ -10,7 +10,7 @@ import kotlinx.android.synthetic.main.view_user.view.*
|
|||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.session.libsession.messaging.contacts.Contact
|
import org.session.libsession.messaging.contacts.Contact
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
import org.thoughtcrime.securesms.loki.utilities.MentionManagerUtilities
|
import org.thoughtcrime.securesms.conversation.v2.utilities.MentionManagerUtilities
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||||
import org.session.libsession.utilities.recipients.Recipient
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
|
|
@ -23,6 +23,8 @@ import android.widget.RelativeLayout
|
|||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.annotation.DimenRes
|
import androidx.annotation.DimenRes
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.core.view.children
|
||||||
|
import androidx.core.view.get
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
@ -40,6 +42,7 @@ import kotlinx.android.synthetic.main.view_conversation.view.*
|
|||||||
import kotlinx.android.synthetic.main.view_input_bar.view.*
|
import kotlinx.android.synthetic.main.view_input_bar.view.*
|
||||||
import kotlinx.android.synthetic.main.view_input_bar_recording.*
|
import kotlinx.android.synthetic.main.view_input_bar_recording.*
|
||||||
import kotlinx.android.synthetic.main.view_input_bar_recording.view.*
|
import kotlinx.android.synthetic.main.view_input_bar_recording.view.*
|
||||||
|
import kotlinx.android.synthetic.main.view_visible_message.view.*
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import nl.komponents.kovenant.ui.failUi
|
import nl.komponents.kovenant.ui.failUi
|
||||||
import nl.komponents.kovenant.ui.successUi
|
import nl.komponents.kovenant.ui.successUi
|
||||||
@ -69,7 +72,6 @@ import org.session.libsession.utilities.concurrent.SimpleTask
|
|||||||
import org.session.libsession.utilities.recipients.Recipient
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
import org.session.libsession.utilities.recipients.RecipientModifiedListener
|
import org.session.libsession.utilities.recipients.RecipientModifiedListener
|
||||||
import org.session.libsignal.utilities.ListenableFuture
|
import org.session.libsignal.utilities.ListenableFuture
|
||||||
import org.session.libsignal.utilities.SettableFuture
|
|
||||||
import org.session.libsignal.utilities.guava.Optional
|
import org.session.libsignal.utilities.guava.Optional
|
||||||
import org.thoughtcrime.securesms.ApplicationContext
|
import org.thoughtcrime.securesms.ApplicationContext
|
||||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||||
@ -84,8 +86,7 @@ import org.thoughtcrime.securesms.conversation.v2.input_bar.mentions.MentionCand
|
|||||||
import org.thoughtcrime.securesms.conversation.v2.menus.ConversationActionModeCallback
|
import org.thoughtcrime.securesms.conversation.v2.menus.ConversationActionModeCallback
|
||||||
import org.thoughtcrime.securesms.conversation.v2.menus.ConversationActionModeCallbackDelegate
|
import org.thoughtcrime.securesms.conversation.v2.menus.ConversationActionModeCallbackDelegate
|
||||||
import org.thoughtcrime.securesms.conversation.v2.menus.ConversationMenuHelper
|
import org.thoughtcrime.securesms.conversation.v2.menus.ConversationMenuHelper
|
||||||
import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageContentViewDelegate
|
import org.thoughtcrime.securesms.conversation.v2.messages.*
|
||||||
import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageView
|
|
||||||
import org.thoughtcrime.securesms.conversation.v2.search.SearchBottomBar
|
import org.thoughtcrime.securesms.conversation.v2.search.SearchBottomBar
|
||||||
import org.thoughtcrime.securesms.conversation.v2.search.SearchViewModel
|
import org.thoughtcrime.securesms.conversation.v2.search.SearchViewModel
|
||||||
import org.thoughtcrime.securesms.conversation.v2.utilities.AttachmentManager
|
import org.thoughtcrime.securesms.conversation.v2.utilities.AttachmentManager
|
||||||
@ -100,20 +101,16 @@ import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository
|
|||||||
import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil
|
import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil
|
||||||
import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel
|
import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel
|
||||||
import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel.LinkPreviewState
|
import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel.LinkPreviewState
|
||||||
import org.thoughtcrime.securesms.loki.activities.SelectContactsActivity
|
import org.thoughtcrime.securesms.contacts.SelectContactsActivity
|
||||||
import org.thoughtcrime.securesms.loki.activities.SelectContactsActivity.Companion.selectedContactsKey
|
import org.thoughtcrime.securesms.contacts.SelectContactsActivity.Companion.selectedContactsKey
|
||||||
import org.thoughtcrime.securesms.loki.utilities.ActivityDispatcher
|
import org.thoughtcrime.securesms.conversation.v2.utilities.MentionUtilities
|
||||||
import org.thoughtcrime.securesms.loki.utilities.MentionUtilities
|
import org.thoughtcrime.securesms.util.toPx
|
||||||
import org.thoughtcrime.securesms.loki.utilities.push
|
|
||||||
import org.thoughtcrime.securesms.loki.utilities.toPx
|
|
||||||
import org.thoughtcrime.securesms.mediasend.Media
|
import org.thoughtcrime.securesms.mediasend.Media
|
||||||
import org.thoughtcrime.securesms.mediasend.MediaSendActivity
|
import org.thoughtcrime.securesms.mediasend.MediaSendActivity
|
||||||
import org.thoughtcrime.securesms.mms.*
|
import org.thoughtcrime.securesms.mms.*
|
||||||
import org.thoughtcrime.securesms.notifications.MarkReadReceiver
|
import org.thoughtcrime.securesms.notifications.MarkReadReceiver
|
||||||
import org.thoughtcrime.securesms.permissions.Permissions
|
import org.thoughtcrime.securesms.permissions.Permissions
|
||||||
import org.thoughtcrime.securesms.util.DateUtils
|
import org.thoughtcrime.securesms.util.*
|
||||||
import org.thoughtcrime.securesms.util.MediaUtil
|
|
||||||
import org.thoughtcrime.securesms.util.SaveAttachmentTask
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.ExecutionException
|
import java.util.concurrent.ExecutionException
|
||||||
import kotlin.math.*
|
import kotlin.math.*
|
||||||
@ -125,7 +122,7 @@ import kotlin.math.*
|
|||||||
class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDelegate,
|
class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDelegate,
|
||||||
InputBarRecordingViewDelegate, AttachmentManager.AttachmentListener, ActivityDispatcher,
|
InputBarRecordingViewDelegate, AttachmentManager.AttachmentListener, ActivityDispatcher,
|
||||||
ConversationActionModeCallbackDelegate, VisibleMessageContentViewDelegate, RecipientModifiedListener,
|
ConversationActionModeCallbackDelegate, VisibleMessageContentViewDelegate, RecipientModifiedListener,
|
||||||
SearchBottomBar.EventListener {
|
SearchBottomBar.EventListener, VoiceMessageViewDelegate {
|
||||||
private val screenWidth = Resources.getSystem().displayMetrics.widthPixels
|
private val screenWidth = Resources.getSystem().displayMetrics.widthPixels
|
||||||
private var linkPreviewViewModel: LinkPreviewViewModel? = null
|
private var linkPreviewViewModel: LinkPreviewViewModel? = null
|
||||||
private var threadID: Long = -1
|
private var threadID: Long = -1
|
||||||
@ -213,6 +210,11 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
threadID = DatabaseFactory.getThreadDatabase(this).getOrCreateThreadIdFor(recipient)
|
threadID = DatabaseFactory.getThreadDatabase(this).getOrCreateThreadIdFor(recipient)
|
||||||
}
|
}
|
||||||
this.threadID = threadID
|
this.threadID = threadID
|
||||||
|
val thread = DatabaseFactory.getThreadDatabase(this).getRecipientForThreadId(threadID)
|
||||||
|
if (thread == null) {
|
||||||
|
Toast.makeText(this, "This thread has been deleted.", Toast.LENGTH_LONG).show()
|
||||||
|
return finish()
|
||||||
|
}
|
||||||
setUpRecyclerView()
|
setUpRecyclerView()
|
||||||
setUpToolBar()
|
setUpToolBar()
|
||||||
setUpInputBar()
|
setUpInputBar()
|
||||||
@ -232,6 +234,13 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
scrollToFirstUnreadMessageIfNeeded()
|
scrollToFirstUnreadMessageIfNeeded()
|
||||||
markAllAsRead()
|
markAllAsRead()
|
||||||
showOrHideInputIfNeeded()
|
showOrHideInputIfNeeded()
|
||||||
|
if (this.thread.isOpenGroupRecipient) {
|
||||||
|
val openGroup = DatabaseFactory.getLokiThreadDatabase(this).getOpenGroupChat(threadID)
|
||||||
|
if (openGroup == null) {
|
||||||
|
Toast.makeText(this, "This thread has been deleted.", Toast.LENGTH_LONG).show()
|
||||||
|
return finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
@ -849,6 +858,21 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
conversationRecyclerView.scrollToPosition(lastSeenItemPosition)
|
conversationRecyclerView.scrollToPosition(lastSeenItemPosition)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun playNextAudioIfPossible(current: Int) {
|
||||||
|
if (current > 0) {
|
||||||
|
val nextVisibleMessageView = conversationRecyclerView[current - 1] as? VisibleMessageView
|
||||||
|
nextVisibleMessageView?.let { visibleMessageView ->
|
||||||
|
visibleMessageView.messageContentView.mainContainer.children.forEach { child ->
|
||||||
|
val nextVoiceMessageView = child as? VoiceMessageView
|
||||||
|
nextVoiceMessageView?.let { voiceMessageView ->
|
||||||
|
voiceMessageView.togglePlayback()
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun sendMessage() {
|
override fun sendMessage() {
|
||||||
if (thread.isContactRecipient && thread.isBlocked) {
|
if (thread.isContactRecipient && thread.isBlocked) {
|
||||||
BlockedDialog(thread).show(supportFragmentManager, "Blocked Dialog")
|
BlockedDialog(thread).show(supportFragmentManager, "Blocked Dialog")
|
||||||
@ -1129,7 +1153,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
for (message in sortedMessages) {
|
for (message in sortedMessages) {
|
||||||
val body = MentionUtilities.highlightMentions(message.body, message.threadId, this)
|
val body = MentionUtilities.highlightMentions(message.body, message.threadId, this)
|
||||||
if (TextUtils.isEmpty(body)) { continue }
|
if (TextUtils.isEmpty(body)) { continue }
|
||||||
val formattedTimestamp = DateUtils.getExtendedRelativeTimeSpanString(this, Locale.getDefault(), message.timestamp)
|
val formattedTimestamp = DateUtils.getDisplayFormattedTimeSpanString(this, Locale.getDefault(), message.timestamp)
|
||||||
builder.append("$formattedTimestamp: $body").append('\n')
|
builder.append("$formattedTimestamp: $body").append('\n')
|
||||||
}
|
}
|
||||||
if (builder.isNotEmpty() && builder[builder.length - 1] == '\n') {
|
if (builder.isNotEmpty() && builder[builder.length - 1] == '\n') {
|
||||||
@ -1265,7 +1289,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun saveDraft() {
|
private fun saveDraft() {
|
||||||
val text = inputBar.text.trim()
|
val text = inputBar?.text?.trim() ?: return
|
||||||
if (text.isEmpty()) { return }
|
if (text.isEmpty()) { return }
|
||||||
val drafts = Drafts()
|
val drafts = Drafts()
|
||||||
drafts.add(DraftDatabase.Draft(DraftDatabase.Draft.TEXT, text))
|
drafts.add(DraftDatabase.Draft(DraftDatabase.Draft.TEXT, text))
|
||||||
|
@ -72,6 +72,7 @@ class ConversationAdapter(context: Context, cursor: Cursor, private val onItemPr
|
|||||||
view.snIsSelected = isSelected
|
view.snIsSelected = isSelected
|
||||||
view.messageTimestampTextView.isVisible = isSelected
|
view.messageTimestampTextView.isVisible = isSelected
|
||||||
val position = viewHolder.adapterPosition
|
val position = viewHolder.adapterPosition
|
||||||
|
view.viewHolderIndex = position
|
||||||
view.bind(message, getMessageBefore(position, cursor), getMessageAfter(position, cursor), glide, searchQuery)
|
view.bind(message, getMessageBefore(position, cursor), getMessageAfter(position, cursor), glide, searchQuery)
|
||||||
view.onPress = { event -> onItemPress(message, viewHolder.adapterPosition, view, event) }
|
view.onPress = { event -> onItemPress(message, viewHolder.adapterPosition, view, event) }
|
||||||
view.onSwipeToReply = { onItemSwipeToReply(message, viewHolder.adapterPosition) }
|
view.onSwipeToReply = { onItemSwipeToReply(message, viewHolder.adapterPosition) }
|
||||||
|
@ -8,8 +8,8 @@ import android.view.VelocityTracker
|
|||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import kotlinx.android.synthetic.main.activity_conversation_v2.*
|
import kotlinx.android.synthetic.main.activity_conversation_v2.*
|
||||||
import org.thoughtcrime.securesms.loki.utilities.disableClipping
|
import org.thoughtcrime.securesms.util.disableClipping
|
||||||
import org.thoughtcrime.securesms.loki.utilities.toPx
|
import org.thoughtcrime.securesms.util.toPx
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ import org.thoughtcrime.securesms.MediaPreviewActivity
|
|||||||
import org.thoughtcrime.securesms.components.CornerMask
|
import org.thoughtcrime.securesms.components.CornerMask
|
||||||
import org.thoughtcrime.securesms.conversation.v2.utilities.KThumbnailView
|
import org.thoughtcrime.securesms.conversation.v2.utilities.KThumbnailView
|
||||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
|
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
|
||||||
import org.thoughtcrime.securesms.loki.utilities.ActivityDispatcher
|
import org.thoughtcrime.securesms.util.ActivityDispatcher
|
||||||
import org.thoughtcrime.securesms.longmessage.LongMessageActivity
|
import org.thoughtcrime.securesms.longmessage.LongMessageActivity
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||||
import org.thoughtcrime.securesms.mms.Slide
|
import org.thoughtcrime.securesms.mms.Slide
|
||||||
|
@ -8,7 +8,7 @@ import androidx.core.view.isVisible
|
|||||||
import kotlinx.android.synthetic.main.view_link_preview_draft.view.*
|
import kotlinx.android.synthetic.main.view_link_preview_draft.view.*
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview
|
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview
|
||||||
import org.thoughtcrime.securesms.loki.utilities.toPx
|
import org.thoughtcrime.securesms.util.toPx
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||||
import org.thoughtcrime.securesms.mms.ImageSlide
|
import org.thoughtcrime.securesms.mms.ImageSlide
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package org.thoughtcrime.securesms.loki.views
|
package org.thoughtcrime.securesms.conversation.v2.components
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
@ -8,7 +8,7 @@ import android.view.ViewGroup
|
|||||||
import android.widget.BaseAdapter
|
import android.widget.BaseAdapter
|
||||||
import android.widget.ListView
|
import android.widget.ListView
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
import org.thoughtcrime.securesms.loki.utilities.toPx
|
import org.thoughtcrime.securesms.util.toPx
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||||
import org.session.libsession.messaging.mentions.Mention
|
import org.session.libsession.messaging.mentions.Mention
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package org.thoughtcrime.securesms.loki.views
|
package org.thoughtcrime.securesms.conversation.v2.components
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
@ -8,8 +8,8 @@ import android.widget.FrameLayout
|
|||||||
import kotlinx.android.synthetic.main.view_open_group_guidelines.view.*
|
import kotlinx.android.synthetic.main.view_open_group_guidelines.view.*
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
|
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
|
||||||
import org.thoughtcrime.securesms.loki.activities.OpenGroupGuidelinesActivity
|
import org.thoughtcrime.securesms.groups.OpenGroupGuidelinesActivity
|
||||||
import org.thoughtcrime.securesms.loki.utilities.push
|
import org.thoughtcrime.securesms.util.push
|
||||||
|
|
||||||
class OpenGroupGuidelinesView : FrameLayout {
|
class OpenGroupGuidelinesView : FrameLayout {
|
||||||
|
|
||||||
|
@ -9,12 +9,11 @@ import androidx.appcompat.app.AlertDialog
|
|||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import kotlinx.android.synthetic.main.dialog_join_open_group.view.*
|
import kotlinx.android.synthetic.main.dialog_join_open_group.view.*
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.session.libsession.messaging.open_groups.OpenGroupV2
|
|
||||||
import org.session.libsession.utilities.OpenGroupUrlParser
|
import org.session.libsession.utilities.OpenGroupUrlParser
|
||||||
import org.session.libsignal.utilities.ThreadUtils
|
import org.session.libsignal.utilities.ThreadUtils
|
||||||
import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog
|
import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog
|
||||||
import org.thoughtcrime.securesms.loki.api.OpenGroupManager
|
import org.thoughtcrime.securesms.groups.OpenGroupManager
|
||||||
import org.thoughtcrime.securesms.loki.protocol.MultiDeviceProtocol
|
import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
|
||||||
|
|
||||||
/** Shown upon tapping an open group invitation. */
|
/** Shown upon tapping an open group invitation. */
|
||||||
class JoinOpenGroupDialog(private val name: String, private val url: String) : BaseDialog() {
|
class JoinOpenGroupDialog(private val name: String, private val url: String) : BaseDialog() {
|
||||||
@ -38,7 +37,7 @@ class JoinOpenGroupDialog(private val name: String, private val url: String) : B
|
|||||||
val activity = requireContext() as AppCompatActivity
|
val activity = requireContext() as AppCompatActivity
|
||||||
ThreadUtils.queue {
|
ThreadUtils.queue {
|
||||||
OpenGroupManager.add(openGroup.server, openGroup.room, openGroup.serverPublicKey, activity)
|
OpenGroupManager.add(openGroup.server, openGroup.room, openGroup.serverPublicKey, activity)
|
||||||
MultiDeviceProtocol.forceSyncConfigurationNowIfNeeded(activity)
|
ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(activity)
|
||||||
}
|
}
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
|
@ -19,8 +19,8 @@ import org.thoughtcrime.securesms.conversation.v2.messages.QuoteView
|
|||||||
import org.thoughtcrime.securesms.conversation.v2.messages.QuoteViewDelegate
|
import org.thoughtcrime.securesms.conversation.v2.messages.QuoteViewDelegate
|
||||||
import org.thoughtcrime.securesms.database.model.MessageRecord
|
import org.thoughtcrime.securesms.database.model.MessageRecord
|
||||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
|
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
|
||||||
import org.thoughtcrime.securesms.loki.utilities.toDp
|
import org.thoughtcrime.securesms.util.toDp
|
||||||
import org.thoughtcrime.securesms.loki.utilities.toPx
|
import org.thoughtcrime.securesms.util.toPx
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
@ -17,10 +17,9 @@ import android.widget.ImageView
|
|||||||
import android.widget.RelativeLayout
|
import android.widget.RelativeLayout
|
||||||
import androidx.annotation.DrawableRes
|
import androidx.annotation.DrawableRes
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageView
|
import org.thoughtcrime.securesms.util.*
|
||||||
import org.thoughtcrime.securesms.loki.utilities.*
|
import org.thoughtcrime.securesms.util.GlowViewUtilities
|
||||||
import org.thoughtcrime.securesms.loki.views.GlowViewUtilities
|
import org.thoughtcrime.securesms.util.InputBarButtonImageViewContainer
|
||||||
import org.thoughtcrime.securesms.loki.views.InputBarButtonImageViewContainer
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ import android.util.Log
|
|||||||
import android.widget.RelativeLayout
|
import android.widget.RelativeLayout
|
||||||
import androidx.appcompat.widget.AppCompatEditText
|
import androidx.appcompat.widget.AppCompatEditText
|
||||||
import org.thoughtcrime.securesms.conversation.v2.utilities.TextUtilities
|
import org.thoughtcrime.securesms.conversation.v2.utilities.TextUtilities
|
||||||
import org.thoughtcrime.securesms.loki.utilities.toPx
|
import org.thoughtcrime.securesms.util.toPx
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
@ -13,9 +13,9 @@ import androidx.core.content.res.ResourcesCompat
|
|||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import kotlinx.android.synthetic.main.view_input_bar_recording.view.*
|
import kotlinx.android.synthetic.main.view_input_bar_recording.view.*
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.thoughtcrime.securesms.loki.utilities.animateSizeChange
|
import org.thoughtcrime.securesms.util.animateSizeChange
|
||||||
import org.thoughtcrime.securesms.loki.utilities.disableClipping
|
import org.thoughtcrime.securesms.util.disableClipping
|
||||||
import org.thoughtcrime.securesms.loki.utilities.toPx
|
import org.thoughtcrime.securesms.util.toPx
|
||||||
import org.thoughtcrime.securesms.util.DateUtils
|
import org.thoughtcrime.securesms.util.DateUtils
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import android.view.ViewGroup
|
|||||||
import android.widget.BaseAdapter
|
import android.widget.BaseAdapter
|
||||||
import android.widget.ListView
|
import android.widget.ListView
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
import org.thoughtcrime.securesms.loki.utilities.toPx
|
import org.thoughtcrime.securesms.util.toPx
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||||
import org.session.libsession.messaging.mentions.Mention
|
import org.session.libsession.messaging.mentions.Mention
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@ class ConversationActionModeCallback(private val adapter: ConversationAdapter, p
|
|||||||
if (selectedItems.isEmpty()) { return }
|
if (selectedItems.isEmpty()) { return }
|
||||||
val firstMessage = selectedItems.iterator().next()
|
val firstMessage = selectedItems.iterator().next()
|
||||||
val openGroup = DatabaseFactory.getLokiThreadDatabase(context).getOpenGroupChat(threadID)
|
val openGroup = DatabaseFactory.getLokiThreadDatabase(context).getOpenGroupChat(threadID)
|
||||||
|
val thread = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadID)!!
|
||||||
val userPublicKey = TextSecurePreferences.getLocalNumber(context)!!
|
val userPublicKey = TextSecurePreferences.getLocalNumber(context)!!
|
||||||
fun userCanDeleteSelectedItems(): Boolean {
|
fun userCanDeleteSelectedItems(): Boolean {
|
||||||
if (openGroup == null) { return true }
|
if (openGroup == null) { return true }
|
||||||
@ -54,7 +55,7 @@ class ConversationActionModeCallback(private val adapter: ConversationAdapter, p
|
|||||||
menu.findItem(R.id.menu_context_copy).isVisible = !containsControlMessage && hasText
|
menu.findItem(R.id.menu_context_copy).isVisible = !containsControlMessage && hasText
|
||||||
// Copy Session ID
|
// Copy Session ID
|
||||||
menu.findItem(R.id.menu_context_copy_public_key).isVisible =
|
menu.findItem(R.id.menu_context_copy_public_key).isVisible =
|
||||||
(openGroup != null && selectedItems.size == 1 && firstMessage.recipient.address.toString() != userPublicKey)
|
(thread.isGroupRecipient && selectedItems.size == 1 && firstMessage.recipient.address.toString() != userPublicKey)
|
||||||
// Resend
|
// Resend
|
||||||
menu.findItem(R.id.menu_context_resend).isVisible = (selectedItems.size == 1 && firstMessage.isFailed)
|
menu.findItem(R.id.menu_context_resend).isVisible = (selectedItems.size == 1 && firstMessage.isFailed)
|
||||||
// Save media
|
// Save media
|
||||||
|
@ -38,10 +38,10 @@ import org.session.libsignal.utilities.toHexString
|
|||||||
import org.thoughtcrime.securesms.*
|
import org.thoughtcrime.securesms.*
|
||||||
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
|
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
import org.thoughtcrime.securesms.loki.activities.EditClosedGroupActivity
|
import org.thoughtcrime.securesms.groups.EditClosedGroupActivity
|
||||||
import org.thoughtcrime.securesms.loki.activities.EditClosedGroupActivity.Companion.groupIDKey
|
import org.thoughtcrime.securesms.groups.EditClosedGroupActivity.Companion.groupIDKey
|
||||||
import org.thoughtcrime.securesms.loki.activities.SelectContactsActivity
|
import org.thoughtcrime.securesms.contacts.SelectContactsActivity
|
||||||
import org.thoughtcrime.securesms.loki.utilities.getColorWithID
|
import org.thoughtcrime.securesms.util.getColorWithID
|
||||||
import org.thoughtcrime.securesms.util.BitmapUtil
|
import org.thoughtcrime.securesms.util.BitmapUtil
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ import org.thoughtcrime.securesms.conversation.v2.utilities.MessageBubbleUtiliti
|
|||||||
import org.thoughtcrime.securesms.conversation.v2.utilities.ModalURLSpan
|
import org.thoughtcrime.securesms.conversation.v2.utilities.ModalURLSpan
|
||||||
import org.thoughtcrime.securesms.conversation.v2.utilities.TextUtilities.getIntersectedModalSpans
|
import org.thoughtcrime.securesms.conversation.v2.utilities.TextUtilities.getIntersectedModalSpans
|
||||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
|
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
|
||||||
import org.thoughtcrime.securesms.loki.utilities.UiModeUtilities
|
import org.thoughtcrime.securesms.util.UiModeUtilities
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||||
import org.thoughtcrime.securesms.mms.ImageSlide
|
import org.thoughtcrime.securesms.mms.ImageSlide
|
||||||
|
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
package org.thoughtcrime.securesms.conversation.v2.messages
|
package org.thoughtcrime.securesms.conversation.v2.messages
|
||||||
|
|
||||||
import android.content.ContentResolver
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.ColorStateList
|
import android.content.res.ColorStateList
|
||||||
import android.content.res.Resources
|
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.util.Log
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.RelativeLayout
|
import android.widget.RelativeLayout
|
||||||
@ -13,22 +10,19 @@ import androidx.annotation.ColorInt
|
|||||||
import androidx.core.content.res.ResourcesCompat
|
import androidx.core.content.res.ResourcesCompat
|
||||||
import androidx.core.text.toSpannable
|
import androidx.core.text.toSpannable
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.core.view.marginStart
|
|
||||||
import com.google.android.exoplayer2.util.MimeTypes
|
|
||||||
import kotlinx.android.synthetic.main.view_link_preview.view.*
|
import kotlinx.android.synthetic.main.view_link_preview.view.*
|
||||||
import kotlinx.android.synthetic.main.view_quote.view.*
|
import kotlinx.android.synthetic.main.view_quote.view.*
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.session.libsession.messaging.contacts.Contact
|
import org.session.libsession.messaging.contacts.Contact
|
||||||
import org.session.libsession.messaging.utilities.UpdateMessageData
|
|
||||||
import org.session.libsession.utilities.recipients.Recipient
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
|
import org.thoughtcrime.securesms.conversation.v2.utilities.MentionUtilities
|
||||||
import org.thoughtcrime.securesms.conversation.v2.utilities.TextUtilities
|
import org.thoughtcrime.securesms.conversation.v2.utilities.TextUtilities
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
import org.thoughtcrime.securesms.database.model.MessageRecord
|
import org.thoughtcrime.securesms.util.*
|
||||||
import org.thoughtcrime.securesms.loki.utilities.*
|
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||||
import org.thoughtcrime.securesms.mms.ImageSlide
|
|
||||||
import org.thoughtcrime.securesms.mms.SlideDeck
|
import org.thoughtcrime.securesms.mms.SlideDeck
|
||||||
import org.thoughtcrime.securesms.util.MediaUtil
|
import org.thoughtcrime.securesms.util.MediaUtil
|
||||||
|
import org.thoughtcrime.securesms.util.UiModeUtilities
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
@ -6,7 +6,6 @@ import android.graphics.Rect
|
|||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.text.style.BackgroundColorSpan
|
import android.text.style.BackgroundColorSpan
|
||||||
import android.text.style.ForegroundColorSpan
|
import android.text.style.ForegroundColorSpan
|
||||||
import android.text.method.LinkMovementMethod
|
|
||||||
import android.text.style.URLSpan
|
import android.text.style.URLSpan
|
||||||
import android.text.util.Linkify
|
import android.text.util.Linkify
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
@ -23,7 +22,6 @@ import androidx.core.graphics.BlendModeColorFilterCompat
|
|||||||
import androidx.core.graphics.BlendModeCompat
|
import androidx.core.graphics.BlendModeCompat
|
||||||
import androidx.core.text.getSpans
|
import androidx.core.text.getSpans
|
||||||
import androidx.core.text.toSpannable
|
import androidx.core.text.toSpannable
|
||||||
import androidx.core.text.util.LinkifyCompat
|
|
||||||
import kotlinx.android.synthetic.main.view_link_preview.view.*
|
import kotlinx.android.synthetic.main.view_link_preview.view.*
|
||||||
import kotlinx.android.synthetic.main.view_visible_message_content.view.*
|
import kotlinx.android.synthetic.main.view_visible_message_content.view.*
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
@ -32,15 +30,18 @@ import org.session.libsession.utilities.ViewUtil
|
|||||||
import org.session.libsession.utilities.recipients.Recipient
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
import org.thoughtcrime.securesms.conversation.v2.components.AlbumThumbnailView
|
import org.thoughtcrime.securesms.conversation.v2.components.AlbumThumbnailView
|
||||||
import org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
import org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||||
|
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
|
||||||
import org.thoughtcrime.securesms.conversation.v2.dialogs.OpenURLDialog
|
import org.thoughtcrime.securesms.conversation.v2.dialogs.OpenURLDialog
|
||||||
|
import org.thoughtcrime.securesms.conversation.v2.utilities.MentionUtilities
|
||||||
import org.thoughtcrime.securesms.conversation.v2.utilities.ModalURLSpan
|
import org.thoughtcrime.securesms.conversation.v2.utilities.ModalURLSpan
|
||||||
import org.thoughtcrime.securesms.conversation.v2.utilities.TextUtilities.getIntersectedModalSpans
|
import org.thoughtcrime.securesms.conversation.v2.utilities.TextUtilities.getIntersectedModalSpans
|
||||||
import org.thoughtcrime.securesms.database.model.MessageRecord
|
import org.thoughtcrime.securesms.database.model.MessageRecord
|
||||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
|
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
|
||||||
import org.thoughtcrime.securesms.loki.utilities.*
|
import org.thoughtcrime.securesms.util.*
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||||
import org.thoughtcrime.securesms.util.SearchUtil
|
import org.thoughtcrime.securesms.util.SearchUtil
|
||||||
import org.thoughtcrime.securesms.util.SearchUtil.StyleFactory
|
import org.thoughtcrime.securesms.util.SearchUtil.StyleFactory
|
||||||
|
import org.thoughtcrime.securesms.util.UiModeUtilities
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
@ -48,6 +49,7 @@ class VisibleMessageContentView : LinearLayout {
|
|||||||
var onContentClick: ((event: MotionEvent) -> Unit)? = null
|
var onContentClick: ((event: MotionEvent) -> Unit)? = null
|
||||||
var onContentDoubleTap: (() -> Unit)? = null
|
var onContentDoubleTap: (() -> Unit)? = null
|
||||||
var delegate: VisibleMessageContentViewDelegate? = null
|
var delegate: VisibleMessageContentViewDelegate? = null
|
||||||
|
var viewHolderIndex: Int = -1
|
||||||
|
|
||||||
// region Lifecycle
|
// region Lifecycle
|
||||||
constructor(context: Context) : super(context) { initialize() }
|
constructor(context: Context) : super(context) { initialize() }
|
||||||
@ -109,7 +111,8 @@ class VisibleMessageContentView : LinearLayout {
|
|||||||
// Audio attachment
|
// Audio attachment
|
||||||
if (contactIsTrusted || message.isOutgoing) {
|
if (contactIsTrusted || message.isOutgoing) {
|
||||||
val voiceMessageView = VoiceMessageView(context)
|
val voiceMessageView = VoiceMessageView(context)
|
||||||
voiceMessageView.bind(message, isStartOfMessageCluster, isEndOfMessageCluster)
|
voiceMessageView.index = viewHolderIndex
|
||||||
|
voiceMessageView.delegate = context as? ConversationActivityV2voiceMessageView.bind(message, isStartOfMessageCluster, isEndOfMessageCluster)
|
||||||
mainContainer.addView(voiceMessageView)
|
mainContainer.addView(voiceMessageView)
|
||||||
// We have to use onContentClick (rather than a click listener directly on the voice
|
// We have to use onContentClick (rather than a click listener directly on the voice
|
||||||
// message view) so as to not interfere with all the other gestures.
|
// message view) so as to not interfere with all the other gestures.
|
||||||
|
@ -24,12 +24,8 @@ import org.session.libsignal.utilities.ThreadUtils
|
|||||||
import org.thoughtcrime.securesms.ApplicationContext
|
import org.thoughtcrime.securesms.ApplicationContext
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
import org.thoughtcrime.securesms.database.model.MessageRecord
|
import org.thoughtcrime.securesms.database.model.MessageRecord
|
||||||
import org.thoughtcrime.securesms.loki.utilities.disableClipping
|
|
||||||
import org.thoughtcrime.securesms.loki.utilities.getColorWithID
|
|
||||||
import org.thoughtcrime.securesms.loki.utilities.toDp
|
|
||||||
import org.thoughtcrime.securesms.loki.utilities.toPx
|
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||||
import org.thoughtcrime.securesms.util.DateUtils
|
import org.thoughtcrime.securesms.util.*
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
@ -47,6 +43,7 @@ class VisibleMessageView : LinearLayout {
|
|||||||
private var longPressCallback: Runnable? = null
|
private var longPressCallback: Runnable? = null
|
||||||
private var onDownTimestamp = 0L
|
private var onDownTimestamp = 0L
|
||||||
private var onDoubleTap: (() -> Unit)? = null
|
private var onDoubleTap: (() -> Unit)? = null
|
||||||
|
var viewHolderIndex: Int = -1
|
||||||
var snIsSelected = false
|
var snIsSelected = false
|
||||||
set(value) { field = value; handleIsSelectedChanged()}
|
set(value) { field = value; handleIsSelectedChanged()}
|
||||||
var onPress: ((event: MotionEvent) -> Unit)? = null
|
var onPress: ((event: MotionEvent) -> Unit)? = null
|
||||||
@ -82,7 +79,7 @@ class VisibleMessageView : LinearLayout {
|
|||||||
val senderSessionID = sender.address.serialize()
|
val senderSessionID = sender.address.serialize()
|
||||||
val threadID = message.threadId
|
val threadID = message.threadId
|
||||||
val threadDB = DatabaseFactory.getThreadDatabase(context)
|
val threadDB = DatabaseFactory.getThreadDatabase(context)
|
||||||
val thread = threadDB.getRecipientForThreadId(threadID)!!
|
val thread = threadDB.getRecipientForThreadId(threadID) ?: return
|
||||||
val contactDB = DatabaseFactory.getSessionContactDatabase(context)
|
val contactDB = DatabaseFactory.getSessionContactDatabase(context)
|
||||||
val contact = contactDB.getContactWithSessionID(senderSessionID)
|
val contact = contactDB.getContactWithSessionID(senderSessionID)
|
||||||
val isGroupThread = thread.isGroupRecipient
|
val isGroupThread = thread.isGroupRecipient
|
||||||
@ -96,7 +93,7 @@ class VisibleMessageView : LinearLayout {
|
|||||||
profilePictureView.glide = glide
|
profilePictureView.glide = glide
|
||||||
profilePictureView.update()
|
profilePictureView.update()
|
||||||
if (thread.isOpenGroupRecipient) {
|
if (thread.isOpenGroupRecipient) {
|
||||||
val openGroup = DatabaseFactory.getLokiThreadDatabase(context).getOpenGroupChat(threadID)!!
|
val openGroup = DatabaseFactory.getLokiThreadDatabase(context).getOpenGroupChat(threadID) ?: return
|
||||||
val isModerator = OpenGroupAPIV2.isUserModerator(senderSessionID, openGroup.room, openGroup.server)
|
val isModerator = OpenGroupAPIV2.isUserModerator(senderSessionID, openGroup.room, openGroup.server)
|
||||||
moderatorIconImageView.visibility = if (isModerator) View.VISIBLE else View.INVISIBLE
|
moderatorIconImageView.visibility = if (isModerator) View.VISIBLE else View.INVISIBLE
|
||||||
} else {
|
} else {
|
||||||
@ -110,11 +107,11 @@ class VisibleMessageView : LinearLayout {
|
|||||||
senderNameTextView.visibility = View.GONE
|
senderNameTextView.visibility = View.GONE
|
||||||
}
|
}
|
||||||
// Date break
|
// Date break
|
||||||
val showDateBreak = (previous == null || !DateUtils.isSameDay(message.timestamp, previous.timestamp))
|
val showDateBreak = (previous == null || !DateUtils.isSameHour(message.timestamp, previous.timestamp))
|
||||||
dateBreakTextView.isVisible = showDateBreak
|
dateBreakTextView.isVisible = showDateBreak
|
||||||
dateBreakTextView.text = if (showDateBreak) DateUtils.getRelativeDate(context, Locale.getDefault(), message.timestamp) else ""
|
dateBreakTextView.text = if (showDateBreak) DateUtils.getDisplayFormattedTimeSpanString(context, Locale.getDefault(), message.timestamp) else ""
|
||||||
// Timestamp
|
// Timestamp
|
||||||
messageTimestampTextView.text = DateUtils.getExtendedRelativeTimeSpanString(context, Locale.getDefault(), message.timestamp)
|
messageTimestampTextView.text = DateUtils.getDisplayFormattedTimeSpanString(context, Locale.getDefault(), message.timestamp)
|
||||||
// Margins
|
// Margins
|
||||||
val startPadding: Int
|
val startPadding: Int
|
||||||
if (isGroupThread) {
|
if (isGroupThread) {
|
||||||
@ -152,6 +149,7 @@ class VisibleMessageView : LinearLayout {
|
|||||||
var maxWidth = screenWidth - startPadding - endPadding
|
var maxWidth = screenWidth - startPadding - endPadding
|
||||||
if (profilePictureContainer.visibility != View.GONE) { maxWidth -= profilePictureContainer.width }
|
if (profilePictureContainer.visibility != View.GONE) { maxWidth -= profilePictureContainer.width }
|
||||||
// Populate content view
|
// Populate content view
|
||||||
|
messageContentView.viewHolderIndex = viewHolderIndex
|
||||||
messageContentView.bind(message, isStartOfMessageCluster, isEndOfMessageCluster, glide, maxWidth, thread, searchQuery, isGroupThread || (contact?.isTrusted ?: false))
|
messageContentView.bind(message, isStartOfMessageCluster, isEndOfMessageCluster, glide, maxWidth, thread, searchQuery, isGroupThread || (contact?.isTrusted ?: false))
|
||||||
messageContentView.delegate = contentViewDelegate
|
messageContentView.delegate = contentViewDelegate
|
||||||
onDoubleTap = { messageContentView.onContentDoubleTap?.invoke() }
|
onDoubleTap = { messageContentView.onContentDoubleTap?.invoke() }
|
||||||
@ -176,10 +174,10 @@ class VisibleMessageView : LinearLayout {
|
|||||||
|
|
||||||
private fun isEndOfMessageCluster(current: MessageRecord, next: MessageRecord?, isGroupThread: Boolean): Boolean {
|
private fun isEndOfMessageCluster(current: MessageRecord, next: MessageRecord?, isGroupThread: Boolean): Boolean {
|
||||||
return if (isGroupThread) {
|
return if (isGroupThread) {
|
||||||
next == null || next.isUpdate || !DateUtils.isSameDay(current.timestamp, next.timestamp)
|
next == null || next.isUpdate || !DateUtils.isSameHour(current.timestamp, next.timestamp)
|
||||||
|| current.recipient.address != next.recipient.address
|
|| current.recipient.address != next.recipient.address
|
||||||
} else {
|
} else {
|
||||||
next == null || next.isUpdate || !DateUtils.isSameDay(current.timestamp, next.timestamp)
|
next == null || next.isUpdate || !DateUtils.isSameHour(current.timestamp, next.timestamp)
|
||||||
|| current.isOutgoing != next.isOutgoing
|
|| current.isOutgoing != next.isOutgoing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,8 @@ class VoiceMessageView : LinearLayout, AudioSlidePlayer.Listener {
|
|||||||
private var duration = 0L
|
private var duration = 0L
|
||||||
private var player: AudioSlidePlayer? = null
|
private var player: AudioSlidePlayer? = null
|
||||||
private var isPreparing = false
|
private var isPreparing = false
|
||||||
|
var delegate: VoiceMessageViewDelegate? = null
|
||||||
|
var index = -1
|
||||||
|
|
||||||
// region Lifecycle
|
// region Lifecycle
|
||||||
constructor(context: Context) : super(context) { initialize() }
|
constructor(context: Context) : super(context) { initialize() }
|
||||||
@ -84,6 +86,7 @@ class VoiceMessageView : LinearLayout, AudioSlidePlayer.Listener {
|
|||||||
if (progress == 1.0) {
|
if (progress == 1.0) {
|
||||||
togglePlayback()
|
togglePlayback()
|
||||||
handleProgressChanged(0.0)
|
handleProgressChanged(0.0)
|
||||||
|
delegate?.playNextAudioIfPossible(index)
|
||||||
} else {
|
} else {
|
||||||
handleProgressChanged(progress)
|
handleProgressChanged(progress)
|
||||||
}
|
}
|
||||||
@ -133,3 +136,8 @@ class VoiceMessageView : LinearLayout, AudioSlidePlayer.Listener {
|
|||||||
}
|
}
|
||||||
// endregion
|
// endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface VoiceMessageViewDelegate {
|
||||||
|
|
||||||
|
fun playNextAudioIfPossible(current: Int)
|
||||||
|
}
|
||||||
|
@ -6,7 +6,7 @@ import android.graphics.drawable.ColorDrawable
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import org.thoughtcrime.securesms.loki.utilities.UiModeUtilities
|
import org.thoughtcrime.securesms.util.UiModeUtilities
|
||||||
|
|
||||||
open class BaseDialog : DialogFragment() {
|
open class BaseDialog : DialogFragment() {
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package org.thoughtcrime.securesms.loki.utilities
|
package org.thoughtcrime.securesms.conversation.v2.utilities
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
@ -1,4 +1,4 @@
|
|||||||
package org.thoughtcrime.securesms.loki.utilities
|
package org.thoughtcrime.securesms.conversation.v2.utilities
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Typeface
|
import android.graphics.Typeface
|
||||||
@ -13,6 +13,7 @@ import nl.komponents.kovenant.combine.Tuple2
|
|||||||
import org.session.libsession.messaging.contacts.Contact
|
import org.session.libsession.messaging.contacts.Contact
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
|
import org.thoughtcrime.securesms.util.UiModeUtilities
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
object MentionUtilities {
|
object MentionUtilities {
|
@ -15,21 +15,22 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
package org.session.libsession.utilities;
|
package org.thoughtcrime.securesms.crypto;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.SharedPreferences.Editor;
|
import android.content.SharedPreferences.Editor;
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.session.libsignal.crypto.ecc.ECPublicKey;
|
|
||||||
import org.session.libsignal.crypto.IdentityKey;
|
import org.session.libsignal.crypto.IdentityKey;
|
||||||
import org.session.libsignal.crypto.IdentityKeyPair;
|
import org.session.libsignal.crypto.IdentityKeyPair;
|
||||||
import org.session.libsignal.exceptions.InvalidKeyException;
|
|
||||||
import org.session.libsignal.crypto.ecc.Curve;
|
import org.session.libsignal.crypto.ecc.Curve;
|
||||||
import org.session.libsignal.crypto.ecc.ECKeyPair;
|
import org.session.libsignal.crypto.ecc.ECKeyPair;
|
||||||
import org.session.libsignal.crypto.ecc.ECPrivateKey;
|
import org.session.libsignal.crypto.ecc.ECPrivateKey;
|
||||||
|
import org.session.libsignal.crypto.ecc.ECPublicKey;
|
||||||
|
import org.session.libsignal.exceptions.InvalidKeyException;
|
||||||
import org.session.libsignal.utilities.Base64;
|
import org.session.libsignal.utilities.Base64;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -45,19 +46,41 @@ public class IdentityKeyUtil {
|
|||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
private static final String TAG = IdentityKeyUtil.class.getSimpleName();
|
private static final String TAG = IdentityKeyUtil.class.getSimpleName();
|
||||||
|
private static final String ENCRYPTED_SUFFIX = "_encrypted";
|
||||||
|
|
||||||
public static final String IDENTITY_PUBLIC_KEY_PREF = "pref_identity_public_v3";
|
public static final String IDENTITY_PUBLIC_KEY_PREF = "pref_identity_public_v3";
|
||||||
public static final String IDENTITY_PRIVATE_KEY_PREF = "pref_identity_private_v3";
|
public static final String IDENTITY_PRIVATE_KEY_PREF = "pref_identity_private_v3";
|
||||||
public static final String ED25519_PUBLIC_KEY = "pref_ed25519_public_key";
|
public static final String ED25519_PUBLIC_KEY = "pref_ed25519_public_key";
|
||||||
public static final String ED25519_SECRET_KEY = "pref_ed25519_secret_key";
|
public static final String ED25519_SECRET_KEY = "pref_ed25519_secret_key";
|
||||||
public static final String LOKI_SEED = "loki_seed";
|
public static final String LOKI_SEED = "loki_seed";
|
||||||
|
public static final String HAS_MIGRATED_KEY = "has_migrated_keys";
|
||||||
|
|
||||||
|
private static SharedPreferences getSharedPreferences(Context context) {
|
||||||
|
return context.getSharedPreferences(MASTER_SECRET_UTIL_PREFERENCES_NAME, 0);
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean hasIdentityKey(Context context) {
|
public static boolean hasIdentityKey(Context context) {
|
||||||
SharedPreferences preferences = context.getSharedPreferences(MASTER_SECRET_UTIL_PREFERENCES_NAME, 0);
|
SharedPreferences preferences = getSharedPreferences(context);
|
||||||
|
|
||||||
return
|
return (preferences.contains(IDENTITY_PUBLIC_KEY_PREF) &&
|
||||||
preferences.contains(IDENTITY_PUBLIC_KEY_PREF) &&
|
preferences.contains(IDENTITY_PRIVATE_KEY_PREF))
|
||||||
preferences.contains(IDENTITY_PRIVATE_KEY_PREF);
|
|| (preferences.contains(IDENTITY_PUBLIC_KEY_PREF+ENCRYPTED_SUFFIX) &&
|
||||||
|
preferences.contains(IDENTITY_PRIVATE_KEY_PREF+ENCRYPTED_SUFFIX));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void checkUpdate(Context context) {
|
||||||
|
SharedPreferences preferences = getSharedPreferences(context);
|
||||||
|
// check if any keys are not migrated
|
||||||
|
if (hasIdentityKey(context) && !preferences.getBoolean(HAS_MIGRATED_KEY, false)) {
|
||||||
|
// this will retrieve and force upgrade if possible
|
||||||
|
// retrieve will force upgrade if available
|
||||||
|
retrieve(context,IDENTITY_PUBLIC_KEY_PREF);
|
||||||
|
retrieve(context,IDENTITY_PRIVATE_KEY_PREF);
|
||||||
|
retrieve(context,ED25519_PUBLIC_KEY);
|
||||||
|
retrieve(context,ED25519_SECRET_KEY);
|
||||||
|
retrieve(context,LOKI_SEED);
|
||||||
|
preferences.edit().putBoolean(HAS_MIGRATED_KEY, true).apply();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NonNull IdentityKey getIdentityKey(@NonNull Context context) {
|
public static @NonNull IdentityKey getIdentityKey(@NonNull Context context) {
|
||||||
@ -94,14 +117,56 @@ public class IdentityKeyUtil {
|
|||||||
|
|
||||||
public static String retrieve(Context context, String key) {
|
public static String retrieve(Context context, String key) {
|
||||||
SharedPreferences preferences = context.getSharedPreferences(MASTER_SECRET_UTIL_PREFERENCES_NAME, 0);
|
SharedPreferences preferences = context.getSharedPreferences(MASTER_SECRET_UTIL_PREFERENCES_NAME, 0);
|
||||||
return preferences.getString(key, null);
|
|
||||||
|
String unencryptedSecret = preferences.getString(key, null);
|
||||||
|
String encryptedSecret = preferences.getString(key+ENCRYPTED_SUFFIX, null);
|
||||||
|
|
||||||
|
if (unencryptedSecret != null) return getUnencryptedSecret(key, unencryptedSecret, context);
|
||||||
|
else if (encryptedSecret != null) return getEncryptedSecret(encryptedSecret);
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String getUnencryptedSecret(String key, String unencryptedSecret, Context context) {
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||||
|
return unencryptedSecret;
|
||||||
|
} else {
|
||||||
|
KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.seal(unencryptedSecret.getBytes());
|
||||||
|
|
||||||
|
// save the encrypted suffix secret "key_encrypted"
|
||||||
|
save(context,key+ENCRYPTED_SUFFIX,encryptedSecret.serialize());
|
||||||
|
// delete the regular secret "key"
|
||||||
|
delete(context,key);
|
||||||
|
|
||||||
|
return unencryptedSecret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getEncryptedSecret(String encryptedSecret) {
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||||
|
throw new AssertionError("OS downgrade not supported. KeyStore sealed data exists on platform < M!");
|
||||||
|
} else {
|
||||||
|
KeyStoreHelper.SealedData sealedData = KeyStoreHelper.SealedData.fromString(encryptedSecret);
|
||||||
|
return new String(KeyStoreHelper.unseal(sealedData));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void save(Context context, String key, String value) {
|
public static void save(Context context, String key, String value) {
|
||||||
SharedPreferences preferences = context.getSharedPreferences(MASTER_SECRET_UTIL_PREFERENCES_NAME, 0);
|
SharedPreferences preferences = context.getSharedPreferences(MASTER_SECRET_UTIL_PREFERENCES_NAME, 0);
|
||||||
Editor preferencesEditor = preferences.edit();
|
Editor preferencesEditor = preferences.edit();
|
||||||
|
|
||||||
preferencesEditor.putString(key, value);
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
boolean isEncryptedSuffix = key.endsWith(ENCRYPTED_SUFFIX);
|
||||||
|
if (isEncryptedSuffix) {
|
||||||
|
preferencesEditor.putString(key, value);
|
||||||
|
} else {
|
||||||
|
KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.seal(value.getBytes());
|
||||||
|
preferencesEditor.putString(key+ENCRYPTED_SUFFIX, encryptedSecret.serialize());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
preferencesEditor.putString(key, value);
|
||||||
|
}
|
||||||
if (!preferencesEditor.commit()) throw new AssertionError("failed to save identity key/value to shared preferences");
|
if (!preferencesEditor.commit()) throw new AssertionError("failed to save identity key/value to shared preferences");
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package org.session.libsession.utilities
|
package org.thoughtcrime.securesms.crypto
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.goterl.lazysodium.LazySodiumAndroid
|
import com.goterl.lazysodium.LazySodiumAndroid
|
@ -1,4 +1,4 @@
|
|||||||
package org.thoughtcrime.securesms.loki.utilities
|
package org.thoughtcrime.securesms.crypto
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package org.thoughtcrime.securesms.loki.database
|
package org.thoughtcrime.securesms.database
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import java.util.*
|
import java.util.*
|
@ -25,13 +25,8 @@ import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider;
|
|||||||
import org.thoughtcrime.securesms.crypto.DatabaseSecret;
|
import org.thoughtcrime.securesms.crypto.DatabaseSecret;
|
||||||
import org.thoughtcrime.securesms.crypto.DatabaseSecretProvider;
|
import org.thoughtcrime.securesms.crypto.DatabaseSecretProvider;
|
||||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||||
import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase;
|
import org.thoughtcrime.securesms.database.LokiAPIDatabase;
|
||||||
import org.thoughtcrime.securesms.loki.database.LokiBackupFilesDatabase;
|
import org.thoughtcrime.securesms.database.SessionJobDatabase;
|
||||||
import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase;
|
|
||||||
import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase;
|
|
||||||
import org.thoughtcrime.securesms.loki.database.LokiUserDatabase;
|
|
||||||
import org.thoughtcrime.securesms.loki.database.SessionJobDatabase;
|
|
||||||
import org.thoughtcrime.securesms.loki.database.SessionContactDatabase;
|
|
||||||
|
|
||||||
public class DatabaseFactory {
|
public class DatabaseFactory {
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package org.thoughtcrime.securesms.loki.utilities
|
package org.thoughtcrime.securesms.database
|
||||||
|
|
||||||
import android.content.ContentValues
|
import android.content.ContentValues
|
||||||
import androidx.core.database.getStringOrNull
|
import androidx.core.database.getStringOrNull
|
@ -1,23 +1,26 @@
|
|||||||
package org.thoughtcrime.securesms.loki.database
|
package org.thoughtcrime.securesms.database
|
||||||
|
|
||||||
import android.content.ContentValues
|
import android.content.ContentValues
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import org.session.libsession.utilities.IdentityKeyUtil
|
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
import org.session.libsignal.crypto.ecc.DjbECPrivateKey
|
import org.session.libsignal.crypto.ecc.DjbECPrivateKey
|
||||||
import org.session.libsignal.crypto.ecc.DjbECPublicKey
|
import org.session.libsignal.crypto.ecc.DjbECPublicKey
|
||||||
import org.session.libsignal.crypto.ecc.ECKeyPair
|
import org.session.libsignal.crypto.ecc.ECKeyPair
|
||||||
import org.session.libsignal.utilities.Snode
|
|
||||||
import org.session.libsignal.database.LokiAPIDatabaseProtocol
|
import org.session.libsignal.database.LokiAPIDatabaseProtocol
|
||||||
import org.session.libsignal.utilities.PublicKeyValidation
|
import org.session.libsignal.utilities.*
|
||||||
import org.session.libsignal.utilities.removing05PrefixIfNeeded
|
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
|
||||||
import org.session.libsignal.utilities.toHexString
|
import org.thoughtcrime.securesms.database.*
|
||||||
import org.session.libsignal.utilities.Hex
|
|
||||||
import org.session.libsignal.utilities.Log
|
|
||||||
import org.thoughtcrime.securesms.database.Database
|
|
||||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
||||||
import org.thoughtcrime.securesms.loki.utilities.*
|
import org.thoughtcrime.securesms.util.*
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import kotlin.Array
|
||||||
|
import kotlin.Boolean
|
||||||
|
import kotlin.Int
|
||||||
|
import kotlin.Long
|
||||||
|
import kotlin.Pair
|
||||||
|
import kotlin.String
|
||||||
|
import kotlin.arrayOf
|
||||||
|
import kotlin.to
|
||||||
|
|
||||||
class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiAPIDatabaseProtocol {
|
class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiAPIDatabaseProtocol {
|
||||||
|
|
@ -1,10 +1,9 @@
|
|||||||
package org.thoughtcrime.securesms.loki.database
|
package org.thoughtcrime.securesms.database
|
||||||
|
|
||||||
import android.content.ContentValues
|
import android.content.ContentValues
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.database.Cursor
|
import android.database.Cursor
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import org.thoughtcrime.securesms.database.Database
|
|
||||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
||||||
import java.lang.IllegalArgumentException
|
import java.lang.IllegalArgumentException
|
||||||
import java.util.*
|
import java.util.*
|
@ -1,14 +1,10 @@
|
|||||||
package org.thoughtcrime.securesms.loki.database
|
package org.thoughtcrime.securesms.database
|
||||||
|
|
||||||
import android.content.ContentValues
|
import android.content.ContentValues
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import net.sqlcipher.database.SQLiteDatabase.CONFLICT_REPLACE
|
import net.sqlcipher.database.SQLiteDatabase.CONFLICT_REPLACE
|
||||||
import org.thoughtcrime.securesms.database.Database
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
|
||||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
||||||
import org.thoughtcrime.securesms.loki.utilities.*
|
|
||||||
import org.session.libsignal.database.LokiMessageDatabaseProtocol
|
import org.session.libsignal.database.LokiMessageDatabaseProtocol
|
||||||
import org.session.libsignal.utilities.Log
|
|
||||||
|
|
||||||
class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiMessageDatabaseProtocol {
|
class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiMessageDatabaseProtocol {
|
||||||
|
|
||||||
@ -63,9 +59,9 @@ class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Datab
|
|||||||
val database = databaseHelper.writableDatabase
|
val database = databaseHelper.writableDatabase
|
||||||
|
|
||||||
val serverID = database.get(messageIDTable,
|
val serverID = database.get(messageIDTable,
|
||||||
"${Companion.messageID} = ? AND ${Companion.messageType} = ?",
|
"${Companion.messageID} = ? AND $messageType = ?",
|
||||||
arrayOf(messageID.toString(), (if (isSms) SMS_TYPE else MMS_TYPE).toString())) { cursor ->
|
arrayOf(messageID.toString(), (if (isSms) SMS_TYPE else MMS_TYPE).toString())) { cursor ->
|
||||||
cursor.getInt(Companion.serverID).toLong()
|
cursor.getInt(serverID).toLong()
|
||||||
} ?: return
|
} ?: return
|
||||||
|
|
||||||
database.beginTransaction()
|
database.beginTransaction()
|
||||||
@ -89,7 +85,7 @@ class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Datab
|
|||||||
return database.get(messageIDTable,
|
return database.get(messageIDTable,
|
||||||
"$messageID = ? AND ${Companion.serverID} = ?",
|
"$messageID = ? AND ${Companion.serverID} = ?",
|
||||||
arrayOf(mappedID.toString(), mappedServerID.toString())) { cursor ->
|
arrayOf(mappedID.toString(), mappedServerID.toString())) { cursor ->
|
||||||
cursor.getInt(Companion.messageID).toLong() to (cursor.getInt(messageType) == SMS_TYPE)
|
cursor.getInt(messageID).toLong() to (cursor.getInt(messageType) == SMS_TYPE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,18 +133,18 @@ class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Datab
|
|||||||
val database = databaseHelper.writableDatabase
|
val database = databaseHelper.writableDatabase
|
||||||
try {
|
try {
|
||||||
val messages = mutableSetOf<Pair<Long,Long>>()
|
val messages = mutableSetOf<Pair<Long,Long>>()
|
||||||
database.get(messageThreadMappingTable, "${Companion.threadID} = ?", arrayOf(threadId.toString())) { cursor ->
|
database.get(messageThreadMappingTable, "$threadID = ?", arrayOf(threadId.toString())) { cursor ->
|
||||||
// for each add
|
// for each add
|
||||||
while (cursor.moveToNext()) {
|
while (cursor.moveToNext()) {
|
||||||
messages.add(cursor.getLong(Companion.messageID) to cursor.getLong(Companion.serverID))
|
messages.add(cursor.getLong(messageID) to cursor.getLong(serverID))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var deletedCount = 0L
|
var deletedCount = 0L
|
||||||
database.beginTransaction()
|
database.beginTransaction()
|
||||||
messages.forEach { (messageId, serverId) ->
|
messages.forEach { (messageId, serverId) ->
|
||||||
deletedCount += database.delete(messageIDTable, "${Companion.messageID} = ? AND ${Companion.serverID} = ?", arrayOf(messageId.toString(), serverId.toString()))
|
deletedCount += database.delete(messageIDTable, "$messageID = ? AND $serverID = ?", arrayOf(messageId.toString(), serverId.toString()))
|
||||||
}
|
}
|
||||||
val mappingDeleted = database.delete(messageThreadMappingTable, "${Companion.threadID} = ?", arrayOf(threadId.toString()))
|
val mappingDeleted = database.delete(messageThreadMappingTable, "$threadID = ?", arrayOf(threadId.toString()))
|
||||||
database.setTransactionSuccessful()
|
database.setTransactionSuccessful()
|
||||||
} finally {
|
} finally {
|
||||||
database.endTransaction()
|
database.endTransaction()
|
@ -1,12 +1,9 @@
|
|||||||
package org.thoughtcrime.securesms.loki.database
|
package org.thoughtcrime.securesms.database
|
||||||
|
|
||||||
import android.content.ContentValues
|
import android.content.ContentValues
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.database.Cursor
|
import android.database.Cursor
|
||||||
import org.thoughtcrime.securesms.database.Database
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
|
||||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
||||||
import org.thoughtcrime.securesms.loki.utilities.*
|
|
||||||
import org.session.libsession.messaging.open_groups.OpenGroupV2
|
import org.session.libsession.messaging.open_groups.OpenGroupV2
|
||||||
import org.session.libsession.utilities.Address
|
import org.session.libsession.utilities.Address
|
||||||
import org.session.libsession.utilities.recipients.Recipient
|
import org.session.libsession.utilities.recipients.Recipient
|
@ -1,9 +1,7 @@
|
|||||||
package org.thoughtcrime.securesms.loki.database
|
package org.thoughtcrime.securesms.database
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import org.thoughtcrime.securesms.database.Database
|
|
||||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
||||||
import org.thoughtcrime.securesms.loki.utilities.get
|
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
|
|
||||||
class LokiUserDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper) {
|
class LokiUserDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper) {
|
@ -883,6 +883,7 @@ public class MmsDatabase extends MessagingDatabase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void deleteQuotedFromMessages(MessageRecord toDeleteRecord) {
|
public void deleteQuotedFromMessages(MessageRecord toDeleteRecord) {
|
||||||
|
if (toDeleteRecord == null) { return; }
|
||||||
String query = THREAD_ID + " = ?";
|
String query = THREAD_ID + " = ?";
|
||||||
Cursor threadMmsCursor = rawQuery(query, new String[]{String.valueOf(toDeleteRecord.getThreadId())});
|
Cursor threadMmsCursor = rawQuery(query, new String[]{String.valueOf(toDeleteRecord.getThreadId())});
|
||||||
Reader reader = readerFor(threadMmsCursor);
|
Reader reader = readerFor(threadMmsCursor);
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
package org.thoughtcrime.securesms.loki.database
|
package org.thoughtcrime.securesms.database
|
||||||
|
|
||||||
import android.content.ContentValues
|
import android.content.ContentValues
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import net.sqlcipher.Cursor
|
import net.sqlcipher.Cursor
|
||||||
import org.session.libsession.messaging.contacts.Contact
|
import org.session.libsession.messaging.contacts.Contact
|
||||||
import org.session.libsignal.utilities.Base64
|
import org.session.libsignal.utilities.Base64
|
||||||
import org.thoughtcrime.securesms.database.Database
|
|
||||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
||||||
import org.thoughtcrime.securesms.loki.utilities.*
|
|
||||||
|
|
||||||
class SessionContactDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper) {
|
class SessionContactDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper) {
|
||||||
|
|
||||||
@ -35,7 +33,7 @@ class SessionContactDatabase(context: Context, helper: SQLCipherOpenHelper) : Da
|
|||||||
|
|
||||||
fun getContactWithSessionID(sessionID: String): Contact? {
|
fun getContactWithSessionID(sessionID: String): Contact? {
|
||||||
val database = databaseHelper.readableDatabase
|
val database = databaseHelper.readableDatabase
|
||||||
return database.get(sessionContactTable, "${SessionContactDatabase.sessionID} = ?", arrayOf( sessionID )) { cursor ->
|
return database.get(sessionContactTable, "${Companion.sessionID} = ?", arrayOf( sessionID )) { cursor ->
|
||||||
contactFromCursor(cursor)
|
contactFromCursor(cursor)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package org.thoughtcrime.securesms.loki.database
|
package org.thoughtcrime.securesms.database
|
||||||
|
|
||||||
import android.content.ContentValues
|
import android.content.ContentValues
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
@ -6,10 +6,10 @@ import net.sqlcipher.Cursor
|
|||||||
import org.session.libsession.messaging.jobs.*
|
import org.session.libsession.messaging.jobs.*
|
||||||
import org.session.libsession.messaging.utilities.Data
|
import org.session.libsession.messaging.utilities.Data
|
||||||
import org.session.libsignal.utilities.Log
|
import org.session.libsignal.utilities.Log
|
||||||
import org.thoughtcrime.securesms.database.Database
|
import org.thoughtcrime.securesms.database.*
|
||||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.JsonDataSerializer
|
import org.thoughtcrime.securesms.jobmanager.impl.JsonDataSerializer
|
||||||
import org.thoughtcrime.securesms.loki.utilities.*
|
import org.thoughtcrime.securesms.util.*
|
||||||
|
|
||||||
class SessionJobDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper) {
|
class SessionJobDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper) {
|
||||||
|
|
@ -26,13 +26,11 @@ import org.session.libsignal.messages.SignalServiceGroup
|
|||||||
import org.session.libsignal.utilities.KeyHelper
|
import org.session.libsignal.utilities.KeyHelper
|
||||||
import org.session.libsignal.utilities.guava.Optional
|
import org.session.libsignal.utilities.guava.Optional
|
||||||
import org.thoughtcrime.securesms.ApplicationContext
|
import org.thoughtcrime.securesms.ApplicationContext
|
||||||
|
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
|
||||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
||||||
import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob
|
import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob
|
||||||
import org.thoughtcrime.securesms.loki.api.OpenGroupManager
|
import org.thoughtcrime.securesms.groups.OpenGroupManager
|
||||||
import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase
|
import org.thoughtcrime.securesms.util.SessionMetaProtocol
|
||||||
import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol
|
|
||||||
import org.thoughtcrime.securesms.loki.utilities.get
|
|
||||||
import org.thoughtcrime.securesms.loki.utilities.getString
|
|
||||||
import org.thoughtcrime.securesms.mms.PartAuthority
|
import org.thoughtcrime.securesms.mms.PartAuthority
|
||||||
|
|
||||||
class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), StorageProtocol {
|
class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), StorageProtocol {
|
||||||
|
@ -49,7 +49,7 @@ import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
|
|||||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
||||||
import org.thoughtcrime.securesms.database.model.ThreadRecord;
|
import org.thoughtcrime.securesms.database.model.ThreadRecord;
|
||||||
import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol;
|
import org.thoughtcrime.securesms.util.SessionMetaProtocol;
|
||||||
import org.thoughtcrime.securesms.mms.Slide;
|
import org.thoughtcrime.securesms.mms.Slide;
|
||||||
import org.thoughtcrime.securesms.mms.SlideDeck;
|
import org.thoughtcrime.securesms.mms.SlideDeck;
|
||||||
|
|
||||||
|
@ -23,14 +23,13 @@ import org.thoughtcrime.securesms.database.SearchDatabase;
|
|||||||
import org.thoughtcrime.securesms.database.SmsDatabase;
|
import org.thoughtcrime.securesms.database.SmsDatabase;
|
||||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||||
import org.session.libsignal.utilities.Log;
|
import org.session.libsignal.utilities.Log;
|
||||||
import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase;
|
import org.thoughtcrime.securesms.database.LokiAPIDatabase;
|
||||||
import org.thoughtcrime.securesms.loki.database.LokiBackupFilesDatabase;
|
import org.thoughtcrime.securesms.database.LokiBackupFilesDatabase;
|
||||||
import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase;
|
import org.thoughtcrime.securesms.database.LokiMessageDatabase;
|
||||||
import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase;
|
import org.thoughtcrime.securesms.database.LokiThreadDatabase;
|
||||||
import org.thoughtcrime.securesms.loki.database.LokiUserDatabase;
|
import org.thoughtcrime.securesms.database.LokiUserDatabase;
|
||||||
import org.thoughtcrime.securesms.loki.database.SessionContactDatabase;
|
import org.thoughtcrime.securesms.database.SessionContactDatabase;
|
||||||
import org.thoughtcrime.securesms.loki.database.SessionJobDatabase;
|
import org.thoughtcrime.securesms.database.SessionJobDatabase;
|
||||||
import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsMigration;
|
|
||||||
|
|
||||||
public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
||||||
|
|
||||||
@ -182,8 +181,6 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
|||||||
|
|
||||||
if (oldVersion < lokiV12) {
|
if (oldVersion < lokiV12) {
|
||||||
db.execSQL(LokiAPIDatabase.getCreateLastMessageHashValueTable2Command());
|
db.execSQL(LokiAPIDatabase.getCreateLastMessageHashValueTable2Command());
|
||||||
db.execSQL(ClosedGroupsMigration.getCreateCurrentClosedGroupRatchetTableCommand());
|
|
||||||
db.execSQL(ClosedGroupsMigration.getCreateClosedGroupPrivateKeyTableCommand());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oldVersion < lokiV13) {
|
if (oldVersion < lokiV13) {
|
||||||
@ -193,10 +190,6 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
|||||||
if (oldVersion < lokiV14_BACKUP_FILES) {
|
if (oldVersion < lokiV14_BACKUP_FILES) {
|
||||||
db.execSQL(LokiBackupFilesDatabase.getCreateTableCommand());
|
db.execSQL(LokiBackupFilesDatabase.getCreateTableCommand());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oldVersion < lokiV15) {
|
|
||||||
db.execSQL(ClosedGroupsMigration.getCreateOldClosedGroupRatchetTableCommand());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oldVersion < lokiV16) {
|
if (oldVersion < lokiV16) {
|
||||||
db.execSQL(LokiAPIDatabase.getCreateOpenGroupProfilePictureTableCommand());
|
db.execSQL(LokiAPIDatabase.getCreateOpenGroupProfilePictureTableCommand());
|
||||||
@ -217,7 +210,6 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
|||||||
if (oldVersion < lokiV19) {
|
if (oldVersion < lokiV19) {
|
||||||
db.execSQL(LokiAPIDatabase.getCreateClosedGroupEncryptionKeyPairsTable());
|
db.execSQL(LokiAPIDatabase.getCreateClosedGroupEncryptionKeyPairsTable());
|
||||||
db.execSQL(LokiAPIDatabase.getCreateClosedGroupPublicKeysTable());
|
db.execSQL(LokiAPIDatabase.getCreateClosedGroupPublicKeysTable());
|
||||||
ClosedGroupsMigration.INSTANCE.perform(db);
|
|
||||||
db.execSQL("DROP TABLE identities");
|
db.execSQL("DROP TABLE identities");
|
||||||
deleteJobRecords(db, "RetrieveProfileJob");
|
deleteJobRecords(db, "RetrieveProfileJob");
|
||||||
deleteJobRecords(db,
|
deleteJobRecords(db,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package org.thoughtcrime.securesms.loki.activities
|
package org.thoughtcrime.securesms.dms
|
||||||
|
|
||||||
import android.animation.Animator
|
import android.animation.Animator
|
||||||
import android.animation.AnimatorListenerAdapter
|
import android.animation.AnimatorListenerAdapter
|
||||||
@ -32,8 +32,8 @@ import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
|||||||
|
|
||||||
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
|
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragment
|
import org.thoughtcrime.securesms.util.ScanQRCodeWrapperFragment
|
||||||
import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragmentDelegate
|
import org.thoughtcrime.securesms.util.ScanQRCodeWrapperFragmentDelegate
|
||||||
|
|
||||||
class CreatePrivateChatActivity : PassphraseRequiredActionBarActivity(), ScanQRCodeWrapperFragmentDelegate {
|
class CreatePrivateChatActivity : PassphraseRequiredActionBarActivity(), ScanQRCodeWrapperFragmentDelegate {
|
||||||
private val adapter = CreatePrivateChatActivityAdapter(this)
|
private val adapter = CreatePrivateChatActivityAdapter(this)
|
||||||
@ -191,6 +191,7 @@ class EnterPublicKeyFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun handleIsKeyboardShowingChanged() {
|
private fun handleIsKeyboardShowingChanged() {
|
||||||
|
val optionalContentContainer = optionalContentContainer ?: return
|
||||||
optionalContentContainer.isVisible = !isKeyboardShowing
|
optionalContentContainer.isVisible = !isKeyboardShowing
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package org.thoughtcrime.securesms.loki.dialogs
|
package org.thoughtcrime.securesms.groups
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
@ -1,4 +1,4 @@
|
|||||||
package org.thoughtcrime.securesms.loki.activities
|
package org.thoughtcrime.securesms.groups
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
@ -19,14 +19,15 @@ import org.session.libsession.messaging.sending_receiving.groupSizeLimit
|
|||||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||||
|
|
||||||
import org.session.libsession.utilities.Address
|
import org.session.libsession.utilities.Address
|
||||||
import org.session.libsession.utilities.DistributionTypes
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
import org.thoughtcrime.securesms.loki.utilities.fadeIn
|
import org.thoughtcrime.securesms.util.fadeIn
|
||||||
import org.thoughtcrime.securesms.loki.utilities.fadeOut
|
import org.thoughtcrime.securesms.util.fadeOut
|
||||||
import org.thoughtcrime.securesms.mms.GlideApp
|
import org.thoughtcrime.securesms.mms.GlideApp
|
||||||
import org.session.libsession.utilities.recipients.Recipient
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
|
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
|
||||||
|
import org.thoughtcrime.securesms.contacts.SelectContactsAdapter
|
||||||
|
import org.thoughtcrime.securesms.contacts.SelectContactsLoader
|
||||||
|
|
||||||
//TODO Refactor to avoid using kotlinx.android.synthetic
|
//TODO Refactor to avoid using kotlinx.android.synthetic
|
||||||
class CreateClosedGroupActivity : PassphraseRequiredActionBarActivity(), LoaderManager.LoaderCallbacks<List<String>> {
|
class CreateClosedGroupActivity : PassphraseRequiredActionBarActivity(), LoaderManager.LoaderCallbacks<List<String>> {
|
@ -1,10 +1,11 @@
|
|||||||
package org.thoughtcrime.securesms.loki.viewmodel
|
package org.thoughtcrime.securesms.groups
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.asLiveData
|
import androidx.lifecycle.asLiveData
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.onStart
|
import kotlinx.coroutines.flow.onStart
|
||||||
import org.session.libsession.messaging.open_groups.OpenGroupAPIV2
|
import org.session.libsession.messaging.open_groups.OpenGroupAPIV2
|
||||||
|
import org.thoughtcrime.securesms.util.State
|
||||||
|
|
||||||
typealias DefaultGroups = List<OpenGroupAPIV2.DefaultGroup>
|
typealias DefaultGroups = List<OpenGroupAPIV2.DefaultGroup>
|
||||||
typealias GroupState = State<DefaultGroups>
|
typealias GroupState = State<DefaultGroups>
|
@ -1,4 +1,4 @@
|
|||||||
package org.thoughtcrime.securesms.loki.activities
|
package org.thoughtcrime.securesms.groups
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
@ -24,15 +24,15 @@ import org.session.libsession.messaging.sending_receiving.groupSizeLimit
|
|||||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||||
import org.session.libsession.utilities.Address
|
import org.session.libsession.utilities.Address
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
import org.thoughtcrime.securesms.loki.dialogs.ClosedGroupEditingOptionsBottomSheet
|
import org.thoughtcrime.securesms.util.fadeIn
|
||||||
import org.thoughtcrime.securesms.loki.utilities.fadeIn
|
import org.thoughtcrime.securesms.util.fadeOut
|
||||||
import org.thoughtcrime.securesms.loki.utilities.fadeOut
|
|
||||||
import org.thoughtcrime.securesms.mms.GlideApp
|
import org.thoughtcrime.securesms.mms.GlideApp
|
||||||
import org.session.libsession.utilities.recipients.Recipient
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
import org.session.libsession.utilities.GroupUtil
|
import org.session.libsession.utilities.GroupUtil
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
import org.session.libsession.utilities.ThemeUtil
|
import org.session.libsession.utilities.ThemeUtil
|
||||||
import org.session.libsignal.utilities.toHexString
|
import org.session.libsignal.utilities.toHexString
|
||||||
|
import org.thoughtcrime.securesms.contacts.SelectContactsActivity
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
|
class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
|
@ -1,4 +1,4 @@
|
|||||||
package org.thoughtcrime.securesms.loki.activities
|
package org.thoughtcrime.securesms.groups
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
@ -1,10 +1,10 @@
|
|||||||
package org.thoughtcrime.securesms.loki.activities
|
package org.thoughtcrime.securesms.groups
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import org.session.libsession.utilities.Address
|
import org.session.libsession.utilities.Address
|
||||||
import org.thoughtcrime.securesms.loki.views.UserView
|
import org.thoughtcrime.securesms.contacts.UserView
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||||
import org.session.libsession.utilities.recipients.Recipient
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
@ -1,4 +1,4 @@
|
|||||||
package org.thoughtcrime.securesms.loki.activities
|
package org.thoughtcrime.securesms.groups
|
||||||
|
|
||||||
import android.animation.Animator
|
import android.animation.Animator
|
||||||
import android.animation.AnimatorListenerAdapter
|
import android.animation.AnimatorListenerAdapter
|
||||||
@ -25,22 +25,20 @@ import network.loki.messenger.R
|
|||||||
import okhttp3.HttpUrl
|
import okhttp3.HttpUrl
|
||||||
import org.session.libsession.messaging.open_groups.OpenGroupAPIV2.DefaultGroup
|
import org.session.libsession.messaging.open_groups.OpenGroupAPIV2.DefaultGroup
|
||||||
import org.session.libsession.utilities.Address
|
import org.session.libsession.utilities.Address
|
||||||
import org.session.libsession.utilities.DistributionTypes
|
|
||||||
import org.session.libsession.utilities.recipients.Recipient
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
import org.session.libsession.utilities.GroupUtil
|
import org.session.libsession.utilities.GroupUtil
|
||||||
import org.session.libsignal.utilities.Log
|
import org.session.libsignal.utilities.Log
|
||||||
import org.session.libsignal.utilities.PublicKeyValidation
|
import org.session.libsignal.utilities.PublicKeyValidation
|
||||||
import org.thoughtcrime.securesms.BaseActionBarActivity
|
import org.thoughtcrime.securesms.BaseActionBarActivity
|
||||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
|
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
|
||||||
|
import org.thoughtcrime.securesms.groups.DefaultGroupsViewModel
|
||||||
import org.thoughtcrime.securesms.groups.GroupManager
|
import org.thoughtcrime.securesms.groups.GroupManager
|
||||||
import org.thoughtcrime.securesms.loki.api.OpenGroupManager
|
import org.thoughtcrime.securesms.groups.OpenGroupManager
|
||||||
import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragment
|
import org.thoughtcrime.securesms.util.ScanQRCodeWrapperFragment
|
||||||
import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragmentDelegate
|
import org.thoughtcrime.securesms.util.ScanQRCodeWrapperFragmentDelegate
|
||||||
import org.thoughtcrime.securesms.loki.protocol.MultiDeviceProtocol
|
import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
|
||||||
import org.thoughtcrime.securesms.loki.viewmodel.DefaultGroupsViewModel
|
import org.thoughtcrime.securesms.util.State
|
||||||
import org.thoughtcrime.securesms.loki.viewmodel.State
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class JoinPublicChatActivity : PassphraseRequiredActionBarActivity(), ScanQRCodeWrapperFragmentDelegate {
|
class JoinPublicChatActivity : PassphraseRequiredActionBarActivity(), ScanQRCodeWrapperFragmentDelegate {
|
||||||
@ -108,7 +106,7 @@ class JoinPublicChatActivity : PassphraseRequiredActionBarActivity(), ScanQRCode
|
|||||||
} else {
|
} else {
|
||||||
throw Exception("No longer supported.")
|
throw Exception("No longer supported.")
|
||||||
}
|
}
|
||||||
MultiDeviceProtocol.forceSyncConfigurationNowIfNeeded(this@JoinPublicChatActivity)
|
ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(this@JoinPublicChatActivity)
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
val recipient = Recipient.from(this@JoinPublicChatActivity, Address.fromSerialized(groupID), false)
|
val recipient = Recipient.from(this@JoinPublicChatActivity, Address.fromSerialized(groupID), false)
|
||||||
openConversationActivity(this@JoinPublicChatActivity, threadID, recipient)
|
openConversationActivity(this@JoinPublicChatActivity, threadID, recipient)
|
@ -1,4 +1,4 @@
|
|||||||
package org.thoughtcrime.securesms.loki.activities
|
package org.thoughtcrime.securesms.groups
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import kotlinx.android.synthetic.main.activity_open_group_guidelines.*
|
import kotlinx.android.synthetic.main.activity_open_group_guidelines.*
|
@ -1,4 +1,4 @@
|
|||||||
package org.thoughtcrime.securesms.loki.api
|
package org.thoughtcrime.securesms.groups
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
@ -11,7 +11,6 @@ import org.session.libsession.messaging.sending_receiving.pollers.OpenGroupPolle
|
|||||||
import org.session.libsession.utilities.Util
|
import org.session.libsession.utilities.Util
|
||||||
import org.session.libsignal.utilities.ThreadUtils
|
import org.session.libsignal.utilities.ThreadUtils
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
import org.thoughtcrime.securesms.groups.GroupManager
|
|
||||||
import org.thoughtcrime.securesms.util.BitmapUtil
|
import org.thoughtcrime.securesms.util.BitmapUtil
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
|
|
||||||
@ -116,6 +115,7 @@ object OpenGroupManager {
|
|||||||
val lokiThreadDB = DatabaseFactory.getLokiThreadDatabase(context)
|
val lokiThreadDB = DatabaseFactory.getLokiThreadDatabase(context)
|
||||||
lokiThreadDB.removeOpenGroupChat(threadID)
|
lokiThreadDB.removeOpenGroupChat(threadID)
|
||||||
ThreadUtils.queue {
|
ThreadUtils.queue {
|
||||||
|
threadDB.deleteConversation(threadID) // Must be invoked on a background thread
|
||||||
GroupManager.deleteGroup(groupID, context) // Must be invoked on a background thread
|
GroupManager.deleteGroup(groupID, context) // Must be invoked on a background thread
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package org.thoughtcrime.securesms.loki.utilities
|
package org.thoughtcrime.securesms.groups
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.annotation.WorkerThread
|
import androidx.annotation.WorkerThread
|
@ -1,4 +1,4 @@
|
|||||||
package org.thoughtcrime.securesms.loki.dialogs
|
package org.thoughtcrime.securesms.home
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
@ -1,4 +1,4 @@
|
|||||||
package org.thoughtcrime.securesms.loki.views
|
package org.thoughtcrime.securesms.home
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
@ -15,8 +15,8 @@ import kotlinx.android.synthetic.main.view_conversation.view.*
|
|||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.session.libsession.utilities.recipients.Recipient
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
import org.thoughtcrime.securesms.database.model.ThreadRecord
|
import org.thoughtcrime.securesms.database.model.ThreadRecord
|
||||||
import org.thoughtcrime.securesms.loki.utilities.MentionManagerUtilities.populateUserPublicKeyCacheIfNeeded
|
import org.thoughtcrime.securesms.conversation.v2.utilities.MentionManagerUtilities.populateUserPublicKeyCacheIfNeeded
|
||||||
import org.thoughtcrime.securesms.loki.utilities.MentionUtilities.highlightMentions
|
import org.thoughtcrime.securesms.conversation.v2.utilities.MentionUtilities.highlightMentions
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||||
import org.thoughtcrime.securesms.util.DateUtils
|
import org.thoughtcrime.securesms.util.DateUtils
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -58,7 +58,7 @@ class ConversationView : LinearLayout {
|
|||||||
profilePictureView.update(thread.recipient, thread.threadId)
|
profilePictureView.update(thread.recipient, thread.threadId)
|
||||||
val senderDisplayName = getUserDisplayName(thread.recipient) ?: thread.recipient.address.toString()
|
val senderDisplayName = getUserDisplayName(thread.recipient) ?: thread.recipient.address.toString()
|
||||||
conversationViewDisplayNameTextView.text = senderDisplayName
|
conversationViewDisplayNameTextView.text = senderDisplayName
|
||||||
timestampTextView.text = DateUtils.getBriefRelativeTimeSpanString(context, Locale.getDefault(), thread.date)
|
timestampTextView.text = DateUtils.getDisplayFormattedTimeSpanString(context, Locale.getDefault(), thread.date)
|
||||||
muteIndicatorImageView.visibility = if (thread.recipient.isMuted) VISIBLE else GONE
|
muteIndicatorImageView.visibility = if (thread.recipient.isMuted) VISIBLE else GONE
|
||||||
val rawSnippet = thread.getDisplayBody(context)
|
val rawSnippet = thread.getDisplayBody(context)
|
||||||
val snippet = highlightMentions(rawSnippet, thread.threadId, context)
|
val snippet = highlightMentions(rawSnippet, thread.threadId, context)
|
@ -1,4 +1,4 @@
|
|||||||
package org.thoughtcrime.securesms.loki.activities
|
package org.thoughtcrime.securesms.home
|
||||||
|
|
||||||
import android.app.AlertDialog
|
import android.app.AlertDialog
|
||||||
import android.content.BroadcastReceiver
|
import android.content.BroadcastReceiver
|
||||||
@ -29,28 +29,31 @@ import org.greenrobot.eventbus.EventBus
|
|||||||
import org.greenrobot.eventbus.Subscribe
|
import org.greenrobot.eventbus.Subscribe
|
||||||
import org.greenrobot.eventbus.ThreadMode
|
import org.greenrobot.eventbus.ThreadMode
|
||||||
import org.session.libsession.messaging.jobs.JobQueue
|
import org.session.libsession.messaging.jobs.JobQueue
|
||||||
import org.session.libsession.messaging.mentions.MentionsManager
|
|
||||||
import org.session.libsession.messaging.sending_receiving.MessageSender
|
import org.session.libsession.messaging.sending_receiving.MessageSender
|
||||||
import org.session.libsession.messaging.sending_receiving.pollers.OpenGroupPollerV2
|
|
||||||
import org.session.libsession.utilities.*
|
import org.session.libsession.utilities.*
|
||||||
import org.session.libsignal.utilities.toHexString
|
import org.session.libsession.utilities.Util
|
||||||
import org.session.libsignal.utilities.ThreadUtils
|
import org.session.libsignal.utilities.ThreadUtils
|
||||||
|
import org.session.libsignal.utilities.toHexString
|
||||||
import org.thoughtcrime.securesms.ApplicationContext
|
import org.thoughtcrime.securesms.ApplicationContext
|
||||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
|
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
|
||||||
|
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
import org.thoughtcrime.securesms.database.model.ThreadRecord
|
import org.thoughtcrime.securesms.database.model.ThreadRecord
|
||||||
import org.thoughtcrime.securesms.loki.api.OpenGroupManager
|
import org.thoughtcrime.securesms.dms.CreatePrivateChatActivity
|
||||||
import org.thoughtcrime.securesms.loki.dialogs.*
|
import org.thoughtcrime.securesms.groups.CreateClosedGroupActivity
|
||||||
import org.thoughtcrime.securesms.loki.protocol.MultiDeviceProtocol
|
import org.thoughtcrime.securesms.groups.JoinPublicChatActivity
|
||||||
import org.thoughtcrime.securesms.loki.utilities.*
|
import org.thoughtcrime.securesms.groups.OpenGroupManager
|
||||||
import org.thoughtcrime.securesms.loki.views.ConversationView
|
import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
|
||||||
import org.thoughtcrime.securesms.loki.views.NewConversationButtonSetViewDelegate
|
import org.thoughtcrime.securesms.util.*
|
||||||
import org.thoughtcrime.securesms.loki.views.SeedReminderViewDelegate
|
import org.thoughtcrime.securesms.onboarding.SeedReminderViewDelegate
|
||||||
import org.thoughtcrime.securesms.mms.GlideApp
|
import org.thoughtcrime.securesms.mms.GlideApp
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||||
|
import org.thoughtcrime.securesms.onboarding.SeedActivity
|
||||||
|
import org.thoughtcrime.securesms.preferences.SettingsActivity
|
||||||
|
import org.thoughtcrime.securesms.util.IP2Country
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
class HomeActivity : PassphraseRequiredActionBarActivity(), ConversationClickListener, SeedReminderViewDelegate, NewConversationButtonSetViewDelegate {
|
class HomeActivity : PassphraseRequiredActionBarActivity(), ConversationClickListener, SeedReminderViewDelegate, NewConversationButtonSetViewDelegate {
|
||||||
private lateinit var glide: GlideRequests
|
private lateinit var glide: GlideRequests
|
||||||
@ -156,8 +159,8 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), ConversationClickLis
|
|||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
if (TextSecurePreferences.getLocalNumber(this) == null) {
|
if (TextSecurePreferences.getLocalNumber(this) == null) { return; } // This can be the case after a secondary device is auto-cleared
|
||||||
return; } // This can be the case after a secondary device is auto-cleared
|
IdentityKeyUtil.checkUpdate(this)
|
||||||
profileButton.recycle() // clear cached image before update tje profilePictureView
|
profileButton.recycle() // clear cached image before update tje profilePictureView
|
||||||
profileButton.update()
|
profileButton.update()
|
||||||
val hasViewedSeed = TextSecurePreferences.getHasViewedSeed(this)
|
val hasViewedSeed = TextSecurePreferences.getHasViewedSeed(this)
|
||||||
@ -166,7 +169,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), ConversationClickLis
|
|||||||
}
|
}
|
||||||
if (TextSecurePreferences.getConfigurationMessageSynced(this)) {
|
if (TextSecurePreferences.getConfigurationMessageSynced(this)) {
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
MultiDeviceProtocol.syncConfigurationIfNeeded(this@HomeActivity)
|
ConfigurationMessageUtilities.syncConfigurationIfNeeded(this@HomeActivity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package org.thoughtcrime.securesms.loki.activities
|
package org.thoughtcrime.securesms.home
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.database.Cursor
|
import android.database.Cursor
|
||||||
@ -7,7 +7,6 @@ import android.view.ViewGroup
|
|||||||
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter
|
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
import org.thoughtcrime.securesms.database.model.ThreadRecord
|
import org.thoughtcrime.securesms.database.model.ThreadRecord
|
||||||
import org.thoughtcrime.securesms.loki.views.ConversationView
|
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||||
|
|
||||||
class HomeAdapter(context: Context, cursor: Cursor) : CursorRecyclerViewAdapter<HomeAdapter.ViewHolder>(context, cursor) {
|
class HomeAdapter(context: Context, cursor: Cursor) : CursorRecyclerViewAdapter<HomeAdapter.ViewHolder>(context, cursor) {
|
@ -1,4 +1,4 @@
|
|||||||
package org.thoughtcrime.securesms.loki.activities
|
package org.thoughtcrime.securesms.home
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.database.Cursor
|
import android.database.Cursor
|
@ -1,4 +1,4 @@
|
|||||||
package org.thoughtcrime.securesms.loki.views
|
package org.thoughtcrime.securesms.home
|
||||||
|
|
||||||
import android.animation.FloatEvaluator
|
import android.animation.FloatEvaluator
|
||||||
import android.animation.PointFEvaluator
|
import android.animation.PointFEvaluator
|
||||||
@ -16,7 +16,9 @@ import android.widget.RelativeLayout
|
|||||||
import androidx.annotation.ColorRes
|
import androidx.annotation.ColorRes
|
||||||
import androidx.annotation.DrawableRes
|
import androidx.annotation.DrawableRes
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.thoughtcrime.securesms.loki.utilities.*
|
import org.thoughtcrime.securesms.util.*
|
||||||
|
import org.thoughtcrime.securesms.util.GlowViewUtilities
|
||||||
|
import org.thoughtcrime.securesms.util.NewConversationButtonImageView
|
||||||
|
|
||||||
class NewConversationButtonSetView : RelativeLayout {
|
class NewConversationButtonSetView : RelativeLayout {
|
||||||
private var expandedButton: Button? = null
|
private var expandedButton: Button? = null
|
@ -1,4 +1,4 @@
|
|||||||
package org.thoughtcrime.securesms.loki.activities
|
package org.thoughtcrime.securesms.home
|
||||||
|
|
||||||
import android.content.BroadcastReceiver
|
import android.content.BroadcastReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
@ -22,9 +22,10 @@ import network.loki.messenger.R
|
|||||||
import org.session.libsession.snode.OnionRequestAPI
|
import org.session.libsession.snode.OnionRequestAPI
|
||||||
import org.session.libsignal.utilities.Snode
|
import org.session.libsignal.utilities.Snode
|
||||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||||
import org.thoughtcrime.securesms.loki.utilities.*
|
import org.thoughtcrime.securesms.util.*
|
||||||
import org.thoughtcrime.securesms.loki.views.GlowViewUtilities
|
import org.thoughtcrime.securesms.util.GlowViewUtilities
|
||||||
import org.thoughtcrime.securesms.loki.views.PathDotView
|
import org.thoughtcrime.securesms.util.IP2Country
|
||||||
|
import org.thoughtcrime.securesms.util.PathDotView
|
||||||
|
|
||||||
class PathActivity : PassphraseRequiredActionBarActivity() {
|
class PathActivity : PassphraseRequiredActionBarActivity() {
|
||||||
private val broadcastReceivers = mutableListOf<BroadcastReceiver>()
|
private val broadcastReceivers = mutableListOf<BroadcastReceiver>()
|
@ -1,4 +1,4 @@
|
|||||||
package org.thoughtcrime.securesms.loki.views
|
package org.thoughtcrime.securesms.home
|
||||||
|
|
||||||
import android.content.BroadcastReceiver
|
import android.content.BroadcastReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
@ -12,8 +12,8 @@ import android.view.View
|
|||||||
import androidx.annotation.ColorInt
|
import androidx.annotation.ColorInt
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.session.libsession.snode.OnionRequestAPI
|
import org.session.libsession.snode.OnionRequestAPI
|
||||||
import org.thoughtcrime.securesms.loki.utilities.getColorWithID
|
import org.thoughtcrime.securesms.util.getColorWithID
|
||||||
import org.thoughtcrime.securesms.loki.utilities.toPx
|
import org.thoughtcrime.securesms.util.toPx
|
||||||
|
|
||||||
class PathStatusView : View {
|
class PathStatusView : View {
|
||||||
private val broadcastReceivers = mutableListOf<BroadcastReceiver>()
|
private val broadcastReceivers = mutableListOf<BroadcastReceiver>()
|
@ -1,4 +1,4 @@
|
|||||||
package org.thoughtcrime.securesms.loki.dialogs
|
package org.thoughtcrime.securesms.home
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.ClipData
|
import android.content.ClipData
|
||||||
@ -17,7 +17,6 @@ import network.loki.messenger.R
|
|||||||
import org.session.libsession.messaging.contacts.Contact
|
import org.session.libsession.messaging.contacts.Contact
|
||||||
import org.session.libsession.utilities.Address
|
import org.session.libsession.utilities.Address
|
||||||
import org.session.libsession.utilities.recipients.Recipient
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
import org.session.libsession.utilities.SSKEnvironment
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
import org.thoughtcrime.securesms.mms.GlideApp
|
import org.thoughtcrime.securesms.mms.GlideApp
|
||||||
|
|
@ -14,7 +14,6 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraintObserver;
|
|||||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkOrCellServiceConstraint;
|
import org.thoughtcrime.securesms.jobmanager.impl.NetworkOrCellServiceConstraint;
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraint;
|
import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraint;
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraintObserver;
|
import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraintObserver;
|
||||||
import org.thoughtcrime.securesms.loki.api.PrepareAttachmentAudioExtrasJob;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
@ -6,7 +6,7 @@ import org.session.libsession.messaging.utilities.Data;
|
|||||||
import org.session.libsignal.utilities.NoExternalStorageException;
|
import org.session.libsignal.utilities.NoExternalStorageException;
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||||
import org.session.libsignal.utilities.Log;
|
import org.session.libsignal.utilities.Log;
|
||||||
import org.thoughtcrime.securesms.loki.database.BackupFileRecord;
|
import org.thoughtcrime.securesms.database.BackupFileRecord;
|
||||||
import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
||||||
import org.thoughtcrime.securesms.service.GenericForegroundService;
|
import org.thoughtcrime.securesms.service.GenericForegroundService;
|
||||||
import org.thoughtcrime.securesms.util.BackupUtil;
|
import org.thoughtcrime.securesms.util.BackupUtil;
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
package org.thoughtcrime.securesms.loki.api
|
package org.thoughtcrime.securesms.jobs
|
||||||
|
|
||||||
import android.media.MediaDataSource
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import org.session.libsignal.utilities.Log
|
import org.session.libsignal.utilities.Log
|
||||||
import androidx.annotation.RequiresApi
|
|
||||||
import org.greenrobot.eventbus.EventBus
|
import org.greenrobot.eventbus.EventBus
|
||||||
import org.session.libsession.messaging.utilities.Data
|
import org.session.libsession.messaging.utilities.Data
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.Attachment
|
import org.session.libsession.messaging.sending_receiving.attachments.Attachment
|
||||||
@ -13,9 +11,7 @@ import org.session.libsession.utilities.DecodedAudio
|
|||||||
import org.session.libsession.utilities.InputStreamMediaDataSource
|
import org.session.libsession.utilities.InputStreamMediaDataSource
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job
|
import org.thoughtcrime.securesms.jobmanager.Job
|
||||||
import org.thoughtcrime.securesms.jobs.BaseJob
|
|
||||||
import org.thoughtcrime.securesms.mms.PartAuthority
|
import org.thoughtcrime.securesms.mms.PartAuthority
|
||||||
import java.io.InputStream
|
|
||||||
import java.lang.IllegalStateException
|
import java.lang.IllegalStateException
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
@ -1,71 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.loki.api
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.work.*
|
|
||||||
import org.session.libsignal.utilities.Log
|
|
||||||
import org.thoughtcrime.securesms.loki.utilities.OpenGroupUtilities
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delegates the [OpenGroupUtilities.updateGroupInfo] call to the work manager.
|
|
||||||
*/
|
|
||||||
class PublicChatInfoUpdateWorker(val context: Context, params: WorkerParameters) : Worker(context, params) {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val TAG = "PublicChatInfoUpdateWorker"
|
|
||||||
|
|
||||||
private const val DATA_KEY_SERVER_URL = "server_uRL"
|
|
||||||
private const val DATA_KEY_CHANNEL = "channel"
|
|
||||||
private const val DATA_KEY_ROOM = "room"
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun scheduleInstant(context: Context, serverUrl: String, room :String) {
|
|
||||||
val workRequest = OneTimeWorkRequestBuilder<PublicChatInfoUpdateWorker>()
|
|
||||||
.setConstraints(Constraints.Builder()
|
|
||||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
.setInputData(workDataOf(
|
|
||||||
DATA_KEY_SERVER_URL to serverUrl,
|
|
||||||
DATA_KEY_ROOM to room
|
|
||||||
))
|
|
||||||
.build()
|
|
||||||
|
|
||||||
WorkManager
|
|
||||||
.getInstance(context)
|
|
||||||
.enqueue(workRequest)
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun scheduleInstant(context: Context, serverURL: String, channel: Long) {
|
|
||||||
val workRequest = OneTimeWorkRequestBuilder<PublicChatInfoUpdateWorker>()
|
|
||||||
.setConstraints(Constraints.Builder()
|
|
||||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
.setInputData(workDataOf(
|
|
||||||
DATA_KEY_SERVER_URL to serverURL,
|
|
||||||
DATA_KEY_CHANNEL to channel
|
|
||||||
))
|
|
||||||
.build()
|
|
||||||
|
|
||||||
WorkManager
|
|
||||||
.getInstance(context)
|
|
||||||
.enqueue(workRequest)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun doWork(): Result {
|
|
||||||
val serverUrl = inputData.getString(DATA_KEY_SERVER_URL)!!
|
|
||||||
val room = inputData.getString(DATA_KEY_ROOM)
|
|
||||||
val openGroupId = "$serverUrl.$room"
|
|
||||||
return try {
|
|
||||||
Log.v(TAG, "Updating open group info for $openGroupId.")
|
|
||||||
OpenGroupUtilities.updateGroupInfo(context, serverUrl, room!!)
|
|
||||||
Log.v(TAG, "Open group info was successfully updated for $openGroupId.")
|
|
||||||
Result.success()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e(TAG, "Failed to update open group info for $openGroupId", e)
|
|
||||||
Result.failure()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.loki.dialogs
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import kotlinx.android.synthetic.main.fragment_device_list_bottom_sheet.*
|
|
||||||
import network.loki.messenger.R
|
|
||||||
|
|
||||||
public class DeviceEditingOptionsBottomSheet : BottomSheetDialogFragment() {
|
|
||||||
var onEditTapped: (() -> Unit)? = null
|
|
||||||
var onUnlinkTapped: (() -> Unit)? = null
|
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
|
||||||
return inflater.inflate(R.layout.fragment_device_list_bottom_sheet, container, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
||||||
super.onViewCreated(view, savedInstanceState)
|
|
||||||
editDisplayNameText.setOnClickListener { onEditTapped?.invoke() }
|
|
||||||
unlinkDeviceText.setOnClickListener { onUnlinkTapped?.invoke() }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,73 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.loki.protocol
|
|
||||||
|
|
||||||
import android.content.ContentValues
|
|
||||||
import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase
|
|
||||||
import org.thoughtcrime.securesms.loki.utilities.get
|
|
||||||
import org.thoughtcrime.securesms.loki.utilities.getAll
|
|
||||||
import org.thoughtcrime.securesms.loki.utilities.getString
|
|
||||||
import org.thoughtcrime.securesms.loki.utilities.insertOrUpdate
|
|
||||||
import org.session.libsignal.utilities.Hex
|
|
||||||
import org.session.libsignal.crypto.ecc.DjbECPrivateKey
|
|
||||||
import org.session.libsignal.crypto.ecc.DjbECPublicKey
|
|
||||||
import org.session.libsignal.crypto.ecc.ECKeyPair
|
|
||||||
import org.session.libsignal.utilities.PublicKeyValidation
|
|
||||||
import org.session.libsignal.utilities.removing05PrefixIfNeeded
|
|
||||||
import org.session.libsignal.utilities.toHexString
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
object ClosedGroupsMigration {
|
|
||||||
|
|
||||||
public val closedGroupPublicKey = "closed_group_public_key"
|
|
||||||
// Ratchets
|
|
||||||
private val oldClosedGroupRatchetTable = "old_closed_group_ratchet_table"
|
|
||||||
private val currentClosedGroupRatchetTable = "closed_group_ratchet_table"
|
|
||||||
private val senderPublicKey = "sender_public_key"
|
|
||||||
private val chainKey = "chain_key"
|
|
||||||
private val keyIndex = "key_index"
|
|
||||||
private val messageKeys = "message_keys"
|
|
||||||
@JvmStatic val createOldClosedGroupRatchetTableCommand
|
|
||||||
= "CREATE TABLE $oldClosedGroupRatchetTable ($closedGroupPublicKey STRING, $senderPublicKey STRING, $chainKey STRING, " +
|
|
||||||
"$keyIndex INTEGER DEFAULT 0, $messageKeys TEXT, PRIMARY KEY ($closedGroupPublicKey, $senderPublicKey));"
|
|
||||||
// Private keys
|
|
||||||
@JvmStatic val createCurrentClosedGroupRatchetTableCommand
|
|
||||||
= "CREATE TABLE $currentClosedGroupRatchetTable ($closedGroupPublicKey STRING, $senderPublicKey STRING, $chainKey STRING, " +
|
|
||||||
"$keyIndex INTEGER DEFAULT 0, $messageKeys TEXT, PRIMARY KEY ($closedGroupPublicKey, $senderPublicKey));"
|
|
||||||
// Private keys
|
|
||||||
public val closedGroupPrivateKeyTable = "closed_group_private_key_table"
|
|
||||||
public val closedGroupPrivateKey = "closed_group_private_key"
|
|
||||||
@JvmStatic val createClosedGroupPrivateKeyTableCommand
|
|
||||||
= "CREATE TABLE $closedGroupPrivateKeyTable ($closedGroupPublicKey STRING PRIMARY KEY, $closedGroupPrivateKey STRING);"
|
|
||||||
|
|
||||||
|
|
||||||
fun perform(database: net.sqlcipher.database.SQLiteDatabase) {
|
|
||||||
val publicKeys = database.getAll(closedGroupPrivateKeyTable, null, null) { cursor ->
|
|
||||||
cursor.getString(closedGroupPublicKey)
|
|
||||||
}.filter {
|
|
||||||
PublicKeyValidation.isValid(it)
|
|
||||||
}
|
|
||||||
val keyPairs = mutableListOf<ECKeyPair>()
|
|
||||||
for (publicKey in publicKeys) {
|
|
||||||
val query = "${closedGroupPublicKey} = ?"
|
|
||||||
val privateKey = database.get(closedGroupPrivateKeyTable, query, arrayOf( publicKey )) { cursor ->
|
|
||||||
cursor.getString(closedGroupPrivateKey)
|
|
||||||
}
|
|
||||||
val keyPair = ECKeyPair(DjbECPublicKey(Hex.fromStringCondensed(publicKey.removing05PrefixIfNeeded())), DjbECPrivateKey(Hex.fromStringCondensed(privateKey)))
|
|
||||||
keyPairs.add(keyPair)
|
|
||||||
val row = ContentValues(1)
|
|
||||||
row.put(LokiAPIDatabase.groupPublicKey, publicKey)
|
|
||||||
database.insertOrUpdate(LokiAPIDatabase.closedGroupPublicKeysTable, row, "${LokiAPIDatabase.groupPublicKey} = ?", arrayOf( publicKey ))
|
|
||||||
}
|
|
||||||
for (keyPair in keyPairs) {
|
|
||||||
// In this particular case keyPair.publicKey == groupPublicKey
|
|
||||||
val timestamp = Date().time.toString()
|
|
||||||
val index = "${keyPair.publicKey.serialize().toHexString()}-$timestamp"
|
|
||||||
val encryptionKeyPairPublicKey = keyPair.publicKey.serialize().toHexString().removing05PrefixIfNeeded()
|
|
||||||
val encryptionKeyPairPrivateKey = keyPair.privateKey.serialize().toHexString()
|
|
||||||
val row = ContentValues(3)
|
|
||||||
row.put(LokiAPIDatabase.closedGroupsEncryptionKeyPairIndex, index)
|
|
||||||
row.put(LokiAPIDatabase.encryptionKeyPairPublicKey, encryptionKeyPairPublicKey)
|
|
||||||
row.put(LokiAPIDatabase.encryptionKeyPairPrivateKey, encryptionKeyPairPrivateKey)
|
|
||||||
database.insertOrUpdate(LokiAPIDatabase.closedGroupEncryptionKeyPairsTable, row, "${LokiAPIDatabase.closedGroupsEncryptionKeyPairIndex} = ?", arrayOf( index ))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,392 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.loki.protocol
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.util.Log
|
|
||||||
import com.google.protobuf.ByteString
|
|
||||||
import org.session.libsession.messaging.sending_receiving.*
|
|
||||||
import org.session.libsignal.crypto.ecc.DjbECPrivateKey
|
|
||||||
import org.session.libsignal.crypto.ecc.DjbECPublicKey
|
|
||||||
import org.session.libsignal.crypto.ecc.ECKeyPair
|
|
||||||
import org.session.libsignal.messages.SignalServiceGroup
|
|
||||||
import org.session.libsignal.protos.SignalServiceProtos
|
|
||||||
import org.session.libsignal.protos.SignalServiceProtos.DataMessage
|
|
||||||
import org.session.libsignal.utilities.removing05PrefixIfNeeded
|
|
||||||
import org.session.libsignal.utilities.toHexString
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
|
||||||
import org.thoughtcrime.securesms.database.GroupDatabase
|
|
||||||
import org.thoughtcrime.securesms.loki.api.LokiPushNotificationManager
|
|
||||||
import org.thoughtcrime.securesms.loki.api.LokiPushNotificationManager.ClosedGroupOperation
|
|
||||||
import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase
|
|
||||||
|
|
||||||
import org.session.libsession.utilities.Address
|
|
||||||
import org.session.libsession.utilities.GroupRecord
|
|
||||||
import org.session.libsession.utilities.recipients.Recipient
|
|
||||||
import org.session.libsession.utilities.GroupUtil
|
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
|
||||||
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
object ClosedGroupsProtocolV2 {
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun handleMessage(context: Context, closedGroupUpdate: DataMessage.ClosedGroupControlMessage, sentTimestamp: Long, groupPublicKey: String, senderPublicKey: String) {
|
|
||||||
if (!isValid(context, closedGroupUpdate, senderPublicKey, sentTimestamp)) { return }
|
|
||||||
when (closedGroupUpdate.type) {
|
|
||||||
DataMessage.ClosedGroupControlMessage.Type.NEW -> handleNewClosedGroup(context, closedGroupUpdate, senderPublicKey, sentTimestamp)
|
|
||||||
DataMessage.ClosedGroupControlMessage.Type.MEMBERS_REMOVED -> handleClosedGroupMembersRemoved(context, closedGroupUpdate, sentTimestamp, groupPublicKey, senderPublicKey)
|
|
||||||
DataMessage.ClosedGroupControlMessage.Type.MEMBERS_ADDED -> handleClosedGroupMembersAdded(context, closedGroupUpdate, sentTimestamp, groupPublicKey, senderPublicKey)
|
|
||||||
DataMessage.ClosedGroupControlMessage.Type.NAME_CHANGE -> handleClosedGroupNameChange(context, closedGroupUpdate, sentTimestamp, groupPublicKey, senderPublicKey)
|
|
||||||
DataMessage.ClosedGroupControlMessage.Type.MEMBER_LEFT -> handleClosedGroupMemberLeft(context, sentTimestamp, groupPublicKey, senderPublicKey)
|
|
||||||
DataMessage.ClosedGroupControlMessage.Type.ENCRYPTION_KEY_PAIR -> handleGroupEncryptionKeyPair(context, closedGroupUpdate, groupPublicKey, senderPublicKey)
|
|
||||||
else -> {
|
|
||||||
Log.d("Loki","Can't handle closed group update of unknown type: ${closedGroupUpdate.type}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isValid(context: Context, closedGroupUpdate: DataMessage.ClosedGroupControlMessage, senderPublicKey: String, sentTimestamp: Long): Boolean {
|
|
||||||
val record = DatabaseFactory.getMmsSmsDatabase(context).getMessageFor(sentTimestamp, senderPublicKey)
|
|
||||||
if (record != null) return false
|
|
||||||
|
|
||||||
return when (closedGroupUpdate.type) {
|
|
||||||
DataMessage.ClosedGroupControlMessage.Type.NEW -> {
|
|
||||||
(!closedGroupUpdate.publicKey.isEmpty && !closedGroupUpdate.name.isNullOrEmpty() && !(closedGroupUpdate.encryptionKeyPair.privateKey ?: ByteString.copyFrom(ByteArray(0))).isEmpty
|
|
||||||
&& !(closedGroupUpdate.encryptionKeyPair.publicKey ?: ByteString.copyFrom(ByteArray(0))).isEmpty && closedGroupUpdate.membersCount > 0 && closedGroupUpdate.adminsCount > 0)
|
|
||||||
}
|
|
||||||
DataMessage.ClosedGroupControlMessage.Type.MEMBERS_ADDED,
|
|
||||||
DataMessage.ClosedGroupControlMessage.Type.MEMBERS_REMOVED -> {
|
|
||||||
closedGroupUpdate.membersCount > 0
|
|
||||||
}
|
|
||||||
DataMessage.ClosedGroupControlMessage.Type.MEMBER_LEFT -> {
|
|
||||||
senderPublicKey.isNotEmpty()
|
|
||||||
}
|
|
||||||
DataMessage.ClosedGroupControlMessage.Type.NAME_CHANGE -> {
|
|
||||||
!closedGroupUpdate.name.isNullOrEmpty()
|
|
||||||
}
|
|
||||||
DataMessage.ClosedGroupControlMessage.Type.ENCRYPTION_KEY_PAIR -> true
|
|
||||||
else -> false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun handleNewClosedGroup(context: Context, closedGroupUpdate: DataMessage.ClosedGroupControlMessage, senderPublicKey: String, sentTimestamp: Long) {
|
|
||||||
// Prepare
|
|
||||||
val userPublicKey = TextSecurePreferences.getLocalNumber(context)!!
|
|
||||||
val apiDB = DatabaseFactory.getLokiAPIDatabase(context)
|
|
||||||
// Unwrap the message
|
|
||||||
val groupPublicKey = closedGroupUpdate.publicKey.toByteArray().toHexString()
|
|
||||||
val name = closedGroupUpdate.name
|
|
||||||
val encryptionKeyPairAsProto = closedGroupUpdate.encryptionKeyPair
|
|
||||||
val members = closedGroupUpdate.membersList.map { it.toByteArray().toHexString() }
|
|
||||||
val admins = closedGroupUpdate.adminsList.map { it.toByteArray().toHexString() }
|
|
||||||
// Create the group
|
|
||||||
val groupID = GroupUtil.doubleEncodeGroupID(groupPublicKey)
|
|
||||||
val groupDB = DatabaseFactory.getGroupDatabase(context)
|
|
||||||
val prevGroup = groupDB.getGroup(groupID).orNull()
|
|
||||||
if (prevGroup != null) {
|
|
||||||
// Update the group
|
|
||||||
groupDB.updateTitle(groupID, name)
|
|
||||||
groupDB.updateMembers(groupID, members.map { Address.fromSerialized(it) })
|
|
||||||
} else {
|
|
||||||
groupDB.create(groupID, name, LinkedList(members.map { Address.fromSerialized(it) }),
|
|
||||||
null, null, LinkedList(admins.map { Address.fromSerialized(it) }), sentTimestamp)
|
|
||||||
}
|
|
||||||
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(Recipient.from(context, Address.fromSerialized(groupID), false), true)
|
|
||||||
// Add the group to the user's set of public keys to poll for
|
|
||||||
apiDB.addClosedGroupPublicKey(groupPublicKey)
|
|
||||||
// Store the encryption key pair
|
|
||||||
val encryptionKeyPair = ECKeyPair(DjbECPublicKey(encryptionKeyPairAsProto.publicKey.toByteArray().removing05PrefixIfNeeded()), DjbECPrivateKey(encryptionKeyPairAsProto.privateKey.toByteArray()))
|
|
||||||
apiDB.addClosedGroupEncryptionKeyPair(encryptionKeyPair, groupPublicKey)
|
|
||||||
// Notify the user (if we didn't make the group)
|
|
||||||
if (userPublicKey != senderPublicKey) {
|
|
||||||
DatabaseFactory.getStorage(context).insertIncomingInfoMessage(context, senderPublicKey, groupID, 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)
|
|
||||||
DatabaseFactory.getStorage(context).insertOutgoingInfoMessage(context, groupID, SignalServiceGroup.Type.UPDATE, name, members, admins, threadID, sentTimestamp)
|
|
||||||
}
|
|
||||||
// Notify the PN server
|
|
||||||
LokiPushNotificationManager.performOperation(context, ClosedGroupOperation.Subscribe, groupPublicKey, userPublicKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun handleClosedGroupMembersRemoved(context: Context, closedGroupUpdate: DataMessage.ClosedGroupControlMessage, sentTimestamp: Long, groupPublicKey: String, senderPublicKey: String) {
|
|
||||||
val apiDB = DatabaseFactory.getLokiAPIDatabase(context)
|
|
||||||
val groupDB = DatabaseFactory.getGroupDatabase(context)
|
|
||||||
val groupID = GroupUtil.doubleEncodeGroupID(groupPublicKey)
|
|
||||||
val group = groupDB.getGroup(groupID).orNull()
|
|
||||||
if (group == null || !group.isActive) {
|
|
||||||
Log.d("Loki", "Ignoring closed group info message for nonexistent or inactive group.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
val userPublicKey = TextSecurePreferences.getLocalNumber(context)!!
|
|
||||||
val name = group.title
|
|
||||||
// Check common group update logic
|
|
||||||
val members = group.members.map { it.serialize() }
|
|
||||||
val admins = group.admins.map { it.toString() }
|
|
||||||
|
|
||||||
// Users that are part of this remove update
|
|
||||||
val updateMembers = closedGroupUpdate.membersList.map { it.toByteArray().toHexString() }
|
|
||||||
|
|
||||||
if (!isValidGroupUpdate(group, sentTimestamp, senderPublicKey)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// If admin leaves the group is disbanded
|
|
||||||
val didAdminLeave = admins.any { it in updateMembers }
|
|
||||||
// newMembers to save is old members minus removed members
|
|
||||||
val newMembers = members - updateMembers
|
|
||||||
// user should be posting MEMBERS_LEFT so this should not be encountered
|
|
||||||
val senderLeft = senderPublicKey in updateMembers
|
|
||||||
if (senderLeft) {
|
|
||||||
Log.d("Loki", "Received a MEMBERS_REMOVED instead of a MEMBERS_LEFT from sender $senderPublicKey")
|
|
||||||
}
|
|
||||||
val wasCurrentUserRemoved = userPublicKey in updateMembers
|
|
||||||
|
|
||||||
// admin should send a MEMBERS_LEFT message but handled here in case
|
|
||||||
if (didAdminLeave || wasCurrentUserRemoved) {
|
|
||||||
disableLocalGroupAndUnsubscribe(context, apiDB, groupPublicKey, groupDB, groupID, userPublicKey)
|
|
||||||
} else {
|
|
||||||
val isCurrentUserAdmin = admins.contains(userPublicKey)
|
|
||||||
groupDB.updateMembers(groupID, newMembers.map { Address.fromSerialized(it) })
|
|
||||||
if (isCurrentUserAdmin) {
|
|
||||||
MessageSender.generateAndSendNewEncryptionKeyPair(groupPublicKey, newMembers)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val type =
|
|
||||||
if (senderLeft) SignalServiceGroup.Type.QUIT
|
|
||||||
else SignalServiceGroup.Type.UPDATE
|
|
||||||
if (userPublicKey == senderPublicKey) {
|
|
||||||
val threadID = DatabaseFactory.getLokiThreadDatabase(context).getThreadID(groupID)
|
|
||||||
DatabaseFactory.getStorage(context).insertOutgoingInfoMessage(context, groupID, type, name, members, admins, threadID, sentTimestamp)
|
|
||||||
} else {
|
|
||||||
DatabaseFactory.getStorage(context).insertIncomingInfoMessage(context, senderPublicKey, groupID, type, name, members, admins, sentTimestamp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun handleClosedGroupMembersAdded(context: Context, closedGroupUpdate: DataMessage.ClosedGroupControlMessage, sentTimestamp: Long, groupPublicKey: String, senderPublicKey: String) {
|
|
||||||
val userPublicKey = TextSecurePreferences.getLocalNumber(context)
|
|
||||||
val apiDB = DatabaseFactory.getLokiAPIDatabase(context)
|
|
||||||
val groupDB = DatabaseFactory.getGroupDatabase(context)
|
|
||||||
val groupID = GroupUtil.doubleEncodeGroupID(groupPublicKey)
|
|
||||||
val group = groupDB.getGroup(groupID).orNull()
|
|
||||||
if (group == null || !group.isActive) {
|
|
||||||
Log.d("Loki", "Ignoring closed group info message for nonexistent or inactive group.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Check common group update logic
|
|
||||||
if (!isValidGroupUpdate(group, sentTimestamp, senderPublicKey)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
val name = group.title
|
|
||||||
val members = group.members.map { it.serialize() }
|
|
||||||
val admins = group.admins.map { it.serialize() }
|
|
||||||
// Users that are part of this add update
|
|
||||||
val updateMembers = closedGroupUpdate.membersList.map { it.toByteArray().toHexString() }
|
|
||||||
// newMembers to save is old members plus members included in this update
|
|
||||||
val newMembers = members + updateMembers
|
|
||||||
groupDB.updateMembers(groupID, newMembers.map { Address.fromSerialized(it) })
|
|
||||||
if (userPublicKey == senderPublicKey) {
|
|
||||||
val threadID = DatabaseFactory.getLokiThreadDatabase(context).getThreadID(groupID)
|
|
||||||
DatabaseFactory.getStorage(context).insertOutgoingInfoMessage(context, groupID, SignalServiceGroup.Type.UPDATE, name, members, admins, threadID, sentTimestamp)
|
|
||||||
} else {
|
|
||||||
DatabaseFactory.getStorage(context).insertIncomingInfoMessage(context, senderPublicKey, groupID, SignalServiceGroup.Type.UPDATE, name, members, admins, sentTimestamp)
|
|
||||||
}
|
|
||||||
if (userPublicKey in admins) {
|
|
||||||
// send current encryption key to the latest added members
|
|
||||||
val encryptionKeyPair = pendingKeyPairs[groupPublicKey]?.orNull()
|
|
||||||
?: apiDB.getLatestClosedGroupEncryptionKeyPair(groupPublicKey)
|
|
||||||
if (encryptionKeyPair == null) {
|
|
||||||
Log.d("Loki", "Couldn't get encryption key pair for closed group.")
|
|
||||||
} else {
|
|
||||||
for (user in updateMembers) {
|
|
||||||
MessageSender.sendEncryptionKeyPair(groupPublicKey, encryptionKeyPair, setOf(user), targetUser = user, force = false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun handleClosedGroupNameChange(context: Context, closedGroupUpdate: DataMessage.ClosedGroupControlMessage, sentTimestamp: Long, groupPublicKey: String, senderPublicKey: String) {
|
|
||||||
// Check that the sender is a member of the group (before the update)
|
|
||||||
val userPublicKey = TextSecurePreferences.getLocalNumber(context)
|
|
||||||
val groupDB = DatabaseFactory.getGroupDatabase(context)
|
|
||||||
val groupID = GroupUtil.doubleEncodeGroupID(groupPublicKey)
|
|
||||||
val group = groupDB.getGroup(groupID).orNull()
|
|
||||||
if (group == null || !group.isActive) {
|
|
||||||
Log.d("Loki", "Ignoring closed group info message for nonexistent or inactive group.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Check common group update logic
|
|
||||||
if (!isValidGroupUpdate(group, sentTimestamp, senderPublicKey)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
val members = group.members.map { it.serialize() }
|
|
||||||
val admins = group.admins.map { it.serialize() }
|
|
||||||
val name = closedGroupUpdate.name
|
|
||||||
groupDB.updateTitle(groupID, name)
|
|
||||||
// Notify the user
|
|
||||||
if (userPublicKey == senderPublicKey) {
|
|
||||||
val threadID = DatabaseFactory.getLokiThreadDatabase(context).getThreadID(groupID)
|
|
||||||
DatabaseFactory.getStorage(context).insertOutgoingInfoMessage(context, groupID, SignalServiceGroup.Type.UPDATE, name, members, admins, threadID, sentTimestamp)
|
|
||||||
} else {
|
|
||||||
DatabaseFactory.getStorage(context).insertIncomingInfoMessage(context, senderPublicKey, groupID, SignalServiceGroup.Type.UPDATE, name, members, admins, sentTimestamp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleClosedGroupMemberLeft(context: Context, sentTimestamp: Long, groupPublicKey: String, senderPublicKey: String) {
|
|
||||||
// Check the user leaving isn't us, will already be handled
|
|
||||||
val userPublicKey = TextSecurePreferences.getLocalNumber(context)!!
|
|
||||||
val apiDB = DatabaseFactory.getLokiAPIDatabase(context)
|
|
||||||
val groupDB = DatabaseFactory.getGroupDatabase(context)
|
|
||||||
val groupID = GroupUtil.doubleEncodeGroupID(groupPublicKey)
|
|
||||||
val group = groupDB.getGroup(groupID).orNull()
|
|
||||||
if (group == null || !group.isActive) {
|
|
||||||
Log.d("Loki", "Ignoring closed group info message for nonexistent or inactive group.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
val name = group.title
|
|
||||||
// Check common group update logic
|
|
||||||
val members = group.members.map { it.serialize() }
|
|
||||||
val admins = group.admins.map { it.toString() }
|
|
||||||
if (!isValidGroupUpdate(group, sentTimestamp, senderPublicKey)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// If the admin leaves the group is disbanded
|
|
||||||
val didAdminLeave = admins.contains(senderPublicKey)
|
|
||||||
val updatedMemberList = members - senderPublicKey
|
|
||||||
val userLeft = (userPublicKey == senderPublicKey)
|
|
||||||
|
|
||||||
// if the admin left, we left, or we are the only remaining member: remove the group
|
|
||||||
if (didAdminLeave || userLeft) {
|
|
||||||
disableLocalGroupAndUnsubscribe(context, apiDB, groupPublicKey, groupDB, groupID, userPublicKey)
|
|
||||||
} else {
|
|
||||||
val isCurrentUserAdmin = admins.contains(userPublicKey)
|
|
||||||
groupDB.updateMembers(groupID, updatedMemberList.map { Address.fromSerialized(it) })
|
|
||||||
if (isCurrentUserAdmin) {
|
|
||||||
MessageSender.generateAndSendNewEncryptionKeyPair(groupPublicKey, updatedMemberList)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Notify user
|
|
||||||
if (userLeft) {
|
|
||||||
val threadID = DatabaseFactory.getLokiThreadDatabase(context).getThreadID(groupID)
|
|
||||||
DatabaseFactory.getStorage(context).insertOutgoingInfoMessage(context, groupID, SignalServiceGroup.Type.QUIT, name, members, admins, threadID, sentTimestamp)
|
|
||||||
} else {
|
|
||||||
DatabaseFactory.getStorage(context).insertIncomingInfoMessage(context, senderPublicKey, groupID, SignalServiceGroup.Type.QUIT, name, members, admins, sentTimestamp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun disableLocalGroupAndUnsubscribe(context: Context, apiDB: LokiAPIDatabase, groupPublicKey: String, groupDB: GroupDatabase, groupID: String, userPublicKey: String) {
|
|
||||||
apiDB.removeClosedGroupPublicKey(groupPublicKey)
|
|
||||||
// Remove the key pairs
|
|
||||||
apiDB.removeAllClosedGroupEncryptionKeyPairs(groupPublicKey)
|
|
||||||
// Mark the group as inactive
|
|
||||||
groupDB.setActive(groupID, false)
|
|
||||||
groupDB.removeMember(groupID, Address.fromSerialized(userPublicKey))
|
|
||||||
// Notify the PN server
|
|
||||||
LokiPushNotificationManager.performOperation(context, ClosedGroupOperation.Unsubscribe, groupPublicKey, userPublicKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isValidGroupUpdate(group: GroupRecord,
|
|
||||||
sentTimestamp: Long,
|
|
||||||
senderPublicKey: String): Boolean {
|
|
||||||
val oldMembers = group.members.map { it.serialize() }
|
|
||||||
// Check that the message isn't from before the group was created
|
|
||||||
// TODO: We should check that formationTimestamp is the sent timestamp of the closed group update that created the group
|
|
||||||
if (group.formationTimestamp > sentTimestamp) {
|
|
||||||
Log.d("Loki", "Ignoring closed group update from before thread was created.")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// Check that the sender is a member of the group (before the update)
|
|
||||||
if (senderPublicKey !in oldMembers) {
|
|
||||||
Log.d("Loki", "Ignoring closed group info message from non-member.")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleGroupEncryptionKeyPair(context: Context, closedGroupUpdate: DataMessage.ClosedGroupControlMessage, groupPublicKey: String, senderPublicKey: String) {
|
|
||||||
// Prepare
|
|
||||||
val userPublicKey = TextSecurePreferences.getLocalNumber(context)
|
|
||||||
val apiDB = DatabaseFactory.getLokiAPIDatabase(context)
|
|
||||||
val userKeyPair = apiDB.getUserX25519KeyPair()
|
|
||||||
// Unwrap the message
|
|
||||||
val groupDB = DatabaseFactory.getGroupDatabase(context)
|
|
||||||
val groupPublicKeyToUse = when {
|
|
||||||
groupPublicKey.isNotEmpty() -> groupPublicKey
|
|
||||||
!closedGroupUpdate.publicKey.isEmpty -> closedGroupUpdate.publicKey.toByteArray().toHexString()
|
|
||||||
else -> ""
|
|
||||||
}
|
|
||||||
val groupID = GroupUtil.doubleEncodeGroupID(groupPublicKeyToUse)
|
|
||||||
val group = groupDB.getGroup(groupID).orNull()
|
|
||||||
if (group == null) {
|
|
||||||
Log.d("Loki", "Ignoring closed group encryption key pair message for nonexistent group.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (!group.admins.map { it.toString() }.contains(senderPublicKey)) {
|
|
||||||
Log.d("Loki", "Ignoring closed group encryption key pair from non-admin.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Find our wrapper and decrypt it if possible
|
|
||||||
val wrapper = closedGroupUpdate.wrappersList.firstOrNull { it.publicKey.toByteArray().toHexString() == userPublicKey } ?: return
|
|
||||||
val encryptedKeyPair = wrapper.encryptedKeyPair.toByteArray()
|
|
||||||
val plaintext = MessageDecrypter.decrypt(encryptedKeyPair, userKeyPair).first
|
|
||||||
// Parse it
|
|
||||||
val proto = SignalServiceProtos.KeyPair.parseFrom(plaintext)
|
|
||||||
val keyPair = ECKeyPair(DjbECPublicKey(proto.publicKey.toByteArray().removing05PrefixIfNeeded()), DjbECPrivateKey(proto.privateKey.toByteArray()))
|
|
||||||
// Store it
|
|
||||||
apiDB.addClosedGroupEncryptionKeyPair(keyPair, groupPublicKeyToUse)
|
|
||||||
Log.d("Loki", "Received a new closed group encryption key pair")
|
|
||||||
}
|
|
||||||
|
|
||||||
// region Deprecated
|
|
||||||
private fun handleClosedGroupUpdate(context: Context, closedGroupUpdate: DataMessage.ClosedGroupControlMessage, sentTimestamp: Long, groupPublicKey: String, senderPublicKey: String) {
|
|
||||||
// Prepare
|
|
||||||
val userPublicKey = TextSecurePreferences.getLocalNumber(context)!!
|
|
||||||
val apiDB = DatabaseFactory.getLokiAPIDatabase(context)
|
|
||||||
// Unwrap the message
|
|
||||||
val name = closedGroupUpdate.name
|
|
||||||
val members = closedGroupUpdate.membersList.map { it.toByteArray().toHexString() }
|
|
||||||
val groupDB = DatabaseFactory.getGroupDatabase(context)
|
|
||||||
val groupID = GroupUtil.doubleEncodeGroupID(groupPublicKey)
|
|
||||||
val group = groupDB.getGroup(groupID).orNull()
|
|
||||||
if (group == null || !group.isActive) {
|
|
||||||
Log.d("Loki", "Ignoring closed group info message for nonexistent or inactive group.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
val oldMembers = group.members.map { it.serialize() }
|
|
||||||
// Check common group update logic
|
|
||||||
if (!isValidGroupUpdate(group, sentTimestamp, senderPublicKey)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Check that the admin wasn't removed unless the group was destroyed entirely
|
|
||||||
if (!members.contains(group.admins.first().toString()) && members.isNotEmpty()) {
|
|
||||||
Log.d("Loki", "Ignoring invalid closed group update message.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Remove the group from the user's set of public keys to poll for if the current user was removed
|
|
||||||
val wasCurrentUserRemoved = !members.contains(userPublicKey)
|
|
||||||
if (wasCurrentUserRemoved) {
|
|
||||||
disableLocalGroupAndUnsubscribe(context, apiDB, groupPublicKey, groupDB, groupID, userPublicKey)
|
|
||||||
}
|
|
||||||
// Generate and distribute a new encryption key pair if needed
|
|
||||||
val wasAnyUserRemoved = (members.toSet().intersect(oldMembers) != oldMembers.toSet())
|
|
||||||
val isCurrentUserAdmin = group.admins.map { it.toString() }.contains(userPublicKey)
|
|
||||||
if (wasAnyUserRemoved && isCurrentUserAdmin) {
|
|
||||||
MessageSender.generateAndSendNewEncryptionKeyPair(groupPublicKey, members)
|
|
||||||
}
|
|
||||||
// Update the group
|
|
||||||
groupDB.updateTitle(groupID, name)
|
|
||||||
if (!wasCurrentUserRemoved) {
|
|
||||||
// The call below sets isActive to true, so if the user is leaving we have to use groupDB.remove(...) instead
|
|
||||||
groupDB.updateMembers(groupID, members.map { Address.fromSerialized(it) })
|
|
||||||
}
|
|
||||||
// Notify the user
|
|
||||||
val wasSenderRemoved = !members.contains(senderPublicKey)
|
|
||||||
val type = if (wasSenderRemoved) SignalServiceGroup.Type.QUIT else SignalServiceGroup.Type.UPDATE
|
|
||||||
val admins = group.admins.map { it.toString() }
|
|
||||||
if (userPublicKey == senderPublicKey) {
|
|
||||||
val threadID = DatabaseFactory.getLokiThreadDatabase(context).getThreadID(groupID)
|
|
||||||
DatabaseFactory.getStorage(context).insertOutgoingInfoMessage(context, groupID, type, name, members, admins, threadID, sentTimestamp)
|
|
||||||
} else {
|
|
||||||
DatabaseFactory.getStorage(context).insertIncomingInfoMessage(context, senderPublicKey, groupID, type, name, members, admins, sentTimestamp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// endregion
|
|
||||||
}
|
|
@ -1,335 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.loki.views
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.res.ColorStateList
|
|
||||||
import android.graphics.Color
|
|
||||||
import android.graphics.PorterDuff
|
|
||||||
import android.graphics.drawable.AnimatedVectorDrawable
|
|
||||||
import android.util.AttributeSet
|
|
||||||
import android.view.View
|
|
||||||
import android.view.View.OnTouchListener
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import android.widget.FrameLayout
|
|
||||||
import android.widget.ImageView
|
|
||||||
import android.widget.ProgressBar
|
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.annotation.ColorInt
|
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import kotlinx.coroutines.*
|
|
||||||
import network.loki.messenger.R
|
|
||||||
import org.greenrobot.eventbus.EventBus
|
|
||||||
import org.greenrobot.eventbus.Subscribe
|
|
||||||
import org.greenrobot.eventbus.ThreadMode
|
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentTransferProgress
|
|
||||||
import org.thoughtcrime.securesms.ApplicationContext
|
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
|
|
||||||
import org.thoughtcrime.securesms.audio.AudioSlidePlayer
|
|
||||||
import org.thoughtcrime.securesms.components.AnimatingToggle
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
|
||||||
import org.thoughtcrime.securesms.events.PartProgressEvent
|
|
||||||
import org.session.libsignal.utilities.Log
|
|
||||||
import org.thoughtcrime.securesms.loki.api.PrepareAttachmentAudioExtrasJob
|
|
||||||
import org.thoughtcrime.securesms.loki.utilities.getColorWithID
|
|
||||||
import org.thoughtcrime.securesms.mms.AudioSlide
|
|
||||||
import org.thoughtcrime.securesms.mms.SlideClickListener
|
|
||||||
import java.io.IOException
|
|
||||||
import java.util.*
|
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
|
|
||||||
class MessageAudioView: FrameLayout, AudioSlidePlayer.Listener {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private const val TAG = "AudioViewKt"
|
|
||||||
}
|
|
||||||
|
|
||||||
private val controlToggle: AnimatingToggle
|
|
||||||
private val container: ViewGroup
|
|
||||||
private val playButton: ImageView
|
|
||||||
private val pauseButton: ImageView
|
|
||||||
private val downloadButton: ImageView
|
|
||||||
private val downloadProgress: ProgressBar
|
|
||||||
private val seekBar: WaveformSeekBar
|
|
||||||
private val totalDuration: TextView
|
|
||||||
|
|
||||||
private var downloadListener: SlideClickListener? = null
|
|
||||||
private var audioSlidePlayer: AudioSlidePlayer? = null
|
|
||||||
|
|
||||||
/** Background coroutine scope that is available when the view is attached to a window. */
|
|
||||||
private var asyncCoroutineScope: CoroutineScope? = null
|
|
||||||
|
|
||||||
private val loadingAnimation: SeekBarLoadingAnimation
|
|
||||||
|
|
||||||
constructor(context: Context): this(context, null)
|
|
||||||
|
|
||||||
constructor(context: Context, attrs: AttributeSet?): this(context, attrs, 0)
|
|
||||||
|
|
||||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int): super(context, attrs, defStyleAttr) {
|
|
||||||
View.inflate(context, R.layout.message_audio_view, this)
|
|
||||||
container = findViewById(R.id.audio_widget_container)
|
|
||||||
controlToggle = findViewById(R.id.control_toggle)
|
|
||||||
playButton = findViewById(R.id.play)
|
|
||||||
pauseButton = findViewById(R.id.pause)
|
|
||||||
downloadButton = findViewById(R.id.download)
|
|
||||||
downloadProgress = findViewById(R.id.download_progress)
|
|
||||||
seekBar = findViewById(R.id.seek)
|
|
||||||
totalDuration = findViewById(R.id.total_duration)
|
|
||||||
|
|
||||||
playButton.setOnClickListener {
|
|
||||||
try {
|
|
||||||
Log.d(TAG, "playbutton onClick")
|
|
||||||
if (audioSlidePlayer != null) {
|
|
||||||
togglePlayToPause()
|
|
||||||
|
|
||||||
// Restart the playback if progress bar is nearly at the end.
|
|
||||||
val progress = if (seekBar.progress < 0.99f) seekBar.progress.toDouble() else 0.0
|
|
||||||
|
|
||||||
audioSlidePlayer!!.play(progress)
|
|
||||||
}
|
|
||||||
} catch (e: IOException) {
|
|
||||||
Log.w(TAG, e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pauseButton.setOnClickListener {
|
|
||||||
Log.d(TAG, "pausebutton onClick")
|
|
||||||
if (audioSlidePlayer != null) {
|
|
||||||
togglePauseToPlay()
|
|
||||||
audioSlidePlayer!!.stop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
seekBar.isEnabled = false
|
|
||||||
seekBar.progressChangeListener = object : WaveformSeekBar.ProgressChangeListener {
|
|
||||||
override fun onProgressChanged(waveformSeekBar: WaveformSeekBar, progress: Float, fromUser: Boolean) {
|
|
||||||
if (fromUser && audioSlidePlayer != null) {
|
|
||||||
synchronized(audioSlidePlayer!!) {
|
|
||||||
audioSlidePlayer!!.seekTo(progress.toDouble())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
playButton.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.play_icon))
|
|
||||||
pauseButton.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.pause_icon))
|
|
||||||
playButton.background = ContextCompat.getDrawable(context, R.drawable.ic_circle_fill_white_48dp)
|
|
||||||
pauseButton.background = ContextCompat.getDrawable(context, R.drawable.ic_circle_fill_white_48dp)
|
|
||||||
|
|
||||||
if (attrs != null) {
|
|
||||||
val typedArray = context.theme.obtainStyledAttributes(attrs, R.styleable.MessageAudioView, 0, 0)
|
|
||||||
setTint(typedArray.getColor(R.styleable.MessageAudioView_foregroundTintColor, Color.WHITE),
|
|
||||||
typedArray.getColor(R.styleable.MessageAudioView_waveformFillColor, Color.WHITE),
|
|
||||||
typedArray.getColor(R.styleable.MessageAudioView_waveformBackgroundColor, Color.WHITE))
|
|
||||||
container.setBackgroundColor(typedArray.getColor(R.styleable.MessageAudioView_widgetBackground, Color.TRANSPARENT))
|
|
||||||
typedArray.recycle()
|
|
||||||
}
|
|
||||||
|
|
||||||
loadingAnimation = SeekBarLoadingAnimation(this, seekBar)
|
|
||||||
loadingAnimation.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onAttachedToWindow() {
|
|
||||||
super.onAttachedToWindow()
|
|
||||||
if (!EventBus.getDefault().isRegistered(this)) EventBus.getDefault().register(this)
|
|
||||||
|
|
||||||
asyncCoroutineScope = CoroutineScope(Job() + Dispatchers.IO)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDetachedFromWindow() {
|
|
||||||
super.onDetachedFromWindow()
|
|
||||||
EventBus.getDefault().unregister(this)
|
|
||||||
|
|
||||||
// Cancel all the background operations.
|
|
||||||
asyncCoroutineScope!!.cancel()
|
|
||||||
asyncCoroutineScope = null
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setAudio(audio: AudioSlide, showControls: Boolean) {
|
|
||||||
when {
|
|
||||||
showControls && audio.isPendingDownload -> {
|
|
||||||
controlToggle.displayQuick(downloadButton)
|
|
||||||
seekBar.isEnabled = false
|
|
||||||
downloadButton.setOnClickListener { v -> downloadListener?.onClick(v, audio) }
|
|
||||||
if (downloadProgress.isIndeterminate) {
|
|
||||||
downloadProgress.isIndeterminate = false
|
|
||||||
downloadProgress.progress = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(showControls && audio.transferState == AttachmentTransferProgress.TRANSFER_PROGRESS_STARTED) -> {
|
|
||||||
controlToggle.displayQuick(downloadProgress)
|
|
||||||
seekBar.isEnabled = false
|
|
||||||
downloadProgress.isIndeterminate = true
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
controlToggle.displayQuick(playButton)
|
|
||||||
seekBar.isEnabled = true
|
|
||||||
if (downloadProgress.isIndeterminate) {
|
|
||||||
downloadProgress.isIndeterminate = false
|
|
||||||
downloadProgress.progress = 100
|
|
||||||
}
|
|
||||||
|
|
||||||
// Post to make sure it executes only when the view is attached to a window.
|
|
||||||
post(::updateFromAttachmentAudioExtras)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
audioSlidePlayer = AudioSlidePlayer.createFor(context, audio, this)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun cleanup() {
|
|
||||||
if (audioSlidePlayer != null && pauseButton.visibility == View.VISIBLE) {
|
|
||||||
audioSlidePlayer!!.stop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setDownloadClickListener(listener: SlideClickListener?) {
|
|
||||||
downloadListener = listener
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setTint(@ColorInt foregroundTint: Int, @ColorInt waveformFill: Int, @ColorInt waveformBackground: Int) {
|
|
||||||
playButton.backgroundTintList = ColorStateList.valueOf(resources.getColorWithID(R.color.white, context.theme))
|
|
||||||
playButton.imageTintList = ColorStateList.valueOf(resources.getColorWithID(R.color.black, context.theme))
|
|
||||||
pauseButton.backgroundTintList = ColorStateList.valueOf(resources.getColorWithID(R.color.white, context.theme))
|
|
||||||
pauseButton.imageTintList = ColorStateList.valueOf(resources.getColorWithID(R.color.black, context.theme))
|
|
||||||
|
|
||||||
downloadButton.setColorFilter(foregroundTint, PorterDuff.Mode.SRC_IN)
|
|
||||||
|
|
||||||
downloadProgress.backgroundTintList = ColorStateList.valueOf(resources.getColorWithID(R.color.white, context.theme))
|
|
||||||
downloadProgress.progressTintList = ColorStateList.valueOf(resources.getColorWithID(R.color.black, context.theme))
|
|
||||||
downloadProgress.indeterminateTintList = ColorStateList.valueOf(resources.getColorWithID(R.color.black, context.theme))
|
|
||||||
|
|
||||||
totalDuration.setTextColor(foregroundTint)
|
|
||||||
|
|
||||||
seekBar.barProgressColor = waveformFill
|
|
||||||
seekBar.barBackgroundColor = waveformBackground
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPlayerStart(player: AudioSlidePlayer) {
|
|
||||||
if (pauseButton.visibility != View.VISIBLE) {
|
|
||||||
togglePlayToPause()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPlayerStop(player: AudioSlidePlayer) {
|
|
||||||
if (playButton.visibility != View.VISIBLE) {
|
|
||||||
togglePauseToPlay()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPlayerProgress(player: AudioSlidePlayer, progress: Double, millis: Long) {
|
|
||||||
seekBar.progress = progress.toFloat()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setFocusable(focusable: Boolean) {
|
|
||||||
super.setFocusable(focusable)
|
|
||||||
playButton.isFocusable = focusable
|
|
||||||
pauseButton.isFocusable = focusable
|
|
||||||
seekBar.isFocusable = focusable
|
|
||||||
seekBar.isFocusableInTouchMode = focusable
|
|
||||||
downloadButton.isFocusable = focusable
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setClickable(clickable: Boolean) {
|
|
||||||
super.setClickable(clickable)
|
|
||||||
playButton.isClickable = clickable
|
|
||||||
pauseButton.isClickable = clickable
|
|
||||||
seekBar.isClickable = clickable
|
|
||||||
seekBar.setOnTouchListener(if (clickable) null else
|
|
||||||
OnTouchListener { _, _ -> return@OnTouchListener true }) // Suppress touch events.
|
|
||||||
downloadButton.isClickable = clickable
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setEnabled(enabled: Boolean) {
|
|
||||||
super.setEnabled(enabled)
|
|
||||||
playButton.isEnabled = enabled
|
|
||||||
pauseButton.isEnabled = enabled
|
|
||||||
downloadButton.isEnabled = enabled
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun togglePlayToPause() {
|
|
||||||
controlToggle.displayQuick(pauseButton)
|
|
||||||
val playToPauseDrawable = ContextCompat.getDrawable(context, R.drawable.play_to_pause_animation) as AnimatedVectorDrawable
|
|
||||||
pauseButton.setImageDrawable(playToPauseDrawable)
|
|
||||||
playToPauseDrawable.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun togglePauseToPlay() {
|
|
||||||
controlToggle.displayQuick(playButton)
|
|
||||||
val pauseToPlayDrawable = ContextCompat.getDrawable(context, R.drawable.pause_to_play_animation) as AnimatedVectorDrawable
|
|
||||||
playButton.setImageDrawable(pauseToPlayDrawable)
|
|
||||||
pauseToPlayDrawable.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun obtainDatabaseAttachment(): DatabaseAttachment? {
|
|
||||||
audioSlidePlayer ?: return null
|
|
||||||
val attachment = audioSlidePlayer!!.audioSlide.asAttachment()
|
|
||||||
return if (attachment is DatabaseAttachment) attachment else null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateFromAttachmentAudioExtras() {
|
|
||||||
val attachment = obtainDatabaseAttachment() ?: return
|
|
||||||
|
|
||||||
val audioExtras = DatabaseFactory.getAttachmentDatabase(context)
|
|
||||||
.getAttachmentAudioExtras(attachment.attachmentId)
|
|
||||||
|
|
||||||
// Schedule a job request if no audio extras were generated yet.
|
|
||||||
if (audioExtras == null) {
|
|
||||||
ApplicationContext.getInstance(context).jobManager
|
|
||||||
.add(PrepareAttachmentAudioExtrasJob(attachment.attachmentId))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
loadingAnimation.stop()
|
|
||||||
seekBar.sampleData = audioExtras.visualSamples
|
|
||||||
|
|
||||||
if (audioExtras.durationMs > 0) {
|
|
||||||
totalDuration.visibility = View.VISIBLE
|
|
||||||
totalDuration.text = String.format("%02d:%02d",
|
|
||||||
TimeUnit.MILLISECONDS.toMinutes(audioExtras.durationMs),
|
|
||||||
TimeUnit.MILLISECONDS.toSeconds(audioExtras.durationMs))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
|
|
||||||
fun onEvent(event: PartProgressEvent) {
|
|
||||||
if (audioSlidePlayer != null && event.attachment == audioSlidePlayer!!.audioSlide.asAttachment()) {
|
|
||||||
val progress = ((event.progress.toFloat() / event.total) * 100f).toInt()
|
|
||||||
downloadProgress.progress = progress
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
|
||||||
fun onEvent(event: PrepareAttachmentAudioExtrasJob.AudioExtrasUpdatedEvent) {
|
|
||||||
if (event.attachmentId == obtainDatabaseAttachment()?.attachmentId) {
|
|
||||||
updateFromAttachmentAudioExtras()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class SeekBarLoadingAnimation(
|
|
||||||
private val hostView: View,
|
|
||||||
private val seekBar: WaveformSeekBar): Runnable {
|
|
||||||
|
|
||||||
private var active = false
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private const val UPDATE_PERIOD = 250L // In milliseconds.
|
|
||||||
private val random = Random()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun start() {
|
|
||||||
stop()
|
|
||||||
active = true
|
|
||||||
hostView.postDelayed(this, UPDATE_PERIOD)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun stop() {
|
|
||||||
active = false
|
|
||||||
hostView.removeCallbacks(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun run() {
|
|
||||||
if (!active) return
|
|
||||||
|
|
||||||
// Generate a random samples with values up to the 50% of the maximum value.
|
|
||||||
seekBar.sampleData = ByteArray(PrepareAttachmentAudioExtrasJob.VISUAL_RMS_FRAMES)
|
|
||||||
{ (random.nextInt(127) - 64).toByte() }
|
|
||||||
hostView.postDelayed(this, UPDATE_PERIOD)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,71 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.loki.views
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.util.AttributeSet
|
|
||||||
import android.view.View
|
|
||||||
import android.widget.*
|
|
||||||
import androidx.appcompat.app.AlertDialog
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.GlobalScope
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import network.loki.messenger.R
|
|
||||||
import org.session.libsession.utilities.OpenGroupUrlParser
|
|
||||||
import org.session.libsignal.utilities.Log
|
|
||||||
import org.thoughtcrime.securesms.loki.api.OpenGroupManager
|
|
||||||
import org.thoughtcrime.securesms.loki.protocol.MultiDeviceProtocol
|
|
||||||
import org.thoughtcrime.securesms.loki.utilities.OpenGroupUtilities
|
|
||||||
|
|
||||||
class OpenGroupInvitationView : FrameLayout {
|
|
||||||
private val joinButton: ImageView
|
|
||||||
private val openGroupIconContainer: RelativeLayout
|
|
||||||
private val openGroupIconImageView: ImageView
|
|
||||||
private val nameTextView: TextView
|
|
||||||
private val urlTextView: TextView
|
|
||||||
private var url: String = ""
|
|
||||||
|
|
||||||
constructor(context: Context): this(context, null)
|
|
||||||
|
|
||||||
constructor(context: Context, attrs: AttributeSet?): this(context, attrs, 0)
|
|
||||||
|
|
||||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int): super(context, attrs, defStyleAttr) {
|
|
||||||
View.inflate(context, R.layout.open_group_invitation_view, this)
|
|
||||||
joinButton = findViewById(R.id.join_open_group_button)
|
|
||||||
openGroupIconContainer = findViewById(R.id.open_group_icon_image_view_container)
|
|
||||||
openGroupIconImageView = findViewById(R.id.open_group_icon_image_view)
|
|
||||||
nameTextView = findViewById(R.id.name_text_view)
|
|
||||||
urlTextView = findViewById(R.id.url_text_view)
|
|
||||||
joinButton.setOnClickListener { joinOpenGroup(url) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setOpenGroup(name: String, url: String, isOutgoing: Boolean = false) {
|
|
||||||
nameTextView.text = name
|
|
||||||
urlTextView.text = OpenGroupUrlParser.trimQueryParameter(url)
|
|
||||||
this.url = url
|
|
||||||
joinButton.visibility = if (isOutgoing) View.GONE else View.VISIBLE
|
|
||||||
openGroupIconContainer.visibility = if (isOutgoing) View.VISIBLE else View.GONE
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun joinOpenGroup(url: String) {
|
|
||||||
val openGroup = OpenGroupUrlParser.parseUrl(url)
|
|
||||||
val builder = AlertDialog.Builder(context)
|
|
||||||
builder.setTitle(context.getString(R.string.ConversationActivity_join_open_group, nameTextView.text.toString()))
|
|
||||||
builder.setCancelable(true)
|
|
||||||
val message: String =
|
|
||||||
context.getString(R.string.ConversationActivity_join_open_group_confirmation_message, nameTextView.text.toString())
|
|
||||||
builder.setMessage(message)
|
|
||||||
builder.setPositiveButton(R.string.yes) { dialog, _ ->
|
|
||||||
GlobalScope.launch(Dispatchers.IO) {
|
|
||||||
try {
|
|
||||||
dialog.dismiss()
|
|
||||||
OpenGroupManager.add(openGroup.server, openGroup.room, openGroup.serverPublicKey, context)
|
|
||||||
MultiDeviceProtocol.forceSyncConfigurationNowIfNeeded(context)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e("Loki", "Failed to join open group.", e)
|
|
||||||
Toast.makeText(context, R.string.activity_join_public_chat_error, Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
builder.setNegativeButton(R.string.no, null)
|
|
||||||
builder.show()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,315 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.loki.views
|
|
||||||
|
|
||||||
import android.animation.ValueAnimator
|
|
||||||
import android.content.Context
|
|
||||||
import android.graphics.Canvas
|
|
||||||
import android.graphics.Color
|
|
||||||
import android.graphics.Paint
|
|
||||||
import android.graphics.RectF
|
|
||||||
import android.util.AttributeSet
|
|
||||||
import android.util.TypedValue
|
|
||||||
import android.view.MotionEvent
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewConfiguration
|
|
||||||
import android.view.animation.DecelerateInterpolator
|
|
||||||
import androidx.core.math.MathUtils
|
|
||||||
import network.loki.messenger.R
|
|
||||||
import org.session.libsession.utilities.byteToNormalizedFloat
|
|
||||||
import kotlin.math.abs
|
|
||||||
import kotlin.math.max
|
|
||||||
import kotlin.math.min
|
|
||||||
import kotlin.math.roundToInt
|
|
||||||
|
|
||||||
class WaveformSeekBar : View {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@JvmStatic
|
|
||||||
fun dp(context: Context, dp: Float): Float {
|
|
||||||
return TypedValue.applyDimension(
|
|
||||||
TypedValue.COMPLEX_UNIT_DIP,
|
|
||||||
dp,
|
|
||||||
context.resources.displayMetrics
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val sampleDataHolder = SampleDataHolder(::invalidate)
|
|
||||||
/** An array of signed byte values representing the audio signal. */
|
|
||||||
var sampleData: ByteArray?
|
|
||||||
get() {
|
|
||||||
return sampleDataHolder.getSamples()
|
|
||||||
}
|
|
||||||
set(value) {
|
|
||||||
sampleDataHolder.setSamples(value)
|
|
||||||
invalidate()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Indicates whether the user is currently interacting with the view and performing a seeking gesture. */
|
|
||||||
private var userSeeking = false
|
|
||||||
private var _progress: Float = 0f
|
|
||||||
/** In [0..1] range. */
|
|
||||||
var progress: Float
|
|
||||||
set(value) {
|
|
||||||
// Do not let to modify the progress value from the outside
|
|
||||||
// when the user is currently interacting with the view.
|
|
||||||
if (userSeeking) return
|
|
||||||
|
|
||||||
_progress = value
|
|
||||||
invalidate()
|
|
||||||
progressChangeListener?.onProgressChanged(this, _progress, false)
|
|
||||||
}
|
|
||||||
get() {
|
|
||||||
return _progress
|
|
||||||
}
|
|
||||||
|
|
||||||
var barBackgroundColor: Int = Color.LTGRAY
|
|
||||||
set(value) {
|
|
||||||
field = value
|
|
||||||
invalidate()
|
|
||||||
}
|
|
||||||
|
|
||||||
var barProgressColor: Int = Color.WHITE
|
|
||||||
set(value) {
|
|
||||||
field = value
|
|
||||||
invalidate()
|
|
||||||
}
|
|
||||||
|
|
||||||
var barGap: Float = dp(context, 2f)
|
|
||||||
set(value) {
|
|
||||||
field = value
|
|
||||||
invalidate()
|
|
||||||
}
|
|
||||||
|
|
||||||
var barWidth: Float = dp(context, 5f)
|
|
||||||
set(value) {
|
|
||||||
field = value
|
|
||||||
invalidate()
|
|
||||||
}
|
|
||||||
|
|
||||||
var barMinHeight: Float = barWidth
|
|
||||||
set(value) {
|
|
||||||
field = value
|
|
||||||
invalidate()
|
|
||||||
}
|
|
||||||
|
|
||||||
var barCornerRadius: Float = dp(context, 2.5f)
|
|
||||||
set(value) {
|
|
||||||
field = value
|
|
||||||
invalidate()
|
|
||||||
}
|
|
||||||
|
|
||||||
var barGravity: WaveGravity = WaveGravity.CENTER
|
|
||||||
set(value) {
|
|
||||||
field = value
|
|
||||||
invalidate()
|
|
||||||
}
|
|
||||||
|
|
||||||
var progressChangeListener: ProgressChangeListener? = null
|
|
||||||
|
|
||||||
private val barPaint = Paint(Paint.ANTI_ALIAS_FLAG)
|
|
||||||
private val barRect = RectF()
|
|
||||||
|
|
||||||
private var canvasWidth = 0
|
|
||||||
private var canvasHeight = 0
|
|
||||||
|
|
||||||
private var touchDownX = 0f
|
|
||||||
private var touchDownProgress: Float = 0f
|
|
||||||
private var scaledTouchSlop = ViewConfiguration.get(context).scaledTouchSlop
|
|
||||||
|
|
||||||
constructor(context: Context) : this(context, null)
|
|
||||||
|
|
||||||
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
|
|
||||||
|
|
||||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int)
|
|
||||||
: super(context, attrs, defStyleAttr) {
|
|
||||||
|
|
||||||
val typedAttrs = context.obtainStyledAttributes(attrs, R.styleable.WaveformSeekBar)
|
|
||||||
barWidth = typedAttrs.getDimension(R.styleable.WaveformSeekBar_bar_width, barWidth)
|
|
||||||
barGap = typedAttrs.getDimension(R.styleable.WaveformSeekBar_bar_gap, barGap)
|
|
||||||
barCornerRadius = typedAttrs.getDimension(
|
|
||||||
R.styleable.WaveformSeekBar_bar_corner_radius,
|
|
||||||
barCornerRadius)
|
|
||||||
barMinHeight =
|
|
||||||
typedAttrs.getDimension(R.styleable.WaveformSeekBar_bar_min_height, barMinHeight)
|
|
||||||
barBackgroundColor = typedAttrs.getColor(
|
|
||||||
R.styleable.WaveformSeekBar_bar_background_color,
|
|
||||||
barBackgroundColor)
|
|
||||||
barProgressColor =
|
|
||||||
typedAttrs.getColor(R.styleable.WaveformSeekBar_bar_progress_color, barProgressColor)
|
|
||||||
progress = typedAttrs.getFloat(R.styleable.WaveformSeekBar_progress, progress)
|
|
||||||
barGravity = WaveGravity.fromString(
|
|
||||||
typedAttrs.getString(R.styleable.WaveformSeekBar_bar_gravity))
|
|
||||||
|
|
||||||
typedAttrs.recycle()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
|
|
||||||
super.onSizeChanged(w, h, oldw, oldh)
|
|
||||||
canvasWidth = w
|
|
||||||
canvasHeight = h
|
|
||||||
invalidate()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDraw(canvas: Canvas) {
|
|
||||||
super.onDraw(canvas)
|
|
||||||
|
|
||||||
val totalWidth = getAvailableWidth()
|
|
||||||
val barAmount = (totalWidth / (barWidth + barGap)).toInt()
|
|
||||||
|
|
||||||
var lastBarRight = paddingLeft.toFloat()
|
|
||||||
|
|
||||||
(0 until barAmount).forEach { barIdx ->
|
|
||||||
// Convert a signed byte to a [0..1] float.
|
|
||||||
val barValue = byteToNormalizedFloat(sampleDataHolder.computeBarValue(barIdx, barAmount))
|
|
||||||
|
|
||||||
val barHeight = max(barMinHeight, getAvailableHeight() * barValue)
|
|
||||||
|
|
||||||
val top: Float = when (barGravity) {
|
|
||||||
WaveGravity.TOP -> paddingTop.toFloat()
|
|
||||||
WaveGravity.CENTER -> paddingTop + getAvailableHeight() * 0.5f - barHeight * 0.5f
|
|
||||||
WaveGravity.BOTTOM -> canvasHeight - paddingBottom - barHeight
|
|
||||||
}
|
|
||||||
|
|
||||||
barRect.set(lastBarRight, top, lastBarRight + barWidth, top + barHeight)
|
|
||||||
|
|
||||||
barPaint.color = if (barRect.right <= totalWidth * progress)
|
|
||||||
barProgressColor else barBackgroundColor
|
|
||||||
|
|
||||||
canvas.drawRoundRect(barRect, barCornerRadius, barCornerRadius, barPaint)
|
|
||||||
|
|
||||||
lastBarRight = barRect.right + barGap
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onTouchEvent(event: MotionEvent): Boolean {
|
|
||||||
if (!isEnabled) return false
|
|
||||||
|
|
||||||
when (event.action) {
|
|
||||||
MotionEvent.ACTION_DOWN -> {
|
|
||||||
userSeeking = true
|
|
||||||
touchDownX = event.x
|
|
||||||
touchDownProgress = progress
|
|
||||||
updateProgress(event, false)
|
|
||||||
}
|
|
||||||
MotionEvent.ACTION_MOVE -> {
|
|
||||||
// Prevent any parent scrolling if the user scrolled more
|
|
||||||
// than scaledTouchSlop on horizontal axis.
|
|
||||||
if (abs(event.x - touchDownX) > scaledTouchSlop) {
|
|
||||||
parent.requestDisallowInterceptTouchEvent(true)
|
|
||||||
}
|
|
||||||
updateProgress(event, false)
|
|
||||||
}
|
|
||||||
MotionEvent.ACTION_UP -> {
|
|
||||||
userSeeking = false
|
|
||||||
updateProgress(event, true)
|
|
||||||
performClick()
|
|
||||||
}
|
|
||||||
MotionEvent.ACTION_CANCEL -> {
|
|
||||||
updateProgress(touchDownProgress, false)
|
|
||||||
userSeeking = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateProgress(event: MotionEvent, notify: Boolean) {
|
|
||||||
updateProgress(event.x / getAvailableWidth(), notify)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateProgress(progress: Float, notify: Boolean) {
|
|
||||||
_progress = MathUtils.clamp(progress, 0f, 1f)
|
|
||||||
invalidate()
|
|
||||||
|
|
||||||
if (notify) {
|
|
||||||
progressChangeListener?.onProgressChanged(this, _progress, true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun performClick(): Boolean {
|
|
||||||
super.performClick()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getAvailableWidth() = canvasWidth - paddingLeft - paddingRight
|
|
||||||
private fun getAvailableHeight() = canvasHeight - paddingTop - paddingBottom
|
|
||||||
|
|
||||||
private class SampleDataHolder(private val invalidateDelegate: () -> Any) {
|
|
||||||
|
|
||||||
private var sampleDataFrom: ByteArray? = null
|
|
||||||
private var sampleDataTo: ByteArray? = null
|
|
||||||
private var progress = 1f // Mix between from and to values.
|
|
||||||
|
|
||||||
private var animation: ValueAnimator? = null
|
|
||||||
|
|
||||||
fun computeBarValue(barIdx: Int, barAmount: Int): Byte {
|
|
||||||
/** @return The array's value at the interpolated index. */
|
|
||||||
fun getSampleValue(sampleData: ByteArray?): Byte {
|
|
||||||
if (sampleData == null || sampleData.isEmpty())
|
|
||||||
return Byte.MIN_VALUE
|
|
||||||
else {
|
|
||||||
val sampleIdx = (barIdx * (sampleData.size / barAmount.toFloat())).toInt()
|
|
||||||
return sampleData[sampleIdx]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (progress == 1f) {
|
|
||||||
return getSampleValue(sampleDataTo)
|
|
||||||
}
|
|
||||||
|
|
||||||
val fromValue = getSampleValue(sampleDataFrom)
|
|
||||||
val toValue = getSampleValue(sampleDataTo)
|
|
||||||
val rawResultValue = fromValue * (1f - progress) + toValue * progress
|
|
||||||
return rawResultValue.roundToInt().toByte()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setSamples(sampleData: ByteArray?) {
|
|
||||||
/** @return a mix between [sampleDataFrom] and [sampleDataTo] arrays according to the current [progress] value. */
|
|
||||||
fun computeNewDataFromArray(): ByteArray? {
|
|
||||||
if (sampleDataTo == null) return null
|
|
||||||
if (sampleDataFrom == null) return sampleDataTo
|
|
||||||
|
|
||||||
val sampleSize = min(sampleDataFrom!!.size, sampleDataTo!!.size)
|
|
||||||
return ByteArray(sampleSize) { i -> computeBarValue(i, sampleSize) }
|
|
||||||
}
|
|
||||||
|
|
||||||
sampleDataFrom = computeNewDataFromArray()
|
|
||||||
sampleDataTo = sampleData
|
|
||||||
progress = 0f
|
|
||||||
|
|
||||||
animation?.cancel()
|
|
||||||
animation = ValueAnimator.ofFloat(0f, 1f).apply {
|
|
||||||
addUpdateListener { animation ->
|
|
||||||
progress = animation.animatedValue as Float
|
|
||||||
invalidateDelegate()
|
|
||||||
}
|
|
||||||
interpolator = DecelerateInterpolator(3f)
|
|
||||||
duration = 500
|
|
||||||
start()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getSamples(): ByteArray? {
|
|
||||||
return sampleDataTo
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class WaveGravity {
|
|
||||||
TOP,
|
|
||||||
CENTER,
|
|
||||||
BOTTOM,
|
|
||||||
;
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@JvmStatic
|
|
||||||
fun fromString(gravity: String?): WaveGravity = when (gravity) {
|
|
||||||
"1" -> TOP
|
|
||||||
"2" -> CENTER
|
|
||||||
else -> BOTTOM
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ProgressChangeListener {
|
|
||||||
fun onProgressChanged(waveformSeekBar: WaveformSeekBar, progress: Float, fromUser: Boolean)
|
|
||||||
}
|
|
||||||
}
|
|
@ -15,7 +15,7 @@ import org.session.libsession.utilities.Address;
|
|||||||
import org.session.libsession.utilities.Util;
|
import org.session.libsession.utilities.Util;
|
||||||
import org.session.libsession.utilities.recipients.Recipient;
|
import org.session.libsession.utilities.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity;
|
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity;
|
||||||
import org.thoughtcrime.securesms.loki.utilities.MentionUtilities;
|
import org.thoughtcrime.securesms.conversation.v2.utilities.MentionUtilities;
|
||||||
|
|
||||||
import network.loki.messenger.R;
|
import network.loki.messenger.R;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package org.thoughtcrime.securesms.loki.api
|
package org.thoughtcrime.securesms.notifications
|
||||||
|
|
||||||
import android.content.BroadcastReceiver
|
import android.content.BroadcastReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
@ -57,9 +57,8 @@ import org.thoughtcrime.securesms.database.ThreadDatabase;
|
|||||||
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
|
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
|
||||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
||||||
import org.thoughtcrime.securesms.loki.api.OpenGroupManager;
|
import org.thoughtcrime.securesms.util.SessionMetaProtocol;
|
||||||
import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol;
|
import org.thoughtcrime.securesms.conversation.v2.utilities.MentionUtilities;
|
||||||
import org.thoughtcrime.securesms.loki.utilities.MentionUtilities;
|
|
||||||
import org.thoughtcrime.securesms.mms.SlideDeck;
|
import org.thoughtcrime.securesms.mms.SlideDeck;
|
||||||
import org.thoughtcrime.securesms.service.KeyCachingService;
|
import org.thoughtcrime.securesms.service.KeyCachingService;
|
||||||
import org.thoughtcrime.securesms.util.SpanUtil;
|
import org.thoughtcrime.securesms.util.SpanUtil;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
@file:JvmName("FcmUtils")
|
@file:JvmName("FcmUtils")
|
||||||
package org.thoughtcrime.securesms.loki.utilities
|
package org.thoughtcrime.securesms.notifications
|
||||||
|
|
||||||
import com.google.android.gms.tasks.Task
|
import com.google.android.gms.tasks.Task
|
||||||
import com.google.firebase.iid.FirebaseInstanceId
|
import com.google.firebase.iid.FirebaseInstanceId
|
@ -1,4 +1,4 @@
|
|||||||
package org.thoughtcrime.securesms.loki.api
|
package org.thoughtcrime.securesms.notifications
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import nl.komponents.kovenant.functional.map
|
import nl.komponents.kovenant.functional.map
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user