From 91aefb7c87161475f841882877b1de2644893c84 Mon Sep 17 00:00:00 2001 From: Harris Date: Mon, 7 Jun 2021 11:53:17 +1000 Subject: [PATCH 01/28] feat: upgrade to keystore sealed identity key preferences --- .../securesms/ApplicationContext.java | 10 +++- .../securesms/crypto}/IdentityKeyUtil.java | 57 ++++++++++++++++--- .../securesms/crypto}/KeyPairUtilities.kt | 2 +- .../securesms/database/Storage.kt | 2 +- .../loki/activities/LandingActivity.kt | 5 +- .../loki/activities/LinkDeviceActivity.kt | 6 +- .../RecoveryPhraseRestoreActivity.kt | 6 +- .../loki/activities/RegisterActivity.kt | 2 +- .../securesms/loki/activities/SeedActivity.kt | 8 +-- .../loki/database/LokiAPIDatabase.kt | 17 +++--- .../loki/dialogs/ClearAllDataDialog.kt | 6 +- .../securesms/loki/dialogs/SeedDialog.kt | 4 +- .../thoughtcrime/securesms/util/BackupUtil.kt | 2 +- .../messaging/MessagingModuleConfiguration.kt | 11 ++-- .../sending_receiving/MessageEncrypter.kt | 3 +- 15 files changed, 96 insertions(+), 45 deletions(-) rename {libsession/src/main/java/org/session/libsession/utilities => app/src/main/java/org/thoughtcrime/securesms/crypto}/IdentityKeyUtil.java (68%) rename {libsession/src/main/java/org/session/libsession/utilities => app/src/main/java/org/thoughtcrime/securesms/crypto}/KeyPairUtilities.kt (98%) diff --git a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java index 0eacf377a5..2d8ff78129 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java +++ b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java @@ -27,6 +27,7 @@ import androidx.lifecycle.DefaultLifecycleObserver; import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.ProcessLifecycleOwner; import androidx.multidex.MultiDexApplication; + import org.conscrypt.Conscrypt; import org.session.libsession.avatars.AvatarHelper; import org.session.libsession.messaging.MessagingModuleConfiguration; @@ -47,6 +48,7 @@ import org.session.libsignal.utilities.Log; import org.session.libsignal.utilities.ThreadUtils; import org.signal.aesgcmprovider.AesGcmProvider; import org.thoughtcrime.securesms.components.TypingStatusSender; +import org.thoughtcrime.securesms.crypto.KeyPairUtilities; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.dependencies.SignalCommunicationModule; @@ -84,12 +86,14 @@ import org.webrtc.PeerConnectionFactory; import org.webrtc.PeerConnectionFactory.InitializationOptions; import org.webrtc.voiceengine.WebRtcAudioManager; import org.webrtc.voiceengine.WebRtcAudioUtils; + import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.security.Security; import java.util.Date; import java.util.HashSet; import java.util.Set; + import dagger.ObjectGraph; import kotlin.Unit; import kotlinx.coroutines.Job; @@ -154,8 +158,10 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc conversationListNotificationHandler = new Handler(Looper.getMainLooper()); LokiAPIDatabase apiDB = DatabaseFactory.getLokiAPIDatabase(this); MessagingModuleConfiguration.Companion.configure(this, - DatabaseFactory.getStorage(this), - DatabaseFactory.getAttachmentProvider(this)); + DatabaseFactory.getStorage(this), + DatabaseFactory.getAttachmentProvider(this), + ()-> KeyPairUtilities.INSTANCE.getUserED25519KeyPair(this) + ); SnodeModule.Companion.configure(apiDB, broadcaster); String userPublicKey = TextSecurePreferences.getLocalNumber(this); if (userPublicKey != null) { diff --git a/libsession/src/main/java/org/session/libsession/utilities/IdentityKeyUtil.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java similarity index 68% rename from libsession/src/main/java/org/session/libsession/utilities/IdentityKeyUtil.java rename to app/src/main/java/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java index d42f5c2ac5..4d48896817 100644 --- a/libsession/src/main/java/org/session/libsession/utilities/IdentityKeyUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java @@ -15,21 +15,22 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.session.libsession.utilities; +package org.thoughtcrime.securesms.crypto; import android.content.Context; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; +import android.os.Build; + import androidx.annotation.NonNull; -import org.session.libsignal.crypto.ecc.ECPublicKey; import org.session.libsignal.crypto.IdentityKey; 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.ECKeyPair; 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 java.io.IOException; @@ -45,6 +46,7 @@ public class IdentityKeyUtil { @SuppressWarnings("unused") 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_PRIVATE_KEY_PREF = "pref_identity_private_v3"; @@ -56,8 +58,10 @@ public class IdentityKeyUtil { SharedPreferences preferences = context.getSharedPreferences(MASTER_SECRET_UTIL_PREFERENCES_NAME, 0); return - preferences.contains(IDENTITY_PUBLIC_KEY_PREF) && - preferences.contains(IDENTITY_PRIVATE_KEY_PREF); + (preferences.contains(IDENTITY_PUBLIC_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 @NonNull IdentityKey getIdentityKey(@NonNull Context context) { @@ -94,14 +98,51 @@ public class IdentityKeyUtil { public static String retrieve(Context context, String key) { 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) { SharedPreferences preferences = context.getSharedPreferences(MASTER_SECRET_UTIL_PREFERENCES_NAME, 0); Editor preferencesEditor = preferences.edit(); - preferencesEditor.putString(key, value); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + 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"); } diff --git a/libsession/src/main/java/org/session/libsession/utilities/KeyPairUtilities.kt b/app/src/main/java/org/thoughtcrime/securesms/crypto/KeyPairUtilities.kt similarity index 98% rename from libsession/src/main/java/org/session/libsession/utilities/KeyPairUtilities.kt rename to app/src/main/java/org/thoughtcrime/securesms/crypto/KeyPairUtilities.kt index dfefaa2f12..652732f081 100644 --- a/libsession/src/main/java/org/session/libsession/utilities/KeyPairUtilities.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/KeyPairUtilities.kt @@ -1,4 +1,4 @@ -package org.session.libsession.utilities +package org.thoughtcrime.securesms.crypto import android.content.Context import com.goterl.lazysodium.LazySodiumAndroid diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt index 66b4fd5004..2f6405dc9b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt @@ -1,6 +1,5 @@ package org.thoughtcrime.securesms.database -import android.app.job.JobScheduler import android.content.Context import android.net.Uri import org.session.libsession.database.StorageProtocol @@ -27,6 +26,7 @@ import org.session.libsignal.messages.SignalServiceGroup import org.session.libsignal.utilities.KeyHelper import org.session.libsignal.utilities.guava.Optional import org.thoughtcrime.securesms.ApplicationContext +import org.thoughtcrime.securesms.crypto.IdentityKeyUtil import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob import org.thoughtcrime.securesms.loki.api.OpenGroupManager diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LandingActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LandingActivity.kt index 50080956ba..3388915609 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LandingActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LandingActivity.kt @@ -4,16 +4,15 @@ import android.content.Intent import android.os.Bundle import android.view.View import network.loki.messenger.R +import org.session.libsession.utilities.TextSecurePreferences import org.thoughtcrime.securesms.BaseActionBarActivity -import org.session.libsession.utilities.IdentityKeyUtil +import org.thoughtcrime.securesms.crypto.IdentityKeyUtil import org.thoughtcrime.securesms.loki.utilities.push import org.thoughtcrime.securesms.loki.utilities.setUpActionBarSessionLogo import org.thoughtcrime.securesms.loki.views.FakeChatView import org.thoughtcrime.securesms.service.KeyCachingService import org.thoughtcrime.securesms.util.Util -import org.session.libsession.utilities.TextSecurePreferences - class LandingActivity : BaseActionBarActivity() { override fun onCreate(savedInstanceState: Bundle?) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkDeviceActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkDeviceActivity.kt index 20f8fcf795..5f04b2b34e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkDeviceActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkDeviceActivity.kt @@ -23,16 +23,16 @@ import kotlinx.coroutines.flow.filter import kotlinx.coroutines.launch import network.loki.messenger.R import org.session.libsession.utilities.TextSecurePreferences -import org.session.libsignal.utilities.KeyHelper import org.session.libsignal.crypto.MnemonicCodec -import org.session.libsignal.utilities.hexEncodedPublicKey import org.session.libsignal.utilities.Hex +import org.session.libsignal.utilities.KeyHelper import org.session.libsignal.utilities.Log +import org.session.libsignal.utilities.hexEncodedPublicKey import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.BaseActionBarActivity +import org.thoughtcrime.securesms.crypto.KeyPairUtilities import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragment import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragmentDelegate -import org.session.libsession.utilities.KeyPairUtilities import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities import org.thoughtcrime.securesms.loki.utilities.push import org.thoughtcrime.securesms.loki.utilities.setUpActionBarSessionLogo diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/RecoveryPhraseRestoreActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/RecoveryPhraseRestoreActivity.kt index c3e01217d9..f4dc7b323c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/RecoveryPhraseRestoreActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/RecoveryPhraseRestoreActivity.kt @@ -14,12 +14,12 @@ import android.widget.Toast import kotlinx.android.synthetic.main.activity_recovery_phrase_restore.* import network.loki.messenger.R import org.session.libsession.utilities.TextSecurePreferences -import org.session.libsignal.utilities.KeyHelper import org.session.libsignal.crypto.MnemonicCodec -import org.session.libsignal.utilities.hexEncodedPublicKey import org.session.libsignal.utilities.Hex +import org.session.libsignal.utilities.KeyHelper +import org.session.libsignal.utilities.hexEncodedPublicKey import org.thoughtcrime.securesms.BaseActionBarActivity -import org.session.libsession.utilities.KeyPairUtilities +import org.thoughtcrime.securesms.crypto.KeyPairUtilities import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities import org.thoughtcrime.securesms.loki.utilities.push import org.thoughtcrime.securesms.loki.utilities.setUpActionBarSessionLogo diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/RegisterActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/RegisterActivity.kt index c9e5bd83cc..27ab501a68 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/RegisterActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/RegisterActivity.kt @@ -18,12 +18,12 @@ import android.widget.Toast import com.goterl.lazysodium.utils.KeyPair import kotlinx.android.synthetic.main.activity_register.* import network.loki.messenger.R -import org.session.libsession.utilities.KeyPairUtilities import org.session.libsession.utilities.TextSecurePreferences import org.session.libsignal.crypto.ecc.ECKeyPair import org.session.libsignal.utilities.KeyHelper import org.session.libsignal.utilities.hexEncodedPublicKey import org.thoughtcrime.securesms.BaseActionBarActivity +import org.thoughtcrime.securesms.crypto.KeyPairUtilities import org.thoughtcrime.securesms.loki.utilities.push import org.thoughtcrime.securesms.loki.utilities.setUpActionBarSessionLogo import java.util.* diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SeedActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SeedActivity.kt index a7f10da4a8..aed826e086 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SeedActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SeedActivity.kt @@ -11,13 +11,13 @@ import android.widget.LinearLayout import android.widget.Toast import kotlinx.android.synthetic.main.activity_seed.* import network.loki.messenger.R -import org.thoughtcrime.securesms.BaseActionBarActivity -import org.session.libsession.utilities.IdentityKeyUtil -import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities -import org.thoughtcrime.securesms.loki.utilities.getColorWithID import org.session.libsession.utilities.TextSecurePreferences import org.session.libsignal.crypto.MnemonicCodec import org.session.libsignal.utilities.hexEncodedPrivateKey +import org.thoughtcrime.securesms.BaseActionBarActivity +import org.thoughtcrime.securesms.crypto.IdentityKeyUtil +import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities +import org.thoughtcrime.securesms.loki.utilities.getColorWithID class SeedActivity : BaseActionBarActivity() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiAPIDatabase.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiAPIDatabase.kt index 91f04adef8..f9bfe7f862 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiAPIDatabase.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiAPIDatabase.kt @@ -2,22 +2,25 @@ package org.thoughtcrime.securesms.loki.database import android.content.ContentValues import android.content.Context -import org.session.libsession.utilities.IdentityKeyUtil import org.session.libsession.utilities.TextSecurePreferences 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.Snode import org.session.libsignal.database.LokiAPIDatabaseProtocol -import org.session.libsignal.utilities.PublicKeyValidation -import org.session.libsignal.utilities.removing05PrefixIfNeeded -import org.session.libsignal.utilities.toHexString -import org.session.libsignal.utilities.Hex -import org.session.libsignal.utilities.Log +import org.session.libsignal.utilities.* +import org.thoughtcrime.securesms.crypto.IdentityKeyUtil import org.thoughtcrime.securesms.database.Database import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper import org.thoughtcrime.securesms.loki.utilities.* 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 { diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/ClearAllDataDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/ClearAllDataDialog.kt index cb1bf22566..7e98a8a949 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/ClearAllDataDialog.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/ClearAllDataDialog.kt @@ -4,14 +4,14 @@ import android.app.Dialog import android.graphics.Color import android.graphics.drawable.ColorDrawable import android.os.Bundle -import androidx.fragment.app.DialogFragment -import androidx.appcompat.app.AlertDialog import android.view.LayoutInflater +import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.DialogFragment import kotlinx.android.synthetic.main.dialog_clear_all_data.view.* import network.loki.messenger.R import org.thoughtcrime.securesms.ApplicationContext +import org.thoughtcrime.securesms.crypto.KeyPairUtilities import org.thoughtcrime.securesms.loki.protocol.MultiDeviceProtocol -import org.session.libsession.utilities.KeyPairUtilities class ClearAllDataDialog : DialogFragment() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/SeedDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/SeedDialog.kt index 2bd9595b22..eceb36c146 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/SeedDialog.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/dialogs/SeedDialog.kt @@ -13,10 +13,10 @@ import androidx.appcompat.app.AlertDialog import androidx.fragment.app.DialogFragment import kotlinx.android.synthetic.main.dialog_seed.view.* import network.loki.messenger.R -import org.session.libsession.utilities.IdentityKeyUtil -import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities import org.session.libsignal.crypto.MnemonicCodec import org.session.libsignal.utilities.hexEncodedPrivateKey +import org.thoughtcrime.securesms.crypto.IdentityKeyUtil +import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities class SeedDialog : DialogFragment() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/BackupUtil.kt b/app/src/main/java/org/thoughtcrime/securesms/util/BackupUtil.kt index b597835e05..e38df7391b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/BackupUtil.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/BackupUtil.kt @@ -21,7 +21,7 @@ import org.thoughtcrime.securesms.backup.BackupPassphrase import org.thoughtcrime.securesms.backup.BackupProtos.SharedPreference import org.thoughtcrime.securesms.backup.FullBackupExporter import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider -import org.session.libsession.utilities.IdentityKeyUtil +import org.thoughtcrime.securesms.crypto.IdentityKeyUtil import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.loki.database.BackupFileRecord import org.thoughtcrime.securesms.service.LocalBackupListener diff --git a/libsession/src/main/java/org/session/libsession/messaging/MessagingModuleConfiguration.kt b/libsession/src/main/java/org/session/libsession/messaging/MessagingModuleConfiguration.kt index f6e4445dde..f887a8f383 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/MessagingModuleConfiguration.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/MessagingModuleConfiguration.kt @@ -1,24 +1,27 @@ package org.session.libsession.messaging import android.content.Context +import com.goterl.lazysodium.utils.KeyPair import org.session.libsession.database.MessageDataProvider import org.session.libsession.database.StorageProtocol class MessagingModuleConfiguration( val context: Context, val storage: StorageProtocol, - val messageDataProvider: MessageDataProvider + val messageDataProvider: MessageDataProvider, + val keyPairProvider: ()-> KeyPair? ) { companion object { lateinit var shared: MessagingModuleConfiguration fun configure(context: Context, - storage: StorageProtocol, - messageDataProvider: MessageDataProvider + storage: StorageProtocol, + messageDataProvider: MessageDataProvider, + keyPairProvider: () -> KeyPair? ) { if (Companion::shared.isInitialized) { return } - shared = MessagingModuleConfiguration(context, storage, messageDataProvider) + shared = MessagingModuleConfiguration(context, storage, messageDataProvider, keyPairProvider) } } } \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageEncrypter.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageEncrypter.kt index ef91eae598..cbd97e12f7 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageEncrypter.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageEncrypter.kt @@ -6,7 +6,6 @@ import com.goterl.lazysodium.interfaces.Box import com.goterl.lazysodium.interfaces.Sign import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.messaging.sending_receiving.MessageSender.Error -import org.session.libsession.utilities.KeyPairUtilities import org.session.libsignal.utilities.Hex import org.session.libsignal.utilities.Log import org.session.libsignal.utilities.removing05PrefixIfNeeded @@ -25,7 +24,7 @@ object MessageEncrypter { */ internal fun encrypt(plaintext: ByteArray, recipientHexEncodedX25519PublicKey: String): ByteArray{ val context = MessagingModuleConfiguration.shared.context - val userED25519KeyPair = KeyPairUtilities.getUserED25519KeyPair(context) ?: throw Error.NoUserED25519KeyPair + val userED25519KeyPair = MessagingModuleConfiguration.shared.keyPairProvider() ?: throw Error.NoUserED25519KeyPair val recipientX25519PublicKey = Hex.fromStringCondensed(recipientHexEncodedX25519PublicKey.removing05PrefixIfNeeded()) val verificationData = plaintext + userED25519KeyPair.publicKey.asBytes + recipientX25519PublicKey From 596b02cfaf363a4c8682958b3118bf7e76c11f14 Mon Sep 17 00:00:00 2001 From: Harris Date: Mon, 7 Jun 2021 12:00:21 +1000 Subject: [PATCH 02/28] fix: don't double encrypt on save --- .../org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java index 4d48896817..c48f900f3f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java @@ -139,7 +139,12 @@ public class IdentityKeyUtil { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.seal(value.getBytes()); - preferencesEditor.putString(key+ENCRYPTED_SUFFIX, encryptedSecret.serialize()); + boolean isEncryptedSuffix = key.endsWith(ENCRYPTED_SUFFIX); + if (isEncryptedSuffix) { + preferencesEditor.putString(key, encryptedSecret.serialize()); + } else { + preferencesEditor.putString(key+ENCRYPTED_SUFFIX, encryptedSecret.serialize()); + } } else { preferencesEditor.putString(key, value); } From f10a99ce41a4a7abf4322e2c649c15c1f5e257ee Mon Sep 17 00:00:00 2001 From: Harris Date: Mon, 7 Jun 2021 15:23:20 +1000 Subject: [PATCH 03/28] fix: don't put encrypted in encrypted --- .../org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java index c48f900f3f..356a8a6fd1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java @@ -138,11 +138,11 @@ public class IdentityKeyUtil { Editor preferencesEditor = preferences.edit(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.seal(value.getBytes()); boolean isEncryptedSuffix = key.endsWith(ENCRYPTED_SUFFIX); if (isEncryptedSuffix) { - preferencesEditor.putString(key, encryptedSecret.serialize()); + preferencesEditor.putString(key, value); } else { + KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.seal(value.getBytes()); preferencesEditor.putString(key+ENCRYPTED_SUFFIX, encryptedSecret.serialize()); } } else { From 11b44a5ddea2bd5262f6a54ef55dcc8dc73b132c Mon Sep 17 00:00:00 2001 From: Harris Date: Wed, 9 Jun 2021 10:35:24 +1000 Subject: [PATCH 04/28] feat: force upgrade on check if identity key exists --- .../securesms/crypto/IdentityKeyUtil.java | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java index 356a8a6fd1..c3ed88e896 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java @@ -57,11 +57,26 @@ public class IdentityKeyUtil { public static boolean hasIdentityKey(Context context) { SharedPreferences preferences = context.getSharedPreferences(MASTER_SECRET_UTIL_PREFERENCES_NAME, 0); - return - (preferences.contains(IDENTITY_PUBLIC_KEY_PREF) && - preferences.contains(IDENTITY_PRIVATE_KEY_PREF)) + boolean hasIdentityKey = (preferences.contains(IDENTITY_PUBLIC_KEY_PREF) && + preferences.contains(IDENTITY_PRIVATE_KEY_PREF)) || (preferences.contains(IDENTITY_PUBLIC_KEY_PREF+ENCRYPTED_SUFFIX) && - preferences.contains(IDENTITY_PRIVATE_KEY_PREF+ENCRYPTED_SUFFIX)); + preferences.contains(IDENTITY_PRIVATE_KEY_PREF+ENCRYPTED_SUFFIX)); + + // check if any keys are not migrated + if (hasIdentityKey) { + checkUpdate(context); + } + + return hasIdentityKey; + } + + private static void checkUpdate(Context context) { + // 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); } public static @NonNull IdentityKey getIdentityKey(@NonNull Context context) { From 645bf66424424e8608d8affc12dc5262bb08d5a2 Mon Sep 17 00:00:00 2001 From: Harris Date: Wed, 9 Jun 2021 11:31:45 +1000 Subject: [PATCH 05/28] feat: only FORCE upgrade if not upgraded yet --- .../org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java index c3ed88e896..2ca215c894 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java @@ -53,6 +53,7 @@ public class IdentityKeyUtil { 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 LOKI_SEED = "loki_seed"; + public static final String HAS_MIGRATED_KEY = "has_migrated_keys"; public static boolean hasIdentityKey(Context context) { SharedPreferences preferences = context.getSharedPreferences(MASTER_SECRET_UTIL_PREFERENCES_NAME, 0); @@ -63,8 +64,10 @@ public class IdentityKeyUtil { preferences.contains(IDENTITY_PRIVATE_KEY_PREF+ENCRYPTED_SUFFIX)); // check if any keys are not migrated - if (hasIdentityKey) { + if (hasIdentityKey && !preferences.getBoolean(HAS_MIGRATED_KEY, false)) { + // this will retrieve and force upgrade if possible checkUpdate(context); + preferences.edit().putBoolean(HAS_MIGRATED_KEY, true).apply(); } return hasIdentityKey; From 3c1b0ff1e1b6cf38cd6df3f1c2cba01a01085c0a Mon Sep 17 00:00:00 2001 From: Harris Date: Wed, 9 Jun 2021 15:56:20 +1000 Subject: [PATCH 06/28] feat: extract upgrade to HomeActivity.kt resume instead of access --- .../securesms/crypto/IdentityKeyUtil.java | 39 ++++++++++--------- .../securesms/loki/activities/HomeActivity.kt | 9 ++--- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java index 2ca215c894..a5333ef5d4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java @@ -55,31 +55,32 @@ public class IdentityKeyUtil { public static final String LOKI_SEED = "loki_seed"; public static final String HAS_MIGRATED_KEY = "has_migrated_keys"; - public static boolean hasIdentityKey(Context context) { - SharedPreferences preferences = context.getSharedPreferences(MASTER_SECRET_UTIL_PREFERENCES_NAME, 0); + private static SharedPreferences getSharedPreferences(Context context) { + return context.getSharedPreferences(MASTER_SECRET_UTIL_PREFERENCES_NAME, 0); + } - boolean hasIdentityKey = (preferences.contains(IDENTITY_PUBLIC_KEY_PREF) && + public static boolean hasIdentityKey(Context context) { + SharedPreferences preferences = getSharedPreferences(context); + + return (preferences.contains(IDENTITY_PUBLIC_KEY_PREF) && preferences.contains(IDENTITY_PRIVATE_KEY_PREF)) || (preferences.contains(IDENTITY_PUBLIC_KEY_PREF+ENCRYPTED_SUFFIX) && preferences.contains(IDENTITY_PRIVATE_KEY_PREF+ENCRYPTED_SUFFIX)); - - // check if any keys are not migrated - if (hasIdentityKey && !preferences.getBoolean(HAS_MIGRATED_KEY, false)) { - // this will retrieve and force upgrade if possible - checkUpdate(context); - preferences.edit().putBoolean(HAS_MIGRATED_KEY, true).apply(); - } - - return hasIdentityKey; } - private static void checkUpdate(Context context) { - // 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); + 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) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt index 54cf2f8d7c..f65b00538d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt @@ -29,15 +29,14 @@ import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode 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.pollers.OpenGroupPollerV2 import org.session.libsession.utilities.* -import org.session.libsignal.utilities.toHexString import org.session.libsignal.utilities.ThreadUtils +import org.session.libsignal.utilities.toHexString import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity import org.thoughtcrime.securesms.conversation.ConversationActivity +import org.thoughtcrime.securesms.crypto.IdentityKeyUtil import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.database.model.ThreadRecord import org.thoughtcrime.securesms.loki.api.OpenGroupManager @@ -155,8 +154,8 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), ConversationClickLis override fun onResume() { super.onResume() - if (TextSecurePreferences.getLocalNumber(this) == null) { - return; } // This can be the case after a secondary device is auto-cleared + if (TextSecurePreferences.getLocalNumber(this) == null) { 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.update() val hasViewedSeed = TextSecurePreferences.getHasViewedSeed(this) From f5835e1b547693a2406cec05cd205ad0284f819e Mon Sep 17 00:00:00 2001 From: Ryan Zhao Date: Thu, 8 Jul 2021 09:25:43 +1000 Subject: [PATCH 07/28] WIP: auto play next voice message --- .../conversation/v2/ConversationActivityV2.kt | 23 ++++++++++++++++--- .../v2/messages/VisibleMessageContentView.kt | 1 + .../v2/messages/VoiceMessageView.kt | 8 +++++++ 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index cf36023b17..b9899c8a32 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt @@ -23,6 +23,8 @@ import android.widget.RelativeLayout import android.widget.Toast import androidx.annotation.DimenRes import androidx.appcompat.app.AlertDialog +import androidx.core.view.children +import androidx.core.view.get import androidx.core.view.isVisible import androidx.lifecycle.Observer 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_recording.* 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 nl.komponents.kovenant.ui.failUi import nl.komponents.kovenant.ui.successUi @@ -84,8 +87,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.ConversationActionModeCallbackDelegate import org.thoughtcrime.securesms.conversation.v2.menus.ConversationMenuHelper -import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageContentViewDelegate -import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageView +import org.thoughtcrime.securesms.conversation.v2.messages.* import org.thoughtcrime.securesms.conversation.v2.search.SearchBottomBar import org.thoughtcrime.securesms.conversation.v2.search.SearchViewModel import org.thoughtcrime.securesms.conversation.v2.utilities.AttachmentManager @@ -124,7 +126,7 @@ import kotlin.math.* class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDelegate, InputBarRecordingViewDelegate, AttachmentManager.AttachmentListener, ActivityDispatcher, ConversationActionModeCallbackDelegate, VisibleMessageContentViewDelegate, RecipientModifiedListener, - SearchBottomBar.EventListener { + SearchBottomBar.EventListener, VoiceMessageViewDelegate { private val screenWidth = Resources.getSystem().displayMetrics.widthPixels private var linkPreviewViewModel: LinkPreviewViewModel? = null private var threadID: Long = -1 @@ -844,6 +846,21 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe conversationRecyclerView.scrollToPosition(lastSeenItemPosition) } + override fun playNextAudioIfPossible(current: Int) { + if (current < conversationRecyclerView.childCount) { + 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() { if (thread.isContactRecipient && thread.isBlocked) { BlockedDialog(thread).show(supportFragmentManager, "Blocked Dialog") diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt index 16721b1625..5c02cb45a4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt @@ -103,6 +103,7 @@ class VisibleMessageContentView : LinearLayout { val voiceMessageView = VoiceMessageView(context) voiceMessageView.bind(message, isStartOfMessageCluster, isEndOfMessageCluster) mainContainer.addView(voiceMessageView) + voiceMessageView.delegate = delegate // 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. onContentClick = { voiceMessageView.togglePlayback() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VoiceMessageView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VoiceMessageView.kt index b957b0a166..8cbd904c29 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VoiceMessageView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VoiceMessageView.kt @@ -25,6 +25,8 @@ class VoiceMessageView : LinearLayout, AudioSlidePlayer.Listener { private var duration = 0L private var player: AudioSlidePlayer? = null private var isPreparing = false + var delegate: VisibleMessageContentViewDelegate? = null + var index = 0 // region Lifecycle constructor(context: Context) : super(context) { initialize() } @@ -70,6 +72,7 @@ class VoiceMessageView : LinearLayout, AudioSlidePlayer.Listener { if (progress == 1.0) { togglePlayback() handleProgressChanged(0.0) + delegate?.playNextAudioIfPossible(index) } else { handleProgressChanged(progress) } @@ -112,3 +115,8 @@ class VoiceMessageView : LinearLayout, AudioSlidePlayer.Listener { } // endregion } + +interface VoiceMessageViewDelegate { + + fun playNextAudioIfPossible(current: Int) +} From a1e63c5f8ecec22b9c1e3a84fa431e3b82f4ce8a Mon Sep 17 00:00:00 2001 From: Ryan Zhao Date: Thu, 8 Jul 2021 10:24:10 +1000 Subject: [PATCH 08/28] auto play next audio --- .../securesms/conversation/v2/ConversationActivityV2.kt | 4 ++-- .../securesms/conversation/v2/ConversationAdapter.kt | 1 + .../conversation/v2/messages/VisibleMessageContentView.kt | 5 ++++- .../securesms/conversation/v2/messages/VisibleMessageView.kt | 2 ++ .../securesms/conversation/v2/messages/VoiceMessageView.kt | 4 ++-- 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index 480c74d33e..7ca866cd76 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt @@ -847,8 +847,8 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe } override fun playNextAudioIfPossible(current: Int) { - if (current < conversationRecyclerView.childCount) { - val nextVisibleMessageView = conversationRecyclerView[current + 1] as? VisibleMessageView + if (current > 0) { + val nextVisibleMessageView = conversationRecyclerView[current - 1] as? VisibleMessageView nextVisibleMessageView?.let { visibleMessageView -> visibleMessageView.messageContentView.mainContainer.children.forEach { child -> val nextVoiceMessageView = child as? VoiceMessageView diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapter.kt index 4b413ef89f..a4a5f1ba28 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapter.kt @@ -72,6 +72,7 @@ class ConversationAdapter(context: Context, cursor: Cursor, private val onItemPr view.snIsSelected = isSelected view.messageTimestampTextView.isVisible = isSelected val position = viewHolder.adapterPosition + view.viewHolderIndex = position view.bind(message, getMessageBefore(position, cursor), getMessageAfter(position, cursor), glide, searchQuery) view.onPress = { event -> onItemPress(message, viewHolder.adapterPosition, view, event) } view.onSwipeToReply = { onItemSwipeToReply(message, viewHolder.adapterPosition) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt index 3f98ba5f87..b8b3b9812e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt @@ -32,6 +32,7 @@ import org.session.libsession.utilities.ViewUtil import org.session.libsession.utilities.recipients.Recipient import org.thoughtcrime.securesms.conversation.v2.components.AlbumThumbnailView 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.utilities.ModalURLSpan import org.thoughtcrime.securesms.conversation.v2.utilities.TextUtilities.getIntersectedModalSpans @@ -48,6 +49,7 @@ class VisibleMessageContentView : LinearLayout { var onContentClick: ((event: MotionEvent) -> Unit)? = null var onContentDoubleTap: (() -> Unit)? = null var delegate: VisibleMessageContentViewDelegate? = null + var viewHolderIndex: Int = -1 // region Lifecycle constructor(context: Context) : super(context) { initialize() } @@ -107,9 +109,10 @@ class VisibleMessageContentView : LinearLayout { } } else if (message is MmsMessageRecord && message.slideDeck.audioSlide != null) { val voiceMessageView = VoiceMessageView(context) + voiceMessageView.index = viewHolderIndex + voiceMessageView.delegate = context as? ConversationActivityV2 voiceMessageView.bind(message, isStartOfMessageCluster, isEndOfMessageCluster) mainContainer.addView(voiceMessageView) - voiceMessageView.delegate = delegate // 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. onContentClick = { voiceMessageView.togglePlayback() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt index ad90bd5328..f938007ada 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt @@ -47,6 +47,7 @@ class VisibleMessageView : LinearLayout { private var longPressCallback: Runnable? = null private var onDownTimestamp = 0L private var onDoubleTap: (() -> Unit)? = null + var viewHolderIndex: Int = -1 var snIsSelected = false set(value) { field = value; handleIsSelectedChanged()} var onPress: ((event: MotionEvent) -> Unit)? = null @@ -151,6 +152,7 @@ class VisibleMessageView : LinearLayout { var maxWidth = screenWidth - startPadding - endPadding if (profilePictureContainer.visibility != View.GONE) { maxWidth -= profilePictureContainer.width } // Populate content view + messageContentView.viewHolderIndex = viewHolderIndex messageContentView.bind(message, isStartOfMessageCluster, isEndOfMessageCluster, glide, maxWidth, thread, searchQuery) messageContentView.delegate = contentViewDelegate onDoubleTap = { messageContentView.onContentDoubleTap?.invoke() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VoiceMessageView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VoiceMessageView.kt index d48d0b4d64..7823cc59d1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VoiceMessageView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VoiceMessageView.kt @@ -32,8 +32,8 @@ class VoiceMessageView : LinearLayout, AudioSlidePlayer.Listener { private var duration = 0L private var player: AudioSlidePlayer? = null private var isPreparing = false - var delegate: VisibleMessageContentViewDelegate? = null - var index = 0 + var delegate: VoiceMessageViewDelegate? = null + var index = -1 // region Lifecycle constructor(context: Context) : super(context) { initialize() } From 8ef2a930e34cfbf020d5a6e8989a04c17aa07b02 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Thu, 8 Jul 2021 10:42:42 +1000 Subject: [PATCH 09/28] Add oxen logo --- .../loki/activities/SettingsActivity.kt | 2 ++ .../res/drawable-xxxhdpi/oxen_dark_mode.png | Bin 0 -> 14333 bytes .../res/drawable-xxxhdpi/oxen_light_mode.png | Bin 0 -> 13543 bytes app/src/main/res/layout/activity_settings.xml | 9 ++++++++- 4 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 app/src/main/res/drawable-xxxhdpi/oxen_dark_mode.png create mode 100644 app/src/main/res/drawable-xxxhdpi/oxen_light_mode.png diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SettingsActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SettingsActivity.kt index 2078e15dc1..c5176d2222 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SettingsActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SettingsActivity.kt @@ -90,6 +90,8 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() { helpTranslateButton.setOnClickListener { helpTranslate() } seedButton.setOnClickListener { showSeed() } clearAllDataButton.setOnClickListener { clearAllData() } + val isLightMode = UiModeUtilities.isDayUiMode(this) + oxenLogoImageView.setImageResource(if (isLightMode) R.drawable.oxen_light_mode else R.drawable.oxen_dark_mode) versionTextView.text = String.format(getString(R.string.version_s), "${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})") } diff --git a/app/src/main/res/drawable-xxxhdpi/oxen_dark_mode.png b/app/src/main/res/drawable-xxxhdpi/oxen_dark_mode.png new file mode 100644 index 0000000000000000000000000000000000000000..84c7bb105919f4b044e1b8b4f6852654ee1c79dc GIT binary patch literal 14333 zcmX|IcRbbq_pc9;3Pp%q8n#f`u646#W-_jk%xh%cxLq>JNLEJnwO6i9#-*~duaS$( zRSDO)ZgRO<`MrJme1HFVxbO3t=XGA^b;k3=7#V6Yp5r=q>eMMl9c^`!Q>SQ%!2h_j zbim&j7sTmPr)0Zz)FEb1vq|&xS=<`Q$6NnIs?ud>d_GSnxbdqKLp|p5g7g2t%DvVk z;FCD-9RD7hxvlLuYv%O$F;Zo8;y!tvRQ{-l2y%c$=y_l@r%)hr5WSJ8-lqUB zl_nfIK!Sfsx7RNZ&i_jy3-amDjiZ&7wD;AI<7rMB*D$b9(uyp%FsqHFoO~s?W^%a6(@Z^JPpo5SwBLLtZQX`Y;tv7c;lQ{ zrt*u4_GDoX`!CT6dXQ2UkHDl24|Wv7o2cG5*vr@{a$c4xuUTkz`C+jQN7G70N~KXc%V zCqyAg07EPPGI)bzCOO^3y=2;dV6Hc5fuR9}=hUhD=KRF8Y~I>t$KBusFngQ^zTOSF zFGf~9A%4(fV+a;;IsRnZ*6#Sx%xtqmeFhACf3AEk=VW)k?@zDS3;bRPSE-s=HBPRL*OUo6J3i|GIdbM|D|9~bXJnt#~L&tynfFYwl{rWHvb z=>IvPK+mUswR(h}`BlBqhzGMjE|y+BpH|jM`OCLf7J-v~_!6X7HFLXT*!h59T%ZTF z{okED7mw7&ipg6%3i%9Xy8I2zzp6)ooQ$c8k2O4D&e#RrS!!@!ctM%ZTsHq&<$tWR zjy1qe5SH!m>uThOMnx98nJ%OLC;b|Q?n3Ub4|mq!4hXd0tDt|+R===}c0PIAbl5)B&)%kaMgnxQgvUL60GC7Eyy-EEs;W^(tRC*w-;()}M2)90fBINwKO6q(k1r zgZ}cV&{q#@>ifsDi)V#Mr=tK&@!ZIUg_4q;*M#FqSS*yywF>`}_1{S6s%fE4qyxqH z*MuMso&9%zKiA}J0AjIXH-itG>(i*7Z{|aef&V?8Y2B%lEsT;#Mk9{@^n>)qtApwP z6_%|2G?cY(NH@XohXPMcn3LYPH{vhlYJc(x1xs-D1u;)YDzr*GO#XxYD}Ya}R~YK~ zS7tR0489S_`wu^y4Qa;l>y2CdcW^rjfbhyMIC!hf!nD%+PWMt~Z8-pGA^nw>3jsN( zV|(wmRz;Fi<27Bx5i~bdIiZus=!urUiVLv^r#NUmg7OYN|q~hSMHG3!< zrtf8y95N3+>v78y7yZYok-YMXn_jmq;aT?LL)M3lI6W{2j1Xpz2oyuwdxJ?kVeng7 z*sm0F3$y!*#v8ffT-KE9gN<&u@3|#M{klCET_ z%oqDZJB58)?HY=zKOypyV0da z@_XMwbfL4Zp3J4a;!^U>@{)C%2X5sJn)wlK@fg;6_13swF-Yh&a5ZeVM^)}I8Lm#`bTa&T|dkieUf}9sd^;($HSR_c=Ed2Rc8d7pj zVLBm<0uf0tZ`4TX0DEspuiQy|GjJrG^rl|_vd@>dDCY7T8);f+&I_;tH&n%l>(bJ7 zE@{*x=2W2Fm|8ZMVVcd2bG+1zgCKs~$8qmDoIy6sm^l=;!9BmlROB28&Gw*UWi`%4 znq3c^f&J_qNl#M^`R?NpY33KMT;!y?eW5H(!aW>)Y0Tv6+Nn1WYp*V!Az#}c+7w-o zzB8clO^v^m>I)h|Yl$tGgl`$69h<&(

UUB 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 YAGah;$XKc4VorYQXc(%a@7ag{A28k!tN;K2 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxxhdpi/oxen_light_mode.png b/app/src/main/res/drawable-xxxhdpi/oxen_light_mode.png new file mode 100644 index 0000000000000000000000000000000000000000..f2c7e60fbced5390c5566923f4b5fe50b1a2aba7 GIT binary patch literal 13543 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 + + Date: Thu, 8 Jul 2021 10:55:08 +1000 Subject: [PATCH 10/28] Update version number --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index d86c8a3a02..2d80c653b9 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -143,8 +143,8 @@ dependencies { testImplementation 'org.robolectric:shadows-multidex:4.2' } -def canonicalVersionCode = 193 -def canonicalVersionName = "1.11.2" +def canonicalVersionCode = 194 +def canonicalVersionName = "1.11.3" def postFixSize = 10 def abiPostFix = ['armeabi-v7a' : 1, From 183f013c31beabed9b64bd8cb0fd982c7aa84e64 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Thu, 8 Jul 2021 13:37:08 +1000 Subject: [PATCH 11/28] Show date break header by hour instead of by day Also ditch relative timestamps in favor of absolute ones --- .../securesms/MediaPreviewActivity.java | 2 +- .../conversation/v2/ConversationActivityV2.kt | 2 +- .../v2/messages/VisibleMessageView.kt | 10 +- .../securesms/loki/views/ConversationView.kt | 2 +- .../thoughtcrime/securesms/util/BackupUtil.kt | 2 +- .../securesms/util/DateUtils.java | 174 +++++++++--------- 6 files changed, 99 insertions(+), 93 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java b/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java index 0b36a73eed..e28b419880 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java @@ -189,7 +189,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im CharSequence relativeTimeSpan; if (mediaItem.date > 0) { - relativeTimeSpan = DateUtils.getExtendedRelativeTimeSpanString(this, Locale.getDefault(), mediaItem.date); + relativeTimeSpan = DateUtils.getDisplayFormattedTimeSpanString(this, Locale.getDefault(), mediaItem.date); } else { relativeTimeSpan = getString(R.string.MediaPreviewActivity_draft); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index 7ca866cd76..d5ceda8d72 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt @@ -1141,7 +1141,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe for (message in sortedMessages) { val body = MentionUtilities.highlightMentions(message.body, message.threadId, this) 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') } if (builder.isNotEmpty() && builder[builder.length - 1] == '\n') { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt index f938007ada..147e18906f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt @@ -110,11 +110,11 @@ class VisibleMessageView : LinearLayout { senderNameTextView.visibility = View.GONE } // 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.text = if (showDateBreak) DateUtils.getRelativeDate(context, Locale.getDefault(), message.timestamp) else "" + dateBreakTextView.text = if (showDateBreak) DateUtils.getDisplayFormattedTimeSpanString(context, Locale.getDefault(), message.timestamp) else "" // Timestamp - messageTimestampTextView.text = DateUtils.getExtendedRelativeTimeSpanString(context, Locale.getDefault(), message.timestamp) + messageTimestampTextView.text = DateUtils.getDisplayFormattedTimeSpanString(context, Locale.getDefault(), message.timestamp) // Margins val startPadding: Int if (isGroupThread) { @@ -177,10 +177,10 @@ class VisibleMessageView : LinearLayout { private fun isEndOfMessageCluster(current: MessageRecord, next: MessageRecord?, isGroupThread: Boolean): Boolean { 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 } 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 } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/views/ConversationView.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/views/ConversationView.kt index 893c73019e..f4341b5ed1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/views/ConversationView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/views/ConversationView.kt @@ -58,7 +58,7 @@ class ConversationView : LinearLayout { profilePictureView.update(thread.recipient, thread.threadId) val senderDisplayName = getUserDisplayName(thread.recipient) ?: thread.recipient.address.toString() 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 val rawSnippet = thread.getDisplayBody(context) val snippet = highlightMentions(rawSnippet, thread.threadId, context) diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/BackupUtil.kt b/app/src/main/java/org/thoughtcrime/securesms/util/BackupUtil.kt index e38df7391b..5d2e84ed7a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/BackupUtil.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/BackupUtil.kt @@ -118,7 +118,7 @@ object BackupUtil { if (timestamp == null) { return context.getString(R.string.BackupUtil_never) } - return DateUtils.getExtendedRelativeTimeSpanString(context, locale, timestamp.time) + return DateUtils.getDisplayFormattedTimeSpanString(context, locale, timestamp.time) } @JvmStatic diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/DateUtils.java b/app/src/main/java/org/thoughtcrime/securesms/util/DateUtils.java index cb822f1eeb..7860e46242 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/DateUtils.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/DateUtils.java @@ -28,6 +28,7 @@ import org.session.libsignal.utilities.Log; import java.text.ParseException; import java.text.SimpleDateFormat; +import java.util.Calendar; import java.util.Date; import java.util.Locale; import java.util.concurrent.TimeUnit; @@ -40,8 +41,9 @@ import network.loki.messenger.R; public class DateUtils extends android.text.format.DateUtils { @SuppressWarnings("unused") - private static final String TAG = DateUtils.class.getSimpleName(); - private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyyMMdd"); + private static final String TAG = DateUtils.class.getSimpleName(); + private static final SimpleDateFormat DAY_PRECISION_DATE_FORMAT = new SimpleDateFormat("yyyyMMdd"); + private static final SimpleDateFormat HOUR_PRECISION_DATE_FORMAT = new SimpleDateFormat("yyyyMMddHH"); private static boolean isWithin(final long millis, final long span, final TimeUnit unit) { return System.currentTimeMillis() - millis <= unit.toMillis(span); @@ -60,6 +62,91 @@ public class DateUtils extends android.text.format.DateUtils { return new SimpleDateFormat(localizedPattern, locale).format(new Date(time)); } + public static String getHourFormat(Context c) { + return (DateFormat.is24HourFormat(c)) ? "HH:mm" : "hh:mm a"; + } + + public static String getDisplayFormattedTimeSpanString(final Context c, final Locale locale, final long timestamp) { + if (isWithin(timestamp, 1, TimeUnit.MINUTES)) { + return c.getString(R.string.DateUtils_just_now); + } else if (isToday(timestamp)) { + return getFormattedDateTime(timestamp, getHourFormat(c), locale); + } else if (isWithin(timestamp, 6, TimeUnit.DAYS)) { + return getFormattedDateTime(timestamp, "EEE " + getHourFormat(c), locale); + } else if (isWithin(timestamp, 365, TimeUnit.DAYS)) { + return getFormattedDateTime(timestamp, "MMM d " + getHourFormat(c), locale); + } else { + return getFormattedDateTime(timestamp, "MMM d " + getHourFormat(c) + ", yyyy", locale); + } + } + + public static SimpleDateFormat getDetailedDateFormatter(Context context, Locale locale) { + String dateFormatPattern; + + if (DateFormat.is24HourFormat(context)) { + dateFormatPattern = getLocalizedPattern("MMM d, yyyy HH:mm:ss zzz", locale); + } else { + dateFormatPattern = getLocalizedPattern("MMM d, yyyy hh:mm:ss a zzz", locale); + } + + return new SimpleDateFormat(dateFormatPattern, locale); + } + + public static String getRelativeDate(@NonNull Context context, + @NonNull Locale locale, + long timestamp) + { + if (isToday(timestamp)) { + return context.getString(R.string.DateUtils_today); + } else if (isYesterday(timestamp)) { + return context.getString(R.string.DateUtils_yesterday); + } else { + return getFormattedDateTime(timestamp, "EEE, MMM d, yyyy", locale); + } + } + + public static boolean isSameDay(long t1, long t2) { + return DAY_PRECISION_DATE_FORMAT.format(new Date(t1)).equals(DAY_PRECISION_DATE_FORMAT.format(new Date(t2))); + } + + public static boolean isSameHour(long t1, long t2) { + return HOUR_PRECISION_DATE_FORMAT.format(new Date(t1)).equals(HOUR_PRECISION_DATE_FORMAT.format(new Date(t2))); + } + + private static String getLocalizedPattern(String template, Locale locale) { + return DateFormat.getBestDateTimePattern(locale, template); + } + + /** + * e.g. 2020-09-04T19:17:51Z + * https://www.iso.org/iso-8601-date-and-time-format.html + * + * Note: SDK_INT == 0 check needed to pass unit tests due to JVM date parser differences. + * + * @return The timestamp if able to be parsed, otherwise -1. + */ + @SuppressLint("ObsoleteSdkInt") + public static long parseIso8601(@Nullable String date) { + SimpleDateFormat format; + if (Build.VERSION.SDK_INT == 0 || Build.VERSION.SDK_INT >= 24) { + format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX", Locale.getDefault()); + } else { + format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault()); + } + + if (date.isEmpty()) { + return -1; + } + + try { + return format.parse(date).getTime(); + } catch (ParseException e) { + Log.w(TAG, "Failed to parse date.", e); + return -1; + } + } + + // region Deprecated public static String getBriefRelativeTimeSpanString(final Context c, final Locale locale, final long timestamp) { if (isWithin(timestamp, 1, TimeUnit.MINUTES)) { return c.getString(R.string.DateUtils_just_now); @@ -96,86 +183,5 @@ public class DateUtils extends android.text.format.DateUtils { return getFormattedDateTime(timestamp, format.toString(), locale); } } - - public static String getDayPrecisionTimeSpanString(Context context, Locale locale, long timestamp) { - SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd"); - - if (simpleDateFormat.format(System.currentTimeMillis()).equals(simpleDateFormat.format(timestamp))) { - return context.getString(R.string.DeviceListItem_today); - } else { - String format; - - if (isWithin(timestamp, 6, TimeUnit.DAYS)) format = "EEE "; - else if (isWithin(timestamp, 365, TimeUnit.DAYS)) format = "MMM d"; - else format = "MMM d, yyy"; - - return getFormattedDateTime(timestamp, format, locale); - } - } - - public static SimpleDateFormat getDetailedDateFormatter(Context context, Locale locale) { - String dateFormatPattern; - - if (DateFormat.is24HourFormat(context)) { - dateFormatPattern = getLocalizedPattern("MMM d, yyyy HH:mm:ss zzz", locale); - } else { - dateFormatPattern = getLocalizedPattern("MMM d, yyyy hh:mm:ss a zzz", locale); - } - - return new SimpleDateFormat(dateFormatPattern, locale); - } - - public static String getRelativeDate(@NonNull Context context, - @NonNull Locale locale, - long timestamp) - { - if (isToday(timestamp)) { - return context.getString(R.string.DateUtils_today); - } else if (isYesterday(timestamp)) { - return context.getString(R.string.DateUtils_yesterday); - } else { - return getFormattedDateTime(timestamp, "EEE, MMM d, yyyy", locale); - } - } - - public static boolean isSameDay(long t1, long t2) { - return DATE_FORMAT.format(new Date(t1)).equals(DATE_FORMAT.format(new Date(t2))); - } - - public static boolean isSameExtendedRelativeTimestamp(@NonNull Context context, @NonNull Locale locale, long t1, long t2) { - return getExtendedRelativeTimeSpanString(context, locale, t1).equals(getExtendedRelativeTimeSpanString(context, locale, t2)); - } - - private static String getLocalizedPattern(String template, Locale locale) { - return DateFormat.getBestDateTimePattern(locale, template); - } - - /** - * e.g. 2020-09-04T19:17:51Z - * https://www.iso.org/iso-8601-date-and-time-format.html - * - * Note: SDK_INT == 0 check needed to pass unit tests due to JVM date parser differences. - * - * @return The timestamp if able to be parsed, otherwise -1. - */ - @SuppressLint("ObsoleteSdkInt") - public static long parseIso8601(@Nullable String date) { - SimpleDateFormat format; - if (Build.VERSION.SDK_INT == 0 || Build.VERSION.SDK_INT >= 24) { - format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX", Locale.getDefault()); - } else { - format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault()); - } - - if (date.isEmpty()) { - return -1; - } - - try { - return format.parse(date).getTime(); - } catch (ParseException e) { - Log.w(TAG, "Failed to parse date.", e); - return -1; - } - } + // endregion } From 5be63cd2979aba08ed6252bc04f4573ab28b2063 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Thu, 8 Jul 2021 13:38:14 +1000 Subject: [PATCH 12/28] Update build number --- app/build.gradle | 2 +- .../securesms/loki/activities/HomeActivity.kt | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 2d80c653b9..833622e129 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -143,7 +143,7 @@ dependencies { testImplementation 'org.robolectric:shadows-multidex:4.2' } -def canonicalVersionCode = 194 +def canonicalVersionCode = 195 def canonicalVersionName = "1.11.3" def postFixSize = 10 diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt index 2b5e2828db..a4b5e6437b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt @@ -11,6 +11,7 @@ import android.text.Spannable import android.text.SpannableString import android.text.style.ForegroundColorSpan import android.util.DisplayMetrics +import android.util.Log import android.view.View import android.widget.RelativeLayout import android.widget.Toast @@ -35,8 +36,8 @@ import org.session.libsignal.utilities.ThreadUtils import org.session.libsignal.utilities.toHexString import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity -import org.thoughtcrime.securesms.crypto.IdentityKeyUtil import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2 +import org.thoughtcrime.securesms.crypto.IdentityKeyUtil import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.database.model.ThreadRecord import org.thoughtcrime.securesms.loki.api.OpenGroupManager @@ -48,7 +49,10 @@ import org.thoughtcrime.securesms.loki.views.NewConversationButtonSetViewDelegat import org.thoughtcrime.securesms.loki.views.SeedReminderViewDelegate import org.thoughtcrime.securesms.mms.GlideApp import org.thoughtcrime.securesms.mms.GlideRequests +import org.thoughtcrime.securesms.util.DateUtils import java.io.IOException +import java.util.* +import java.util.concurrent.TimeUnit class HomeActivity : PassphraseRequiredActionBarActivity(), ConversationClickListener, SeedReminderViewDelegate, NewConversationButtonSetViewDelegate { private lateinit var glide: GlideRequests @@ -150,6 +154,16 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), ConversationClickLis } } EventBus.getDefault().register(this@HomeActivity) + testDateFormatting() + } + + private fun testDateFormatting() { + val timestamp = Date().time + Log.d("Test", getString(R.string.DateUtils_just_now)) + Log.d("Test", DateUtils.getFormattedDateTime(timestamp, DateUtils.getHourFormat(this), Locale.getDefault())) + Log.d("Test", DateUtils.getFormattedDateTime(timestamp, "EEE " + DateUtils.getHourFormat(this), Locale.getDefault())) + Log.d("Test", DateUtils.getFormattedDateTime(timestamp, "MMM d " + DateUtils.getHourFormat(this), Locale.getDefault())) + Log.d("Test", DateUtils.getFormattedDateTime(timestamp, "MMM d " + DateUtils.getHourFormat(this) + ", yyyy", Locale.getDefault())) } override fun onResume() { From 666cab9df64ac9158c290b895e70c6152a9d03a1 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Thu, 8 Jul 2021 16:31:37 +1000 Subject: [PATCH 13/28] Clean up TextSecurePreferences --- .../securesms/ApplicationContext.java | 22 ---- .../loki/activities/BackupRestoreActivity.kt | 1 - .../loki/activities/LandingActivity.kt | 1 - .../utilities/TextSecurePreferences.kt | 120 +++++------------- 4 files changed, 29 insertions(+), 115 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java index 2d8ff78129..c07c4c8ea6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java +++ b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java @@ -187,27 +187,6 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc Log.i(TAG, "App is now visible."); KeyCachingService.onAppForegrounded(this); - boolean hasPerformedContactMigration = TextSecurePreferences.INSTANCE.hasPerformedContactMigration(this); - if (!hasPerformedContactMigration) { - TextSecurePreferences.INSTANCE.setPerformedContactMigration(this); - Set 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) { poller.setCaughtUp(false); } @@ -493,7 +472,6 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc boolean isUsingFCM = TextSecurePreferences.isUsingFCM(this); TextSecurePreferences.clearAll(this); if (isMigratingToV2KeyPair) { - TextSecurePreferences.setIsMigratingKeyPair(this, true); TextSecurePreferences.setIsUsingFCM(this, isUsingFCM); TextSecurePreferences.setProfileName(this, displayName); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/BackupRestoreActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/BackupRestoreActivity.kt index a1bc90237b..19a0f24a04 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/BackupRestoreActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/BackupRestoreActivity.kt @@ -188,7 +188,6 @@ class BackupRestoreViewModel(application: Application): AndroidViewModel(applica TextSecurePreferences.setRestorationTime(context, System.currentTimeMillis()) TextSecurePreferences.setHasViewedSeed(context, true) TextSecurePreferences.setHasSeenWelcomeScreen(context, true) - val application = ApplicationContext.getInstance(context) BackupRestoreResult.SUCCESS } catch (e: DatabaseDowngradeException) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LandingActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LandingActivity.kt index 3388915609..bd7cfc381a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LandingActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LandingActivity.kt @@ -24,7 +24,6 @@ class LandingActivity : BaseActionBarActivity() { findViewById(R.id.restoreButton).setOnClickListener { restore() } findViewById(R.id.linkButton).setOnClickListener { link() } IdentityKeyUtil.generateIdentityKeyPair(this) - TextSecurePreferences.setLastExperienceVersionCode(this, Util.getCanonicalVersionCode()) TextSecurePreferences.setPasswordDisabled(this, true) TextSecurePreferences.setReadReceiptsEnabled(this, true) TextSecurePreferences.setTypingIndicatorsEnabled(this, true) diff --git a/libsession/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt b/libsession/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt index 47e187cddc..7ac0feae6d 100644 --- a/libsession/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt +++ b/libsession/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt @@ -22,13 +22,10 @@ object TextSecurePreferences { val events get() = _events.asSharedFlow() const val DISABLE_PASSPHRASE_PREF = "pref_disable_passphrase" - const val THEME_PREF = "pref_theme" const val LANGUAGE_PREF = "pref_language" const val THREAD_TRIM_LENGTH = "pref_trim_length" const val THREAD_TRIM_NOW = "pref_trim_now" - private const val LAST_VERSION_CODE_PREF = "last_version_code" - private const val LAST_EXPERIENCE_VERSION_PREF = "last_experience_version_code" const val RINGTONE_PREF = "pref_key_ringtone" const val VIBRATE_PREF = "pref_key_vibrate" private const val NOTIFICATION_PREF = "pref_key_enable_notifications" @@ -46,20 +43,15 @@ object TextSecurePreferences { private const val UPDATE_APK_REFRESH_TIME_PREF = "pref_update_apk_refresh_time" private const val UPDATE_APK_DOWNLOAD_ID = "pref_update_apk_download_id" private const val UPDATE_APK_DIGEST = "pref_update_apk_digest" - private const val IN_THREAD_NOTIFICATION_PREF = "pref_key_inthread_notifications" const val MESSAGE_BODY_TEXT_SIZE_PREF = "pref_message_body_text_size" - const val LOCAL_REGISTRATION_ID_PREF = "pref_local_registration_id" - const val REPEAT_ALERTS_PREF = "pref_repeat_alerts" const val NOTIFICATION_PRIVACY_PREF = "pref_notification_privacy" const val NOTIFICATION_PRIORITY_PREF = "pref_notification_priority" - const val MEDIA_DOWNLOAD_MOBILE_PREF = "pref_media_download_mobile" const val MEDIA_DOWNLOAD_WIFI_PREF = "pref_media_download_wifi" const val MEDIA_DOWNLOAD_ROAMING_PREF = "pref_media_download_roaming" - const val SYSTEM_EMOJI_PREF = "pref_system_emoji" const val DIRECT_CAPTURE_CAMERA_ID = "pref_direct_capture_camera_id" const val PROFILE_KEY_PREF = "pref_profile_key" @@ -68,45 +60,33 @@ object TextSecurePreferences { const val PROFILE_AVATAR_URL_PREF = "pref_profile_avatar_url" const val READ_RECEIPTS_PREF = "pref_read_receipts" const val INCOGNITO_KEYBORAD_PREF = "pref_incognito_keyboard" - private const val DATABASE_ENCRYPTED_SECRET = "pref_database_encrypted_secret" private const val DATABASE_UNENCRYPTED_SECRET = "pref_database_unencrypted_secret" private const val ATTACHMENT_ENCRYPTED_SECRET = "pref_attachment_encrypted_secret" private const val ATTACHMENT_UNENCRYPTED_SECRET = "pref_attachment_unencrypted_secret" private const val NEEDS_SQLCIPHER_MIGRATION = "pref_needs_sql_cipher_migration" - const val BACKUP_ENABLED = "pref_backup_enabled_v3" private const val BACKUP_PASSPHRASE = "pref_backup_passphrase" private const val ENCRYPTED_BACKUP_PASSPHRASE = "pref_encrypted_backup_passphrase" private const val BACKUP_TIME = "pref_backup_next_time" const val BACKUP_NOW = "pref_backup_create" private const val BACKUP_SAVE_DIR = "pref_save_dir" - const val SCREEN_LOCK = "pref_android_screen_lock" const val SCREEN_LOCK_TIMEOUT = "pref_android_screen_lock_timeout" - private const val LOG_ENCRYPTED_SECRET = "pref_log_encrypted_secret" private const val LOG_UNENCRYPTED_SECRET = "pref_log_unencrypted_secret" - private const val NOTIFICATION_CHANNEL_VERSION = "pref_notification_channel_version" private const val NOTIFICATION_MESSAGES_CHANNEL_VERSION = "pref_notification_messages_channel_version" - const val UNIVERSAL_UNIDENTIFIED_ACCESS = "pref_universal_unidentified_access" - const val TYPING_INDICATORS = "pref_typing_indicators" - const val LINK_PREVIEWS = "pref_link_previews" - private const val GIF_GRID_LAYOUT = "pref_gif_grid_layout" - const val IS_USING_FCM = "pref_is_using_fcm" private const val FCM_TOKEN = "pref_fcm_token" private const val LAST_FCM_TOKEN_UPLOAD_TIME = "pref_last_fcm_token_upload_time_2" - private const val LAST_CONFIGURATION_SYNC_TIME = "pref_last_configuration_sync_time" const val CONFIGURATION_SYNCED = "pref_configuration_synced" private const val LAST_PROFILE_UPDATE_TIME = "pref_last_profile_update_time" - private const val LAST_OPEN_DATE = "pref_last_open_date" @JvmStatic @@ -338,7 +318,7 @@ object TextSecurePreferences { @JvmStatic fun setProfileAvatarId(context: Context, id: Int) { - setIntegerPrefrence(context, PROFILE_AVATAR_ID_PREF, id) + setIntegerPreference(context, PROFILE_AVATAR_ID_PREF, id) } @JvmStatic @@ -367,7 +347,7 @@ object TextSecurePreferences { @JvmStatic fun setDirectCaptureCameraId(context: Context, value: Int) { - setIntegerPrefrence(context, DIRECT_CAPTURE_CAMERA_ID, value) + setIntegerPreference(context, DIRECT_CAPTURE_CAMERA_ID, value) } @JvmStatic @@ -395,7 +375,7 @@ object TextSecurePreferences { } fun setLocalRegistrationId(context: Context, registrationId: Int) { - setIntegerPrefrence(context, LOCAL_REGISTRATION_ID_PREF, registrationId) + setIntegerPreference(context, LOCAL_REGISTRATION_ID_PREF, registrationId) } @JvmStatic @@ -476,24 +456,11 @@ object TextSecurePreferences { @Throws(IOException::class) fun setLastVersionCode(context: Context, versionCode: Int) { - if (!setIntegerPrefrenceBlocking(context, LAST_VERSION_CODE_PREF, versionCode)) { + if (!setIntegerPreferenceBlocking(context, LAST_VERSION_CODE_PREF, versionCode)) { throw IOException("couldn't write version code to sharedpreferences") } } - fun setLastExperienceVersionCode(context: Context, versionCode: Int) { - setIntegerPrefrence(context, LAST_EXPERIENCE_VERSION_PREF, versionCode) - } - - fun getTheme(context: Context): String? { - return getStringPreference(context, THEME_PREF, "light") - } - - @JvmStatic - fun isPushRegistered(context: Context): Boolean { - return getBooleanPreference(context, REGISTERED_GCM_PREF, false) - } - @JvmStatic fun isPassphraseTimeoutEnabled(context: Context): Boolean { return getBooleanPreference(context, PASSPHRASE_TIMEOUT_PREF, false) @@ -601,19 +568,14 @@ object TextSecurePreferences { return getStringSetPreference(context, key, HashSet(Arrays.asList(*context.resources.getStringArray(defaultValuesRes)))) } - @JvmStatic - fun setLogEncryptedSecret(context: Context, base64Secret: String?) { - setStringPreference(context, LOG_ENCRYPTED_SECRET, base64Secret) - } - @JvmStatic fun getLogEncryptedSecret(context: Context): String? { return getStringPreference(context, LOG_ENCRYPTED_SECRET, null) } @JvmStatic - fun setLogUnencryptedSecret(context: Context, base64Secret: String?) { - setStringPreference(context, LOG_UNENCRYPTED_SECRET, base64Secret) + fun setLogEncryptedSecret(context: Context, base64Secret: String?) { + setStringPreference(context, LOG_ENCRYPTED_SECRET, base64Secret) } @JvmStatic @@ -621,6 +583,11 @@ object TextSecurePreferences { return getStringPreference(context, LOG_UNENCRYPTED_SECRET, null) } + @JvmStatic + fun setLogUnencryptedSecret(context: Context, base64Secret: String?) { + setStringPreference(context, LOG_UNENCRYPTED_SECRET, base64Secret) + } + @JvmStatic fun getNotificationChannelVersion(context: Context): Int { return getIntegerPreference(context, NOTIFICATION_CHANNEL_VERSION, 1) @@ -628,7 +595,7 @@ object TextSecurePreferences { @JvmStatic fun setNotificationChannelVersion(context: Context, version: Int) { - setIntegerPrefrence(context, NOTIFICATION_CHANNEL_VERSION, version) + setIntegerPreference(context, NOTIFICATION_CHANNEL_VERSION, version) } @JvmStatic @@ -638,12 +605,7 @@ object TextSecurePreferences { @JvmStatic fun setNotificationMessagesChannelVersion(context: Context, version: Int) { - setIntegerPrefrence(context, NOTIFICATION_MESSAGES_CHANNEL_VERSION, version) - } - - @JvmStatic - fun setBooleanPreference(context: Context, key: String?, value: Boolean) { - getDefaultSharedPreferences(context).edit().putBoolean(key, value).apply() + setIntegerPreference(context, NOTIFICATION_MESSAGES_CHANNEL_VERSION, version) } @JvmStatic @@ -652,8 +614,8 @@ object TextSecurePreferences { } @JvmStatic - fun setStringPreference(context: Context, key: String?, value: String?) { - getDefaultSharedPreferences(context).edit().putString(key, value).apply() + fun setBooleanPreference(context: Context, key: String?, value: Boolean) { + getDefaultSharedPreferences(context).edit().putBoolean(key, value).apply() } @JvmStatic @@ -661,15 +623,20 @@ object TextSecurePreferences { return getDefaultSharedPreferences(context).getString(key, defaultValue) } + @JvmStatic + fun setStringPreference(context: Context, key: String?, value: String?) { + getDefaultSharedPreferences(context).edit().putString(key, value).apply() + } + private fun getIntegerPreference(context: Context, key: String, defaultValue: Int): Int { return getDefaultSharedPreferences(context).getInt(key, defaultValue) } - private fun setIntegerPrefrence(context: Context, key: String, value: Int) { + private fun setIntegerPreference(context: Context, key: String, value: Int) { getDefaultSharedPreferences(context).edit().putInt(key, value).apply() } - private fun setIntegerPrefrenceBlocking(context: Context, key: String, value: Int): Boolean { + private fun setIntegerPreferenceBlocking(context: Context, key: String, value: Int): Boolean { return getDefaultSharedPreferences(context).edit().putInt(key, value).commit() } @@ -694,9 +661,6 @@ object TextSecurePreferences { } } - // region Loki - @JvmStatic - fun getHasViewedSeed(context: Context): Boolean { return getBooleanPreference(context, "has_viewed_seed", false) } @@ -723,21 +687,6 @@ object TextSecurePreferences { setLongPreference(context, "last_profile_picture_upload", newValue) } - @JvmStatic - fun hasSeenGIFMetaDataWarning(context: Context): Boolean { - return getBooleanPreference(context, "has_seen_gif_metadata_warning", false) - } - - @JvmStatic - fun setHasSeenGIFMetaDataWarning(context: Context) { - setBooleanPreference(context, "has_seen_gif_metadata_warning", true) - } - - @JvmStatic - fun clearAll(context: Context) { - getDefaultSharedPreferences(context).edit().clear().commit() - } - fun getLastSnodePoolRefreshDate(context: Context?): Long { return getLongPreference(context!!, "last_snode_pool_refresh_date", 0) } @@ -746,13 +695,9 @@ object TextSecurePreferences { setLongPreference(context!!, "last_snode_pool_refresh_date", date.time) } - fun getIsMigratingKeyPair(context: Context?): Boolean { - return getBooleanPreference(context!!, "is_migrating_key_pair", false) - } - @JvmStatic - fun setIsMigratingKeyPair(context: Context?, newValue: Boolean) { - setBooleanPreference(context!!, "is_migrating_key_pair", newValue) + fun shouldUpdateProfile(context: Context, profileUpdateTime: Long): Boolean { + return profileUpdateTime > getLongPreference(context, LAST_PROFILE_UPDATE_TIME, 0) } @JvmStatic @@ -760,18 +705,6 @@ object TextSecurePreferences { setLongPreference(context, LAST_PROFILE_UPDATE_TIME, profileUpdateTime) } - @JvmStatic - fun shouldUpdateProfile(context: Context, profileUpdateTime: Long) = - profileUpdateTime > getLongPreference(context, LAST_PROFILE_UPDATE_TIME, 0) - - fun hasPerformedContactMigration(context: Context): Boolean { - return getBooleanPreference(context, "has_performed_contact_migration", false) - } - - fun setPerformedContactMigration(context: Context) { - setBooleanPreference(context, "has_performed_contact_migration", true) - } - fun getLastOpenTimeDate(context: Context): Long { return getLongPreference(context, LAST_OPEN_DATE, 0) } @@ -787,4 +720,9 @@ object TextSecurePreferences { fun setHasSeenLinkPreviewSuggestionDialog(context: Context) { setBooleanPreference(context, "has_seen_link_preview_suggestion_dialog", true) } + + @JvmStatic + fun clearAll(context: Context) { + getDefaultSharedPreferences(context).edit().clear().commit() + } } \ No newline at end of file From 004ea5b87a82dc920a37565d83ad0135ecef3d0b Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Fri, 9 Jul 2021 09:24:43 +1000 Subject: [PATCH 14/28] Fix various crashes --- .../securesms/conversation/v2/ConversationActivityV2.kt | 2 +- .../securesms/conversation/v2/messages/VisibleMessageView.kt | 2 +- .../java/org/thoughtcrime/securesms/database/MmsDatabase.java | 1 + .../securesms/loki/activities/CreatePrivateChatActivity.kt | 1 + 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index eeb9deacab..85ac3e4807 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt @@ -1260,7 +1260,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe } private fun saveDraft() { - val text = inputBar.text.trim() + val text = inputBar?.text?.trim() ?: return if (text.isEmpty()) { return } val drafts = Drafts() drafts.add(DraftDatabase.Draft(DraftDatabase.Draft.TEXT, text)) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt index ad90bd5328..0cd7831add 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt @@ -82,7 +82,7 @@ class VisibleMessageView : LinearLayout { val senderSessionID = sender.address.serialize() val threadID = message.threadId val threadDB = DatabaseFactory.getThreadDatabase(context) - val thread = threadDB.getRecipientForThreadId(threadID)!! + val thread = threadDB.getRecipientForThreadId(threadID) ?: return val contactDB = DatabaseFactory.getSessionContactDatabase(context) val isGroupThread = thread.isGroupRecipient val isStartOfMessageCluster = isStartOfMessageCluster(message, previous, isGroupThread) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java index 87b59132d4..ef68a39cea 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java @@ -883,6 +883,7 @@ public class MmsDatabase extends MessagingDatabase { } public void deleteQuotedFromMessages(MessageRecord toDeleteRecord) { + if (toDeleteRecord == null) { return; } String query = THREAD_ID + " = ?"; Cursor threadMmsCursor = rawQuery(query, new String[]{String.valueOf(toDeleteRecord.getThreadId())}); Reader reader = readerFor(threadMmsCursor); diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/CreatePrivateChatActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/CreatePrivateChatActivity.kt index 6eedd97013..eeb9ae833b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/CreatePrivateChatActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/CreatePrivateChatActivity.kt @@ -191,6 +191,7 @@ class EnterPublicKeyFragment : Fragment() { } private fun handleIsKeyboardShowingChanged() { + val optionalContentContainer = optionalContentContainer ?: return optionalContentContainer.isVisible = !isKeyboardShowing } From ecc7f50ac431da518615a1c5d2cf9e7ef9c2764b Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Fri, 9 Jul 2021 09:38:45 +1000 Subject: [PATCH 15/28] Fix open group deletion issue --- .../conversation/v2/ConversationActivityV2.kt | 12 ++++++++++++ .../conversation/v2/messages/VisibleMessageView.kt | 2 +- .../securesms/loki/activities/HomeActivity.kt | 10 ---------- .../securesms/loki/api/OpenGroupManager.kt | 1 + 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index e46c37d795..02d67f3520 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt @@ -214,6 +214,11 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe threadID = DatabaseFactory.getThreadDatabase(this).getOrCreateThreadIdFor(recipient) } 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() setUpToolBar() setUpInputBar() @@ -233,6 +238,13 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe scrollToFirstUnreadMessageIfNeeded() markAllAsRead() 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() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt index 02889ff956..afb2683b89 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt @@ -96,7 +96,7 @@ class VisibleMessageView : LinearLayout { profilePictureView.glide = glide profilePictureView.update() 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) moderatorIconImageView.visibility = if (isModerator) View.VISIBLE else View.INVISIBLE } else { diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt index a4b5e6437b..601820e5a0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt @@ -154,16 +154,6 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), ConversationClickLis } } EventBus.getDefault().register(this@HomeActivity) - testDateFormatting() - } - - private fun testDateFormatting() { - val timestamp = Date().time - Log.d("Test", getString(R.string.DateUtils_just_now)) - Log.d("Test", DateUtils.getFormattedDateTime(timestamp, DateUtils.getHourFormat(this), Locale.getDefault())) - Log.d("Test", DateUtils.getFormattedDateTime(timestamp, "EEE " + DateUtils.getHourFormat(this), Locale.getDefault())) - Log.d("Test", DateUtils.getFormattedDateTime(timestamp, "MMM d " + DateUtils.getHourFormat(this), Locale.getDefault())) - Log.d("Test", DateUtils.getFormattedDateTime(timestamp, "MMM d " + DateUtils.getHourFormat(this) + ", yyyy", Locale.getDefault())) } override fun onResume() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/api/OpenGroupManager.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/api/OpenGroupManager.kt index a1b2b208f7..72afe11c6e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/api/OpenGroupManager.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/api/OpenGroupManager.kt @@ -116,6 +116,7 @@ object OpenGroupManager { val lokiThreadDB = DatabaseFactory.getLokiThreadDatabase(context) lokiThreadDB.removeOpenGroupChat(threadID) ThreadUtils.queue { + threadDB.deleteConversation(threadID) // Must be invoked on a background thread GroupManager.deleteGroup(groupID, context) // Must be invoked on a background thread } } From 2f7002ac7a86d25c15c0275740bf58fc6f541828 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Fri, 9 Jul 2021 09:39:09 +1000 Subject: [PATCH 16/28] Update build number --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 833622e129..7d75fc196e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -143,7 +143,7 @@ dependencies { testImplementation 'org.robolectric:shadows-multidex:4.2' } -def canonicalVersionCode = 195 +def canonicalVersionCode = 196 def canonicalVersionName = "1.11.3" def postFixSize = 10 From b2bdbeb31dfb0d27bb2899f989bf339a3eee188b Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Fri, 9 Jul 2021 10:12:30 +1000 Subject: [PATCH 17/28] Fix camera intent issue --- app/src/main/AndroidManifest.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index bc9e379d11..90c34008dc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -55,6 +55,12 @@ + + + + + + Date: Fri, 9 Jul 2021 10:12:47 +1000 Subject: [PATCH 18/28] Update build number --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 7d75fc196e..b2db92a7f9 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -143,7 +143,7 @@ dependencies { testImplementation 'org.robolectric:shadows-multidex:4.2' } -def canonicalVersionCode = 196 +def canonicalVersionCode = 197 def canonicalVersionName = "1.11.3" def postFixSize = 10 From a9b5c269f6f86c487a67533e5399c79d381de5a7 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Fri, 9 Jul 2021 11:14:21 +1000 Subject: [PATCH 19/28] Re-organize files & delete unused code --- app/src/main/AndroidManifest.xml | 36 +- .../securesms/ApplicationContext.java | 7 +- .../MessageDetailsRecipientAdapter.java | 2 +- .../PassphraseRequiredActionBarActivity.java | 4 +- .../securesms/ShortcutLauncherActivity.java | 2 +- .../BackupRestoreActivity.kt | 9 +- .../SelectContactsActivity.kt | 6 +- .../SelectContactsAdapter.kt | 3 +- .../SelectContactsLoader.kt | 2 +- .../{loki/views => contacts}/UserView.kt | 2 +- .../conversation/v2/ConversationActivityV2.kt | 5 +- .../MentionCandidateSelectionView.kt | 2 +- .../v2/components}/MentionCandidateView.kt | 2 +- .../v2/components/OpenGroupGuidelinesView.kt | 2 +- .../v2/input_bar/InputBarButton.kt | 5 +- .../v2/menus/ConversationMenuHelper.kt | 6 +- .../CreateClosedGroupActivity.kt | 5 +- .../EditClosedGroupActivity.kt | 3 +- .../EditClosedGroupLoader.kt | 2 +- .../EditClosedGroupMembersAdapter.kt | 4 +- .../JoinPublicChatActivity.kt | 0 .../OpenGroupGuidelinesActivity.kt | 2 +- .../{loki/views => home}/ConversationView.kt | 2 +- .../{loki/activities => home}/HomeActivity.kt | 13 +- .../{loki/activities => home}/HomeAdapter.kt | 3 +- .../{loki/activities => home}/HomeLoader.kt | 2 +- .../NewConversationButtonSetView.kt | 4 +- .../{loki/activities => home}/PathActivity.kt | 6 +- .../{loki/views => home}/PathStatusView.kt | 2 +- .../fragments/ContactSelectionListAdapter.kt | 2 +- .../securesms/loki/views/MessageAudioView.kt | 335 ------------------ .../loki/views/OpenGroupInvitationView.kt | 71 ---- .../securesms/loki/views/WaveformSeekBar.kt | 315 ---------------- .../MultipleRecipientNotificationBuilder.java | 2 +- .../PendingMessageNotificationBuilder.java | 2 +- .../DisplayNameActivity.kt | 2 +- .../views => onboarding}/FakeChatView.kt | 2 +- .../LandingActivity.kt | 4 +- .../LinkDeviceActivity.kt | 3 +- .../PNModeActivity.kt | 7 +- .../RecoveryPhraseRestoreActivity.kt | 2 +- .../RegisterActivity.kt | 2 +- .../activities => onboarding}/SeedActivity.kt | 2 +- .../views => onboarding}/SeedReminderView.kt | 2 +- .../ChatSettingsActivity.kt | 2 +- .../NotificationSettingsActivity.kt | 2 +- .../PrivacySettingsActivity.kt | 2 +- .../QRCodeActivity.kt | 2 +- .../SettingsActivity.kt | 2 +- .../service/GenericForegroundService.java | 2 +- .../securesms/service/KeyCachingService.java | 2 +- .../{loki/views => util}/GlowView.kt | 2 +- .../res/layout-sw400dp/activity_landing.xml | 2 +- .../res/layout-sw400dp/activity_pn_mode.xml | 8 +- .../main/res/layout-sw400dp/activity_seed.xml | 2 +- .../res/layout/activity_edit_closed_group.xml | 2 +- app/src/main/res/layout/activity_home.xml | 8 +- app/src/main/res/layout/activity_landing.xml | 2 +- app/src/main/res/layout/activity_pn_mode.xml | 8 +- app/src/main/res/layout/activity_seed.xml | 2 +- app/src/main/res/layout/activity_settings.xml | 2 +- .../res/layout/view_mention_candidate.xml | 4 +- 62 files changed, 110 insertions(+), 842 deletions(-) rename app/src/main/java/org/thoughtcrime/securesms/{loki/activities => backup}/BackupRestoreActivity.kt (96%) rename app/src/main/java/org/thoughtcrime/securesms/{loki/activities => contacts}/SelectContactsActivity.kt (93%) rename app/src/main/java/org/thoughtcrime/securesms/{loki/activities => contacts}/SelectContactsAdapter.kt (96%) rename app/src/main/java/org/thoughtcrime/securesms/{loki/activities => contacts}/SelectContactsLoader.kt (92%) rename app/src/main/java/org/thoughtcrime/securesms/{loki/views => contacts}/UserView.kt (98%) rename app/src/main/java/org/thoughtcrime/securesms/{loki/views => conversation/v2/components}/MentionCandidateSelectionView.kt (98%) rename app/src/main/java/org/thoughtcrime/securesms/{loki/views => conversation/v2/components}/MentionCandidateView.kt (96%) rename app/src/main/java/org/thoughtcrime/securesms/{loki/activities => groups}/CreateClosedGroupActivity.kt (97%) rename app/src/main/java/org/thoughtcrime/securesms/{loki/activities => groups}/EditClosedGroupActivity.kt (99%) rename app/src/main/java/org/thoughtcrime/securesms/{loki/activities => groups}/EditClosedGroupLoader.kt (94%) rename app/src/main/java/org/thoughtcrime/securesms/{loki/activities => groups}/EditClosedGroupMembersAdapter.kt (95%) rename app/src/main/java/org/thoughtcrime/securesms/{loki/activities => groups}/JoinPublicChatActivity.kt (100%) rename app/src/main/java/org/thoughtcrime/securesms/{loki/activities => groups}/OpenGroupGuidelinesActivity.kt (97%) rename app/src/main/java/org/thoughtcrime/securesms/{loki/views => home}/ConversationView.kt (99%) rename app/src/main/java/org/thoughtcrime/securesms/{loki/activities => home}/HomeActivity.kt (97%) rename app/src/main/java/org/thoughtcrime/securesms/{loki/activities => home}/HomeAdapter.kt (94%) rename app/src/main/java/org/thoughtcrime/securesms/{loki/activities => home}/HomeLoader.kt (88%) rename app/src/main/java/org/thoughtcrime/securesms/{loki/views => home}/NewConversationButtonSetView.kt (98%) rename app/src/main/java/org/thoughtcrime/securesms/{loki/activities => home}/PathActivity.kt (98%) rename app/src/main/java/org/thoughtcrime/securesms/{loki/views => home}/PathStatusView.kt (98%) delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/loki/views/MessageAudioView.kt delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/loki/views/OpenGroupInvitationView.kt delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/loki/views/WaveformSeekBar.kt rename app/src/main/java/org/thoughtcrime/securesms/{loki/activities => onboarding}/DisplayNameActivity.kt (98%) rename app/src/main/java/org/thoughtcrime/securesms/{loki/views => onboarding}/FakeChatView.kt (98%) rename app/src/main/java/org/thoughtcrime/securesms/{loki/activities => onboarding}/LandingActivity.kt (92%) rename app/src/main/java/org/thoughtcrime/securesms/{loki/activities => onboarding}/LinkDeviceActivity.kt (98%) rename app/src/main/java/org/thoughtcrime/securesms/{loki/activities => onboarding}/PNModeActivity.kt (97%) rename app/src/main/java/org/thoughtcrime/securesms/{loki/activities => onboarding}/RecoveryPhraseRestoreActivity.kt (98%) rename app/src/main/java/org/thoughtcrime/securesms/{loki/activities => onboarding}/RegisterActivity.kt (99%) rename app/src/main/java/org/thoughtcrime/securesms/{loki/activities => onboarding}/SeedActivity.kt (98%) rename app/src/main/java/org/thoughtcrime/securesms/{loki/views => onboarding}/SeedReminderView.kt (97%) rename app/src/main/java/org/thoughtcrime/securesms/{loki/activities => preferences}/ChatSettingsActivity.kt (93%) rename app/src/main/java/org/thoughtcrime/securesms/{loki/activities => preferences}/NotificationSettingsActivity.kt (93%) rename app/src/main/java/org/thoughtcrime/securesms/{loki/activities => preferences}/PrivacySettingsActivity.kt (93%) rename app/src/main/java/org/thoughtcrime/securesms/{loki/activities => preferences}/QRCodeActivity.kt (99%) rename app/src/main/java/org/thoughtcrime/securesms/{loki/activities => preferences}/SettingsActivity.kt (99%) rename app/src/main/java/org/thoughtcrime/securesms/{loki/views => util}/GlowView.kt (99%) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 90c34008dc..d734317703 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -93,15 +93,15 @@ android:value="false" /> @@ -111,25 +111,25 @@ android:windowSoftInputMode="adjustResize" android:theme="@style/Theme.Session.DayNight.FlatActionBar" /> + android:targetActivity="org.thoughtcrime.securesms.home.HomeActivity"> @@ -215,14 +215,14 @@ + android:value="org.thoughtcrime.securesms.home.HomeActivity" /> > { diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/EditClosedGroupActivity.kt similarity index 99% rename from app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupActivity.kt rename to app/src/main/java/org/thoughtcrime/securesms/groups/EditClosedGroupActivity.kt index d6508e8975..8c56d9aac2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/EditClosedGroupActivity.kt @@ -1,4 +1,4 @@ -package org.thoughtcrime.securesms.loki.activities +package org.thoughtcrime.securesms.groups import android.content.Context import android.content.Intent @@ -33,6 +33,7 @@ import org.session.libsession.utilities.GroupUtil import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.ThemeUtil import org.session.libsignal.utilities.toHexString +import org.thoughtcrime.securesms.contacts.SelectContactsActivity import java.io.IOException class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupLoader.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/EditClosedGroupLoader.kt similarity index 94% rename from app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupLoader.kt rename to app/src/main/java/org/thoughtcrime/securesms/groups/EditClosedGroupLoader.kt index d2bb17a732..5360f4261d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupLoader.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/EditClosedGroupLoader.kt @@ -1,4 +1,4 @@ -package org.thoughtcrime.securesms.loki.activities +package org.thoughtcrime.securesms.groups import android.content.Context import org.thoughtcrime.securesms.database.DatabaseFactory diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupMembersAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/EditClosedGroupMembersAdapter.kt similarity index 95% rename from app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupMembersAdapter.kt rename to app/src/main/java/org/thoughtcrime/securesms/groups/EditClosedGroupMembersAdapter.kt index 4a451ba222..b2d0f6255a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupMembersAdapter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/EditClosedGroupMembersAdapter.kt @@ -1,10 +1,10 @@ -package org.thoughtcrime.securesms.loki.activities +package org.thoughtcrime.securesms.groups import android.content.Context import androidx.recyclerview.widget.RecyclerView import android.view.ViewGroup 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.session.libsession.utilities.recipients.Recipient import org.session.libsession.utilities.TextSecurePreferences diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/JoinPublicChatActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/JoinPublicChatActivity.kt similarity index 100% rename from app/src/main/java/org/thoughtcrime/securesms/loki/activities/JoinPublicChatActivity.kt rename to app/src/main/java/org/thoughtcrime/securesms/groups/JoinPublicChatActivity.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/OpenGroupGuidelinesActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/OpenGroupGuidelinesActivity.kt similarity index 97% rename from app/src/main/java/org/thoughtcrime/securesms/loki/activities/OpenGroupGuidelinesActivity.kt rename to app/src/main/java/org/thoughtcrime/securesms/groups/OpenGroupGuidelinesActivity.kt index 9c714b6eff..3b7827679e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/OpenGroupGuidelinesActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/OpenGroupGuidelinesActivity.kt @@ -1,4 +1,4 @@ -package org.thoughtcrime.securesms.loki.activities +package org.thoughtcrime.securesms.groups import android.os.Bundle import kotlinx.android.synthetic.main.activity_open_group_guidelines.* diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/views/ConversationView.kt b/app/src/main/java/org/thoughtcrime/securesms/home/ConversationView.kt similarity index 99% rename from app/src/main/java/org/thoughtcrime/securesms/loki/views/ConversationView.kt rename to app/src/main/java/org/thoughtcrime/securesms/home/ConversationView.kt index f4341b5ed1..934d40a63b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/views/ConversationView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/home/ConversationView.kt @@ -1,4 +1,4 @@ -package org.thoughtcrime.securesms.loki.views +package org.thoughtcrime.securesms.home import android.content.Context import android.content.res.Resources diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt similarity index 97% rename from app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt rename to app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt index 601820e5a0..643f2b98e4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt @@ -1,4 +1,4 @@ -package org.thoughtcrime.securesms.loki.activities +package org.thoughtcrime.securesms.home import android.app.AlertDialog import android.content.BroadcastReceiver @@ -11,7 +11,6 @@ import android.text.Spannable import android.text.SpannableString import android.text.style.ForegroundColorSpan import android.util.DisplayMetrics -import android.util.Log import android.view.View import android.widget.RelativeLayout import android.widget.Toast @@ -40,19 +39,19 @@ import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2 import org.thoughtcrime.securesms.crypto.IdentityKeyUtil import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.database.model.ThreadRecord +import org.thoughtcrime.securesms.groups.CreateClosedGroupActivity +import org.thoughtcrime.securesms.loki.activities.* import org.thoughtcrime.securesms.loki.api.OpenGroupManager import org.thoughtcrime.securesms.loki.dialogs.* import org.thoughtcrime.securesms.loki.protocol.MultiDeviceProtocol import org.thoughtcrime.securesms.loki.utilities.* -import org.thoughtcrime.securesms.loki.views.ConversationView -import org.thoughtcrime.securesms.loki.views.NewConversationButtonSetViewDelegate -import org.thoughtcrime.securesms.loki.views.SeedReminderViewDelegate +import org.thoughtcrime.securesms.onboarding.SeedReminderViewDelegate import org.thoughtcrime.securesms.mms.GlideApp import org.thoughtcrime.securesms.mms.GlideRequests -import org.thoughtcrime.securesms.util.DateUtils +import org.thoughtcrime.securesms.onboarding.SeedActivity +import org.thoughtcrime.securesms.preferences.SettingsActivity import java.io.IOException import java.util.* -import java.util.concurrent.TimeUnit class HomeActivity : PassphraseRequiredActionBarActivity(), ConversationClickListener, SeedReminderViewDelegate, NewConversationButtonSetViewDelegate { private lateinit var glide: GlideRequests diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/home/HomeAdapter.kt similarity index 94% rename from app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeAdapter.kt rename to app/src/main/java/org/thoughtcrime/securesms/home/HomeAdapter.kt index 6005b53b8d..ba718d355f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeAdapter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/home/HomeAdapter.kt @@ -1,4 +1,4 @@ -package org.thoughtcrime.securesms.loki.activities +package org.thoughtcrime.securesms.home import android.content.Context import android.database.Cursor @@ -7,7 +7,6 @@ import android.view.ViewGroup import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.database.model.ThreadRecord -import org.thoughtcrime.securesms.loki.views.ConversationView import org.thoughtcrime.securesms.mms.GlideRequests class HomeAdapter(context: Context, cursor: Cursor) : CursorRecyclerViewAdapter(context, cursor) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeLoader.kt b/app/src/main/java/org/thoughtcrime/securesms/home/HomeLoader.kt similarity index 88% rename from app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeLoader.kt rename to app/src/main/java/org/thoughtcrime/securesms/home/HomeLoader.kt index a48da0c3ca..92e1abacb6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeLoader.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/home/HomeLoader.kt @@ -1,4 +1,4 @@ -package org.thoughtcrime.securesms.loki.activities +package org.thoughtcrime.securesms.home import android.content.Context import android.database.Cursor diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/views/NewConversationButtonSetView.kt b/app/src/main/java/org/thoughtcrime/securesms/home/NewConversationButtonSetView.kt similarity index 98% rename from app/src/main/java/org/thoughtcrime/securesms/loki/views/NewConversationButtonSetView.kt rename to app/src/main/java/org/thoughtcrime/securesms/home/NewConversationButtonSetView.kt index a418369c90..cb99321eb5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/views/NewConversationButtonSetView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/home/NewConversationButtonSetView.kt @@ -1,4 +1,4 @@ -package org.thoughtcrime.securesms.loki.views +package org.thoughtcrime.securesms.home import android.animation.FloatEvaluator import android.animation.PointFEvaluator @@ -17,6 +17,8 @@ import androidx.annotation.ColorRes import androidx.annotation.DrawableRes import network.loki.messenger.R import org.thoughtcrime.securesms.loki.utilities.* +import org.thoughtcrime.securesms.util.GlowViewUtilities +import org.thoughtcrime.securesms.util.NewConversationButtonImageView class NewConversationButtonSetView : RelativeLayout { private var expandedButton: Button? = null diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/PathActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/home/PathActivity.kt similarity index 98% rename from app/src/main/java/org/thoughtcrime/securesms/loki/activities/PathActivity.kt rename to app/src/main/java/org/thoughtcrime/securesms/home/PathActivity.kt index 2a06562bdf..10ddb11034 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/PathActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/home/PathActivity.kt @@ -1,4 +1,4 @@ -package org.thoughtcrime.securesms.loki.activities +package org.thoughtcrime.securesms.home import android.content.BroadcastReceiver import android.content.Context @@ -23,8 +23,8 @@ import org.session.libsession.snode.OnionRequestAPI import org.session.libsignal.utilities.Snode import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity import org.thoughtcrime.securesms.loki.utilities.* -import org.thoughtcrime.securesms.loki.views.GlowViewUtilities -import org.thoughtcrime.securesms.loki.views.PathDotView +import org.thoughtcrime.securesms.util.GlowViewUtilities +import org.thoughtcrime.securesms.util.PathDotView class PathActivity : PassphraseRequiredActionBarActivity() { private val broadcastReceivers = mutableListOf() diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/views/PathStatusView.kt b/app/src/main/java/org/thoughtcrime/securesms/home/PathStatusView.kt similarity index 98% rename from app/src/main/java/org/thoughtcrime/securesms/loki/views/PathStatusView.kt rename to app/src/main/java/org/thoughtcrime/securesms/home/PathStatusView.kt index 6990849306..5d571f1a9b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/views/PathStatusView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/home/PathStatusView.kt @@ -1,4 +1,4 @@ -package org.thoughtcrime.securesms.loki.views +package org.thoughtcrime.securesms.home import android.content.BroadcastReceiver import android.content.Context diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/fragments/ContactSelectionListAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/fragments/ContactSelectionListAdapter.kt index ba039009bc..043ba1207f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/fragments/ContactSelectionListAdapter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/fragments/ContactSelectionListAdapter.kt @@ -7,7 +7,7 @@ import android.view.View import android.view.ViewGroup import kotlinx.android.synthetic.main.contact_selection_list_divider.view.* 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.session.libsession.utilities.recipients.Recipient diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/views/MessageAudioView.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/views/MessageAudioView.kt deleted file mode 100644 index 76f5dee9c4..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/views/MessageAudioView.kt +++ /dev/null @@ -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) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/views/OpenGroupInvitationView.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/views/OpenGroupInvitationView.kt deleted file mode 100644 index edfc81cf79..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/views/OpenGroupInvitationView.kt +++ /dev/null @@ -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() - } -} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/views/WaveformSeekBar.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/views/WaveformSeekBar.kt deleted file mode 100644 index df74bd9ca0..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/views/WaveformSeekBar.kt +++ /dev/null @@ -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) - } -} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/MultipleRecipientNotificationBuilder.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/MultipleRecipientNotificationBuilder.java index 5165e87ff9..4c4f96b25e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/MultipleRecipientNotificationBuilder.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/MultipleRecipientNotificationBuilder.java @@ -16,7 +16,7 @@ import org.session.libsession.utilities.TextSecurePreferences; import org.session.libsession.utilities.Util; import org.session.libsession.utilities.recipients.Recipient; import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.loki.activities.HomeActivity; +import org.thoughtcrime.securesms.home.HomeActivity; import org.thoughtcrime.securesms.loki.database.SessionContactDatabase; import java.util.LinkedList; diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/PendingMessageNotificationBuilder.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/PendingMessageNotificationBuilder.java index efd040a26d..1d19c2c8e3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/PendingMessageNotificationBuilder.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/PendingMessageNotificationBuilder.java @@ -7,7 +7,7 @@ import android.content.Intent; import androidx.core.app.NotificationCompat; import org.session.libsession.utilities.recipients.Recipient; -import org.thoughtcrime.securesms.loki.activities.HomeActivity; +import org.thoughtcrime.securesms.home.HomeActivity; import org.session.libsession.utilities.NotificationPrivacyPreference; import org.session.libsession.utilities.TextSecurePreferences; diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/DisplayNameActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/DisplayNameActivity.kt similarity index 98% rename from app/src/main/java/org/thoughtcrime/securesms/loki/activities/DisplayNameActivity.kt rename to app/src/main/java/org/thoughtcrime/securesms/onboarding/DisplayNameActivity.kt index 50403dca9c..bf4fb25cc8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/DisplayNameActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/DisplayNameActivity.kt @@ -1,4 +1,4 @@ -package org.thoughtcrime.securesms.loki.activities +package org.thoughtcrime.securesms.onboarding import android.content.Intent import android.os.Bundle diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/views/FakeChatView.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/FakeChatView.kt similarity index 98% rename from app/src/main/java/org/thoughtcrime/securesms/loki/views/FakeChatView.kt rename to app/src/main/java/org/thoughtcrime/securesms/onboarding/FakeChatView.kt index 5f84df4913..835ae4cc70 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/views/FakeChatView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/FakeChatView.kt @@ -1,4 +1,4 @@ -package org.thoughtcrime.securesms.loki.views +package org.thoughtcrime.securesms.onboarding import android.animation.FloatEvaluator import android.animation.ValueAnimator diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LandingActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/LandingActivity.kt similarity index 92% rename from app/src/main/java/org/thoughtcrime/securesms/loki/activities/LandingActivity.kt rename to app/src/main/java/org/thoughtcrime/securesms/onboarding/LandingActivity.kt index bd7cfc381a..85bb05c464 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LandingActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/LandingActivity.kt @@ -1,4 +1,4 @@ -package org.thoughtcrime.securesms.loki.activities +package org.thoughtcrime.securesms.onboarding import android.content.Intent import android.os.Bundle @@ -9,9 +9,7 @@ import org.thoughtcrime.securesms.BaseActionBarActivity import org.thoughtcrime.securesms.crypto.IdentityKeyUtil import org.thoughtcrime.securesms.loki.utilities.push import org.thoughtcrime.securesms.loki.utilities.setUpActionBarSessionLogo -import org.thoughtcrime.securesms.loki.views.FakeChatView import org.thoughtcrime.securesms.service.KeyCachingService -import org.thoughtcrime.securesms.util.Util class LandingActivity : BaseActionBarActivity() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkDeviceActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/LinkDeviceActivity.kt similarity index 98% rename from app/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkDeviceActivity.kt rename to app/src/main/java/org/thoughtcrime/securesms/onboarding/LinkDeviceActivity.kt index f9e1b07b33..c98463f14a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/LinkDeviceActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/LinkDeviceActivity.kt @@ -1,4 +1,4 @@ -package org.thoughtcrime.securesms.loki.activities +package org.thoughtcrime.securesms.onboarding import android.content.Context import android.content.Intent @@ -35,6 +35,7 @@ import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragmentDelega import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities import org.thoughtcrime.securesms.loki.utilities.push import org.thoughtcrime.securesms.loki.utilities.setUpActionBarSessionLogo +import org.thoughtcrime.securesms.onboarding.PNModeActivity class LinkDeviceActivity : BaseActionBarActivity(), ScanQRCodeWrapperFragmentDelegate { private val adapter = LinkDeviceActivityAdapter(this) diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/PNModeActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/PNModeActivity.kt similarity index 97% rename from app/src/main/java/org/thoughtcrime/securesms/loki/activities/PNModeActivity.kt rename to app/src/main/java/org/thoughtcrime/securesms/onboarding/PNModeActivity.kt index 5ddc8b9682..d83a14819c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/PNModeActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/PNModeActivity.kt @@ -1,4 +1,4 @@ -package org.thoughtcrime.securesms.loki.activities +package org.thoughtcrime.securesms.onboarding import android.animation.ArgbEvaluator import android.animation.ValueAnimator @@ -19,12 +19,13 @@ import network.loki.messenger.R import org.session.libsession.utilities.TextSecurePreferences import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.BaseActionBarActivity +import org.thoughtcrime.securesms.home.HomeActivity import org.thoughtcrime.securesms.loki.utilities.disableClipping import org.thoughtcrime.securesms.loki.utilities.getColorWithID import org.thoughtcrime.securesms.loki.utilities.setUpActionBarSessionLogo import org.thoughtcrime.securesms.loki.utilities.show -import org.thoughtcrime.securesms.loki.views.GlowViewUtilities -import org.thoughtcrime.securesms.loki.views.PNModeView +import org.thoughtcrime.securesms.util.GlowViewUtilities +import org.thoughtcrime.securesms.util.PNModeView class PNModeActivity : BaseActionBarActivity() { private var selectedOptionView: PNModeView? = null diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/RecoveryPhraseRestoreActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/RecoveryPhraseRestoreActivity.kt similarity index 98% rename from app/src/main/java/org/thoughtcrime/securesms/loki/activities/RecoveryPhraseRestoreActivity.kt rename to app/src/main/java/org/thoughtcrime/securesms/onboarding/RecoveryPhraseRestoreActivity.kt index f4dc7b323c..649490bfc5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/RecoveryPhraseRestoreActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/RecoveryPhraseRestoreActivity.kt @@ -1,4 +1,4 @@ -package org.thoughtcrime.securesms.loki.activities +package org.thoughtcrime.securesms.onboarding import android.content.Intent import android.graphics.Typeface diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/RegisterActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/RegisterActivity.kt similarity index 99% rename from app/src/main/java/org/thoughtcrime/securesms/loki/activities/RegisterActivity.kt rename to app/src/main/java/org/thoughtcrime/securesms/onboarding/RegisterActivity.kt index 27ab501a68..636f0be4c4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/RegisterActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/RegisterActivity.kt @@ -1,4 +1,4 @@ -package org.thoughtcrime.securesms.loki.activities +package org.thoughtcrime.securesms.onboarding import android.content.ClipData import android.content.ClipboardManager diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SeedActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/SeedActivity.kt similarity index 98% rename from app/src/main/java/org/thoughtcrime/securesms/loki/activities/SeedActivity.kt rename to app/src/main/java/org/thoughtcrime/securesms/onboarding/SeedActivity.kt index aed826e086..0b281fb6b0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SeedActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/SeedActivity.kt @@ -1,4 +1,4 @@ -package org.thoughtcrime.securesms.loki.activities +package org.thoughtcrime.securesms.onboarding import android.content.ClipData import android.content.ClipboardManager diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/views/SeedReminderView.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/SeedReminderView.kt similarity index 97% rename from app/src/main/java/org/thoughtcrime/securesms/loki/views/SeedReminderView.kt rename to app/src/main/java/org/thoughtcrime/securesms/onboarding/SeedReminderView.kt index 591a458227..199ed7a5a1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/views/SeedReminderView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/SeedReminderView.kt @@ -1,4 +1,4 @@ -package org.thoughtcrime.securesms.loki.views +package org.thoughtcrime.securesms.onboarding import android.content.Context import android.os.Build diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/ChatSettingsActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/ChatSettingsActivity.kt similarity index 93% rename from app/src/main/java/org/thoughtcrime/securesms/loki/activities/ChatSettingsActivity.kt rename to app/src/main/java/org/thoughtcrime/securesms/preferences/ChatSettingsActivity.kt index 67a2c4f2d5..67faa9da46 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/ChatSettingsActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/ChatSettingsActivity.kt @@ -1,4 +1,4 @@ -package org.thoughtcrime.securesms.loki.activities +package org.thoughtcrime.securesms.preferences import android.os.Bundle import network.loki.messenger.R diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/NotificationSettingsActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/NotificationSettingsActivity.kt similarity index 93% rename from app/src/main/java/org/thoughtcrime/securesms/loki/activities/NotificationSettingsActivity.kt rename to app/src/main/java/org/thoughtcrime/securesms/preferences/NotificationSettingsActivity.kt index 1468539a72..af039a4fdb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/NotificationSettingsActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/NotificationSettingsActivity.kt @@ -1,4 +1,4 @@ -package org.thoughtcrime.securesms.loki.activities +package org.thoughtcrime.securesms.preferences import android.os.Bundle import network.loki.messenger.R diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/PrivacySettingsActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/PrivacySettingsActivity.kt similarity index 93% rename from app/src/main/java/org/thoughtcrime/securesms/loki/activities/PrivacySettingsActivity.kt rename to app/src/main/java/org/thoughtcrime/securesms/preferences/PrivacySettingsActivity.kt index 966dd9b437..bf277dc17f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/PrivacySettingsActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/PrivacySettingsActivity.kt @@ -1,4 +1,4 @@ -package org.thoughtcrime.securesms.loki.activities +package org.thoughtcrime.securesms.preferences import android.os.Bundle import network.loki.messenger.R diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/QRCodeActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/QRCodeActivity.kt similarity index 99% rename from app/src/main/java/org/thoughtcrime/securesms/loki/activities/QRCodeActivity.kt rename to app/src/main/java/org/thoughtcrime/securesms/preferences/QRCodeActivity.kt index 2f5638d952..9c48552e55 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/QRCodeActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/QRCodeActivity.kt @@ -1,4 +1,4 @@ -package org.thoughtcrime.securesms.loki.activities +package org.thoughtcrime.securesms.preferences import android.content.Intent import android.graphics.Bitmap diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SettingsActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/SettingsActivity.kt similarity index 99% rename from app/src/main/java/org/thoughtcrime/securesms/loki/activities/SettingsActivity.kt rename to app/src/main/java/org/thoughtcrime/securesms/preferences/SettingsActivity.kt index c5176d2222..5fbc23848a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SettingsActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/SettingsActivity.kt @@ -1,4 +1,4 @@ -package org.thoughtcrime.securesms.loki.activities +package org.thoughtcrime.securesms.preferences import android.Manifest import android.app.Activity diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/GenericForegroundService.java b/app/src/main/java/org/thoughtcrime/securesms/service/GenericForegroundService.java index 58ed5d7d75..0581883c5e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/GenericForegroundService.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/GenericForegroundService.java @@ -13,7 +13,7 @@ import androidx.core.app.NotificationCompat; import androidx.core.content.ContextCompat; import org.session.libsignal.utilities.Log; -import org.thoughtcrime.securesms.loki.activities.HomeActivity; +import org.thoughtcrime.securesms.home.HomeActivity; import org.thoughtcrime.securesms.notifications.NotificationChannels; import org.session.libsignal.utilities.guava.Preconditions; diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/KeyCachingService.java b/app/src/main/java/org/thoughtcrime/securesms/service/KeyCachingService.java index c4507cd766..9e79b93d60 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/KeyCachingService.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/KeyCachingService.java @@ -37,7 +37,7 @@ import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.DatabaseUpgradeActivity; import org.thoughtcrime.securesms.DummyActivity; import org.session.libsignal.utilities.Log; -import org.thoughtcrime.securesms.loki.activities.HomeActivity; +import org.thoughtcrime.securesms.home.HomeActivity; import org.thoughtcrime.securesms.notifications.NotificationChannels; import org.session.libsession.utilities.ServiceUtil; import org.session.libsession.utilities.TextSecurePreferences; diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/views/GlowView.kt b/app/src/main/java/org/thoughtcrime/securesms/util/GlowView.kt similarity index 99% rename from app/src/main/java/org/thoughtcrime/securesms/loki/views/GlowView.kt rename to app/src/main/java/org/thoughtcrime/securesms/util/GlowView.kt index c2619a6044..821800a3ef 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/views/GlowView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/GlowView.kt @@ -1,4 +1,4 @@ -package org.thoughtcrime.securesms.loki.views +package org.thoughtcrime.securesms.util import android.animation.ArgbEvaluator import android.animation.ValueAnimator diff --git a/app/src/main/res/layout-sw400dp/activity_landing.xml b/app/src/main/res/layout-sw400dp/activity_landing.xml index dc36e5fa57..d1cbc8f2f3 100644 --- a/app/src/main/res/layout-sw400dp/activity_landing.xml +++ b/app/src/main/res/layout-sw400dp/activity_landing.xml @@ -19,7 +19,7 @@ android:textStyle="bold" android:text="@string/activity_landing_title_2" /> - - - + - - +