diff --git a/build.gradle b/build.gradle index 8bf64b9d56..f88246e35d 100644 --- a/build.gradle +++ b/build.gradle @@ -185,8 +185,8 @@ dependencies { implementation "com.opencsv:opencsv:4.6" } -def canonicalVersionCode = 65 -def canonicalVersionName = "1.4.1" +def canonicalVersionCode = 69 +def canonicalVersionName = "1.4.2" def postFixSize = 10 def abiPostFix = ['armeabi-v7a' : 1, diff --git a/res/layout/dialog_seed.xml b/res/layout/dialog_seed.xml index 37b523ad98..300f910810 100644 --- a/res/layout/dialog_seed.xml +++ b/res/layout/dialog_seed.xml @@ -65,4 +65,14 @@ + + \ No newline at end of file diff --git a/res/values/strings.xml b/res/values/strings.xml index 158f0f8a67..32907f5304 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1409,7 +1409,7 @@ Settings Lock Mark all read - Invite friends + Invite Contacts Help @@ -1714,7 +1714,7 @@ Your Recovery Phrase Meet your recovery phrase - Your recovery phrase is the master key to your Session ID — you can use it to restore your Session ID if you lose access to your device. Store your recovery phrase in a safe place, and don\’t give it to anyone. + Your recovery phrase is the master key to your Session ID — you can use it to restore your Session ID if you lose access to your device. Store your recovery phrase in a safe place, and don\'t give it to anyone. Hold to reveal Secure your account by saving your recovery phrase @@ -1732,7 +1732,7 @@ New Session Enter Session ID Scan QR Code - Scan a user\’s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings. + Scan a user\'s QR code to start a session. QR codes can be found by tapping the QR code icon in account settings. Enter Session ID of recipient Users can share their Session ID by going into their account settings and tapping "Share Session ID", or by sharing their QR code. @@ -1851,4 +1851,6 @@ Select Contacts + *Please note that it is not possible to use the same Session ID on multiple devices simultaneously + diff --git a/src/org/thoughtcrime/securesms/ApplicationPreferencesActivity.java b/src/org/thoughtcrime/securesms/ApplicationPreferencesActivity.java index 4400f061a8..7f75443aed 100644 --- a/src/org/thoughtcrime/securesms/ApplicationPreferencesActivity.java +++ b/src/org/thoughtcrime/securesms/ApplicationPreferencesActivity.java @@ -29,16 +29,17 @@ import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Build.VERSION; import android.os.Bundle; +import android.util.Log; +import android.widget.Toast; + import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import androidx.core.content.ContextCompat; +import androidx.core.graphics.drawable.DrawableCompat; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; -import androidx.core.content.ContextCompat; -import androidx.core.graphics.drawable.DrawableCompat; -import androidx.appcompat.app.AlertDialog; import androidx.preference.Preference; -import android.util.Log; -import android.widget.Toast; import org.thoughtcrime.securesms.crypto.IdentityKeyUtil; import org.thoughtcrime.securesms.loki.activities.HomeActivity; @@ -51,11 +52,8 @@ import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.DynamicTheme; import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.signalservice.loki.crypto.MnemonicCodec; import org.whispersystems.signalservice.loki.utilities.HexEncodingKt; -import java.io.File; - import network.loki.messenger.R; /** @@ -337,13 +335,12 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA case PREFERENCE_CATEGORY_QR_CODE: break; case PREFERENCE_CATEGORY_LINKED_DEVICES: break; case PREFERENCE_CATEGORY_SEED: - File languageFileDirectory = new File(getContext().getApplicationInfo().dataDir); try { String hexEncodedSeed = IdentityKeyUtil.retrieve(getContext(), IdentityKeyUtil.lokiSeedKey); if (hexEncodedSeed == null) { hexEncodedSeed = HexEncodingKt.getHexEncodedPrivateKey(IdentityKeyUtil.getIdentityKeyPair(getContext())); // Legacy account } - String seed = new MnemonicCodec(languageFileDirectory).encode(hexEncodedSeed, MnemonicCodec.Language.Configuration.Companion.getEnglish()); + String seed = ""; new AlertDialog.Builder(getContext()) .setTitle(R.string.activity_settings_seed_dialog_title) .setMessage(seed) diff --git a/src/org/thoughtcrime/securesms/DeviceListFragment.java b/src/org/thoughtcrime/securesms/DeviceListFragment.java index ea400e0481..54b17038f6 100644 --- a/src/org/thoughtcrime/securesms/DeviceListFragment.java +++ b/src/org/thoughtcrime/securesms/DeviceListFragment.java @@ -4,11 +4,6 @@ import android.app.Activity; import android.content.Context; import android.content.DialogInterface; import android.os.Bundle; -import androidx.annotation.NonNull; -import androidx.fragment.app.ListFragment; -import androidx.loader.app.LoaderManager; -import androidx.loader.content.Loader; -import androidx.appcompat.app.AlertDialog; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -19,6 +14,12 @@ import android.widget.EditText; import android.widget.LinearLayout; import android.widget.ListView; +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.ListFragment; +import androidx.loader.app.LoaderManager; +import androidx.loader.content.Loader; + import com.melnykov.fab.FloatingActionButton; import org.thoughtcrime.securesms.database.DatabaseFactory; @@ -26,12 +27,10 @@ import org.thoughtcrime.securesms.database.loaders.DeviceListLoader; import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.devicelist.Device; import org.thoughtcrime.securesms.loki.dialogs.DeviceEditingOptionsBottomSheet; -import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.ViewUtil; import org.whispersystems.libsignal.util.guava.Function; -import java.io.File; import java.util.List; import java.util.Locale; @@ -48,7 +47,6 @@ public class DeviceListFragment extends ListFragment private static final String TAG = DeviceListFragment.class.getSimpleName(); - private File languageFileDirectory; private Locale locale; private View empty; private View progressContainer; @@ -85,7 +83,6 @@ public class DeviceListFragment extends ListFragment @Override public void onActivityCreated(Bundle bundle) { super.onActivityCreated(bundle); - this.languageFileDirectory = MnemonicUtilities.getLanguageFileDirectory(getContext()); getLoaderManager().initLoader(0, null, this); getListView().setOnItemClickListener(this); } @@ -107,7 +104,7 @@ public class DeviceListFragment extends ListFragment empty.setVisibility(View.GONE); progressContainer.setVisibility(View.VISIBLE); - return new DeviceListLoader(getActivity(), languageFileDirectory); + return new DeviceListLoader(getActivity()); } @Override diff --git a/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java b/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java index 2d903374a7..507f8df7e8 100644 --- a/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java +++ b/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java @@ -1530,9 +1530,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } private void onSecurityUpdated() { - Log.i(TAG, "onSecurityUpdated()"); - updateReminders(recipient.hasSeenInviteReminder()); - updateDefaultSubscriptionId(recipient.getDefaultSubscriptionId()); + if (recipient != null) { + updateDefaultSubscriptionId(recipient.getDefaultSubscriptionId()); + } } protected void updateReminders(boolean seenInvite) { @@ -1853,9 +1853,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity return; } + /* ApplicationContext.getInstance(this) .getJobManager() .add(new RetrieveProfileJob(recipient)); + */ } @Override diff --git a/src/org/thoughtcrime/securesms/conversation/ConversationItem.java b/src/org/thoughtcrime/securesms/conversation/ConversationItem.java index ff0dc8509a..aeb1712ceb 100644 --- a/src/org/thoughtcrime/securesms/conversation/ConversationItem.java +++ b/src/org/thoughtcrime/securesms/conversation/ConversationItem.java @@ -25,10 +25,6 @@ import android.graphics.Color; import android.graphics.PorterDuff; import android.graphics.Typeface; import android.net.Uri; -import androidx.annotation.DimenRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AlertDialog; import android.text.Spannable; import android.text.SpannableString; import android.text.SpannableStringBuilder; @@ -50,6 +46,11 @@ import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; +import androidx.annotation.DimenRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; + import com.annimon.stream.Stream; import org.thoughtcrime.securesms.ApplicationContext; @@ -975,7 +976,11 @@ public class ConversationItem extends LinearLayout } private void setAuthor(@NonNull MessageRecord current, @NonNull Optional previous, @NonNull Optional next, boolean isGroupThread) { - String threadName = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(current.getThreadId()).getName(); + Recipient recipient = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(current.getThreadId()); + String threadName = null; + if (recipient != null) { + threadName = recipient.getName(); + } boolean isRSSFeed = threadName != null && (threadName.equals("Loki News") || threadName.equals("Session Updates")); if (isGroupThread && !isRSSFeed && !current.isOutgoing()) { contactPhotoHolder.setVisibility(VISIBLE); diff --git a/src/org/thoughtcrime/securesms/crypto/KeyStoreHelper.java b/src/org/thoughtcrime/securesms/crypto/KeyStoreHelper.java index 994c139434..fe032fa3d4 100644 --- a/src/org/thoughtcrime/securesms/crypto/KeyStoreHelper.java +++ b/src/org/thoughtcrime/securesms/crypto/KeyStoreHelper.java @@ -4,9 +4,10 @@ package org.thoughtcrime.securesms.crypto; import android.os.Build; import android.security.keystore.KeyGenParameterSpec; import android.security.keystore.KeyProperties; +import android.util.Base64; + import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; -import android.util.Base64; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonGenerator; @@ -28,6 +29,7 @@ import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.UnrecoverableEntryException; +import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import javax.crypto.BadPaddingException; @@ -38,7 +40,7 @@ import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import javax.crypto.spec.GCMParameterSpec; -public class KeyStoreHelper { +public final class KeyStoreHelper { private static final String ANDROID_KEY_STORE = "AndroidKeyStore"; private static final String KEY_ALIAS = "SignalSecret"; @@ -99,12 +101,38 @@ public class KeyStoreHelper { @RequiresApi(Build.VERSION_CODES.M) private static SecretKey getKeyStoreEntry() { + KeyStore keyStore = getKeyStore(); + + try { + // Attempt 1 + return getSecretKey(keyStore); + } catch (UnrecoverableKeyException e1) { + try { + // Attempt 2 + return getSecretKey(keyStore); + } catch (UnrecoverableKeyException e2) { + throw new AssertionError(e2); + } + } + } + + private static SecretKey getSecretKey(KeyStore keyStore) throws UnrecoverableKeyException { + try { + KeyStore.SecretKeyEntry entry = (KeyStore.SecretKeyEntry) keyStore.getEntry(KEY_ALIAS, null); + return entry.getSecretKey(); + } catch (UnrecoverableKeyException e) { + throw e; + } catch (KeyStoreException | NoSuchAlgorithmException | UnrecoverableEntryException e) { + throw new AssertionError(e); + } + } + + private static KeyStore getKeyStore() { try { KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE); keyStore.load(null); - - return ((KeyStore.SecretKeyEntry) keyStore.getEntry(KEY_ALIAS, null)).getSecretKey(); - } catch (KeyStoreException | IOException | NoSuchAlgorithmException | CertificateException | UnrecoverableEntryException e) { + return keyStore; + } catch (KeyStoreException | CertificateException | IOException | NoSuchAlgorithmException e) { throw new AssertionError(e); } } diff --git a/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java b/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java index 4d0da39a3c..698910d674 100644 --- a/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java +++ b/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java @@ -6,9 +6,10 @@ import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.os.SystemClock; -import androidx.annotation.NonNull; import android.text.TextUtils; +import androidx.annotation.NonNull; + import net.sqlcipher.database.SQLiteDatabase; import net.sqlcipher.database.SQLiteDatabaseHook; import net.sqlcipher.database.SQLiteOpenHelper; @@ -87,8 +88,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { private static final int lokiV10 = 31; private static final int lokiV11 = 32; private static final int lokiV12 = 33; + private static final int lokiV13 = 34; - private static final int DATABASE_VERSION = lokiV12; // Loki - onUpgrade(...) must be updated to use Loki version numbers if Signal makes any database changes + private static final int DATABASE_VERSION = lokiV13; // Loki - onUpgrade(...) must be updated to use Loki version numbers if Signal makes any database changes private static final String DATABASE_NAME = "signal.db"; private final Context context; @@ -140,7 +142,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { db.execSQL(LokiAPIDatabase.getCreateOnionRequestPathTableCommand()); db.execSQL(LokiAPIDatabase.getCreateSwarmTableCommand()); db.execSQL(LokiAPIDatabase.getCreateLastMessageHashValueTable2Command()); - db.execSQL(LokiAPIDatabase.getCreateReceivedMessageHashValuesTable2Command()); + db.execSQL(LokiAPIDatabase.getCreateReceivedMessageHashValuesTable3Command()); db.execSQL(LokiAPIDatabase.getCreateOpenGroupAuthTokenTableCommand()); db.execSQL(LokiAPIDatabase.getCreateLastMessageServerIDTableCommand()); db.execSQL(LokiAPIDatabase.getCreateLastDeletionServerIDTableCommand()); @@ -609,11 +611,14 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { if (oldVersion < lokiV12) { db.execSQL(LokiAPIDatabase.getCreateLastMessageHashValueTable2Command()); - db.execSQL(LokiAPIDatabase.getCreateReceivedMessageHashValuesTable2Command()); db.execSQL(SharedSenderKeysDatabase.getCreateClosedGroupRatchetTableCommand()); db.execSQL(SharedSenderKeysDatabase.getCreateClosedGroupPrivateKeyTableCommand()); } + if (oldVersion < lokiV13) { + db.execSQL(LokiAPIDatabase.getCreateReceivedMessageHashValuesTable3Command()); + } + db.setTransactionSuccessful(); } finally { db.endTransaction(); diff --git a/src/org/thoughtcrime/securesms/database/loaders/DeviceListLoader.java b/src/org/thoughtcrime/securesms/database/loaders/DeviceListLoader.java index 711d557ce9..1f868d28ca 100644 --- a/src/org/thoughtcrime/securesms/database/loaders/DeviceListLoader.java +++ b/src/org/thoughtcrime/securesms/database/loaders/DeviceListLoader.java @@ -1,6 +1,7 @@ package org.thoughtcrime.securesms.database.loaders; import android.content.Context; + import androidx.annotation.NonNull; import com.annimon.stream.Stream; @@ -8,13 +9,10 @@ import com.annimon.stream.Stream; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.devicelist.Device; import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities; import org.thoughtcrime.securesms.util.AsyncLoader; import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.signalservice.loki.crypto.MnemonicCodec; import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.MultiDeviceProtocol; -import java.io.File; import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -23,11 +21,9 @@ import java.util.Set; public class DeviceListLoader extends AsyncLoader> { private static final String TAG = DeviceListLoader.class.getSimpleName(); - private MnemonicCodec mnemonicCodec; - public DeviceListLoader(Context context, File languageFileDirectory) { + public DeviceListLoader(Context context) { super(context); - this.mnemonicCodec = new MnemonicCodec(languageFileDirectory); } @Override @@ -45,7 +41,7 @@ public class DeviceListLoader extends AsyncLoader> { } private Device mapToDevice(@NonNull String hexEncodedPublicKey) { - String shortId = MnemonicUtilities.getFirst3Words(mnemonicCodec, hexEncodedPublicKey); + String shortId = ""; String name = DatabaseFactory.getLokiUserDatabase(getContext()).getDisplayName(hexEncodedPublicKey); return new Device(hexEncodedPublicKey, shortId, name); } diff --git a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java index 4ce544efb2..52bfb5541f 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java @@ -5,12 +5,13 @@ import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.os.Build; +import android.text.TextUtils; +import android.util.Pair; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationManagerCompat; -import android.text.TextUtils; -import android.util.Pair; import com.annimon.stream.Collectors; import com.annimon.stream.Stream; @@ -66,10 +67,10 @@ import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.loki.activities.HomeActivity; import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase; import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol; -import org.thoughtcrime.securesms.loki.protocol.shelved.MultiDeviceProtocol; import org.thoughtcrime.securesms.loki.protocol.SessionManagementProtocol; import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol; import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation; +import org.thoughtcrime.securesms.loki.protocol.shelved.MultiDeviceProtocol; import org.thoughtcrime.securesms.loki.protocol.shelved.SyncMessagesProtocol; import org.thoughtcrime.securesms.loki.utilities.MentionManagerUtilities; import org.thoughtcrime.securesms.loki.utilities.PromiseUtilities; @@ -303,7 +304,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType { SessionMetaProtocol.handleProfileKeyUpdate(context, content); } - if (content.isNeedsReceipt() && SessionMetaProtocol.shouldSendDeliveryReceipt(Address.fromSerialized(content.getSender()))) { + if (SessionMetaProtocol.shouldSendDeliveryReceipt(message, Address.fromSerialized(content.getSender()))) { handleNeedsDeliveryReceipt(content, message); } } diff --git a/src/org/thoughtcrime/securesms/jobs/SendDeliveryReceiptJob.java b/src/org/thoughtcrime/securesms/jobs/SendDeliveryReceiptJob.java index dead5266cd..b19fbba85c 100644 --- a/src/org/thoughtcrime/securesms/jobs/SendDeliveryReceiptJob.java +++ b/src/org/thoughtcrime/securesms/jobs/SendDeliveryReceiptJob.java @@ -10,7 +10,6 @@ import org.thoughtcrime.securesms.jobmanager.Data; import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; import org.thoughtcrime.securesms.logging.Log; -import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol; import org.thoughtcrime.securesms.recipients.Recipient; import org.whispersystems.signalservice.api.SignalServiceMessageSender; import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; @@ -79,13 +78,12 @@ public class SendDeliveryReceiptJob extends BaseJob implements InjectableType { @Override public void onRun() throws IOException, UntrustedIdentityException { + Log.d("Loki", "Sending delivery receipt."); SignalServiceAddress remoteAddress = new SignalServiceAddress(address); SignalServiceReceiptMessage receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.DELIVERY, Collections.singletonList(messageId), timestamp); - if (!SessionMetaProtocol.shouldSendDeliveryReceipt(Address.fromSerialized(address))) { return; } - messageSender.sendReceipt(remoteAddress, UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(address), false)), receiptMessage); diff --git a/src/org/thoughtcrime/securesms/loki/activities/LinkedDevicesLoader.kt b/src/org/thoughtcrime/securesms/loki/activities/LinkedDevicesLoader.kt index 785566b2d7..663f83dc84 100644 --- a/src/org/thoughtcrime/securesms/loki/activities/LinkedDevicesLoader.kt +++ b/src/org/thoughtcrime/securesms/loki/activities/LinkedDevicesLoader.kt @@ -13,8 +13,10 @@ import java.io.File class LinkedDevicesLoader(context: Context) : AsyncLoader>(context) { private val mnemonicCodec by lazy { - val languageFileDirectory = File(context.applicationInfo.dataDir) - MnemonicCodec(languageFileDirectory) + val loadFileContents: (String) -> String = { fileName -> + MnemonicUtilities.loadFileContents(context, fileName) + } + MnemonicCodec(loadFileContents) } override fun loadInBackground(): List? { diff --git a/src/org/thoughtcrime/securesms/loki/activities/PathActivity.kt b/src/org/thoughtcrime/securesms/loki/activities/PathActivity.kt index b42661be1f..7e3b641b12 100644 --- a/src/org/thoughtcrime/securesms/loki/activities/PathActivity.kt +++ b/src/org/thoughtcrime/securesms/loki/activities/PathActivity.kt @@ -142,7 +142,11 @@ class PathActivity : PassphraseRequiredActionBarActivity() { private fun getPathRow(snode: Snode, location: LineView.Location, dotAnimationStartDelay: Long, dotAnimationRepeatInterval: Long, isGuardSnode: Boolean): LinearLayout { val title = if (isGuardSnode) resources.getString(R.string.activity_path_guard_node_row_title) else resources.getString(R.string.activity_path_service_node_row_title) - val subtitle = IP2Country.shared.countryNamesCache[snode.ip] ?: "Resolving..." + val subtitle = if (IP2Country.isInitialized) { + IP2Country.shared.countryNamesCache[snode.ip] ?: "Resolving..." + } else { + "Resolving..." + } return getPathRow(title, subtitle, location, dotAnimationStartDelay, dotAnimationRepeatInterval) } // endregion diff --git a/src/org/thoughtcrime/securesms/loki/activities/RegisterActivity.kt b/src/org/thoughtcrime/securesms/loki/activities/RegisterActivity.kt index 9d5a71c85b..1ebf1ca1fd 100644 --- a/src/org/thoughtcrime/securesms/loki/activities/RegisterActivity.kt +++ b/src/org/thoughtcrime/securesms/loki/activities/RegisterActivity.kt @@ -44,7 +44,6 @@ class RegisterActivity : BaseActionBarActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_register) - setUpLanguageFileDirectory() setUpActionBarSessionLogo() registerButton.setOnClickListener { register() } copyButton.setOnClickListener { copyPublicKey() } @@ -69,28 +68,6 @@ class RegisterActivity : BaseActionBarActivity() { } // endregion - // region General - private fun setUpLanguageFileDirectory() { - val languages = listOf( "english", "japanese", "portuguese", "spanish" ) - val directory = File(applicationInfo.dataDir) - for (language in languages) { - val fileName = "$language.txt" - if (directory.list().contains(fileName)) { continue } - val inputStream = assets.open("mnemonic/$fileName") - val file = File(directory, fileName) - val outputStream = FileOutputStream(file) - val buffer = ByteArray(1024) - while (true) { - val count = inputStream.read(buffer) - if (count < 0) { break } - outputStream.write(buffer, 0, count) - } - inputStream.close() - outputStream.close() - } - } - // endregion - // region Updating private fun updateKeyPair() { val seedCandidate = Curve25519.getInstance(Curve25519.BEST).generateSeed(16) diff --git a/src/org/thoughtcrime/securesms/loki/activities/RestoreActivity.kt b/src/org/thoughtcrime/securesms/loki/activities/RestoreActivity.kt index 195801191c..5c4ffa7863 100644 --- a/src/org/thoughtcrime/securesms/loki/activities/RestoreActivity.kt +++ b/src/org/thoughtcrime/securesms/loki/activities/RestoreActivity.kt @@ -18,6 +18,7 @@ import org.thoughtcrime.securesms.crypto.IdentityKeyUtil import org.thoughtcrime.securesms.database.Address import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.database.IdentityDatabase +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.util.Base64 @@ -31,12 +32,10 @@ import java.io.File import java.io.FileOutputStream class RestoreActivity : BaseActionBarActivity() { - private lateinit var languageFileDirectory: File // region Lifecycle override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setUpLanguageFileDirectory() setUpActionBarSessionLogo() setContentView(R.layout.activity_restore) mnemonicEditText.imeOptions = mnemonicEditText.imeOptions or 16777216 // Always use incognito keyboard @@ -61,34 +60,14 @@ class RestoreActivity : BaseActionBarActivity() { } // endregion - // region General - private fun setUpLanguageFileDirectory() { - val languages = listOf( "english", "japanese", "portuguese", "spanish" ) - val directory = File(applicationInfo.dataDir) - for (language in languages) { - val fileName = "$language.txt" - if (directory.list().contains(fileName)) { continue } - val inputStream = assets.open("mnemonic/$fileName") - val file = File(directory, fileName) - val outputStream = FileOutputStream(file) - val buffer = ByteArray(1024) - while (true) { - val count = inputStream.read(buffer) - if (count < 0) { break } - outputStream.write(buffer, 0, count) - } - inputStream.close() - outputStream.close() - } - languageFileDirectory = directory - } - // endregion - // region Interaction private fun restore() { val mnemonic = mnemonicEditText.text.toString() try { - val hexEncodedSeed = MnemonicCodec(languageFileDirectory).decode(mnemonic) + val loadFileContents: (String) -> String = { fileName -> + MnemonicUtilities.loadFileContents(this, fileName) + } + val hexEncodedSeed = MnemonicCodec(loadFileContents).decode(mnemonic) var seed = Hex.fromStringCondensed(hexEncodedSeed) IdentityKeyUtil.save(this, IdentityKeyUtil.lokiSeedKey, Hex.toStringCondensed(seed)) if (seed.size == 16) { seed = seed + seed } diff --git a/src/org/thoughtcrime/securesms/loki/activities/SeedActivity.kt b/src/org/thoughtcrime/securesms/loki/activities/SeedActivity.kt index d0995ee019..c91113aa98 100644 --- a/src/org/thoughtcrime/securesms/loki/activities/SeedActivity.kt +++ b/src/org/thoughtcrime/securesms/loki/activities/SeedActivity.kt @@ -7,12 +7,14 @@ import android.os.Bundle import android.text.Spannable import android.text.SpannableString import android.text.style.ForegroundColorSpan +import android.util.Log 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.thoughtcrime.securesms.crypto.IdentityKeyUtil +import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities import org.thoughtcrime.securesms.loki.utilities.getColorWithID import org.thoughtcrime.securesms.util.TextSecurePreferences import org.whispersystems.signalservice.loki.crypto.MnemonicCodec @@ -22,12 +24,14 @@ import java.io.File class SeedActivity : BaseActionBarActivity() { private val seed by lazy { - val languageFileDirectory = File(applicationInfo.dataDir) var hexEncodedSeed = IdentityKeyUtil.retrieve(this, IdentityKeyUtil.lokiSeedKey) if (hexEncodedSeed == null) { hexEncodedSeed = IdentityKeyUtil.getIdentityKeyPair(this).hexEncodedPrivateKey // Legacy account } - MnemonicCodec(languageFileDirectory).encode(hexEncodedSeed!!, MnemonicCodec.Language.Configuration.english) + val loadFileContents: (String) -> String = { fileName -> + MnemonicUtilities.loadFileContents(this, fileName) + } + MnemonicCodec(loadFileContents).encode(hexEncodedSeed!!, MnemonicCodec.Language.Configuration.english) } // region Lifecycle diff --git a/src/org/thoughtcrime/securesms/loki/database/LokiAPIDatabase.kt b/src/org/thoughtcrime/securesms/loki/database/LokiAPIDatabase.kt index 58728ed199..e81f3c9fb6 100644 --- a/src/org/thoughtcrime/securesms/loki/database/LokiAPIDatabase.kt +++ b/src/org/thoughtcrime/securesms/loki/database/LokiAPIDatabase.kt @@ -38,10 +38,10 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database( @JvmStatic val createLastMessageHashValueTable2Command = "CREATE TABLE $lastMessageHashValueTable2 ($snode TEXT, $publicKey TEXT, $lastMessageHashValue TEXT, PRIMARY KEY ($snode, $publicKey));" // Received message hash values - private val receivedMessageHashValuesTable2 = "received_message_hash_values_table" + private val receivedMessageHashValuesTable3 = "received_message_hash_values_table_3" private val receivedMessageHashValues = "received_message_hash_values" - @JvmStatic val createReceivedMessageHashValuesTable2Command - = "CREATE TABLE $receivedMessageHashValuesTable2 ($snode STRING, $publicKey STRING, $receivedMessageHashValues TEXT, PRIMARY KEY ($snode, $publicKey));" + @JvmStatic val createReceivedMessageHashValuesTable3Command + = "CREATE TABLE $receivedMessageHashValuesTable3 ($publicKey STRING PRIMARY KEY, $receivedMessageHashValues TEXT);" // Open group auth tokens private val openGroupAuthTokenTable = "loki_api_group_chat_auth_token_database" private val server = "server" @@ -215,9 +215,9 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database( override fun getReceivedMessageHashValues(publicKey: String): Set? { val database = databaseHelper.readableDatabase - val query = "$Companion.publicKey = ?" - return database.get(receivedMessageHashValuesTable2, query, arrayOf( publicKey )) { cursor -> - val receivedMessageHashValuesAsString = cursor.getString(cursor.getColumnIndexOrThrow(receivedMessageHashValues)) + val query = "${Companion.publicKey} = ?" + return database.get(receivedMessageHashValuesTable3, query, arrayOf( publicKey )) { cursor -> + val receivedMessageHashValuesAsString = cursor.getString(cursor.getColumnIndexOrThrow(Companion.receivedMessageHashValues)) receivedMessageHashValuesAsString.split("-").toSet() } } @@ -225,9 +225,9 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database( override fun setReceivedMessageHashValues(publicKey: String, newValue: Set) { val database = databaseHelper.writableDatabase val receivedMessageHashValuesAsString = newValue.joinToString("-") - val row = wrap(mapOf( Companion.publicKey to publicKey, receivedMessageHashValues to receivedMessageHashValuesAsString )) - val query = "$Companion.publicKey = ?" - database.insertOrUpdate(receivedMessageHashValuesTable2, row, query, arrayOf( publicKey )) + val row = wrap(mapOf( Companion.publicKey to publicKey, Companion.receivedMessageHashValues to receivedMessageHashValuesAsString )) + val query = "${Companion.publicKey} = ?" + database.insertOrUpdate(receivedMessageHashValuesTable3, row, query, arrayOf( publicKey )) } override fun getAuthToken(server: String): String? { diff --git a/src/org/thoughtcrime/securesms/loki/dialogs/LinkDeviceMasterModeDialog.kt b/src/org/thoughtcrime/securesms/loki/dialogs/LinkDeviceMasterModeDialog.kt index ed55eca238..91edb872c2 100644 --- a/src/org/thoughtcrime/securesms/loki/dialogs/LinkDeviceMasterModeDialog.kt +++ b/src/org/thoughtcrime/securesms/loki/dialogs/LinkDeviceMasterModeDialog.kt @@ -29,16 +29,15 @@ import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.Device import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.DeviceLinkingSessionListener class LinkDeviceMasterModeDialog : DialogFragment(), DeviceLinkingSessionListener { - private val languageFileDirectory by lazy { MnemonicUtilities.getLanguageFileDirectory(context!!) } private lateinit var contentView: View private var deviceLink: DeviceLink? = null var delegate: LinkDeviceMasterModeDialogDelegate? = null override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val builder = AlertDialog.Builder(context!!) - contentView = LayoutInflater.from(context!!).inflate(R.layout.dialog_link_device_master_mode, null) + val builder = AlertDialog.Builder(requireContext()) + contentView = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_link_device_master_mode, null) val size = toPx(128, resources) - val hexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context!!) + val hexEncodedPublicKey = TextSecurePreferences.getLocalNumber(requireContext()) val qrCode = QRCodeUtilities.encode(hexEncodedPublicKey, size, false, false) contentView.qrCodeImageView.setImageBitmap(qrCode) contentView.cancelButton.setOnClickListener { onDeviceLinkCanceled() } @@ -52,7 +51,7 @@ class LinkDeviceMasterModeDialog : DialogFragment(), DeviceLinkingSessionListene } override fun requestUserAuthorization(deviceLink: DeviceLink) { - if (deviceLink.type != DeviceLink.Type.REQUEST || deviceLink.masterPublicKey != TextSecurePreferences.getLocalNumber(context!!) || this.deviceLink != null) { return } + if (deviceLink.type != DeviceLink.Type.REQUEST || deviceLink.masterPublicKey != TextSecurePreferences.getLocalNumber(requireContext()) || this.deviceLink != null) { return } Util.runOnMain { this.deviceLink = deviceLink contentView.qrCodeImageView.visibility = View.GONE @@ -62,7 +61,10 @@ class LinkDeviceMasterModeDialog : DialogFragment(), DeviceLinkingSessionListene contentView.titleTextView.text = resources.getString(R.string.dialog_link_device_master_mode_title_2) contentView.explanationTextView.text = resources.getString(R.string.dialog_link_device_master_mode_explanation_2) contentView.mnemonicTextView.visibility = View.VISIBLE - contentView.mnemonicTextView.text = MnemonicUtilities.getFirst3Words(MnemonicCodec(languageFileDirectory), deviceLink.slavePublicKey) + val loadFileContents: (String) -> String = { fileName -> + MnemonicUtilities.loadFileContents(requireContext(), fileName) + } + contentView.mnemonicTextView.text = MnemonicUtilities.getFirst3Words(MnemonicCodec(loadFileContents), deviceLink.slavePublicKey) contentView.authorizeButton.visibility = View.VISIBLE } } @@ -85,15 +87,15 @@ class LinkDeviceMasterModeDialog : DialogFragment(), DeviceLinkingSessionListene contentView.authorizeButton.visibility = View.GONE } FileServerAPI.shared.addDeviceLink(deviceLink).bind(SnodeAPI.sharedContext) { - MultiDeviceProtocol.signAndSendDeviceLinkMessage(context!!, deviceLink) + MultiDeviceProtocol.signAndSendDeviceLinkMessage(requireContext(), deviceLink) }.success { - TextSecurePreferences.setMultiDevice(context!!, true) + TextSecurePreferences.setMultiDevice(requireContext(), true) }.successUi { delegate?.onDeviceLinkRequestAuthorized() dismiss() }.fail { FileServerAPI.shared.removeDeviceLink(deviceLink) // If this fails we have a problem - DatabaseFactory.getLokiPreKeyBundleDatabase(context!!).removePreKeyBundle(deviceLink.slavePublicKey) + DatabaseFactory.getLokiPreKeyBundleDatabase(requireContext()).removePreKeyBundle(deviceLink.slavePublicKey) }.failUi { delegate?.onDeviceLinkAuthorizationFailed() dismiss() diff --git a/src/org/thoughtcrime/securesms/loki/dialogs/LinkDeviceSlaveModeDialog.kt b/src/org/thoughtcrime/securesms/loki/dialogs/LinkDeviceSlaveModeDialog.kt index d795e3148f..54f5d3d48b 100644 --- a/src/org/thoughtcrime/securesms/loki/dialogs/LinkDeviceSlaveModeDialog.kt +++ b/src/org/thoughtcrime/securesms/loki/dialogs/LinkDeviceSlaveModeDialog.kt @@ -21,16 +21,18 @@ import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.Device import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.DeviceLinkingSessionListener class LinkDeviceSlaveModeDialog : DialogFragment(), DeviceLinkingSessionListener { - private val languageFileDirectory by lazy { MnemonicUtilities.getLanguageFileDirectory(context!!) } private lateinit var contentView: View private var deviceLink: DeviceLink? = null var delegate: LinkDeviceSlaveModeDialogDelegate? = null override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val builder = AlertDialog.Builder(context!!) - contentView = LayoutInflater.from(context!!).inflate(R.layout.dialog_link_device_slave_mode, null) + val builder = AlertDialog.Builder(requireContext()) + contentView = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_link_device_slave_mode, null) val hexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context) - contentView.mnemonicTextView.text = MnemonicUtilities.getFirst3Words(MnemonicCodec(languageFileDirectory), hexEncodedPublicKey) + val loadFileContents: (String) -> String = { fileName -> + MnemonicUtilities.loadFileContents(requireContext(), fileName) + } + contentView.mnemonicTextView.text = MnemonicUtilities.getFirst3Words(MnemonicCodec(loadFileContents), hexEncodedPublicKey) contentView.cancelButton.setOnClickListener { onDeviceLinkCanceled() } builder.setView(contentView) DeviceLinkingSession.shared.startListeningForLinkingRequests() @@ -41,7 +43,7 @@ class LinkDeviceSlaveModeDialog : DialogFragment(), DeviceLinkingSessionListener } override fun onDeviceLinkRequestAuthorized(deviceLink: DeviceLink) { - if (deviceLink.type != DeviceLink.Type.AUTHORIZATION || deviceLink.slavePublicKey != TextSecurePreferences.getLocalNumber(context!!) || this.deviceLink != null) { return } + if (deviceLink.type != DeviceLink.Type.AUTHORIZATION || deviceLink.slavePublicKey != TextSecurePreferences.getLocalNumber(requireContext()) || this.deviceLink != null) { return } Util.runOnMain { this.deviceLink = deviceLink DeviceLinkingSession.shared.stopListeningForLinkingRequests() diff --git a/src/org/thoughtcrime/securesms/loki/dialogs/SeedDialog.kt b/src/org/thoughtcrime/securesms/loki/dialogs/SeedDialog.kt index 59f96ae433..a52e08f606 100644 --- a/src/org/thoughtcrime/securesms/loki/dialogs/SeedDialog.kt +++ b/src/org/thoughtcrime/securesms/loki/dialogs/SeedDialog.kt @@ -14,6 +14,7 @@ import android.widget.Toast import kotlinx.android.synthetic.main.dialog_seed.view.* import network.loki.messenger.R import org.thoughtcrime.securesms.crypto.IdentityKeyUtil +import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities import org.whispersystems.signalservice.loki.crypto.MnemonicCodec import org.whispersystems.signalservice.loki.utilities.hexEncodedPrivateKey import java.io.File @@ -21,17 +22,19 @@ import java.io.File class SeedDialog : DialogFragment() { private val seed by lazy { - val languageFileDirectory = File(context!!.applicationInfo.dataDir) - var hexEncodedSeed = IdentityKeyUtil.retrieve(context!!, IdentityKeyUtil.lokiSeedKey) + var hexEncodedSeed = IdentityKeyUtil.retrieve(requireContext(), IdentityKeyUtil.lokiSeedKey) if (hexEncodedSeed == null) { - hexEncodedSeed = IdentityKeyUtil.getIdentityKeyPair(context!!).hexEncodedPrivateKey // Legacy account + hexEncodedSeed = IdentityKeyUtil.getIdentityKeyPair(requireContext()).hexEncodedPrivateKey // Legacy account } - MnemonicCodec(languageFileDirectory).encode(hexEncodedSeed!!, MnemonicCodec.Language.Configuration.english) + val loadFileContents: (String) -> String = { fileName -> + MnemonicUtilities.loadFileContents(requireContext(), fileName) + } + MnemonicCodec(loadFileContents).encode(hexEncodedSeed!!, MnemonicCodec.Language.Configuration.english) } override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val builder = AlertDialog.Builder(context!!) - val contentView = LayoutInflater.from(context!!).inflate(R.layout.dialog_seed, null) + val builder = AlertDialog.Builder(requireContext()) + val contentView = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_seed, null) contentView.seedTextView.text = seed contentView.cancelButton.setOnClickListener { dismiss() } contentView.copyButton.setOnClickListener { copySeed() } @@ -42,10 +45,10 @@ class SeedDialog : DialogFragment() { } private fun copySeed() { - val clipboard = activity!!.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + val clipboard = requireActivity().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager val clip = ClipData.newPlainText("Seed", seed) clipboard.setPrimaryClip(clip) - Toast.makeText(context!!, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show() + Toast.makeText(requireContext(), R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show() dismiss() } } \ No newline at end of file diff --git a/src/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocol.kt b/src/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocol.kt index 80b3f98496..3df6187a7b 100644 --- a/src/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocol.kt +++ b/src/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocol.kt @@ -218,7 +218,7 @@ object ClosedGroupsProtocol { public fun handleSharedSenderKeysUpdate(context: Context, closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdate, senderPublicKey: String) { if (!isValid(closedGroupUpdate)) { return; } when (closedGroupUpdate.type) { - SignalServiceProtos.ClosedGroupUpdate.Type.NEW -> handleNewClosedGroup(context, closedGroupUpdate) + SignalServiceProtos.ClosedGroupUpdate.Type.NEW -> handleNewClosedGroup(context, closedGroupUpdate, senderPublicKey) SignalServiceProtos.ClosedGroupUpdate.Type.INFO -> handleClosedGroupUpdate(context, closedGroupUpdate, senderPublicKey) SignalServiceProtos.ClosedGroupUpdate.Type.SENDER_KEY_REQUEST -> handleSenderKeyRequest(context, closedGroupUpdate, senderPublicKey) SignalServiceProtos.ClosedGroupUpdate.Type.SENDER_KEY -> handleSenderKey(context, closedGroupUpdate, senderPublicKey) @@ -244,7 +244,7 @@ object ClosedGroupsProtocol { } } - public fun handleNewClosedGroup(context: Context, closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdate) { + public fun handleNewClosedGroup(context: Context, closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdate, senderPublicKey: String) { // Prepare val sskDatabase = DatabaseFactory.getSSKDatabase(context) // Unwrap the message @@ -270,7 +270,7 @@ object ClosedGroupsProtocol { // Add the group to the user's set of public keys to poll for sskDatabase.setClosedGroupPrivateKey(groupPublicKey, groupPrivateKey.toHexString()) // Notify the user - insertIncomingInfoMessage(context, groupID, GroupContext.Type.UPDATE, SignalServiceGroup.Type.UPDATE, name, members, admins) + insertIncomingInfoMessage(context, senderPublicKey, groupID, GroupContext.Type.UPDATE, SignalServiceGroup.Type.UPDATE, name, members, admins) // Establish sessions if needed establishSessionsWithMembersIfNeeded(context, members) } @@ -311,6 +311,7 @@ object ClosedGroupsProtocol { // • Remove the group from the user's set of public keys to poll for if the current user was among the members that were removed val wasCurrentUserRemoved = !members.contains(userPublicKey) val wasAnyUserRemoved = members.toSet().intersect(oldMembers) != oldMembers.toSet() + val wasSenderRemoved = !members.contains(senderPublicKey) if (wasAnyUserRemoved) { sskDatabase.removeAllClosedGroupRatchets(groupPublicKey) if (wasCurrentUserRemoved) { @@ -331,9 +332,9 @@ object ClosedGroupsProtocol { groupDB.updateTitle(groupID, name) groupDB.updateMembers(groupID, members.map { Address.fromSerialized(it) }) // Notify the user - val type0 = if (wasAnyUserRemoved) GroupContext.Type.QUIT else GroupContext.Type.UPDATE - val type1 = if (wasAnyUserRemoved) SignalServiceGroup.Type.QUIT else SignalServiceGroup.Type.UPDATE - insertIncomingInfoMessage(context, groupID, type0, type1, name, members, admins) + val type0 = if (wasSenderRemoved) GroupContext.Type.QUIT else GroupContext.Type.UPDATE + val type1 = if (wasSenderRemoved) SignalServiceGroup.Type.QUIT else SignalServiceGroup.Type.UPDATE + insertIncomingInfoMessage(context, senderPublicKey, groupID, type0, type1, name, members, admins) } public fun handleSenderKeyRequest(context: Context, closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdate, senderPublicKey: String) { @@ -410,8 +411,13 @@ object ClosedGroupsProtocol { if (GroupUtil.isOpenGroup(groupID)) { return listOf( Address.fromSerialized(groupID) ) } else { - val groupPublicKey = doubleDecodeGroupID(groupID).toHexString() - if (DatabaseFactory.getSSKDatabase(context).isSSKBasedClosedGroup(groupPublicKey)) { + var groupPublicKey: String? = null + try { + groupPublicKey = doubleDecodeGroupID(groupID).toHexString() + } catch (exception: Exception) { + // Do nothing + } + if (groupPublicKey != null && DatabaseFactory.getSSKDatabase(context).isSSKBasedClosedGroup(groupPublicKey)) { return listOf( Address.fromSerialized(groupPublicKey) ) } else { return DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupID, false).map { it.address } @@ -475,8 +481,8 @@ object ClosedGroupsProtocol { } } - private fun insertIncomingInfoMessage(context: Context, groupID: String, type0: GroupContext.Type, type1: SignalServiceGroup.Type, name: String, - members: Collection, admins: Collection) { + private fun insertIncomingInfoMessage(context: Context, senderPublicKey: String, groupID: String, type0: GroupContext.Type, type1: SignalServiceGroup.Type, + name: String, members: Collection, admins: Collection) { val groupContextBuilder = GroupContext.newBuilder() .setId(ByteString.copyFrom(GroupUtil.getDecodedId(groupID))) .setType(type0) @@ -484,7 +490,7 @@ object ClosedGroupsProtocol { .addAllMembers(members) .addAllAdmins(admins) val group = SignalServiceGroup(type1, GroupUtil.getDecodedId(groupID), GroupType.SIGNAL, name, members.toList(), null, admins.toList()) - val m = IncomingTextMessage(Address.fromSerialized(groupID), 1, System.currentTimeMillis(), "", Optional.of(group), 0, true) + val m = IncomingTextMessage(Address.fromSerialized(senderPublicKey), 1, System.currentTimeMillis(), "", Optional.of(group), 0, true) val infoMessage = IncomingGroupMessage(m, groupContextBuilder.build(), "") val smsDB = DatabaseFactory.getSmsDatabase(context) smsDB.insertMessageInbox(infoMessage) diff --git a/src/org/thoughtcrime/securesms/loki/protocol/SessionMetaProtocol.kt b/src/org/thoughtcrime/securesms/loki/protocol/SessionMetaProtocol.kt index 1aacde4196..2327cbe3bb 100644 --- a/src/org/thoughtcrime/securesms/loki/protocol/SessionMetaProtocol.kt +++ b/src/org/thoughtcrime/securesms/loki/protocol/SessionMetaProtocol.kt @@ -79,8 +79,12 @@ object SessionMetaProtocol { } @JvmStatic - fun shouldSendDeliveryReceipt(address: Address): Boolean { - return !address.isGroup + fun shouldSendDeliveryReceipt(message: SignalServiceDataMessage, address: Address): Boolean { + if (address.isGroup) { return false } + val hasBody = message.body.isPresent && message.body.get().isNotEmpty() + val hasAttachment = message.attachments.isPresent && message.attachments.get().isNotEmpty() + val hasLinkPreview = message.previews.isPresent && message.previews.get().isNotEmpty() + return hasBody || hasAttachment || hasLinkPreview } /** diff --git a/src/org/thoughtcrime/securesms/loki/utilities/GeneralUtilities.kt b/src/org/thoughtcrime/securesms/loki/utilities/GeneralUtilities.kt index 3ff3bd19a5..37bc30c395 100644 --- a/src/org/thoughtcrime/securesms/loki/utilities/GeneralUtilities.kt +++ b/src/org/thoughtcrime/securesms/loki/utilities/GeneralUtilities.kt @@ -19,7 +19,3 @@ fun toPx(dp: Int, resources: Resources): Int { val scale = resources.displayMetrics.density return (dp * scale).roundToInt() } - -fun isPublicChat(context: Context, recipient: String): Boolean { - return DatabaseFactory.getLokiThreadDatabase(context).getAllPublicChats().values.map { it.server }.contains(recipient) -} diff --git a/src/org/thoughtcrime/securesms/loki/utilities/IP2Country.kt b/src/org/thoughtcrime/securesms/loki/utilities/IP2Country.kt index 8cd1204167..7f99b2c699 100644 --- a/src/org/thoughtcrime/securesms/loki/utilities/IP2Country.kt +++ b/src/org/thoughtcrime/securesms/loki/utilities/IP2Country.kt @@ -29,8 +29,10 @@ class IP2Country private constructor(private val context: Context) { public lateinit var shared: IP2Country + public val isInitialized: Boolean get() = ::shared.isInitialized + public fun configureIfNeeded(context: Context) { - if (::shared.isInitialized) { return; } + if (isInitialized) { return; } shared = IP2Country(context) } } diff --git a/src/org/thoughtcrime/securesms/loki/utilities/MnemonicUtilities.kt b/src/org/thoughtcrime/securesms/loki/utilities/MnemonicUtilities.kt index c1e10551c2..040419a1df 100644 --- a/src/org/thoughtcrime/securesms/loki/utilities/MnemonicUtilities.kt +++ b/src/org/thoughtcrime/securesms/loki/utilities/MnemonicUtilities.kt @@ -8,30 +8,17 @@ import java.io.FileOutputStream object MnemonicUtilities { - @JvmStatic - public fun getLanguageFileDirectory(context: Context): File { - val languages = listOf( "english", "japanese", "portuguese", "spanish" ) - val directory = File(context.applicationInfo.dataDir) - for (language in languages) { - val fileName = "$language.txt" - if (directory.list().contains(fileName)) { continue } - val inputStream = context.assets.open("mnemonic/$fileName") - val file = File(directory, fileName) - val outputStream = FileOutputStream(file) - val buffer = ByteArray(1024) - while (true) { - val count = inputStream.read(buffer) - if (count < 0) { break } - outputStream.write(buffer, 0, count) - } + public fun loadFileContents(context: Context, fileName: String): String { + val inputStream = context.assets.open("mnemonic/$fileName.txt") + val size = inputStream.available() + val buffer = ByteArray(size) + inputStream.read(buffer) inputStream.close() - outputStream.close() - } - return directory + return String(buffer) } @JvmStatic public fun getFirst3Words(codec: MnemonicCodec, hexEncodedPublicKey: String): String { - return codec.encode(hexEncodedPublicKey.removing05PrefixIfNeeded()).split(" ").slice(0 until 3).joinToString(" ") + return codec.encode(hexEncodedPublicKey.removing05PrefixIfNeeded()).split(" ").slice(0 until 3).joinToString(" ") } } \ No newline at end of file diff --git a/src/org/thoughtcrime/securesms/loki/utilities/OpenGroupUtilities.kt b/src/org/thoughtcrime/securesms/loki/utilities/OpenGroupUtilities.kt index c2abd36dd5..4cf69ea315 100644 --- a/src/org/thoughtcrime/securesms/loki/utilities/OpenGroupUtilities.kt +++ b/src/org/thoughtcrime/securesms/loki/utilities/OpenGroupUtilities.kt @@ -23,15 +23,15 @@ object OpenGroupUtilities { val displayName = TextSecurePreferences.getProfileName(context) val lokiPublicChatAPI = application.publicChatAPI ?: throw Error("LokiPublicChatAPI is not initialized.") return application.publicChatManager.addChat(url, channel).then { group -> - DatabaseFactory.getLokiAPIDatabase(context).removeLastMessageServerID(channel, url) - DatabaseFactory.getLokiAPIDatabase(context).removeLastDeletionServerID(channel, url) - lokiPublicChatAPI.getMessages(channel, url) - lokiPublicChatAPI.setDisplayName(displayName, url) - lokiPublicChatAPI.join(channel, url) - val profileKey: ByteArray = ProfileKeyUtil.getProfileKey(context) - val profileUrl: String? = TextSecurePreferences.getProfilePictureURL(context) - lokiPublicChatAPI.setProfilePicture(url, profileKey, profileUrl) - group + DatabaseFactory.getLokiAPIDatabase(context).removeLastMessageServerID(channel, url) + DatabaseFactory.getLokiAPIDatabase(context).removeLastDeletionServerID(channel, url) + lokiPublicChatAPI.getMessages(channel, url) + lokiPublicChatAPI.setDisplayName(displayName, url) + lokiPublicChatAPI.join(channel, url) + val profileKey: ByteArray = ProfileKeyUtil.getProfileKey(context) + val profileUrl: String? = TextSecurePreferences.getProfilePictureURL(context) + lokiPublicChatAPI.setProfilePicture(url, profileKey, profileUrl) + group } } } \ No newline at end of file diff --git a/src/org/thoughtcrime/securesms/preferences/widgets/ProfilePreference.java b/src/org/thoughtcrime/securesms/preferences/widgets/ProfilePreference.java index 21ad43fdd4..f6c15d2ac5 100644 --- a/src/org/thoughtcrime/securesms/preferences/widgets/ProfilePreference.java +++ b/src/org/thoughtcrime/securesms/preferences/widgets/ProfilePreference.java @@ -7,9 +7,6 @@ import android.content.Context; import android.graphics.Outline; import android.graphics.drawable.Drawable; import android.os.Build; -import androidx.annotation.RequiresApi; -import androidx.preference.Preference; -import androidx.preference.PreferenceViewHolder; import android.text.TextUtils; import android.util.AttributeSet; import android.view.View; @@ -18,14 +15,16 @@ import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; +import androidx.annotation.RequiresApi; +import androidx.preference.Preference; +import androidx.preference.PreferenceViewHolder; + import com.bumptech.glide.load.engine.DiskCacheStrategy; import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities; import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.signalservice.loki.crypto.MnemonicCodec; import network.loki.messenger.R; @@ -124,8 +123,7 @@ public class ProfilePreference extends Preference { profileTagView.setVisibility(userMasterPublicKey == null ? View.GONE : View.VISIBLE); if (userMasterPublicKey != null && shortDeviceMnemonic == null) { - MnemonicCodec codec = new MnemonicCodec(MnemonicUtilities.getLanguageFileDirectory(context)); - shortDeviceMnemonic = MnemonicUtilities.getFirst3Words(codec, userPublicKey); + shortDeviceMnemonic = ""; } String tag = context.getResources().getString(R.string.activity_settings_linked_device_tag);