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:
jubb 2021-07-12 09:28:14 +10:00
commit 842cfc25a1
172 changed files with 665 additions and 2034 deletions

View File

@ -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,

View File

@ -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" />

View File

@ -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;
@ -155,7 +154,9 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
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);
} }

View File

@ -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);
} }

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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) {

View File

@ -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

View File

@ -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;

View File

@ -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 {

View File

@ -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 {

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
} }

View File

@ -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

View File

@ -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) {

View File

@ -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

View File

@ -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))

View File

@ -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) }

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -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()
} }

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.*

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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
} }
} }

View File

@ -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)
}

View File

@ -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() {

View File

@ -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

View File

@ -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 {

View File

@ -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();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
boolean isEncryptedSuffix = key.endsWith(ENCRYPTED_SUFFIX);
if (isEncryptedSuffix) {
preferencesEditor.putString(key, value); 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");
} }

View File

@ -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

View File

@ -1,4 +1,4 @@
package org.thoughtcrime.securesms.loki.utilities package org.thoughtcrime.securesms.crypto
import android.content.Context import android.content.Context

View File

@ -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.*

View File

@ -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 {

View File

@ -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

View File

@ -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 {

View File

@ -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.*

View File

@ -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()

View File

@ -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

View File

@ -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) {

View File

@ -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);

View File

@ -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)
} }
} }

View File

@ -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) {

View File

@ -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 {

View File

@ -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;

View File

@ -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) {
@ -194,10 +191,6 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
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,

View File

@ -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
} }

View File

@ -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

View File

@ -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>> {

View File

@ -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>

View File

@ -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() {

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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.*

View File

@ -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
} }
} }

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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)
} }
} }
} }

View File

@ -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) {

View File

@ -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

View File

@ -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

View File

@ -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>()

View File

@ -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>()

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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()
}
}
}

View File

@ -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() }
}
}

View File

@ -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 ))
}
}
}

View File

@ -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
}

View File

@ -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)
}
}
}

View File

@ -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()
}
}

View File

@ -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)
}
}

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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