Compare commits

..

82 Commits

Author SHA1 Message Date
Greyson Parrelli
1621c060b5 Bump version to 5.27.7 2021-11-28 10:53:50 -05:00
Greyson Parrelli
b1c32476b0 Updated language translations. 2021-11-28 10:52:56 -05:00
Greyson Parrelli
ba96db2ae0 Update permission string at Google's request to reflect private contact discovery. 2021-11-27 18:12:23 -05:00
Greyson Parrelli
182a112cdd Bump version to 5.27.6 2021-11-24 16:41:12 -05:00
Greyson Parrelli
a45e26ab6b Updated language translations. 2021-11-24 16:41:12 -05:00
Greyson Parrelli
b6022be41f Fix possible crash in MediaPreviewActivity. 2021-11-24 16:41:12 -05:00
Greyson Parrelli
0801a0e329 Fix typo in badge density selection. 2021-11-24 16:41:12 -05:00
Greyson Parrelli
89cbfd3299 Log errorCode when a stripe request fails. 2021-11-24 16:41:12 -05:00
Cody Henthorne
5b2ca6a1d3 Display badge on Payment Details. 2021-11-24 16:41:12 -05:00
Cody Henthorne
c5d7188dcb Fix country specific translations for badges. 2021-11-24 16:41:12 -05:00
Cody Henthorne
818eb81f87 Fix Boost bottom sheet dark theme bugs. 2021-11-24 16:41:12 -05:00
Cody Henthorne
510a295198 Fix crash in custom boost input. 2021-11-24 16:41:12 -05:00
Greyson Parrelli
98fab95683 Bump version to 5.27.5 2021-11-23 20:22:06 -05:00
Greyson Parrelli
79d45bb497 Updated language translations. 2021-11-23 20:18:58 -05:00
Greyson Parrelli
fc3d77ed9a Remove emails from payments. 2021-11-23 20:18:57 -05:00
Greyson Parrelli
ee05cf87aa Bump version to 5.27.4 2021-11-23 17:37:32 -05:00
Greyson Parrelli
ae18aed15b Updated language translations. 2021-11-23 17:35:54 -05:00
Greyson Parrelli
a5aa079216 Include more debug info around badges. 2021-11-23 17:28:24 -05:00
Greyson Parrelli
ae7a03bc8f Improve boost expiration UI when you're also a sustainer. 2021-11-23 17:00:47 -05:00
Cody Henthorne
6ed797c031 Fix custom input formatting and display bugs. 2021-11-23 17:00:47 -05:00
Greyson Parrelli
ef4015aec9 Fix badge size and navigation in expiration bottom sheet. 2021-11-23 17:00:47 -05:00
Greyson Parrelli
ffedc3fa7d Fix error string placeholder. 2021-11-23 17:00:47 -05:00
Greyson Parrelli
20285e7e5b Ensure the user's profile gets uploaded. 2021-11-23 17:00:47 -05:00
Cody Henthorne
89e55a7133 Fix truncated text on View Badges bottom sheet. 2021-11-23 17:00:47 -05:00
Greyson Parrelli
11aa168a6b Improve handling of unregistered failure during sender key send. 2021-11-23 17:00:47 -05:00
Greyson Parrelli
0fc6e642fe Default the donor badge flags to 'on'. 2021-11-23 00:11:35 -05:00
Greyson Parrelli
8e0553c849 Bump version to 5.27.3 2021-11-22 23:53:48 -05:00
Greyson Parrelli
75b4ffc16e Updated language translations. 2021-11-22 23:53:18 -05:00
Greyson Parrelli
643b07d564 Reduce occurrence of the media preview jumping. 2021-11-22 23:39:59 -05:00
Greyson Parrelli
637a44379c Ensure onboarding cards are cleared with enough conversations. 2021-11-22 23:15:05 -05:00
Greyson Parrelli
a2d42b0415 Fix clickable area of avatars. 2021-11-22 22:59:21 -05:00
Greyson Parrelli
a76983ca0a Add logging around changes in badges on a profile. 2021-11-22 22:44:10 -05:00
Cody Henthorne
22e79a045c Fix boosts made in UGX. 2021-11-22 22:44:10 -05:00
Cody Henthorne
061b87ead0 Fix boosts buttons in RTL. 2021-11-22 22:44:10 -05:00
Greyson Parrelli
511abd67c6 Show correct animation in boost fragment. 2021-11-22 22:44:10 -05:00
Greyson Parrelli
1627d92009 Fix display of boost payment processing dialog. 2021-11-22 22:44:10 -05:00
Greyson Parrelli
2cb67f6ee3 Fix logic around storage crash. 2021-11-22 22:44:10 -05:00
Greyson Parrelli
13e0b8dec0 Fix issue with recycling mute icon in conversation list. 2021-11-22 22:44:10 -05:00
Greyson Parrelli
7626070c28 Make the badge a selectable area in the subscriptions screen. 2021-11-22 22:44:10 -05:00
Greyson Parrelli
ca5140d3ec Fix overloaded usages of the word 'boost'. 2021-11-22 22:44:10 -05:00
Greyson Parrelli
3694431503 Fix navigation to badge management screen. 2021-11-22 22:44:10 -05:00
Greyson Parrelli
cd1f0632fa Improve recognition of failed payment states. 2021-11-22 22:44:10 -05:00
Cody Henthorne
1508b1d401 Fix invalid string resource. 2021-11-22 22:44:10 -05:00
Cody Henthorne
bf874e17e5 Fix various bottom sheet scroll but off bugs. 2021-11-22 22:44:10 -05:00
Cody Henthorne
d2b8a17723 Fix load jump/jank when opening subscription. 2021-11-22 22:44:10 -05:00
Greyson Parrelli
67cfdf101d Better logging around redemption failures. 2021-11-22 22:44:10 -05:00
Greyson Parrelli
125840e5fc Fix rendering of subscription error string. 2021-11-22 22:44:10 -05:00
Cody Henthorne
f5ab4bec7a Make Become a Sustainer scrollable for longer translations. 2021-11-22 22:44:10 -05:00
Greyson Parrelli
ef7d5d55cb Protect against individual item updates being put into an invalidated list. 2021-11-22 10:40:06 -05:00
Greyson Parrelli
1a9d785cbb Fix typo in database call. 2021-11-21 22:02:48 -05:00
Cody Henthorne
cad0bab435 Bump version to 5.27.2 2021-11-19 16:41:47 -05:00
Cody Henthorne
bdc3435fc1 Updated language translations. 2021-11-19 16:32:37 -05:00
Alex Hart
f260633c9d Update payment failure ux. 2021-11-19 16:28:39 -05:00
Alex Hart
8a00caabd7 Update how we deal with failed or in progress subscriptions. 2021-11-19 16:28:39 -05:00
Alex Hart
b4fe5bdcc6 Add new night boost icon. 2021-11-19 16:28:39 -05:00
Alex Hart
1f649057d6 A lot more logging for donor badges. 2021-11-19 16:28:39 -05:00
Jim Gustafson
41059a2b67 Update to RingRTC v2.14.3 2021-11-19 16:28:39 -05:00
Alex Hart
3d65a957f4 Treat google payment request token error as setup failure in boost. 2021-11-19 16:28:39 -05:00
Greyson Parrelli
ff038e3ade Fix some issues with restoring old backups.
There's a bug where if you restore a database with a different column
definition order than a new install, then column indexes in cursors
could be wrong. Closing and re-opening the database fixes this.

I also removed a reference to a possibly-closed database we were holding
onto in LiveRecipient.
2021-11-19 16:28:39 -05:00
Alex Hart
44fa42fca4 Do not select sub row as active if the sub itself is inactive. 2021-11-19 16:28:39 -05:00
Alex Hart
73d8c74718 Expand donation job logging. 2021-11-19 16:28:39 -05:00
Alex Hart
db4a0deccc Slide badge on swipe to reply. 2021-11-19 16:28:39 -05:00
Alex Hart
8b23a409ef Fix custom amount parsing for languages that utilize , separator. 2021-11-19 16:28:39 -05:00
Alex Hart
ec7e73bb7c Do not use secondary colors for titles of unset values in manage profile fragment. 2021-11-19 16:28:39 -05:00
Alex Hart
321b85d5d0 Fix badge redemption failure copy. 2021-11-19 16:28:39 -05:00
Alex Hart
98c9638bc4 Reintroduce native currency symbols. 2021-11-19 09:16:20 -04:00
Alex Hart
de1c9f2581 Fix custom amount filter regex. 2021-11-19 09:11:12 -04:00
Alex Hart
1af6af5045 Increase max line count in badge viewer page to 4. 2021-11-19 08:55:21 -04:00
Alex Hart
0121811195 Add padding to the bottom of the Thank You bottom sheet. 2021-11-19 08:48:29 -04:00
Alex Hart
18cf55b156 Fix error when trying to create payment in languages which use , instead of . 2021-11-19 08:45:10 -04:00
Alex Hart
0d4e109c72 Implement several badge job tweaks to align with iOS. 2021-11-19 08:33:04 -04:00
Alex Hart
3e358da83a Do not show become a sustainer if user is already a sustainer. 2021-11-18 17:56:56 -04:00
Greyson Parrelli
85453ca442 Fix retrieval of PNI. 2021-11-18 14:38:13 -05:00
Cody Henthorne
a5e5a73580 Bump version to 5.27.1 2021-11-18 13:29:53 -05:00
Cody Henthorne
95f7b8d79f Updated language translations. 2021-11-18 13:24:14 -05:00
Greyson Parrelli
42d0d84ae0 Handle the case where a number changes during a recipient merge. 2021-11-18 13:19:32 -05:00
Alex Hart
686219d473 Remove old donate megaphone and replace with sustainer megaphone. 2021-11-18 14:02:55 -04:00
Greyson Parrelli
843ed24bbb Introduce SignalDatabase as the main database entrypoint. 2021-11-18 12:36:52 -05:00
Alex Hart
e17c49505c Implement several donor badge fixes and rotate flags.
* Add white Google Pay buttons for use in dark mode.
* Always display badges for self.
* Disallow toggling / feature selection if no network is present.
* Only display bottom sheet overscroll if content scrolls.
* Flatten settings xml for better animations.
* Add a bit of space to the bottom of subscribe fragment.
* Treat GooglePay errors as setup failures.
* Add quieter log for 404.
* Ensure we check case before initial currency code comparison.
* Fix timeout dialog copy.
* Fix double settings activity on top issue.
* Rotate FF.
2021-11-18 13:25:37 -04:00
Alex Hart
473747ee03 Fix missing space between also and become. 2021-11-18 11:42:08 -04:00
Alex Hart
9ea97aabbb Fix badge row count calculation. 2021-11-18 11:35:47 -04:00
Greyson Parrelli
811d79c873 Prefer 'nightly' tags when reading current git tag. 2021-11-17 21:17:36 -05:00
588 changed files with 9660 additions and 7059 deletions

View File

@@ -67,8 +67,8 @@ protobuf {
}
}
def canonicalVersionCode = 959
def canonicalVersionName = "5.27.0"
def canonicalVersionCode = 966
def canonicalVersionName = "5.27.7"
def postFixSize = 100
def abiPostFix = ['universal' : 0,
@@ -83,7 +83,6 @@ def selectableVariants = [
'nightlyProdFlipper',
'nightlyProdPerf',
'nightlyProdRelease',
'nightlyStagingPerf',
'playProdDebug',
'playProdFlipper',
'playProdPerf',
@@ -599,7 +598,8 @@ def getCurrentGitTag() {
def output = stdout.toString().trim()
if (output != null && output.size() > 0) {
return output.split('\n')[0];
def tags = output.split('\n').toList()
return tags.stream().filter(t -> t.contains('nightly')).findFirst().orElse(tags.get(0))
} else {
return null
}

View File

@@ -1,8 +1,6 @@
package org.thoughtcrime.securesms.database
import android.app.Application
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotEquals
@@ -24,7 +22,7 @@ class RecipientDatabaseTest {
@Before
fun setup() {
recipientDatabase = DatabaseFactory.getRecipientDatabase(context)
recipientDatabase = SignalDatabase.recipients
ensureDbEmpty()
}
@@ -304,6 +302,28 @@ class RecipientDatabaseTest {
assertEquals(E164_A, existingRecipient2.requireE164())
}
/**
* Another high trust case that results in a merge. Nothing strictly new here, but this case is called out because its a merge but *also* an E164 change,
* which clients may need to know for UX purposes.
*/
@Test
fun getAndPossiblyMerge_bothAciAndE164MapToExistingUser_aciAndE164_mergeAndPhoneNumberChange_highTrust() {
val existingId1: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_B, true)
val existingId2: RecipientId = recipientDatabase.getAndPossiblyMerge(null, E164_A, true)
val retrievedId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_A, true)
assertEquals(existingId1, retrievedId)
val retrievedRecipient = Recipient.resolved(retrievedId)
assertEquals(ACI_A, retrievedRecipient.requireAci())
assertEquals(E164_A, retrievedRecipient.requireE164())
assertFalse(recipientDatabase.getByE164(E164_B).isPresent)
val recipientWithId2 = Recipient.resolved(existingId2)
assertEquals(retrievedId, recipientWithId2.id)
}
// ==============================================================
// Misc
// ==============================================================
@@ -347,11 +367,8 @@ class RecipientDatabaseTest {
recipientDatabase.getAndPossiblyMerge(null, null, true)
}
private val context: Application
get() = InstrumentationRegistry.getInstrumentation().targetContext.applicationContext as Application
private fun ensureDbEmpty() {
DatabaseFactory.getInstance(context).rawDatabase.rawQuery("SELECT COUNT(*) FROM ${RecipientDatabase.TABLE_NAME}", null).use { cursor ->
SignalDatabase.rawDatabase.rawQuery("SELECT COUNT(*) FROM ${RecipientDatabase.TABLE_NAME}", null).use { cursor ->
assertTrue(cursor.moveToFirst())
assertEquals(0, cursor.getLong(0))
}

View File

@@ -3,7 +3,6 @@ package org.thoughtcrime.securesms.database
import android.app.Application
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import net.zetetic.database.sqlcipher.SQLiteDatabase
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotEquals
import org.junit.Assert.assertNotNull
@@ -49,16 +48,16 @@ class RecipientDatabaseTest_merges {
@Before
fun setup() {
recipientDatabase = DatabaseFactory.getRecipientDatabase(context)
identityDatabase = DatabaseFactory.getIdentityDatabase(context)
groupReceiptDatabase = DatabaseFactory.getGroupReceiptDatabase(context)
groupDatabase = DatabaseFactory.getGroupDatabase(context)
threadDatabase = DatabaseFactory.getThreadDatabase(context)
smsDatabase = DatabaseFactory.getSmsDatabase(context)
mmsDatabase = DatabaseFactory.getMmsDatabase(context)
sessionDatabase = DatabaseFactory.getSessionDatabase(context)
mentionDatabase = DatabaseFactory.getMentionDatabase(context)
reactionDatabase = DatabaseFactory.getReactionDatabase(context)
recipientDatabase = SignalDatabase.recipients
identityDatabase = SignalDatabase.identities
groupReceiptDatabase = SignalDatabase.groupReceipts
groupDatabase = SignalDatabase.groups
threadDatabase = SignalDatabase.threads
smsDatabase = SignalDatabase.sms
mmsDatabase = SignalDatabase.mms
sessionDatabase = SignalDatabase.sessions
mentionDatabase = SignalDatabase.mentions
reactionDatabase = SignalDatabase.reactions
ensureDbEmpty()
}
@@ -178,7 +177,7 @@ class RecipientDatabaseTest_merges {
get() = InstrumentationRegistry.getInstrumentation().targetContext.applicationContext as Application
private fun ensureDbEmpty() {
DatabaseFactory.getInstance(context).rawDatabase.rawQuery("SELECT COUNT(*) FROM ${RecipientDatabase.TABLE_NAME}", null).use { cursor ->
SignalDatabase.rawDatabase.rawQuery("SELECT COUNT(*) FROM ${RecipientDatabase.TABLE_NAME}", null).use { cursor ->
assertTrue(cursor.moveToFirst())
assertEquals(0, cursor.getLong(0))
}
@@ -212,8 +211,7 @@ class RecipientDatabaseTest_merges {
}
private fun getMention(messageId: Long): MentionModel {
val db: SQLiteDatabase = DatabaseFactory.getInstance(context).rawDatabase
db.rawQuery("SELECT * FROM ${MentionDatabase.TABLE_NAME}").use { cursor ->
SignalDatabase.rawDatabase.rawQuery("SELECT * FROM ${MentionDatabase.TABLE_NAME} WHERE ${MentionDatabase.MESSAGE_ID} = $messageId").use { cursor ->
cursor.moveToFirst()
return MentionModel(
recipientId = RecipientId.from(CursorUtil.requireLong(cursor, MentionDatabase.RECIPIENT_ID)),

View File

@@ -16,7 +16,6 @@ import net.zetetic.database.sqlcipher.SQLiteDatabase;
import net.zetetic.database.sqlcipher.SQLiteStatement;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.util.Hex;
import java.lang.reflect.Field;
@@ -43,14 +42,11 @@ public class FlipperSqlCipherAdapter extends DatabaseDriver<FlipperSqlCipherAdap
@Override
public List<Descriptor> getDatabases() {
try {
Field databaseHelperField = DatabaseFactory.class.getDeclaredField("databaseHelper");
databaseHelperField.setAccessible(true);
SignalDatabase mainOpenHelper = Objects.requireNonNull((SQLCipherOpenHelper) databaseHelperField.get(DatabaseFactory.getInstance(getContext())));
SignalDatabase keyValueOpenHelper = KeyValueDatabase.getInstance((Application) getContext());
SignalDatabase megaphoneOpenHelper = MegaphoneDatabase.getInstance((Application) getContext());
SignalDatabase jobManagerOpenHelper = JobDatabase.getInstance((Application) getContext());
SignalDatabase metricsOpenHelper = LocalMetricsDatabase.getInstance((Application) getContext());
SignalDatabaseOpenHelper mainOpenHelper = Objects.requireNonNull(SignalDatabase.getInstance());
SignalDatabaseOpenHelper keyValueOpenHelper = KeyValueDatabase.getInstance((Application) getContext());
SignalDatabaseOpenHelper megaphoneOpenHelper = MegaphoneDatabase.getInstance((Application) getContext());
SignalDatabaseOpenHelper jobManagerOpenHelper = JobDatabase.getInstance((Application) getContext());
SignalDatabaseOpenHelper metricsOpenHelper = LocalMetricsDatabase.getInstance((Application) getContext());
return Arrays.asList(new Descriptor(mainOpenHelper),
new Descriptor(keyValueOpenHelper),
@@ -253,9 +249,9 @@ public class FlipperSqlCipherAdapter extends DatabaseDriver<FlipperSqlCipherAdap
}
static class Descriptor implements DatabaseDescriptor {
private final SignalDatabase sqlCipherOpenHelper;
private final SignalDatabaseOpenHelper sqlCipherOpenHelper;
Descriptor(@NonNull SignalDatabase sqlCipherOpenHelper) {
Descriptor(@NonNull SignalDatabaseOpenHelper sqlCipherOpenHelper) {
this.sqlCipherOpenHelper = sqlCipherOpenHelper;
}

View File

@@ -37,10 +37,11 @@ import org.signal.core.util.tracing.Tracer;
import org.signal.glide.SignalGlideCodecs;
import org.signal.ringrtc.CallManager;
import org.thoughtcrime.securesms.avatar.AvatarPickerStorage;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider;
import org.thoughtcrime.securesms.crypto.DatabaseSecretProvider;
import org.thoughtcrime.securesms.database.LogDatabase;
import org.thoughtcrime.securesms.database.SqlCipherLibraryLoader;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencyProvider;
import org.thoughtcrime.securesms.emoji.EmojiSource;
@@ -51,6 +52,7 @@ import org.thoughtcrime.securesms.jobs.EmojiSearchIndexDownloadJob;
import org.thoughtcrime.securesms.jobs.FcmRefreshJob;
import org.thoughtcrime.securesms.jobs.GroupV1MigrationJob;
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
import org.thoughtcrime.securesms.jobs.ProfileUploadJob;
import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob;
import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob;
import org.thoughtcrime.securesms.jobs.RetrieveProfileJob;
@@ -63,6 +65,7 @@ import org.thoughtcrime.securesms.migrations.ApplicationMigrations;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.providers.BlobProvider;
import org.thoughtcrime.securesms.ratelimit.RateLimitUtil;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.registration.RegistrationUtil;
import org.thoughtcrime.securesms.ringrtc.RingRtcLogger;
import org.thoughtcrime.securesms.service.DirectoryRefreshListener;
@@ -76,6 +79,7 @@ import org.thoughtcrime.securesms.util.AppForegroundObserver;
import org.thoughtcrime.securesms.util.AppStartup;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.ProfileUtil;
import org.thoughtcrime.securesms.util.SignalLocalMetrics;
import org.thoughtcrime.securesms.util.SignalUncaughtExceptionHandler;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
@@ -123,7 +127,12 @@ public class ApplicationContext extends MultiDexApplication implements AppForegr
super.onCreate();
AppStartup.getInstance().addBlocking("security-provider", this::initializeSecurityProvider)
.addBlocking("sqlcipher-init", () -> SqlCipherLibraryLoader.load())
.addBlocking("sqlcipher-init", () -> {
SqlCipherLibraryLoader.load();
SignalDatabase.init(this,
DatabaseSecretProvider.getOrCreateDatabaseSecret(this),
AttachmentSecretProvider.getInstance(this).getOrCreateAttachmentSecret());
})
.addBlocking("logging", () -> {
initializeLogging();
Log.i(TAG, "onCreate()");
@@ -171,12 +180,13 @@ public class ApplicationContext extends MultiDexApplication implements AppForegr
.addNonBlocking(() -> ApplicationDependencies.getJobManager().beginJobLoop())
.addNonBlocking(EmojiSource::refresh)
.addNonBlocking(() -> ApplicationDependencies.getGiphyMp4Cache().onAppStart(this))
.addNonBlocking(this::ensureProfileUploaded)
.addPostRender(() -> RateLimitUtil.retryAllRateLimitedMessages(this))
.addPostRender(this::initializeExpiringMessageManager)
.addPostRender(() -> SignalStore.settings().setDefaultSms(Util.isDefaultSmsProvider(this)))
.addPostRender(() -> DownloadLatestEmojiDataJob.scheduleIfNecessary(this))
.addPostRender(EmojiSearchIndexDownloadJob::scheduleIfNecessary)
.addPostRender(() -> DatabaseFactory.getMessageLogDatabase(this).trimOldMessages(System.currentTimeMillis(), FeatureFlags.retryRespondMaxAge()))
.addPostRender(() -> SignalDatabase.messageLog().trimOldMessages(System.currentTimeMillis(), FeatureFlags.retryRespondMaxAge()))
.execute();
Log.d(TAG, "onCreate() took " + (System.currentTimeMillis() - startTime) + " ms");
@@ -284,7 +294,7 @@ public class ApplicationContext extends MultiDexApplication implements AppForegr
private void initializeFirstEverAppLaunch() {
if (TextSecurePreferences.getFirstInstallVersion(this) == -1) {
if (!SQLCipherOpenHelper.databaseFileExists(this) || VersionTracker.getDaysSinceFirstInstalled(this) < 365) {
if (!SignalDatabase.databaseFileExists(this) || VersionTracker.getDaysSinceFirstInstalled(this) < 365) {
Log.i(TAG, "First ever app launch!");
AppInitialization.onFirstEverAppLaunch(this);
}
@@ -359,6 +369,13 @@ public class ApplicationContext extends MultiDexApplication implements AppForegr
}
}
private void ensureProfileUploaded() {
if (SignalStore.account().isRegistered() && !SignalStore.registrationValues().hasUploadedProfile() && !Recipient.self().getProfileName().isEmpty()) {
Log.w(TAG, "User has a profile, but has not uploaded one. Uploading now.");
ApplicationDependencies.getJobManager().add(new ProfileUploadJob());
}
}
private void executePendingContactSync() {
if (TextSecurePreferences.needsFullContactSync(this)) {
ApplicationDependencies.getJobManager().add(new MultiDeviceContactUpdateJob(true));
@@ -389,7 +406,7 @@ public class ApplicationContext extends MultiDexApplication implements AppForegr
@WorkerThread
private void initializeCleanup() {
int deleted = DatabaseFactory.getAttachmentDatabase(this).deleteAbandonedPreuploadedAttachments();
int deleted = SignalDatabase.attachments().deleteAbandonedPreuploadedAttachments();
Log.i(TAG, "Deleted " + deleted + " abandoned attachments.");
}

View File

@@ -11,7 +11,7 @@ import androidx.lifecycle.Lifecycle;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
@@ -65,7 +65,7 @@ public final class BlockUnblockDialog {
Resources resources = context.getResources();
if (recipient.isGroup()) {
if (DatabaseFactory.getGroupDatabase(context).isActive(recipient.requireGroupId())) {
if (SignalDatabase.groups().isActive(recipient.requireGroupId())) {
builder.setTitle(resources.getString(R.string.BlockUnblockDialog_block_and_leave_s, recipient.getDisplayName(context)));
builder.setMessage(R.string.BlockUnblockDialog_you_will_no_longer_receive_messages_or_updates);
builder.setPositiveButton(R.string.BlockUnblockDialog_block_and_leave, ((dialog, which) -> onBlock.run()));
@@ -104,7 +104,7 @@ public final class BlockUnblockDialog {
Resources resources = context.getResources();
if (recipient.isGroup()) {
if (DatabaseFactory.getGroupDatabase(context).isActive(recipient.requireGroupId())) {
if (SignalDatabase.groups().isActive(recipient.requireGroupId())) {
builder.setTitle(resources.getString(R.string.BlockUnblockDialog_unblock_s, recipient.getDisplayName(context)));
builder.setMessage(R.string.BlockUnblockDialog_group_members_will_be_able_to_add_you);
builder.setPositiveButton(R.string.RecipientPreferenceActivity_unblock, ((dialog, which) -> onUnblock.run()));

View File

@@ -24,7 +24,7 @@ import org.thoughtcrime.securesms.components.ContactFilterView;
import org.thoughtcrime.securesms.components.ContactFilterView.OnFilterChangedListener;
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
import org.thoughtcrime.securesms.contacts.SelectedContact;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.groups.SelectionLimits;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
@@ -255,7 +255,7 @@ public class InviteActivity extends PassphraseRequiredActivity implements Contac
MessageSender.send(context, new OutgoingTextMessage(recipient, message, subscriptionId), -1L, true, null, null);
if (recipient.getContactUri() != null) {
DatabaseFactory.getRecipientDatabase(context).setHasSentInvite(recipient.getId());
SignalDatabase.recipients().setHasSentInvite(recipient.getId());
}
}

View File

@@ -600,7 +600,10 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
if (adapter != null) {
MediaItem item = adapter.getMediaItemFor(position);
if (item.recipient != null) item.recipient.live().observe(MediaPreviewActivity.this, r -> initializeActionBar());
if (item != null && item.recipient != null) {
item.recipient.live().observe(MediaPreviewActivity.this, r -> initializeActionBar());
}
viewModel.setActiveAlbumRailItem(MediaPreviewActivity.this, position);
initializeActionBar();
}
@@ -613,7 +616,9 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
if (adapter != null) {
MediaItem item = adapter.getMediaItemFor(position);
if (item.recipient != null) item.recipient.live().removeObservers(MediaPreviewActivity.this);
if (item != null && item.recipient != null) {
item.recipient.live().removeObservers(MediaPreviewActivity.this);
}
adapter.pause(position);
}
@@ -663,7 +668,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
}
@Override
public MediaItem getMediaItemFor(int position) {
public @Nullable MediaItem getMediaItemFor(int position) {
return new MediaItem(null, null, null, uri, mediaType, -1, true);
}
@@ -769,8 +774,15 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
super.destroyItem(container, position, object);
}
public MediaItem getMediaItemFor(int position) {
cursor.moveToPosition(getCursorPosition(position));
public @Nullable MediaItem getMediaItemFor(int position) {
int cursorPosition = getCursorPosition(position);
if (cursor.isClosed() || cursorPosition < 0) {
Log.w(TAG, "Invalid cursor state! Closed: " + cursor.isClosed() + " Position: " + cursorPosition);
return null;
}
cursor.moveToPosition(cursorPosition);
MediaRecord mediaRecord = MediaRecord.from(context, cursor);
DatabaseAttachment attachment = Objects.requireNonNull(mediaRecord.getAttachment());
@@ -838,7 +850,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
}
interface MediaItemAdapter {
MediaItem getMediaItemFor(int position);
@Nullable MediaItem getMediaItemFor(int position);
void pause(int position);
@Nullable View getPlaybackControls(int position);
boolean hasFragmentFor(int position);

View File

@@ -26,13 +26,12 @@ import androidx.appcompat.app.AlertDialog;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.contacts.sync.DirectoryHelper;
import org.thoughtcrime.securesms.conversation.ConversationIntents;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.groups.ui.creategroup.CreateGroupActivity;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
import org.thoughtcrime.securesms.util.views.SimpleProgressDialog;
import org.whispersystems.libsignal.util.guava.Optional;
@@ -104,7 +103,7 @@ public class NewConversationActivity extends ContactSelectionActivity
}
private void launch(Recipient recipient) {
long existingThread = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipient.getId());
long existingThread = SignalDatabase.threads().getThreadIdIfExistsFor(recipient.getId());
Intent intent = ConversationIntents.createBuilder(this, recipient.getId(), existingThread)
.withDraftText(getIntent().getStringExtra(Intent.EXTRA_TEXT))
.withDataUri(getIntent().getData())

View File

@@ -12,7 +12,7 @@ import androidx.annotation.NonNull;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.conversation.ConversationIntents;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.Rfc5724Uri;
@@ -48,7 +48,7 @@ public class SmsSendtoActivity extends Activity {
Toast.makeText(this, R.string.ConversationActivity_specify_recipient, Toast.LENGTH_LONG).show();
} else {
Recipient recipient = Recipient.external(this, destination.getDestination());
long threadId = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipient.getId());
long threadId = SignalDatabase.threads().getThreadIdIfExistsFor(recipient.getId());
nextIntent = ConversationIntents.createBuilder(this, recipient.getId(), threadId)
.withDraftText(destination.getBody())

View File

@@ -22,7 +22,7 @@ import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.databaseprotos.AudioWaveFormData;
import org.thoughtcrime.securesms.media.DecryptableUriMediaInput;
import org.thoughtcrime.securesms.media.MediaInput;
@@ -100,7 +100,7 @@ public final class AudioWaveForm {
if (attachment instanceof DatabaseAttachment) {
try {
AttachmentDatabase attachmentDatabase = DatabaseFactory.getAttachmentDatabase(context);
AttachmentDatabase attachmentDatabase = SignalDatabase.attachments();
DatabaseAttachment dbAttachment = (DatabaseAttachment) attachment;
long startTime = System.currentTimeMillis();

View File

@@ -1,7 +1,6 @@
package org.thoughtcrime.securesms.avatar
import android.os.Bundle
import java.lang.IllegalStateException
/**
* Utility class which encapsulates reading and writing Avatar objects to and from Bundles.

View File

@@ -3,7 +3,7 @@ package org.thoughtcrime.securesms.avatar
import android.content.Context
import android.net.Uri
import android.webkit.MimeTypeMap
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.mediasend.Media
import org.thoughtcrime.securesms.mms.PartAuthority
import org.thoughtcrime.securesms.util.MediaUtil
@@ -33,7 +33,7 @@ object AvatarPickerStorage {
@JvmStatic
fun cleanOrphans(context: Context) {
val avatarFiles = FileStorage.getAllFiles(context, DIRECTORY, FILENAME_BASE)
val database = DatabaseFactory.getAvatarPickerDatabase(context)
val database = SignalDatabase.avatarPicker
val photoAvatars = database
.getAllAvatars()
.filterIsInstance<Avatar.Photo>()

View File

@@ -11,7 +11,7 @@ import org.signal.core.util.concurrent.SignalExecutors
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.avatar.AvatarBundler
import org.thoughtcrime.securesms.avatar.AvatarPickerStorage
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.providers.BlobProvider
import org.thoughtcrime.securesms.scribbles.ImageEditorFragment
@@ -44,7 +44,7 @@ class PhotoEditorFragment : Fragment(R.layout.avatar_photo_editor_fragment), Ima
val inputStream = BlobProvider.getInstance().getStream(applicationContext, editedImageUri)
val onDiskUri = AvatarPickerStorage.save(applicationContext, inputStream)
val photo = AvatarBundler.extractPhoto(args.photoAvatar)
val database = DatabaseFactory.getAvatarPickerDatabase(applicationContext)
val database = SignalDatabase.avatarPicker
val newPhoto = photo.copy(uri = onDiskUri, size = size)
database.update(newPhoto)

View File

@@ -14,7 +14,7 @@ import org.thoughtcrime.securesms.avatar.AvatarPickerStorage
import org.thoughtcrime.securesms.avatar.AvatarRenderer
import org.thoughtcrime.securesms.avatar.Avatars
import org.thoughtcrime.securesms.conversation.colors.AvatarColor
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.groups.GroupId
import org.thoughtcrime.securesms.mediasend.Media
import org.thoughtcrime.securesms.profiles.AvatarHelper
@@ -70,11 +70,11 @@ class AvatarPickerRepository(context: Context) {
}
fun getPersistedAvatarsForSelf(): Single<List<Avatar>> = Single.fromCallable {
DatabaseFactory.getAvatarPickerDatabase(applicationContext).getAvatarsForSelf()
SignalDatabase.avatarPicker.getAvatarsForSelf()
}
fun getPersistedAvatarsForGroup(groupId: GroupId): Single<List<Avatar>> = Single.fromCallable {
DatabaseFactory.getAvatarPickerDatabase(applicationContext).getAvatarsForGroup(groupId)
SignalDatabase.avatarPicker.getAvatarsForGroup(groupId)
}
fun getDefaultAvatarsForSelf(): Single<List<Avatar>> = Single.fromCallable {
@@ -97,7 +97,7 @@ class AvatarPickerRepository(context: Context) {
fun persistAvatarForSelf(avatar: Avatar, onPersisted: (Avatar) -> Unit) {
SignalExecutors.BOUNDED.execute {
val avatarDatabase = DatabaseFactory.getAvatarPickerDatabase(applicationContext)
val avatarDatabase = SignalDatabase.avatarPicker
val savedAvatar = avatarDatabase.saveAvatarForSelf(avatar)
avatarDatabase.markUsage(savedAvatar)
onPersisted(savedAvatar)
@@ -106,7 +106,7 @@ class AvatarPickerRepository(context: Context) {
fun persistAvatarForGroup(avatar: Avatar, groupId: GroupId, onPersisted: (Avatar) -> Unit) {
SignalExecutors.BOUNDED.execute {
val avatarDatabase = DatabaseFactory.getAvatarPickerDatabase(applicationContext)
val avatarDatabase = SignalDatabase.avatarPicker
val savedAvatar = avatarDatabase.saveAvatarForGroup(avatar, groupId)
avatarDatabase.markUsage(savedAvatar)
onPersisted(savedAvatar)
@@ -180,7 +180,7 @@ class AvatarPickerRepository(context: Context) {
fun delete(avatar: Avatar, onDelete: () -> Unit) {
SignalExecutors.BOUNDED.execute {
if (avatar.databaseId is Avatar.DatabaseId.Saved) {
val avatarDatabase = DatabaseFactory.getAvatarPickerDatabase(applicationContext)
val avatarDatabase = SignalDatabase.avatarPicker
avatarDatabase.deleteAvatar(avatar)
}
onDelete()

View File

@@ -43,7 +43,6 @@ import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.keyvalue.KeyValueDataSet;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.profiles.AvatarHelper;
import org.thoughtcrime.securesms.service.PendingRetryReceiptManager;
import org.thoughtcrime.securesms.util.SetUtil;
import org.thoughtcrime.securesms.util.Stopwatch;
import org.thoughtcrime.securesms.util.TextSecurePreferences;

View File

@@ -4,8 +4,8 @@ import android.content.Context
import io.reactivex.rxjava3.core.Completable
import io.reactivex.rxjava3.schedulers.Schedulers
import org.thoughtcrime.securesms.badges.models.Badge
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.RecipientDatabase
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.storage.StorageSyncHelper
@@ -19,16 +19,14 @@ class BadgeRepository(context: Context) {
displayBadgesOnProfile: Boolean,
selfBadges: List<Badge> = Recipient.self().badges
): Completable = Completable.fromAction {
val recipientDatabase: RecipientDatabase = DatabaseFactory.getRecipientDatabase(context)
val recipientDatabase: RecipientDatabase = SignalDatabase.recipients
val badges = selfBadges.map { it.copy(visible = displayBadgesOnProfile) }
ProfileUtil.uploadProfileWithBadges(context, badges)
SignalStore.donationsValues().setDisplayBadgesOnProfile(displayBadgesOnProfile)
recipientDatabase.markNeedsSync(Recipient.self().id)
StorageSyncHelper.scheduleSyncForDataChange()
val badges = selfBadges.map { it.copy(visible = displayBadgesOnProfile) }
ProfileUtil.uploadProfileWithBadges(context, badges)
recipientDatabase.setBadges(Recipient.self().id, badges)
}.subscribeOn(Schedulers.io())
@@ -37,7 +35,7 @@ class BadgeRepository(context: Context) {
val reOrderedBadges = listOf(featuredBadge.copy(visible = true)) + (badges.filterNot { it.id == featuredBadge.id })
ProfileUtil.uploadProfileWithBadges(context, reOrderedBadges)
val recipientDatabase: RecipientDatabase = DatabaseFactory.getRecipientDatabase(context)
val recipientDatabase: RecipientDatabase = SignalDatabase.recipients
recipientDatabase.setBadges(Recipient.self().id, reOrderedBadges)
}.subscribeOn(Schedulers.io())
}

View File

@@ -9,7 +9,6 @@ import com.google.android.flexbox.FlexboxLayoutManager
import com.google.android.flexbox.JustifyContent
import org.signal.core.util.DimensionUnit
import org.thoughtcrime.securesms.BuildConfig
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.badges.models.Badge
import org.thoughtcrime.securesms.badges.models.Badge.Category.Companion.fromCode
import org.thoughtcrime.securesms.components.settings.DSLConfiguration
@@ -37,13 +36,9 @@ object Badges {
}
.forEach { customPref(it) }
val gutter = context.resources.getDimensionPixelSize(R.dimen.dsl_settings_gutter)
val buffer = DimensionUnit.DP.toPixels(12f)
val gutterExtra = gutter - buffer
val badgeSize = DimensionUnit.DP.toPixels(88f)
val windowWidth = context.resources.displayMetrics.widthPixels
val availableWidth = windowWidth - gutterExtra
val perRow = (availableWidth / badgeSize).toInt()
val perRow = (windowWidth / badgeSize).toInt()
val empties = ((perRow - (badges.size % perRow)) % perRow)
repeat(empties) {
@@ -74,7 +69,7 @@ object Badges {
"hdpi" -> Pair(getBadgeImageUri(serviceBadge.sprites6[2]), "hdpi")
"xxhdpi" -> Pair(getBadgeImageUri(serviceBadge.sprites6[4]), "xxhdpi")
"xxxhdpi" -> Pair(getBadgeImageUri(serviceBadge.sprites6[5]), "xxxhdpi")
else -> Pair(getBadgeImageUri(serviceBadge.sprites6[3]), "xdpi")
else -> Pair(getBadgeImageUri(serviceBadge.sprites6[3]), "xhdpi")
}
}

View File

@@ -12,13 +12,13 @@ data class LargeBadge(
val badge: Badge
) {
class Model(val largeBadge: LargeBadge, val shortName: String) : MappingModel<Model> {
class Model(val largeBadge: LargeBadge, val shortName: String, val maxLines: Int) : MappingModel<Model> {
override fun areItemsTheSame(newItem: Model): Boolean {
return newItem.largeBadge.badge.id == largeBadge.badge.id
}
override fun areContentsTheSame(newItem: Model): Boolean {
return newItem.largeBadge == largeBadge && newItem.shortName == shortName
return newItem.largeBadge == largeBadge && newItem.shortName == shortName && newItem.maxLines == maxLines
}
}
@@ -43,6 +43,9 @@ data class LargeBadge(
name.text = model.largeBadge.badge.name
description.text = model.largeBadge.badge.resolveDescription(model.shortName)
description.setLines(model.maxLines)
description.maxLines = model.maxLines
description.minLines = model.maxLines
}
}

View File

@@ -1,7 +1,6 @@
package org.thoughtcrime.securesms.badges.self.expired
import androidx.fragment.app.FragmentManager
import androidx.navigation.fragment.findNavController
import org.signal.core.util.DimensionUnit
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.badges.models.Badge
@@ -10,7 +9,9 @@ import org.thoughtcrime.securesms.components.settings.DSLConfiguration
import org.thoughtcrime.securesms.components.settings.DSLSettingsAdapter
import org.thoughtcrime.securesms.components.settings.DSLSettingsBottomSheetFragment
import org.thoughtcrime.securesms.components.settings.DSLSettingsText
import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity
import org.thoughtcrime.securesms.components.settings.configure
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.util.BottomSheetUtil
/**
@@ -26,7 +27,8 @@ class ExpiredBadgeBottomSheetDialogFragment : DSLSettingsBottomSheetFragment(
}
private fun getConfiguration(): DSLConfiguration {
val badge = ExpiredBadgeBottomSheetDialogFragmentArgs.fromBundle(requireArguments()).badge
val badge: Badge = ExpiredBadgeBottomSheetDialogFragmentArgs.fromBundle(requireArguments()).badge
val isLikelyASustainer = SignalStore.donationsValues().isLikelyASustainer()
return configure {
customPref(ExpiredBadge.Model(badge))
@@ -60,7 +62,11 @@ class ExpiredBadgeBottomSheetDialogFragment : DSLSettingsBottomSheetFragment(
noPadTextPref(
DSLSettingsText.from(
if (badge.isBoost()) {
R.string.ExpiredBadgeBottomSheetDialogFragment__to_continue_supporting_technology
if (isLikelyASustainer) {
R.string.ExpiredBadgeBottomSheetDialogFragment__you_can_reactivate
} else {
R.string.ExpiredBadgeBottomSheetDialogFragment__to_continue_supporting_technology
}
} else {
R.string.ExpiredBadgeBottomSheetDialogFragment__you_can
},
@@ -73,14 +79,22 @@ class ExpiredBadgeBottomSheetDialogFragment : DSLSettingsBottomSheetFragment(
primaryButton(
text = DSLSettingsText.from(
if (badge.isBoost()) {
R.string.ExpiredBadgeBottomSheetDialogFragment__become_a_sustainer
if (isLikelyASustainer) {
R.string.ExpiredBadgeBottomSheetDialogFragment__add_a_boost
} else {
R.string.ExpiredBadgeBottomSheetDialogFragment__become_a_sustainer
}
} else {
R.string.ExpiredBadgeBottomSheetDialogFragment__renew_subscription
}
),
onClick = {
dismiss()
findNavController().navigate(R.id.action_directly_to_subscribe)
if (isLikelyASustainer) {
requireActivity().startActivity(AppSettingsActivity.boost(requireContext()))
} else {
requireActivity().startActivity(AppSettingsActivity.subscriptions(requireContext()))
}
}
)

View File

@@ -1,5 +1,6 @@
package org.thoughtcrime.securesms.badges.self.none
import android.content.Intent
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.viewModels
import org.signal.core.util.DimensionUnit
@@ -60,7 +61,7 @@ class BecomeASustainerFragment : DSLSettingsBottomSheetFragment() {
),
onClick = {
requireActivity().finish()
requireActivity().startActivity(AppSettingsActivity.subscriptions(requireContext()))
requireActivity().startActivity(AppSettingsActivity.subscriptions(requireContext()).setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP))
}
)

View File

@@ -71,7 +71,7 @@ class BadgesOverviewFragment : DSLSettingsFragment(
asyncSwitchPref(
title = DSLSettingsText.from(R.string.BadgesOverviewFragment__display_badges_on_profile),
isChecked = state.displayBadgesOnProfile,
isEnabled = state.stage == BadgesOverviewState.Stage.READY && state.hasUnexpiredBadges,
isEnabled = state.stage == BadgesOverviewState.Stage.READY && state.hasUnexpiredBadges && state.hasInternet,
isProcessing = state.stage == BadgesOverviewState.Stage.UPDATING_BADGE_DISPLAY_STATE,
onClick = {
viewModel.setDisplayBadgesOnProfile(!state.displayBadgesOnProfile)
@@ -81,7 +81,7 @@ class BadgesOverviewFragment : DSLSettingsFragment(
clickPref(
title = DSLSettingsText.from(R.string.BadgesOverviewFragment__featured_badge),
summary = state.featuredBadge?.name?.let { DSLSettingsText.from(it) },
isEnabled = state.stage == BadgesOverviewState.Stage.READY && state.hasUnexpiredBadges,
isEnabled = state.stage == BadgesOverviewState.Stage.READY && state.hasUnexpiredBadges && state.hasInternet,
onClick = {
findNavController().navigate(BadgesOverviewFragmentDirections.actionBadgeManageFragmentToFeaturedBadgeFragment())
}

View File

@@ -7,7 +7,8 @@ data class BadgesOverviewState(
val allUnlockedBadges: List<Badge> = listOf(),
val featuredBadge: Badge? = null,
val displayBadgesOnProfile: Boolean = false,
val fadedBadgeId: String? = null
val fadedBadgeId: String? = null,
val hasInternet: Boolean = false
) {
val hasUnexpiredBadges = allUnlockedBadges.any { it.expirationTimestamp > System.currentTimeMillis() }

View File

@@ -15,6 +15,7 @@ import org.thoughtcrime.securesms.badges.BadgeRepository
import org.thoughtcrime.securesms.components.settings.app.subscription.SubscriptionsRepository
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.util.InternetConnectionObserver
import org.thoughtcrime.securesms.util.livedata.Store
import org.whispersystems.libsignal.util.guava.Optional
@@ -42,6 +43,12 @@ class BadgesOverviewViewModel(
)
}
disposables += InternetConnectionObserver.observe()
.distinctUntilChanged()
.subscribeBy { isConnected ->
store.update { it.copy(hasInternet = isConnected) }
}
disposables += Single.zip(
subscriptionsRepository.getActiveSubscription(),
subscriptionsRepository.getSubscriptions()

View File

@@ -1,5 +1,7 @@
package org.thoughtcrime.securesms.badges.view
import android.graphics.Paint
import android.graphics.Rect
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@@ -24,7 +26,10 @@ import org.thoughtcrime.securesms.util.CommunicationActions
import org.thoughtcrime.securesms.util.FeatureFlags
import org.thoughtcrime.securesms.util.MappingAdapter
import org.thoughtcrime.securesms.util.PlayServicesUtil
import org.thoughtcrime.securesms.util.ViewUtil
import org.thoughtcrime.securesms.util.visible
import kotlin.math.ceil
import kotlin.math.max
class ViewBadgeBottomSheetDialogFragment : FixedRoundedCornerBottomSheetDialogFragment() {
@@ -32,6 +37,13 @@ class ViewBadgeBottomSheetDialogFragment : FixedRoundedCornerBottomSheetDialogFr
override val peekHeightPercentage: Float = 1f
private val textWidth: Float
get() = (resources.displayMetrics.widthPixels - ViewUtil.dpToPx(64)).toFloat()
private val textBounds: Rect = Rect()
private val textPaint: Paint = Paint().apply {
textSize = ViewUtil.spToPx(16f).toFloat()
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.view_badge_bottom_sheet_dialog_fragment, container, false)
}
@@ -56,7 +68,10 @@ class ViewBadgeBottomSheetDialogFragment : FixedRoundedCornerBottomSheetDialogFr
action.setOnClickListener {
CommunicationActions.openBrowserLink(requireContext(), getString(R.string.donate_url))
}
} else if (FeatureFlags.donorBadges()) {
} else if (
FeatureFlags.donorBadges() &&
Recipient.self().badges.none { it.category == Badge.Category.Donor && !it.isBoost() && !it.isExpired() }
) {
action.setOnClickListener {
startActivity(AppSettingsActivity.subscriptions(requireContext()))
}
@@ -92,9 +107,17 @@ class ViewBadgeBottomSheetDialogFragment : FixedRoundedCornerBottomSheetDialogFr
tabs.visible = state.allBadgesVisibleOnProfile.size > 1
var maxLines = 3
state.allBadgesVisibleOnProfile.forEach { badge ->
val text = badge.resolveDescription(state.recipient.getShortDisplayName(requireContext()))
textPaint.getTextBounds(text, 0, text.length, textBounds)
val estimatedLines = ceil(textBounds.width().toFloat() / textWidth).toInt()
maxLines = max(maxLines, estimatedLines)
}
adapter.submitList(
state.allBadgesVisibleOnProfile.map {
LargeBadge.Model(LargeBadge(it), state.recipient.getShortDisplayName(requireContext()))
LargeBadge.Model(LargeBadge(it), state.recipient.getShortDisplayName(requireContext()), maxLines + 1)
}
) {
val stateSelectedIndex = state.allBadgesVisibleOnProfile.indexOf(state.selectedBadge)
@@ -120,7 +143,7 @@ class ViewBadgeBottomSheetDialogFragment : FixedRoundedCornerBottomSheetDialogFr
recipientId: RecipientId,
startBadge: Badge? = null
) {
if (!FeatureFlags.displayDonorBadges()) {
if (!FeatureFlags.displayDonorBadges() && recipientId != Recipient.self().id) {
return
}

View File

@@ -7,8 +7,8 @@ import androidx.core.util.Consumer;
import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.groups.GroupChangeBusyException;
import org.thoughtcrime.securesms.groups.GroupChangeFailedException;
import org.thoughtcrime.securesms.recipients.Recipient;
@@ -32,7 +32,7 @@ class BlockedUsersRepository {
void getBlocked(@NonNull Consumer<List<Recipient>> blockedUsers) {
SignalExecutors.BOUNDED.execute(() -> {
RecipientDatabase db = DatabaseFactory.getRecipientDatabase(context);
RecipientDatabase db = SignalDatabase.recipients();
try (RecipientDatabase.RecipientReader reader = db.readerForBlocked(db.getBlocked())) {
int count = reader.getCount();
if (count == 0) {

View File

@@ -28,7 +28,7 @@ import com.airbnb.lottie.model.KeyPath;
import org.signal.core.util.concurrent.SignalExecutors;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.animation.AnimationCompleteListener;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
@@ -360,8 +360,11 @@ public class ConversationItemFooter extends ConstraintLayout {
long id = messageRecord.getId();
boolean mms = messageRecord.isMms();
if (mms) DatabaseFactory.getMmsDatabase(getContext()).markExpireStarted(id);
else DatabaseFactory.getSmsDatabase(getContext()).markExpireStarted(id);
if (mms) {
SignalDatabase.mms().markExpireStarted(id);
} else {
SignalDatabase.sms().markExpireStarted(id);
}
expirationManager.scheduleDeletion(id, mms, messageRecord.getExpiresIn());
});

View File

@@ -5,14 +5,10 @@ import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.style.CharacterStyle;
import android.text.style.MetricAffectingSpan;
import android.text.style.StyleSpan;
import android.text.style.TypefaceSpan;
import android.util.AttributeSet;
import androidx.annotation.Nullable;
@@ -20,7 +16,6 @@ import androidx.core.content.ContextCompat;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.emoji.EmojiTextView;
import org.thoughtcrime.securesms.components.emoji.SimpleEmojiTextView;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.SpanUtil;
@@ -32,9 +27,6 @@ public class FromTextView extends SimpleEmojiTextView {
private static final String TAG = Log.tag(FromTextView.class);
private final static Typeface BOLD_TYPEFACE = Typeface.create("sans-serif-medium", Typeface.NORMAL);
private final static Typeface LIGHT_TYPEFACE = Typeface.create("sans-serif", Typeface.NORMAL);
public FromTextView(Context context) {
super(context);
}
@@ -52,8 +44,10 @@ public class FromTextView extends SimpleEmojiTextView {
}
public void setText(Recipient recipient, boolean read, @Nullable String suffix) {
String fromString = recipient.getDisplayName(getContext());
setText(recipient, recipient.getDisplayName(getContext()), read, suffix);
}
public void setText(Recipient recipient, @Nullable CharSequence fromString, boolean read, @Nullable String suffix) {
SpannableStringBuilder builder = new SpannableStringBuilder();
SpannableString fromSpan = new SpannableString(fromString);
fromSpan.setSpan(getFontSpan(!read), 0, fromSpan.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);

View File

@@ -10,8 +10,6 @@ import androidx.appcompat.app.AlertDialog;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.crypto.ReentrantSessionLock;
import org.thoughtcrime.securesms.crypto.storage.TextSecureIdentityKeyStore;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.IdentityDatabase;
import org.thoughtcrime.securesms.database.model.IdentityRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;

View File

@@ -9,7 +9,6 @@ import androidx.appcompat.app.AlertDialog;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.crypto.ReentrantSessionLock;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.IdentityDatabase;
import org.thoughtcrime.securesms.database.model.IdentityRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;

View File

@@ -34,6 +34,7 @@ abstract class DSLSettingsBottomSheetFragment(
recyclerView.layoutManager = layoutManagerProducer(requireContext())
recyclerView.adapter = adapter
recyclerView.overScrollMode = RecyclerView.OVER_SCROLL_IF_CONTENT_SCROLLS
bindAdapter(adapter)
}

View File

@@ -47,8 +47,9 @@ class AppSettingsActivity : DSLSettingsActivity(), DonationPaymentComponent {
StartLocation.PROXY -> AppSettingsFragmentDirections.actionDirectToEditProxyFragment()
StartLocation.NOTIFICATIONS -> AppSettingsFragmentDirections.actionDirectToNotificationsSettingsFragment()
StartLocation.CHANGE_NUMBER -> AppSettingsFragmentDirections.actionDirectToChangeNumberFragment()
StartLocation.SUBSCRIPTIONS -> AppSettingsFragmentDirections.actionDirectToSubscriptions().setSkipToSubscribe(true)
StartLocation.MANAGE_SUBSCRIPTIONS -> AppSettingsFragmentDirections.actionDirectToSubscriptions()
StartLocation.SUBSCRIPTIONS -> AppSettingsFragmentDirections.actionDirectToSubscriptions()
StartLocation.BOOST -> AppSettingsFragmentDirections.actionAppSettingsFragmentToBoostsFragment()
StartLocation.MANAGE_SUBSCRIPTIONS -> AppSettingsFragmentDirections.actionDirectToManageDonations()
}
}
@@ -75,6 +76,12 @@ class AppSettingsActivity : DSLSettingsActivity(), DonationPaymentComponent {
}
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
finish()
startActivity(intent)
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putBoolean(STATE_WAS_CONFIGURATION_UPDATED, wasConfigurationUpdated)
@@ -118,6 +125,9 @@ class AppSettingsActivity : DSLSettingsActivity(), DonationPaymentComponent {
@JvmStatic
fun subscriptions(context: Context): Intent = getIntentForStartLocation(context, StartLocation.SUBSCRIPTIONS)
@JvmStatic
fun boost(context: Context): Intent = getIntentForStartLocation(context, StartLocation.BOOST)
@JvmStatic
fun manageSubscriptions(context: Context): Intent = getIntentForStartLocation(context, StartLocation.MANAGE_SUBSCRIPTIONS)
@@ -136,7 +146,8 @@ class AppSettingsActivity : DSLSettingsActivity(), DonationPaymentComponent {
NOTIFICATIONS(4),
CHANGE_NUMBER(5),
SUBSCRIPTIONS(6),
MANAGE_SUBSCRIPTIONS(7);
BOOST(7),
MANAGE_SUBSCRIPTIONS(8);
companion object {
fun fromCode(code: Int?): StartLocation {

View File

@@ -157,11 +157,11 @@ class AppSettingsFragment : DSLSettingsFragment(R.string.text_secure_normal__men
icon = DSLSettingsIcon.from(R.drawable.ic_heart_24),
isActive = state.hasActiveSubscription,
onClick = { isActive ->
findNavController()
.navigate(
AppSettingsFragmentDirections.actionAppSettingsFragmentToSubscriptions()
.setSkipToSubscribe(!isActive)
)
if (isActive) {
findNavController().navigate(AppSettingsFragmentDirections.actionAppSettingsFragmentToManageDonationsFragment())
} else {
findNavController().navigate(AppSettingsFragmentDirections.actionAppSettingsFragmentToSubscribeFragment())
}
}
)
)
@@ -169,7 +169,7 @@ class AppSettingsFragment : DSLSettingsFragment(R.string.text_secure_normal__men
title = DSLSettingsText.from(R.string.preferences__signal_boost),
icon = DSLSettingsIcon.from(R.drawable.ic_boost_24),
onClick = {
findNavController().navigate(R.id.action_appSettingsFragment_to_boostsFragment)
findNavController().navigate(AppSettingsFragmentDirections.actionAppSettingsFragmentToBoostsFragment())
}
)
} else {

View File

@@ -11,6 +11,8 @@ import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.util.FeatureFlags
import org.thoughtcrime.securesms.util.livedata.Store
import org.whispersystems.signalservice.api.push.exceptions.NotFoundException
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException
import java.util.concurrent.TimeUnit
class AppSettingsViewModel(private val subscriptionsRepository: SubscriptionsRepository) : ViewModel() {
@@ -37,8 +39,12 @@ class AppSettingsViewModel(private val subscriptionsRepository: SubscriptionsRep
}
subscriptionsRepository.getActiveSubscription().subscribeBy(
onSuccess = { subscription -> store.update { it.copy(hasActiveSubscription = subscription.isActive) } },
onSuccess = { subscription -> store.update { it.copy(hasActiveSubscription = subscription.activeSubscription != null) } },
onError = { throwable ->
if (throwable.isNotFoundException()) {
Log.w(TAG, "Could not load active subscription due to unset SubscriberId (404).")
}
Log.w(TAG, "Could not load active subscription", throwable)
}
)
@@ -53,4 +59,8 @@ class AppSettingsViewModel(private val subscriptionsRepository: SubscriptionsRep
companion object {
private val TAG = Log.tag(AppSettingsViewModel::class.java)
}
private fun Throwable.isNotFoundException(): Boolean {
return this is PushNetworkException && this.cause is NotFoundException || this is NotFoundException
}
}

View File

@@ -5,7 +5,7 @@ import androidx.annotation.WorkerThread
import io.reactivex.rxjava3.core.Single
import io.reactivex.rxjava3.schedulers.Schedulers
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.keyvalue.CertificateType
import org.thoughtcrime.securesms.keyvalue.SignalStore
@@ -59,7 +59,7 @@ class ChangeNumberRepository(private val context: Context) {
@WorkerThread
fun changeLocalNumber(e164: String): Single<Unit> {
DatabaseFactory.getRecipientDatabase(context).updateSelfPhone(e164)
SignalDatabase.recipients.updateSelfPhone(e164)
SignalStore.account().setE164(e164)

View File

@@ -2,7 +2,7 @@ package org.thoughtcrime.securesms.components.settings.app.chats
import android.content.Context
import org.signal.core.util.concurrent.SignalExecutors
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.jobs.MultiDeviceConfigurationUpdateJob
import org.thoughtcrime.securesms.keyvalue.SignalStore
@@ -19,7 +19,7 @@ class ChatsSettingsRepository {
SignalExecutors.BOUNDED.execute {
val isLinkPreviewsEnabled = SignalStore.settings().isLinkPreviewsEnabled
DatabaseFactory.getRecipientDatabase(context).markNeedsSync(Recipient.self().id)
SignalDatabase.recipients.markNeedsSync(Recipient.self().id)
StorageSyncHelper.scheduleSyncForDataChange()
ApplicationDependencies.getJobManager().add(
MultiDeviceConfigurationUpdateJob(

View File

@@ -2,7 +2,7 @@ package org.thoughtcrime.securesms.components.settings.app.data
import android.content.Context
import org.signal.core.util.concurrent.SignalExecutors
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
class DataAndStorageSettingsRepository {
@@ -11,7 +11,7 @@ class DataAndStorageSettingsRepository {
fun getTotalStorageUse(consumer: (Long) -> Unit) {
SignalExecutors.BOUNDED.execute {
val breakdown = DatabaseFactory.getMediaDatabase(context).storageBreakdown
val breakdown = SignalDatabase.media.storageBreakdown
consumer(listOf(breakdown.audioSize, breakdown.documentSize, breakdown.photoSize, breakdown.videoSize).sum())
}

View File

@@ -15,8 +15,8 @@ import org.thoughtcrime.securesms.components.settings.DSLSettingsAdapter
import org.thoughtcrime.securesms.components.settings.DSLSettingsFragment
import org.thoughtcrime.securesms.components.settings.DSLSettingsText
import org.thoughtcrime.securesms.components.settings.configure
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.LocalMetricsDatabase
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.jobs.DownloadLatestEmojiDataJob
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob
@@ -399,13 +399,13 @@ class InternalSettingsFragment : DSLSettingsFragment(R.string.preferences__inter
}
private fun clearAllSenderKeyState() {
DatabaseFactory.getSenderKeyDatabase(requireContext()).deleteAll()
DatabaseFactory.getSenderKeySharedDatabase(requireContext()).deleteAll()
SignalDatabase.senderKeys.deleteAll()
SignalDatabase.senderKeyShared.deleteAll()
Toast.makeText(context, "Deleted all sender key state.", Toast.LENGTH_SHORT).show()
}
private fun clearAllSenderKeySharedState() {
DatabaseFactory.getSenderKeySharedDatabase(requireContext()).deleteAll()
SignalDatabase.senderKeyShared.deleteAll()
Toast.makeText(context, "Deleted all sender key shared state.", Toast.LENGTH_SHORT).show()
}

View File

@@ -2,7 +2,7 @@ package org.thoughtcrime.securesms.components.settings.app.privacy
import android.content.Context
import org.signal.core.util.concurrent.SignalExecutors
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.jobs.MultiDeviceConfigurationUpdateJob
import org.thoughtcrime.securesms.keyvalue.SignalStore
@@ -16,7 +16,7 @@ class PrivacySettingsRepository {
fun getBlockedCount(consumer: (Int) -> Unit) {
SignalExecutors.BOUNDED.execute {
val recipientDatabase = DatabaseFactory.getRecipientDatabase(context)
val recipientDatabase = SignalDatabase.recipients
consumer(recipientDatabase.blocked.count)
}
@@ -24,7 +24,7 @@ class PrivacySettingsRepository {
fun syncReadReceiptState() {
SignalExecutors.BOUNDED.execute {
DatabaseFactory.getRecipientDatabase(context).markNeedsSync(Recipient.self().id)
SignalDatabase.recipients.markNeedsSync(Recipient.self().id)
StorageSyncHelper.scheduleSyncForDataChange()
ApplicationDependencies.getJobManager().add(
MultiDeviceConfigurationUpdateJob(
@@ -40,7 +40,7 @@ class PrivacySettingsRepository {
fun syncTypingIndicatorsState() {
val enabled = TextSecurePreferences.isTypingIndicatorsEnabled(context)
DatabaseFactory.getRecipientDatabase(context).markNeedsSync(Recipient.self().id)
SignalDatabase.recipients.markNeedsSync(Recipient.self().id)
StorageSyncHelper.scheduleSyncForDataChange()
ApplicationDependencies.getJobManager().add(
MultiDeviceConfigurationUpdateJob(

View File

@@ -5,7 +5,7 @@ import com.google.android.gms.tasks.Tasks
import com.google.firebase.installations.FirebaseInstallations
import org.signal.core.util.concurrent.SignalExecutors
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.jobs.MultiDeviceConfigurationUpdateJob
import org.thoughtcrime.securesms.keyvalue.SignalStore
@@ -51,7 +51,7 @@ class AdvancedPrivacySettingsRepository(private val context: Context) {
fun syncShowSealedSenderIconState() {
SignalExecutors.BOUNDED.execute {
DatabaseFactory.getRecipientDatabase(context).markNeedsSync(Recipient.self().id)
SignalDatabase.recipients.markNeedsSync(Recipient.self().id)
StorageSyncHelper.scheduleSyncForDataChange()
ApplicationDependencies.getJobManager().add(
MultiDeviceConfigurationUpdateJob(

View File

@@ -4,7 +4,7 @@ import android.content.Context
import androidx.annotation.WorkerThread
import org.signal.core.util.concurrent.SignalExecutors
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.ThreadDatabase
import org.thoughtcrime.securesms.groups.GroupChangeException
import org.thoughtcrime.securesms.groups.GroupManager
@@ -36,7 +36,7 @@ class ExpireTimerSettingsRepository(val context: Context) {
consumer.invoke(Result.failure(e))
}
} else {
DatabaseFactory.getRecipientDatabase(context).setExpireMessages(recipientId, newExpirationTime)
SignalDatabase.recipients.setExpireMessages(recipientId, newExpirationTime)
val outgoingMessage = OutgoingExpirationUpdateMessage(Recipient.resolved(recipientId), System.currentTimeMillis(), newExpirationTime * 1000L)
MessageSender.send(context, outgoingMessage, getThreadId(recipientId), false, null, null)
consumer.invoke(Result.success(newExpirationTime))
@@ -46,7 +46,7 @@ class ExpireTimerSettingsRepository(val context: Context) {
@WorkerThread
private fun getThreadId(recipientId: RecipientId): Long {
val threadDatabase: ThreadDatabase = DatabaseFactory.getThreadDatabase(context)
val threadDatabase: ThreadDatabase = SignalDatabase.threads
val recipient: Recipient = Recipient.resolved(recipientId)
return threadDatabase.getOrCreateThreadIdFor(recipient)
}

View File

@@ -8,7 +8,7 @@ import org.thoughtcrime.securesms.badges.models.Badge
sealed class DonationEvent {
class GooglePayUnavailableError(val throwable: Throwable) : DonationEvent()
object RequestTokenSuccess : DonationEvent()
object RequestTokenError : DonationEvent()
class RequestTokenError(val throwable: Throwable) : DonationEvent()
class PaymentConfirmationError(val throwable: Throwable) : DonationEvent()
class PaymentConfirmationSuccess(val badge: Badge) : DonationEvent()
class SubscriptionCancellationFailed(val throwable: Throwable) : DonationEvent()

View File

@@ -1,14 +1,9 @@
package org.thoughtcrime.securesms.components.settings.app.subscription
import android.app.Activity
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.net.ConnectivityManager
import com.google.android.gms.wallet.PaymentData
import io.reactivex.rxjava3.core.Completable
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.core.Single
import io.reactivex.rxjava3.schedulers.Schedulers
import org.signal.core.util.concurrent.SignalExecutors
@@ -18,10 +13,9 @@ import org.signal.donations.GooglePayApi
import org.signal.donations.GooglePayPaymentSource
import org.signal.donations.StripeApi
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.jobmanager.JobTracker
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
import org.thoughtcrime.securesms.jobs.BoostReceiptRequestResponseJob
import org.thoughtcrime.securesms.jobs.SubscriptionReceiptRequestResponseJob
import org.thoughtcrime.securesms.keyvalue.SignalStore
@@ -71,24 +65,12 @@ class DonationPaymentRepository(activity: Activity) : StripeApi.PaymentIntentFet
}
private fun scheduleSyncForAccountRecordChangeSync() {
DatabaseFactory.getRecipientDatabase(application).markNeedsSync(Recipient.self().id)
SignalDatabase.recipients.markNeedsSync(Recipient.self().id)
StorageSyncHelper.scheduleSyncForDataChange()
}
fun internetConnectionObserver(): Observable<Boolean> = Observable.create {
val observer = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (!it.isDisposed) {
it.onNext(NetworkConstraint.isMet(application))
}
}
}
it.setCancellable { application.unregisterReceiver(observer) }
application.registerReceiver(observer, IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION))
}
fun requestTokenFromGooglePay(price: FiatMoney, label: String, requestCode: Int) {
Log.d(TAG, "Requesting a token from google pay...")
googlePayApi.requestPayment(price, label, requestCode)
}
@@ -99,6 +81,7 @@ class DonationPaymentRepository(activity: Activity) : StripeApi.PaymentIntentFet
expectedRequestCode: Int,
paymentsRequestCallback: GooglePayApi.PaymentRequestCallback
) {
Log.d(TAG, "Processing possible google pay result...")
googlePayApi.onActivityResult(requestCode, resultCode, data, expectedRequestCode, paymentsRequestCallback)
}
@@ -118,26 +101,36 @@ class DonationPaymentRepository(activity: Activity) : StripeApi.PaymentIntentFet
}
fun continueSubscriptionSetup(paymentData: PaymentData): Completable {
Log.d(TAG, "Continuing subscription setup...", true)
return stripeApi.createSetupIntent()
.flatMapCompletable { result ->
stripeApi.confirmSetupIntent(GooglePayPaymentSource(paymentData), result.setupIntent)
Log.d(TAG, "Retrieved SetupIntent, confirming...", true)
stripeApi.confirmSetupIntent(GooglePayPaymentSource(paymentData), result.setupIntent).doOnComplete {
Log.d(TAG, "Confirmed SetupIntent...", true)
}
}
}
fun cancelActiveSubscription(): Completable {
Log.d(TAG, "Canceling active subscription...", true)
val localSubscriber = SignalStore.donationsValues().requireSubscriber()
return ApplicationDependencies.getDonationsService()
.cancelSubscription(localSubscriber.subscriberId)
.flatMap(ServiceResponse<EmptyResponse>::flattenResult).ignoreElement()
.flatMap(ServiceResponse<EmptyResponse>::flattenResult)
.ignoreElement()
.doOnComplete { Log.d(TAG, "Cancelled active subscription.", true) }
}
fun ensureSubscriberId(): Completable {
Log.d(TAG, "Ensuring SubscriberId exists on Signal service...", true)
val subscriberId = SignalStore.donationsValues().getSubscriber()?.subscriberId ?: SubscriberId.generate()
return ApplicationDependencies
.getDonationsService()
.putSubscription(subscriberId)
.flatMap(ServiceResponse<EmptyResponse>::flattenResult).ignoreElement()
.doOnComplete {
Log.d(TAG, "Successfully set SubscriberId exists on Signal service.", true)
SignalStore
.donationsValues()
.setSubscriber(Subscriber(subscriberId, SignalStore.donationsValues().getSubscriptionCurrency().currencyCode))
@@ -179,9 +172,11 @@ class DonationPaymentRepository(activity: Activity) : StripeApi.PaymentIntentFet
}
}
} else {
Log.d(TAG, "Boost redemption timed out waiting for job completion.", true)
it.onError(DonationExceptions.TimedOutWaitingForTokenRedemption)
}
} catch (e: InterruptedException) {
Log.d(TAG, "Boost redemption job interrupted", e, true)
it.onError(DonationExceptions.TimedOutWaitingForTokenRedemption)
}
}
@@ -262,6 +257,7 @@ class DonationPaymentRepository(activity: Activity) : StripeApi.PaymentIntentFet
}
private fun getOrCreateLevelUpdateOperation(subscriptionLevel: String): Single<LevelUpdateOperation> = Single.fromCallable {
Log.d(TAG, "Retrieving level update operation for $subscriptionLevel")
val levelUpdateOperation = SignalStore.donationsValues().getLevelOperation(subscriptionLevel)
if (levelUpdateOperation == null) {
val newOperation = LevelUpdateOperation(
@@ -271,36 +267,48 @@ class DonationPaymentRepository(activity: Activity) : StripeApi.PaymentIntentFet
SignalStore.donationsValues().setLevelOperation(newOperation)
LevelUpdate.updateProcessingState(true)
Log.d(TAG, "Created a new operation for $subscriptionLevel")
newOperation
} else {
LevelUpdate.updateProcessingState(true)
Log.d(TAG, "Reusing operation for $subscriptionLevel")
levelUpdateOperation
}
}
override fun fetchPaymentIntent(price: FiatMoney, description: String?): Single<StripeApi.PaymentIntent> {
Log.d(TAG, "Fetching payment intent from Signal service...")
return ApplicationDependencies
.getDonationsService()
.createDonationIntentWithAmount(price.minimumUnitPrecisionString, price.currency.currencyCode, description)
.flatMap(ServiceResponse<SubscriptionClientSecret>::flattenResult)
.map {
StripeApi.PaymentIntent(it.id, it.clientSecret)
}.doOnSuccess {
Log.d(TAG, "Got payment intent from Signal service!")
}
}
override fun fetchSetupIntent(): Single<StripeApi.SetupIntent> {
Log.d(TAG, "Fetching setup intent from Signal service...")
return Single.fromCallable { SignalStore.donationsValues().requireSubscriber() }
.flatMap { ApplicationDependencies.getDonationsService().createSubscriptionPaymentMethod(it.subscriberId) }
.flatMap(ServiceResponse<SubscriptionClientSecret>::flattenResult)
.map { StripeApi.SetupIntent(it.id, it.clientSecret) }
.doOnSuccess {
Log.d(TAG, "Got setup intent from Signal service!")
}
}
override fun setDefaultPaymentMethod(paymentMethodId: String): Completable {
Log.d(TAG, "Setting default payment method via Signal service...")
return Single.fromCallable {
SignalStore.donationsValues().requireSubscriber()
}.flatMap {
ApplicationDependencies.getDonationsService().setDefaultPaymentMethodId(it.subscriberId, paymentMethodId)
}.flatMap(ServiceResponse<EmptyResponse>::flattenResult).ignoreElement()
}.flatMap(ServiceResponse<EmptyResponse>::flattenResult).ignoreElement().doOnComplete {
Log.d(TAG, "Set default payment method via Signal service!")
}
}
companion object {

View File

@@ -24,6 +24,7 @@ import org.thoughtcrime.securesms.util.MappingViewHolder
import org.thoughtcrime.securesms.util.ViewUtil
import java.lang.Integer.min
import java.text.DecimalFormatSymbols
import java.text.NumberFormat
import java.util.Currency
import java.util.Locale
import java.util.regex.Pattern
@@ -122,6 +123,15 @@ data class Boost(
private val boost6: MaterialButton = itemView.findViewById(R.id.boost_6)
private val custom: AppCompatEditText = itemView.findViewById(R.id.boost_custom)
private val boostButtons: List<MaterialButton>
get() {
return if (ViewUtil.isLtr(context)) {
listOf(boost1, boost2, boost3, boost4, boost5, boost6)
} else {
listOf(boost3, boost2, boost1, boost6, boost5, boost4)
}
}
private var filter: MoneyFilter? = null
init {
@@ -131,14 +141,13 @@ data class Boost(
override fun bind(model: SelectionModel) {
itemView.isEnabled = model.isEnabled
model.boosts.zip(listOf(boost1, boost2, boost3, boost4, boost5, boost6)).forEach { (boost, button) ->
model.boosts.zip(boostButtons).forEach { (boost, button) ->
button.isSelected = boost == model.selectedBoost && !model.isCustomAmountFocused
button.text = FiatMoneyUtil.format(
context.resources,
boost.price,
FiatMoneyUtil
.formatOptions()
.numberOnly()
.trimZerosAfterDecimal()
)
button.setOnClickListener {
@@ -150,7 +159,7 @@ data class Boost(
if (filter == null || filter?.currency != model.currency) {
custom.removeTextChangedListener(filter)
filter = MoneyFilter(model.currency) {
filter = MoneyFilter(model.currency, custom) {
model.onCustomAmountChanged(it)
}
@@ -183,12 +192,14 @@ data class Boost(
}
@VisibleForTesting
class MoneyFilter(val currency: Currency, private val onCustomAmountChanged: (String) -> Unit = {}) : DigitsKeyListener(false, true), TextWatcher {
class MoneyFilter(val currency: Currency, private val text: AppCompatEditText? = null, private val onCustomAmountChanged: (String) -> Unit = {}) : DigitsKeyListener(false, true), TextWatcher {
val separator = DecimalFormatSymbols.getInstance().decimalSeparator
val separatorCount = min(1, currency.defaultFractionDigits)
val prefix: String = currency.getSymbol(Locale.getDefault())
val pattern: Pattern = "[0-9]*($separator){0,$separatorCount}[0-9]{0,${currency.defaultFractionDigits}}".toPattern()
val symbol: String = currency.getSymbol(Locale.getDefault())
val pattern: Pattern = "[0-9]*([$separator]){0,$separatorCount}[0-9]{0,${currency.defaultFractionDigits}}".toPattern()
val symbolPattern: Regex = """\s*${Regex.escape(symbol)}\s*""".toRegex()
val leadingZeroesPattern: Regex = """^0*""".toRegex()
override fun filter(
source: CharSequence,
@@ -200,9 +211,9 @@ data class Boost(
): CharSequence? {
val result = dest.subSequence(0, dstart).toString() + source.toString() + dest.subSequence(dend, dest.length)
val resultWithoutCurrencyPrefix = result.removePrefix(prefix)
val resultWithoutCurrencyPrefix = result.removePrefix(symbol).removeSuffix(symbol).trim()
if (result.length == 1 && !result.isDigitsOnly() && result != separator.toString()) {
if (resultWithoutCurrencyPrefix.length == 1 && !resultWithoutCurrencyPrefix.isDigitsOnly() && resultWithoutCurrencyPrefix != separator.toString()) {
return dest.subSequence(dstart, dend)
}
@@ -222,14 +233,46 @@ data class Boost(
override fun afterTextChanged(s: Editable?) {
if (s.isNullOrEmpty()) return
val hasPrefix = s.startsWith(prefix)
if (hasPrefix && s.length == prefix.length) {
val hasSymbol = s.startsWith(symbol) || s.endsWith(symbol)
if (hasSymbol && symbolPattern.matchEntire(s.toString()) != null) {
s.clear()
} else if (!hasPrefix) {
s.insert(0, prefix)
} else if (!hasSymbol) {
val formatter = NumberFormat.getCurrencyInstance()
formatter.currency = currency
formatter.minimumFractionDigits = 0
formatter.maximumFractionDigits = currency.defaultFractionDigits
val value = s.toString().toDoubleOrNull()
if (value != null) {
val formatted = formatter.format(value)
text?.removeTextChangedListener(this)
s.replace(0, s.length, formatted)
if (formatted.endsWith(symbol)) {
val result: MatchResult? = symbolPattern.find(formatted)
if (result != null && result.range.first < s.length) {
text?.setSelection(result.range.first)
}
}
text?.addTextChangedListener(this)
}
}
onCustomAmountChanged(s.removePrefix(prefix).toString())
val withoutSymbol = s.removePrefix(symbol).removeSuffix(symbol).trim().toString()
val withoutLeadingZeroes = withoutSymbol.replace(leadingZeroesPattern, "")
if (withoutSymbol != withoutLeadingZeroes) {
text?.removeTextChangedListener(this)
val start = s.indexOf(withoutSymbol)
s.replace(start, start + withoutSymbol.length, withoutLeadingZeroes)
text?.addTextChangedListener(this)
}
onCustomAmountChanged(s.removePrefix(symbol).removeSuffix(symbol).trim().toString())
}
}

View File

@@ -0,0 +1,44 @@
package org.thoughtcrime.securesms.components.settings.app.subscription.boost
import android.animation.Animator
import android.view.View
import com.airbnb.lottie.LottieAnimationView
import com.airbnb.lottie.LottieDrawable
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.animation.AnimationCompleteListener
import org.thoughtcrime.securesms.components.settings.PreferenceModel
import org.thoughtcrime.securesms.util.MappingAdapter
import org.thoughtcrime.securesms.util.MappingViewHolder
/**
* A simple mapping model to show a boost animation.
*/
object BoostAnimation {
class Model : PreferenceModel<Model>(isEnabled = true) {
override fun areItemsTheSame(newItem: Model): Boolean = true
}
class ViewHolder(itemView: View) : MappingViewHolder<Model>(itemView) {
private val lottie: LottieAnimationView = findViewById(R.id.boost_animation_view)
override fun bind(model: Model) {
lottie.playAnimation()
lottie.addAnimatorListener(object : AnimationCompleteListener() {
override fun onAnimationEnd(animation: Animator?) {
lottie.removeAnimatorListener(this)
lottie.setMinAndMaxFrame(30, 91)
lottie.repeatMode = LottieDrawable.RESTART
lottie.repeatCount = LottieDrawable.INFINITE
lottie.frame = 30
lottie.playAnimation()
}
})
}
}
fun register(adapter: MappingAdapter) {
adapter.registerFactory(Model::class.java, MappingAdapter.LayoutFactory({ ViewHolder(it) }, R.layout.boost_animation_pref))
}
}

View File

@@ -21,7 +21,6 @@ import org.thoughtcrime.securesms.components.settings.DSLSettingsAdapter
import org.thoughtcrime.securesms.components.settings.DSLSettingsBottomSheetFragment
import org.thoughtcrime.securesms.components.settings.DSLSettingsIcon
import org.thoughtcrime.securesms.components.settings.DSLSettingsText
import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity
import org.thoughtcrime.securesms.components.settings.app.subscription.DonationEvent
import org.thoughtcrime.securesms.components.settings.app.subscription.DonationExceptions
import org.thoughtcrime.securesms.components.settings.app.subscription.DonationPaymentComponent
@@ -31,7 +30,6 @@ import org.thoughtcrime.securesms.components.settings.app.subscription.models.Ne
import org.thoughtcrime.securesms.components.settings.configure
import org.thoughtcrime.securesms.components.settings.models.Progress
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.help.HelpFragment
import org.thoughtcrime.securesms.keyboard.findListener
import org.thoughtcrime.securesms.util.BottomSheetUtil.requireCoordinatorLayout
import org.thoughtcrime.securesms.util.CommunicationActions
@@ -84,6 +82,7 @@ class BoostFragment : DSLSettingsBottomSheetFragment(
GooglePayButton.register(adapter)
Progress.register(adapter)
NetworkFailure.register(adapter)
BoostAnimation.register(adapter)
processingDonationPaymentDialog = MaterialAlertDialogBuilder(requireContext())
.setView(R.layout.processing_payment_dialog)
@@ -121,7 +120,7 @@ class BoostFragment : DSLSettingsBottomSheetFragment(
is DonationEvent.GooglePayUnavailableError -> Unit
is DonationEvent.PaymentConfirmationError -> onPaymentError(event.throwable)
is DonationEvent.PaymentConfirmationSuccess -> onPaymentConfirmed(event.badge)
DonationEvent.RequestTokenError -> onPaymentError(null)
is DonationEvent.RequestTokenError -> onPaymentError(DonationExceptions.SetupFailed(event.throwable))
DonationEvent.RequestTokenSuccess -> Log.i(TAG, "Successfully got request token from Google Pay")
DonationEvent.SubscriptionCancelled -> Unit
is DonationEvent.SubscriptionCancellationFailed -> Unit
@@ -145,7 +144,7 @@ class BoostFragment : DSLSettingsBottomSheetFragment(
}
return configure {
customPref(BadgePreview.SubscriptionModel(state.boostBadge))
customPref(BoostAnimation.Model())
sectionHeaderPref(
title = DSLSettingsText.from(
@@ -251,7 +250,7 @@ class BoostFragment : DSLSettingsBottomSheetFragment(
} else if (throwable is DonationExceptions.SetupFailed) {
Log.w(TAG, "Error occurred while processing payment", throwable, true)
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.DonationsErrors__payment_failed)
.setTitle(R.string.DonationsErrors__error_processing_payment)
.setMessage(R.string.DonationsErrors__your_payment)
.setPositiveButton(android.R.string.ok) { dialog, _ ->
dialog.dismiss()
@@ -261,12 +260,11 @@ class BoostFragment : DSLSettingsBottomSheetFragment(
} else {
Log.w(TAG, "Error occurred while trying to redeem token", throwable, true)
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.DonationsErrors__redemption_failed)
.setMessage(R.string.DonationsErrors__please_contact_support)
.setTitle(R.string.DonationsErrors__couldnt_add_badge)
.setMessage(R.string.DonationsErrors__your_badge_could_not)
.setPositiveButton(R.string.Subscription__contact_support) { dialog, _ ->
dialog.dismiss()
requireActivity().finish()
requireActivity().startActivity(AppSettingsActivity.help(requireContext(), HelpFragment.DONATION_INDEX))
findNavController().popBackStack()
}
.show()
}

View File

@@ -19,9 +19,12 @@ import org.thoughtcrime.securesms.badges.models.Badge
import org.thoughtcrime.securesms.components.settings.app.subscription.DonationEvent
import org.thoughtcrime.securesms.components.settings.app.subscription.DonationPaymentRepository
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.util.InternetConnectionObserver
import org.thoughtcrime.securesms.util.PlatformCurrencyUtil
import org.thoughtcrime.securesms.util.livedata.Store
import java.lang.NumberFormatException
import java.math.BigDecimal
import java.text.DecimalFormat
import java.text.DecimalFormatSymbols
import java.util.Currency
@@ -42,8 +45,8 @@ class BoostViewModel(
private var boostToPurchase: Boost? = null
init {
networkDisposable = donationPaymentRepository
.internetConnectionObserver()
networkDisposable = InternetConnectionObserver
.observe()
.distinctUntilChanged()
.subscribe { isConnected ->
if (isConnected) {
@@ -137,6 +140,8 @@ class BoostViewModel(
if (boost != null) {
eventPublisher.onNext(DonationEvent.RequestTokenSuccess)
store.update { it.copy(stage = BoostState.Stage.PAYMENT_PIPELINE) }
donationPaymentRepository.continuePayment(boost.price, paymentData).subscribeBy(
onError = { throwable ->
store.update { it.copy(stage = BoostState.Stage.READY) }
@@ -152,9 +157,9 @@ class BoostViewModel(
}
}
override fun onError() {
override fun onError(googlePayException: GooglePayApi.GooglePayException) {
store.update { it.copy(stage = BoostState.Stage.READY) }
eventPublisher.onNext(DonationEvent.RequestTokenError)
eventPublisher.onNext(DonationEvent.RequestTokenError(googlePayException))
}
override fun onCancelled() {
@@ -170,15 +175,16 @@ class BoostViewModel(
return
}
store.update { it.copy(stage = BoostState.Stage.PAYMENT_PIPELINE) }
store.update { it.copy(stage = BoostState.Stage.TOKEN_REQUEST) }
boostToPurchase = if (snapshot.isCustomAmountFocused) {
val boost = if (snapshot.isCustomAmountFocused) {
Boost(snapshot.customAmount)
} else {
snapshot.selectedBoost
}
donationPaymentRepository.requestTokenFromGooglePay(snapshot.selectedBoost.price, label, fetchTokenRequestCode)
boostToPurchase = boost
donationPaymentRepository.requestTokenFromGooglePay(boost.price, label, fetchTokenRequestCode)
}
fun setSelectedBoost(boost: Boost) {
@@ -191,10 +197,17 @@ class BoostViewModel(
}
fun setCustomAmount(amount: String) {
val bigDecimalAmount = if (amount.isEmpty() || amount == DecimalFormatSymbols.getInstance().decimalSeparator.toString()) {
val bigDecimalAmount: BigDecimal = if (amount.isEmpty() || amount == DecimalFormatSymbols.getInstance().decimalSeparator.toString()) {
BigDecimal.ZERO
} else {
BigDecimal(amount)
val decimalFormat = DecimalFormat.getInstance() as DecimalFormat
decimalFormat.isParseBigDecimal = true
try {
decimalFormat.parse(amount) as BigDecimal
} catch (e: NumberFormatException) {
BigDecimal.ZERO
}
}
store.update { it.copy(customAmount = FiatMoney(bigDecimalAmount, it.customAmount.currency)) }

View File

@@ -1,12 +1,12 @@
package org.thoughtcrime.securesms.components.settings.app.subscription.manage
import android.text.SpannableStringBuilder
import android.text.method.LinkMovementMethod
import android.view.View
import android.widget.ProgressBar
import android.widget.TextView
import androidx.core.content.ContextCompat
import com.google.android.material.button.MaterialButton
import org.signal.core.util.money.FiatMoney
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.badges.BadgeImageView
import org.thoughtcrime.securesms.components.settings.PreferenceModel
@@ -18,6 +18,7 @@ import org.thoughtcrime.securesms.util.MappingAdapter
import org.thoughtcrime.securesms.util.MappingViewHolder
import org.thoughtcrime.securesms.util.SpanUtil
import org.thoughtcrime.securesms.util.visible
import org.whispersystems.signalservice.api.subscriptions.ActiveSubscription
import java.util.Locale
/**
@@ -27,10 +28,12 @@ import java.util.Locale
object ActiveSubscriptionPreference {
class Model(
val price: FiatMoney,
val subscription: Subscription,
val onAddBoostClick: () -> Unit,
val renewalTimestamp: Long = -1L,
val redemptionState: ManageDonationsState.SubscriptionRedemptionState,
val activeSubscription: ActiveSubscription.Subscription,
val onContactSupport: () -> Unit
) : PreferenceModel<Model>() {
override fun areItemsTheSame(newItem: Model): Boolean {
@@ -41,7 +44,9 @@ object ActiveSubscriptionPreference {
return super.areContentsTheSame(newItem) &&
subscription == newItem.subscription &&
renewalTimestamp == newItem.renewalTimestamp &&
redemptionState == newItem.redemptionState
redemptionState == newItem.redemptionState &&
FiatMoney.equals(price, newItem.price) &&
activeSubscription == newItem.activeSubscription
}
}
@@ -62,7 +67,7 @@ object ActiveSubscriptionPreference {
R.string.MySupportPreference__s_per_month,
FiatMoneyUtil.format(
context.resources,
model.subscription.prices.first { it.currency == SignalStore.donationsValues().getSubscriptionCurrency() },
model.price,
FiatMoneyUtil.formatOptions()
)
)
@@ -98,14 +103,39 @@ object ActiveSubscriptionPreference {
}
private fun presentFailureState(model: Model) {
expiry.text = SpannableStringBuilder(context.getString(R.string.MySupportPreference__couldnt_add_badge))
.append(" ")
.append(
SpanUtil.clickable(
context.getString(R.string.MySupportPreference__please_contact_support),
ContextCompat.getColor(context, R.color.signal_accent_primary)
) { model.onContactSupport() }
)
if (model.activeSubscription.isFailedPayment || SignalStore.donationsValues().shouldCancelSubscriptionBeforeNextSubscribeAttempt) {
presentPaymentFailureState(model)
} else {
presentRedemptionFailureState(model)
}
}
private fun presentPaymentFailureState(model: Model) {
val contactString = context.getString(R.string.MySupportPreference__please_contact_support)
expiry.text = SpanUtil.clickSubstring(
context.getString(R.string.DonationsErrors__error_processing_payment_s, contactString),
contactString,
{
model.onContactSupport()
},
ContextCompat.getColor(context, R.color.signal_accent_primary)
)
badge.alpha = 0.2f
progress.visible = false
}
private fun presentRedemptionFailureState(model: Model) {
val contactString = context.getString(R.string.MySupportPreference__please_contact_support)
expiry.text = SpanUtil.clickSubstring(
context.getString(R.string.MySupportPreference__couldnt_add_badge_s, contactString),
contactString,
{
model.onContactSupport()
},
ContextCompat.getColor(context, R.color.signal_accent_primary)
)
badge.alpha = 0.2f
progress.visible = false
}

View File

@@ -1,11 +1,10 @@
package org.thoughtcrime.securesms.components.settings.app.subscription.manage
import android.os.Bundle
import android.widget.Toast
import androidx.fragment.app.viewModels
import androidx.navigation.NavOptions
import androidx.navigation.fragment.findNavController
import org.signal.core.util.DimensionUnit
import org.signal.core.util.money.FiatMoney
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.badges.models.BadgePreview
import org.thoughtcrime.securesms.components.settings.DSLConfiguration
@@ -21,6 +20,7 @@ import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.help.HelpFragment
import org.thoughtcrime.securesms.subscription.Subscription
import org.thoughtcrime.securesms.util.LifecycleDisposable
import java.util.Currency
import java.util.concurrent.TimeUnit
/**
@@ -37,29 +37,12 @@ class ManageDonationsFragment : DSLSettingsFragment() {
private val lifecycleDisposable = LifecycleDisposable()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val args = ManageDonationsFragmentArgs.fromBundle(requireArguments())
if (args.skipToSubscribe) {
findNavController().navigate(
ManageDonationsFragmentDirections.actionManageDonationsFragmentToSubscribeFragment(),
NavOptions.Builder().setPopUpTo(R.id.manageDonationsFragment, true).build()
)
}
}
override fun onResume() {
super.onResume()
viewModel.refresh()
}
override fun bindAdapter(adapter: DSLSettingsAdapter) {
val args = ManageDonationsFragmentArgs.fromBundle(requireArguments())
if (args.skipToSubscribe) {
return
}
ActiveSubscriptionPreference.register(adapter)
IndeterminateLoadingCircle.register(adapter)
BadgePreview.register(adapter)
@@ -104,24 +87,29 @@ class ManageDonationsFragment : DSLSettingsFragment() {
)
if (state.transactionState is ManageDonationsState.TransactionState.NotInTransaction) {
val activeSubscription = state.transactionState.activeSubscription
if (activeSubscription.isActive) {
val subscription: Subscription? = state.availableSubscriptions.firstOrNull { activeSubscription.activeSubscription.level == it.level }
val activeSubscription = state.transactionState.activeSubscription.activeSubscription
if (activeSubscription != null) {
val subscription: Subscription? = state.availableSubscriptions.firstOrNull { activeSubscription.level == it.level }
if (subscription != null) {
space(DimensionUnit.DP.toPixels(12f).toInt())
val activeCurrency = Currency.getInstance(activeSubscription.currency)
val activeAmount = activeSubscription.amount.movePointLeft(activeCurrency.defaultFractionDigits)
customPref(
ActiveSubscriptionPreference.Model(
price = FiatMoney(activeAmount, activeCurrency),
subscription = subscription,
onAddBoostClick = {
findNavController().navigate(ManageDonationsFragmentDirections.actionManageDonationsFragmentToBoosts())
},
renewalTimestamp = TimeUnit.SECONDS.toMillis(activeSubscription.activeSubscription.endOfCurrentPeriod),
redemptionState = state.subscriptionRedemptionState,
renewalTimestamp = TimeUnit.SECONDS.toMillis(activeSubscription.endOfCurrentPeriod),
redemptionState = state.getRedemptionState(),
onContactSupport = {
requireActivity().finish()
requireActivity().startActivity(AppSettingsActivity.help(requireContext(), HelpFragment.DONATION_INDEX))
}
},
activeSubscription = activeSubscription
)
)
@@ -139,7 +127,7 @@ class ManageDonationsFragment : DSLSettingsFragment() {
clickPref(
title = DSLSettingsText.from(R.string.ManageDonationsFragment__manage_subscription),
icon = DSLSettingsIcon.from(R.drawable.ic_person_white_24dp),
isEnabled = state.subscriptionRedemptionState != ManageDonationsState.SubscriptionRedemptionState.IN_PROGRESS,
isEnabled = state.getRedemptionState() != ManageDonationsState.SubscriptionRedemptionState.IN_PROGRESS,
onClick = {
findNavController().navigate(ManageDonationsFragmentDirections.actionManageDonationsFragmentToSubscribeFragment())
}
@@ -149,7 +137,7 @@ class ManageDonationsFragment : DSLSettingsFragment() {
title = DSLSettingsText.from(R.string.ManageDonationsFragment__badges),
icon = DSLSettingsIcon.from(R.drawable.ic_badge_24),
onClick = {
findNavController().navigate(ManageDonationsFragmentDirections.actionManageDonationsFragmentToSubscriptionBadgeManageFragment())
findNavController().navigate(ManageDonationsFragmentDirections.actionManageDonationsFragmentToManageBadges())
}
)

View File

@@ -8,8 +8,25 @@ data class ManageDonationsState(
val featuredBadge: Badge? = null,
val transactionState: TransactionState = TransactionState.Init,
val availableSubscriptions: List<Subscription> = emptyList(),
val subscriptionRedemptionState: SubscriptionRedemptionState = SubscriptionRedemptionState.NONE
private val subscriptionRedemptionState: SubscriptionRedemptionState = SubscriptionRedemptionState.NONE
) {
fun getRedemptionState(): SubscriptionRedemptionState {
return when (transactionState) {
TransactionState.Init -> subscriptionRedemptionState
TransactionState.InTransaction -> SubscriptionRedemptionState.IN_PROGRESS
is TransactionState.NotInTransaction -> getStateFromActiveSubscription(transactionState.activeSubscription) ?: subscriptionRedemptionState
}
}
fun getStateFromActiveSubscription(activeSubscription: ActiveSubscription): SubscriptionRedemptionState? {
return when {
activeSubscription.isFailedPayment -> SubscriptionRedemptionState.FAILED
activeSubscription.isInProgress -> SubscriptionRedemptionState.IN_PROGRESS
else -> null
}
}
sealed class TransactionState {
object Init : TransactionState()
object InTransaction : TransactionState()

View File

@@ -73,7 +73,7 @@ class ManageDonationsViewModel(
it.copy(transactionState = transactionState)
}
if (transactionState is ManageDonationsState.TransactionState.NotInTransaction && !transactionState.activeSubscription.isActive) {
if (transactionState is ManageDonationsState.TransactionState.NotInTransaction && transactionState.activeSubscription.activeSubscription == null) {
eventPublisher.onNext(ManageDonationsEvent.NOT_SUBSCRIBED)
}
},

View File

@@ -10,13 +10,13 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
import org.signal.core.util.DimensionUnit
import org.signal.core.util.logging.Log
import org.signal.core.util.money.FiatMoney
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.badges.models.Badge
import org.thoughtcrime.securesms.badges.models.BadgePreview
import org.thoughtcrime.securesms.components.settings.DSLConfiguration
import org.thoughtcrime.securesms.components.settings.DSLSettingsAdapter
import org.thoughtcrime.securesms.components.settings.DSLSettingsFragment
import org.thoughtcrime.securesms.components.settings.DSLSettingsIcon
import org.thoughtcrime.securesms.components.settings.DSLSettingsText
import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity
import org.thoughtcrime.securesms.components.settings.app.subscription.DonationEvent
@@ -31,12 +31,13 @@ import org.thoughtcrime.securesms.components.settings.models.Progress
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.help.HelpFragment
import org.thoughtcrime.securesms.keyboard.findListener
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.payments.FiatMoneyUtil
import org.thoughtcrime.securesms.subscription.Subscription
import org.thoughtcrime.securesms.util.CommunicationActions
import org.thoughtcrime.securesms.util.LifecycleDisposable
import org.thoughtcrime.securesms.util.SpanUtil
import java.util.Calendar
import java.util.Currency
import java.util.concurrent.TimeUnit
/**
@@ -52,7 +53,7 @@ class SubscribeFragment : DSLSettingsFragment(
SpannableStringBuilder(requireContext().getString(R.string.SubscribeFragment__support_technology_that_is_built_for_you_not))
.append(" ")
.append(
SpanUtil.readMore(requireContext(), ContextCompat.getColor(requireContext(), R.color.signal_accent_primary)) {
SpanUtil.readMore(requireContext(), ContextCompat.getColor(requireContext(), R.color.signal_button_secondary_text)) {
findNavController().navigate(SubscribeFragmentDirections.actionSubscribeFragmentToSubscribeLearnMoreBottomSheetDialog())
}
)
@@ -98,7 +99,7 @@ class SubscribeFragment : DSLSettingsFragment(
is DonationEvent.GooglePayUnavailableError -> Unit
is DonationEvent.PaymentConfirmationError -> onPaymentError(it.throwable)
is DonationEvent.PaymentConfirmationSuccess -> onPaymentConfirmed(it.badge)
DonationEvent.RequestTokenError -> onPaymentError(null)
is DonationEvent.RequestTokenError -> onPaymentError(DonationExceptions.SetupFailed(it.throwable))
DonationEvent.RequestTokenSuccess -> Log.w(TAG, "Successfully got request token from Google Pay")
DonationEvent.SubscriptionCancelled -> onSubscriptionCancelled()
is DonationEvent.SubscriptionCancellationFailed -> onSubscriptionFailedToCancel(it.throwable)
@@ -169,9 +170,19 @@ class SubscribeFragment : DSLSettingsFragment(
space(DimensionUnit.DP.toPixels(75f).toInt())
} else {
state.subscriptions.forEach {
val isActive = state.activeSubscription?.activeSubscription?.level == it.level
val isActive = state.activeSubscription?.activeSubscription?.level == it.level && state.activeSubscription.isActive
val activePrice = state.activeSubscription?.activeSubscription?.let { sub ->
val activeCurrency = Currency.getInstance(sub.currency)
val activeAmount = sub.amount.movePointLeft(activeCurrency.defaultFractionDigits)
FiatMoney(activeAmount, activeCurrency)
}
customPref(
Subscription.Model(
activePrice = if (isActive) activePrice else null,
subscription = it,
isSelected = state.selectedSubscription == it,
isEnabled = areFieldsEnabled,
@@ -249,13 +260,7 @@ class SubscribeFragment : DSLSettingsFragment(
)
)
secondaryButtonNoOutline(
text = DSLSettingsText.from(R.string.SubscribeFragment__more_payment_options),
icon = DSLSettingsIcon.from(R.drawable.ic_open_20, R.color.signal_accent_primary),
onClick = {
CommunicationActions.openBrowserLink(requireContext(), getString(R.string.donate_url))
}
)
space(DimensionUnit.DP.toPixels(8f).toInt())
}
}
}
@@ -275,7 +280,7 @@ class SubscribeFragment : DSLSettingsFragment(
Log.w(TAG, "Timeout occurred while redeeming token", throwable, true)
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.DonationsErrors__still_processing)
.setMessage(R.string.DonationsErrors__your_payment)
.setMessage(R.string.DonationsErrors__your_payment_is_still)
.setPositiveButton(android.R.string.ok) { dialog, _ ->
dialog.dismiss()
requireActivity().finish()
@@ -285,17 +290,28 @@ class SubscribeFragment : DSLSettingsFragment(
} else if (throwable is DonationExceptions.SetupFailed) {
Log.w(TAG, "Error occurred while processing payment", throwable, true)
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.DonationsErrors__payment_failed)
.setTitle(R.string.DonationsErrors__error_processing_payment)
.setMessage(R.string.DonationsErrors__your_payment)
.setPositiveButton(android.R.string.ok) { dialog, _ ->
dialog.dismiss()
}
.show()
} else if (SignalStore.donationsValues().shouldCancelSubscriptionBeforeNextSubscribeAttempt) {
Log.w(TAG, "Stripe failed to process payment", throwable, true)
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.DonationsErrors__error_processing_payment)
.setMessage(R.string.DonationsErrors__your_badge_could_not_be_added)
.setPositiveButton(R.string.Subscription__contact_support) { dialog, _ ->
dialog.dismiss()
requireActivity().finish()
requireActivity().startActivity(AppSettingsActivity.help(requireContext(), HelpFragment.DONATION_INDEX))
}
.show()
} else {
Log.w(TAG, "Error occurred while trying to redeem token", throwable, true)
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.DonationsErrors__redemption_failed)
.setMessage(R.string.DonationsErrors__please_contact_support)
.setTitle(R.string.DonationsErrors__couldnt_add_badge)
.setMessage(R.string.DonationsErrors__your_badge_could_not)
.setPositiveButton(R.string.Subscription__contact_support) { dialog, _ ->
dialog.dismiss()
requireActivity().finish()

View File

@@ -25,6 +25,7 @@ import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.subscription.LevelUpdate
import org.thoughtcrime.securesms.subscription.Subscriber
import org.thoughtcrime.securesms.subscription.Subscription
import org.thoughtcrime.securesms.util.InternetConnectionObserver
import org.thoughtcrime.securesms.util.PlatformCurrencyUtil
import org.thoughtcrime.securesms.util.livedata.Store
import org.whispersystems.signalservice.api.subscriptions.ActiveSubscription
@@ -49,8 +50,8 @@ class SubscribeViewModel(
private val activeSubscriptionSubject = PublishSubject.create<ActiveSubscription>()
init {
networkDisposable = donationPaymentRepository
.internetConnectionObserver()
networkDisposable = InternetConnectionObserver
.observe()
.distinctUntilChanged()
.subscribe { isConnected ->
if (isConnected) {
@@ -161,6 +162,20 @@ class SubscribeViewModel(
}
}
private fun cancelActiveSubscriptionIfNecessary(): Completable {
return Single.just(SignalStore.donationsValues().shouldCancelSubscriptionBeforeNextSubscribeAttempt).flatMapCompletable {
if (it) {
donationPaymentRepository.cancelActiveSubscription().doOnComplete {
SignalStore.donationsValues().setLastEndOfPeriod(0L)
SignalStore.donationsValues().clearLevelOperations()
SignalStore.donationsValues().shouldCancelSubscriptionBeforeNextSubscribeAttempt = false
}
} else {
Completable.complete()
}
}
}
fun cancel() {
store.update { it.copy(stage = SubscribeState.Stage.CANCELLING) }
disposables += donationPaymentRepository.cancelActiveSubscription().subscribeBy(
@@ -200,7 +215,10 @@ class SubscribeViewModel(
store.update { it.copy(stage = SubscribeState.Stage.PAYMENT_PIPELINE) }
val setup = ensureSubscriberId.andThen(continueSetup).onErrorResumeNext { Completable.error(DonationExceptions.SetupFailed(it)) }
val setup = ensureSubscriberId
.andThen(cancelActiveSubscriptionIfNecessary())
.andThen(continueSetup)
.onErrorResumeNext { Completable.error(DonationExceptions.SetupFailed(it)) }
setup.andThen(setLevel).subscribeBy(
onError = { throwable ->
@@ -218,9 +236,9 @@ class SubscribeViewModel(
}
}
override fun onError() {
override fun onError(googlePayException: GooglePayApi.GooglePayException) {
store.update { it.copy(stage = SubscribeState.Stage.READY) }
eventPublisher.onNext(DonationEvent.RequestTokenError)
eventPublisher.onNext(DonationEvent.RequestTokenError(googlePayException))
}
override fun onCancelled() {
@@ -232,7 +250,7 @@ class SubscribeViewModel(
fun updateSubscription() {
store.update { it.copy(stage = SubscribeState.Stage.PAYMENT_PIPELINE) }
donationPaymentRepository.setSubscriptionLevel(store.state.selectedSubscription!!.level.toString())
cancelActiveSubscriptionIfNecessary().andThen(donationPaymentRepository.setSubscriptionLevel(store.state.selectedSubscription!!.level.toString()))
.subscribeBy(
onComplete = {
store.update { it.copy(stage = SubscribeState.Stage.READY) }

View File

@@ -72,6 +72,7 @@ class ThanksForYourSupportBottomSheetDialogFragment : FixedRoundedCornerBottomSh
subhead.text = SpannableStringBuilder(getString(R.string.SubscribeThanksForYourSupportBottomSheetDialogFragment__youve_earned_a_boost_badge_help_signal))
.append(" ")
.append(getString(R.string.SubscribeThanksForYourSupportBottomSheetDialogFragment__you_can_also))
.append(" ")
.append(
SpanUtil.clickable(
getString(R.string.SubscribeThanksForYourSupportBottomSheetDialogFragment__become_a_montly_sustainer),

View File

@@ -9,9 +9,9 @@ import org.signal.core.util.logging.Log
import org.signal.storageservice.protos.groups.local.DecryptedGroup
import org.signal.storageservice.protos.groups.local.DecryptedPendingMember
import org.thoughtcrime.securesms.contacts.sync.DirectoryHelper
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.GroupDatabase
import org.thoughtcrime.securesms.database.MediaDatabase
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.model.IdentityRecord
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.groups.GroupId
@@ -40,27 +40,27 @@ class ConversationSettingsRepository(
return if (threadId <= 0) {
Optional.absent()
} else {
Optional.of(DatabaseFactory.getMediaDatabase(context).getGalleryMediaForThread(threadId, MediaDatabase.Sorting.Newest))
Optional.of(SignalDatabase.media.getGalleryMediaForThread(threadId, MediaDatabase.Sorting.Newest))
}
}
fun getThreadId(recipientId: RecipientId, consumer: (Long) -> Unit) {
SignalExecutors.BOUNDED.execute {
consumer(DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(recipientId))
consumer(SignalDatabase.threads.getThreadIdIfExistsFor(recipientId))
}
}
fun getThreadId(groupId: GroupId, consumer: (Long) -> Unit) {
SignalExecutors.BOUNDED.execute {
val recipientId = Recipient.externalGroupExact(context, groupId).id
consumer(DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(recipientId))
consumer(SignalDatabase.threads.getThreadIdIfExistsFor(recipientId))
}
}
fun isInternalRecipientDetailsEnabled(): Boolean = SignalStore.internalValues().recipientDetails()
fun hasGroups(consumer: (Boolean) -> Unit) {
SignalExecutors.BOUNDED.execute { consumer(DatabaseFactory.getGroupDatabase(context).activeGroupCount > 0) }
SignalExecutors.BOUNDED.execute { consumer(SignalDatabase.groups.activeGroupCount > 0) }
}
fun getIdentity(recipientId: RecipientId, consumer: (IdentityRecord?) -> Unit) {
@@ -72,8 +72,8 @@ class ConversationSettingsRepository(
fun getGroupsInCommon(recipientId: RecipientId, consumer: (List<Recipient>) -> Unit) {
SignalExecutors.BOUNDED.execute {
consumer(
DatabaseFactory
.getGroupDatabase(context)
SignalDatabase
.groups
.getPushGroupsContainingMember(recipientId)
.asSequence()
.filter { it.members.contains(Recipient.self().id) }
@@ -87,7 +87,7 @@ class ConversationSettingsRepository(
fun getGroupMembership(recipientId: RecipientId, consumer: (List<RecipientId>) -> Unit) {
SignalExecutors.BOUNDED.execute {
val groupDatabase = DatabaseFactory.getGroupDatabase(context)
val groupDatabase = SignalDatabase.groups
val groupRecords = groupDatabase.getPushGroupsContainingMember(recipientId)
val groupRecipients = ArrayList<RecipientId>(groupRecords.size)
for (groupRecord in groupRecords) {
@@ -109,13 +109,13 @@ class ConversationSettingsRepository(
fun setMuteUntil(recipientId: RecipientId, until: Long) {
SignalExecutors.BOUNDED.execute {
DatabaseFactory.getRecipientDatabase(context).setMuted(recipientId, until)
SignalDatabase.recipients.setMuted(recipientId, until)
}
}
fun getGroupCapacity(groupId: GroupId, consumer: (GroupCapacityResult) -> Unit) {
SignalExecutors.BOUNDED.execute {
val groupRecord: GroupDatabase.GroupRecord = DatabaseFactory.getGroupDatabase(context).getGroup(groupId).get()
val groupRecord: GroupDatabase.GroupRecord = SignalDatabase.groups.getGroup(groupId).get()
consumer(
if (groupRecord.isV2Group) {
val decryptedGroup: DecryptedGroup = groupRecord.requireV2GroupProperties().decryptedGroup
@@ -138,7 +138,7 @@ class ConversationSettingsRepository(
fun addMembers(groupId: GroupId, selected: List<RecipientId>, consumer: (GroupAddMembersResult) -> Unit) {
SignalExecutors.BOUNDED.execute {
val record: GroupDatabase.GroupRecord = DatabaseFactory.getGroupDatabase(context).getGroup(groupId).get()
val record: GroupDatabase.GroupRecord = SignalDatabase.groups.getGroup(groupId).get()
if (record.isAnnouncementGroup) {
val needsResolve = selected
@@ -171,7 +171,7 @@ class ConversationSettingsRepository(
fun setMuteUntil(groupId: GroupId, until: Long) {
SignalExecutors.BOUNDED.execute {
val recipientId = Recipient.externalGroupExact(context, groupId).id
DatabaseFactory.getRecipientDatabase(context).setMuted(recipientId, until)
SignalDatabase.recipients.setMuted(recipientId, until)
}
}

View File

@@ -1,6 +1,5 @@
package org.thoughtcrime.securesms.components.settings.conversation
import android.content.Context
import android.graphics.Color
import android.text.TextUtils
import android.widget.Toast
@@ -15,8 +14,7 @@ import org.thoughtcrime.securesms.components.settings.DSLSettingsAdapter
import org.thoughtcrime.securesms.components.settings.DSLSettingsFragment
import org.thoughtcrime.securesms.components.settings.DSLSettingsText
import org.thoughtcrime.securesms.components.settings.configure
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.groups.GroupId
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.recipients.Recipient
@@ -134,7 +132,7 @@ class InternalConversationSettingsFragment : DSLSettingsFragment(
MaterialAlertDialogBuilder(requireContext())
.setTitle("Are you sure?")
.setNegativeButton(android.R.string.cancel) { d, _ -> d.dismiss() }
.setPositiveButton(android.R.string.ok) { _, _ -> DatabaseFactory.getRecipientDatabase(requireContext()).setProfileSharing(recipient.id, false) }
.setPositiveButton(android.R.string.ok) { _, _ -> SignalDatabase.recipients.setProfileSharing(recipient.id, false) }
.show()
}
)
@@ -148,10 +146,10 @@ class InternalConversationSettingsFragment : DSLSettingsFragment(
.setNegativeButton(android.R.string.cancel) { d, _ -> d.dismiss() }
.setPositiveButton(android.R.string.ok) { _, _ ->
if (recipient.hasAci()) {
DatabaseFactory.getSessionDatabase(context).deleteAllFor(recipient.requireAci().toString())
SignalDatabase.sessions.deleteAllFor(recipient.requireAci().toString())
}
if (recipient.hasE164()) {
DatabaseFactory.getSessionDatabase(context).deleteAllFor(recipient.requireE164())
SignalDatabase.sessions.deleteAllFor(recipient.requireE164())
}
}
.show()
@@ -230,9 +228,8 @@ class InternalConversationSettingsFragment : DSLSettingsFragment(
liveRecipient.observeForever(this)
SignalExecutors.BOUNDED.execute {
val context: Context = ApplicationDependencies.getApplication()
val threadId: Long? = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipientId)
val groupId: GroupId? = DatabaseFactory.getGroupDatabase(context).getGroup(recipientId).transform { it.id }.orNull()
val threadId: Long? = SignalDatabase.threads.getThreadIdFor(recipientId)
val groupId: GroupId? = SignalDatabase.groups.getGroup(recipientId).transform { it.id }.orNull()
store.update { state -> state.copy(threadId = threadId, groupId = groupId) }
}
}

View File

@@ -2,8 +2,8 @@ package org.thoughtcrime.securesms.components.settings.conversation.sounds
import android.content.Context
import org.signal.core.util.concurrent.SignalExecutors
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.RecipientDatabase
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.notifications.NotificationChannels
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
@@ -12,13 +12,13 @@ class SoundsAndNotificationsSettingsRepository(private val context: Context) {
fun setMuteUntil(recipientId: RecipientId, muteUntil: Long) {
SignalExecutors.BOUNDED.execute {
DatabaseFactory.getRecipientDatabase(context).setMuted(recipientId, muteUntil)
SignalDatabase.recipients.setMuted(recipientId, muteUntil)
}
}
fun setMentionSetting(recipientId: RecipientId, mentionSetting: RecipientDatabase.MentionSetting) {
SignalExecutors.BOUNDED.execute {
DatabaseFactory.getRecipientDatabase(context).setMentionSetting(recipientId, mentionSetting)
SignalDatabase.recipients.setMentionSetting(recipientId, mentionSetting)
}
}

View File

@@ -4,8 +4,8 @@ import android.content.Context
import android.net.Uri
import androidx.annotation.WorkerThread
import org.signal.core.util.concurrent.SignalExecutors
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.RecipientDatabase
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.notifications.NotificationChannels
import org.thoughtcrime.securesms.recipients.Recipient
@@ -20,7 +20,7 @@ class CustomNotificationsSettingsRepository(context: Context) {
fun initialize(recipientId: RecipientId, onInitializationComplete: () -> Unit) {
executor.execute {
val recipient = Recipient.resolved(recipientId)
val database = DatabaseFactory.getRecipientDatabase(context)
val database = SignalDatabase.recipients
if (NotificationChannels.supported() && recipient.notificationChannel != null) {
database.setMessageRingtone(recipient.id, NotificationChannels.getMessageRingtone(context, recipient))
@@ -47,14 +47,14 @@ class CustomNotificationsSettingsRepository(context: Context) {
executor.execute {
val recipient: Recipient = Recipient.resolved(recipientId)
DatabaseFactory.getRecipientDatabase(context).setMessageVibrate(recipient.id, vibrateState)
SignalDatabase.recipients.setMessageVibrate(recipient.id, vibrateState)
NotificationChannels.updateMessageVibrate(context, recipient, vibrateState)
}
}
fun setCallingVibrate(recipientId: RecipientId, vibrateState: RecipientDatabase.VibrateState) {
executor.execute {
DatabaseFactory.getRecipientDatabase(context).setCallVibrate(recipientId, vibrateState)
SignalDatabase.recipients.setCallVibrate(recipientId, vibrateState)
}
}
@@ -64,7 +64,7 @@ class CustomNotificationsSettingsRepository(context: Context) {
val defaultValue = SignalStore.settings().messageNotificationSound
val newValue: Uri? = if (defaultValue == sound) null else sound ?: Uri.EMPTY
DatabaseFactory.getRecipientDatabase(context).setMessageRingtone(recipient.id, newValue)
SignalDatabase.recipients.setMessageRingtone(recipient.id, newValue)
NotificationChannels.updateMessageRingtone(context, recipient, newValue)
}
}
@@ -74,7 +74,7 @@ class CustomNotificationsSettingsRepository(context: Context) {
val defaultValue = SignalStore.settings().callRingtone
val newValue: Uri? = if (defaultValue == sound) null else sound ?: Uri.EMPTY
DatabaseFactory.getRecipientDatabase(context).setCallRingtone(recipientId, newValue)
SignalDatabase.recipients.setCallRingtone(recipientId, newValue)
}
}
@@ -82,13 +82,13 @@ class CustomNotificationsSettingsRepository(context: Context) {
private fun createCustomNotificationChannel(recipientId: RecipientId) {
val recipient: Recipient = Recipient.resolved(recipientId)
val channelId = NotificationChannels.createChannelFor(context, recipient)
DatabaseFactory.getRecipientDatabase(context).setNotificationChannel(recipient.id, channelId)
SignalDatabase.recipients.setNotificationChannel(recipient.id, channelId)
}
@WorkerThread
private fun deleteCustomNotificationChannel(recipientId: RecipientId) {
val recipient: Recipient = Recipient.resolved(recipientId)
DatabaseFactory.getRecipientDatabase(context).setNotificationChannel(recipient.id, null)
SignalDatabase.recipients.setNotificationChannel(recipient.id, null)
NotificationChannels.deleteChannelFor(context, recipient)
}
}

View File

@@ -39,7 +39,7 @@ object AsyncSwitch {
super.bind(model)
switchWidget.isEnabled = model.isEnabled
switchWidget.isChecked = model.isChecked
itemView.isEnabled = !model.isProcessing
itemView.isEnabled = !model.isProcessing && model.isEnabled
switcher.displayedChild = if (model.isProcessing) 1 else 0
itemView.setOnClickListener {

View File

@@ -14,7 +14,7 @@ import com.google.android.exoplayer2.MediaMetadata;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
@@ -52,7 +52,7 @@ class VoiceNoteMediaItemFactory {
@NonNull Uri draftUri)
{
Recipient threadRecipient = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadId);
Recipient threadRecipient = SignalDatabase.threads().getRecipientForThreadId(threadId);
if (threadRecipient == null) {
threadRecipient = Recipient.UNKNOWN;
}
@@ -80,11 +80,11 @@ class VoiceNoteMediaItemFactory {
@Nullable static MediaItem buildMediaItem(@NonNull Context context,
@NonNull MessageRecord messageRecord)
{
int startingPosition = DatabaseFactory.getMmsSmsDatabase(context)
int startingPosition = SignalDatabase.mmsSms()
.getMessagePositionInConversation(messageRecord.getThreadId(),
messageRecord.getDateReceived());
Recipient threadRecipient = Objects.requireNonNull(DatabaseFactory.getThreadDatabase(context)
Recipient threadRecipient = Objects.requireNonNull(SignalDatabase.threads()
.getRecipientForThreadId(messageRecord.getThreadId()));
Recipient sender = messageRecord.isOutgoing() ? Recipient.self() : messageRecord.getIndividualRecipient();
Recipient avatarRecipient = threadRecipient.isGroup() ? threadRecipient : sender;

View File

@@ -23,8 +23,8 @@ import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector;
import org.signal.core.util.ThreadUtil;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.NoSuchMessageException;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.util.MessageRecordUtil;
import org.thoughtcrime.securesms.util.Util;
@@ -240,8 +240,8 @@ final class VoiceNotePlaybackPreparer implements MediaSessionConnector.PlaybackP
private @NonNull List<MediaItem> loadMediaItemsForSinglePlayback(long messageId) {
try {
MessageRecord messageRecord = DatabaseFactory.getMmsDatabase(context)
.getMessageRecord(messageId);
MessageRecord messageRecord = SignalDatabase.mms()
.getMessageRecord(messageId);
if (!MessageRecordUtil.hasAudio(messageRecord)) {
Log.w(TAG, "Message does not contain audio.");
@@ -268,7 +268,7 @@ final class VoiceNotePlaybackPreparer implements MediaSessionConnector.PlaybackP
@WorkerThread
private @NonNull List<MediaItem> loadMediaItemsForConsecutivePlayback(long messageId) {
try {
List<MessageRecord> recordsAfter = DatabaseFactory.getMmsSmsDatabase(context)
List<MessageRecord> recordsAfter = SignalDatabase.mmsSms()
.getMessagesAfterVoiceNoteInclusive(messageId, LIMIT);
return buildFilteredMessageRecordList(recordsAfter).stream()

View File

@@ -29,8 +29,8 @@ import com.google.android.exoplayer2.ui.PlayerNotificationManager;
import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessageDatabase;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.MessageId;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobs.MultiDeviceViewedUpdateJob;
@@ -241,7 +241,7 @@ public class VoiceNotePlaybackService extends MediaBrowserServiceCompat {
}
long messageId = extras.getLong(VoiceNoteMediaItemFactory.EXTRA_MESSAGE_ID);
RecipientId recipientId = RecipientId.from(extras.getString(VoiceNoteMediaItemFactory.EXTRA_INDIVIDUAL_RECIPIENT_ID));
MessageDatabase messageDatabase = DatabaseFactory.getMmsDatabase(this);
MessageDatabase messageDatabase = SignalDatabase.mms();
MessageDatabase.MarkedMessageInfo markedMessageInfo = messageDatabase.setIncomingMessageViewed(messageId);

View File

@@ -7,8 +7,8 @@ import androidx.annotation.WorkerThread;
import androidx.core.util.Consumer;
import org.signal.core.util.concurrent.SignalExecutors;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.identity.IdentityRecordList;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.recipients.Recipient;
@@ -30,7 +30,7 @@ class WebRtcCallRepository {
List<Recipient> recipients;
if (recipient.isGroup()) {
recipients = DatabaseFactory.getGroupDatabase(context).getGroupMembers(recipient.requireGroupId(), GroupDatabase.MemberSet.FULL_MEMBERS_EXCLUDING_SELF);
recipients = SignalDatabase.groups().getGroupMembers(recipient.requireGroupId(), GroupDatabase.MemberSet.FULL_MEMBERS_EXCLUDING_SELF);
} else {
recipients = Collections.singletonList(recipient);
}

View File

@@ -26,39 +26,18 @@ import android.os.Parcelable;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.PhoneLookup;
import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.contactshare.Contact;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
import org.thoughtcrime.securesms.profiles.ProfileName;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.CursorUtil;
import org.thoughtcrime.securesms.util.SqlUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
/**
* This class was originally a layer of indirection between
* ContactAccessorNewApi and ContactAccessorOldApi, which corresponded

View File

@@ -11,8 +11,8 @@ import androidx.annotation.NonNull;
import androidx.annotation.WorkerThread;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.CursorUtil;
@@ -102,7 +102,7 @@ public class ContactRepository {
}};
public ContactRepository(@NonNull Context context) {
this.recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
this.recipientDatabase = SignalDatabase.recipients();
this.noteToSelfTitle = context.getString(R.string.note_to_self);
this.context = context.getApplicationContext();
}

View File

@@ -24,8 +24,8 @@ import androidx.annotation.NonNull;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.phonenumbers.NumberUtil;
@@ -181,7 +181,7 @@ public class ContactsCursorLoader extends AbstractContactsCursorLoader {
}
private Cursor getRecentConversationsCursor(boolean groupsOnly) {
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(getContext());
ThreadDatabase threadDatabase = SignalDatabase.threads();
MatrixCursor recentConversations = ContactsCursorRows.createMatrixCursor(RECENT_CONVERSATION_MAX);
try (Cursor rawConversations = threadDatabase.getRecentConversationList(RECENT_CONVERSATION_MAX, flagSet(mode, DisplayMode.FLAG_INACTIVE_GROUPS), groupsOnly, hideGroupsV1(mode), !smsEnabled(mode))) {
@@ -210,7 +210,7 @@ public class ContactsCursorLoader extends AbstractContactsCursorLoader {
private Cursor getGroupsCursor() {
MatrixCursor groupContacts = ContactsCursorRows.createMatrixCursor();
try (GroupDatabase.Reader reader = DatabaseFactory.getGroupDatabase(getContext()).getGroupsFilteredByTitle(getFilter(), flagSet(mode, DisplayMode.FLAG_INACTIVE_GROUPS), hideGroupsV1(mode), !smsEnabled(mode))) {
try (GroupDatabase.Reader reader = SignalDatabase.groups().getGroupsFilteredByTitle(getFilter(), flagSet(mode, DisplayMode.FLAG_INACTIVE_GROUPS), hideGroupsV1(mode), !smsEnabled(mode))) {
GroupDatabase.GroupRecord groupRecord;
while ((groupRecord = reader.getNext()) != null) {
groupContacts.addRow(ContactsCursorRows.forGroup(groupRecord));

View File

@@ -7,23 +7,20 @@ import android.content.Context;
import android.content.SyncResult;
import android.os.Bundle;
import androidx.annotation.NonNull;
import com.annimon.stream.Stream;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.contacts.sync.DirectoryHelper;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.SetUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class ContactsSyncAdapter extends AbstractThreadedSyncAdapter {
@@ -51,7 +48,7 @@ public class ContactsSyncAdapter extends AbstractThreadedSyncAdapter {
}
Set<String> allSystemNumbers = ContactAccessor.getInstance().getAllContactsWithNumbers(context);
Set<String> knownSystemNumbers = DatabaseFactory.getRecipientDatabase(context).getAllPhoneNumbers();
Set<String> knownSystemNumbers = SignalDatabase.recipients().getAllPhoneNumbers();
Set<String> unknownSystemNumbers = SetUtil.difference(allSystemNumbers, knownSystemNumbers);
if (unknownSystemNumbers.size() > FULL_SYNC_THRESHOLD) {

View File

@@ -9,13 +9,12 @@ import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessageDatabase;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
import java.util.List;
@@ -53,7 +52,7 @@ public class TurnOffContactJoinedNotificationsActivity extends AppCompatActivity
private void handlePositiveAction(@NonNull DialogInterface dialog) {
SimpleTask.run(getLifecycle(), () -> {
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(this);
ThreadDatabase threadDatabase = SignalDatabase.threads();
List<MessageDatabase.MarkedMessageInfo> marked = threadDatabase.setRead(getIntent().getLongExtra(EXTRA_THREAD_ID, -1), false);
MarkReadReceiver.process(this, marked);

View File

@@ -8,8 +8,8 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.signal.core.util.Conversions;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.profiles.AvatarHelper;
import org.whispersystems.libsignal.util.guava.Optional;
@@ -30,7 +30,7 @@ public final class GroupRecordContactPhoto implements ContactPhoto {
@Override
public InputStream openInputStream(Context context) throws IOException {
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
GroupDatabase groupDatabase = SignalDatabase.groups();
Optional<GroupDatabase.GroupRecord> groupRecord = groupDatabase.getGroup(groupId);
if (!groupRecord.isPresent() || !AvatarHelper.hasAvatar(context, groupRecord.get().getRecipientId())) {

View File

@@ -24,11 +24,11 @@ import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.contacts.ContactAccessor;
import org.thoughtcrime.securesms.contacts.ContactsDatabase;
import org.thoughtcrime.securesms.crypto.SessionUtil;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessageDatabase.InsertResult;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase.BulkOperationsHandle;
import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.whispersystems.signalservice.api.push.ACI;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
@@ -98,7 +98,7 @@ public class DirectoryHelper {
return;
}
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
RecipientDatabase recipientDatabase = SignalDatabase.recipients();
Set<String> databaseNumbers = sanitizeNumbers(recipientDatabase.getAllPhoneNumbers());
Set<String> systemNumbers = sanitizeNumbers(ContactAccessor.getInstance().getAllContactsWithNumbers(context));
@@ -109,7 +109,7 @@ public class DirectoryHelper {
@WorkerThread
public static void refreshDirectoryFor(@NonNull Context context, @NonNull List<Recipient> recipients, boolean notifyOfNewUsers) throws IOException {
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
RecipientDatabase recipientDatabase = SignalDatabase.recipients();
for (Recipient recipient : recipients) {
if (recipient.hasAci() && !recipient.hasE164()) {
@@ -132,7 +132,7 @@ public class DirectoryHelper {
@WorkerThread
public static RegisteredState refreshDirectoryFor(@NonNull Context context, @NonNull Recipient recipient, boolean notifyOfNewUsers) throws IOException {
Stopwatch stopwatch = new Stopwatch("single");
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
RecipientDatabase recipientDatabase = SignalDatabase.recipients();
RegisteredState originalRegisteredState = recipient.resolve().getRegistered();
RegisteredState newRegisteredState;
@@ -221,7 +221,7 @@ public class DirectoryHelper {
@WorkerThread
private static void refreshNumbers(@NonNull Context context, @NonNull Set<String> databaseNumbers, @NonNull Set<String> systemNumbers, boolean notifyOfNewUsers, boolean removeSystemContactEntryForMissing) throws IOException {
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
RecipientDatabase recipientDatabase = SignalDatabase.recipients();
Set<String> allNumbers = SetUtil.union(databaseNumbers, systemNumbers);
if (allNumbers.isEmpty()) {
@@ -325,7 +325,7 @@ public class DirectoryHelper {
}
try {
ContactsDatabase contactsDatabase = DatabaseFactory.getContactsDatabase(context);
ContactsDatabase contactsDatabase = SignalDatabase.contacts();
List<String> activeAddresses = Stream.of(activeIds)
.map(Recipient::resolved)
.filter(Recipient::hasE164)
@@ -342,7 +342,7 @@ public class DirectoryHelper {
}
private static void syncRecipientInfoWithSystemContacts(@NonNull Context context, @NonNull Map<String, String> rewrites) {
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
RecipientDatabase recipientDatabase = SignalDatabase.recipients();
BulkOperationsHandle handle = recipientDatabase.beginBulkSystemContactUpdate();
try (Cursor cursor = ContactAccessor.getInstance().getAllSystemContacts(context)) {
@@ -406,7 +406,7 @@ public class DirectoryHelper {
}
if (NotificationChannels.supported()) {
try (RecipientDatabase.RecipientReader recipients = DatabaseFactory.getRecipientDatabase(context).getRecipientsWithNotificationChannels()) {
try (RecipientDatabase.RecipientReader recipients = SignalDatabase.recipients().getRecipientsWithNotificationChannels()) {
Recipient recipient;
while ((recipient = recipients.getNext()) != null) {
NotificationChannels.updateContactChannelName(context, recipient);
@@ -480,7 +480,7 @@ public class DirectoryHelper {
recipient.hasAUserSetDisplayName(context))
{
IncomingJoinedMessage message = new IncomingJoinedMessage(recipient.getId());
Optional<InsertResult> insertResult = DatabaseFactory.getSmsDatabase(context).insertMessageInbox(message);
Optional<InsertResult> insertResult = SignalDatabase.sms().insertMessageInbox(message);
if (insertResult.isPresent()) {
int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
@@ -549,9 +549,9 @@ public class DirectoryHelper {
}
private static boolean hasCommunicatedWith(@NonNull Context context, @NonNull Recipient recipient) {
return DatabaseFactory.getThreadDatabase(context).hasThread(recipient.getId()) ||
(recipient.hasAci() && DatabaseFactory.getSessionDatabase(context).hasSessionFor(recipient.requireAci().toString())) ||
(recipient.hasE164() && DatabaseFactory.getSessionDatabase(context).hasSessionFor(recipient.requireE164()));
return SignalDatabase.threads().hasThread(recipient.getId()) ||
(recipient.hasAci() && SignalDatabase.sessions().hasSessionFor(recipient.requireAci().toString())) ||
(recipient.hasE164() && SignalDatabase.sessions().hasSessionFor(recipient.requireE164()));
}
static class DirectoryResult {

View File

@@ -17,7 +17,7 @@ import androidx.recyclerview.widget.RecyclerView;
import org.thoughtcrime.securesms.PassphraseRequiredActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme;
@@ -78,8 +78,8 @@ public class ContactShareEditActivity extends PassphraseRequiredActivity impleme
contactList.setAdapter(contactAdapter);
SharedContactRepository contactRepository = new SharedContactRepository(this,
AsyncTask.THREAD_POOL_EXECUTOR,
DatabaseFactory.getContactsDatabase(this));
AsyncTask.THREAD_POOL_EXECUTOR,
SignalDatabase.contacts());
viewModel = ViewModelProviders.of(this, new Factory(contactUris, contactRepository)).get(ContactShareEditViewModel.class);
viewModel.getContacts().observe(this, contacts -> {

View File

@@ -152,7 +152,6 @@ import org.thoughtcrime.securesms.conversation.ui.groupcall.GroupCallViewModel;
import org.thoughtcrime.securesms.conversation.ui.mentions.MentionsPickerViewModel;
import org.thoughtcrime.securesms.crypto.ReentrantSessionLock;
import org.thoughtcrime.securesms.crypto.SecurityEvent;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.DraftDatabase;
import org.thoughtcrime.securesms.database.DraftDatabase.Draft;
import org.thoughtcrime.securesms.database.DraftDatabase.Drafts;
@@ -163,6 +162,7 @@ import org.thoughtcrime.securesms.database.MentionUtil.UpdatedBodyAndMentions;
import org.thoughtcrime.securesms.database.MmsSmsColumns.Types;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.database.identity.IdentityRecordList;
import org.thoughtcrime.securesms.database.model.IdentityRecord;
@@ -1195,8 +1195,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
DatabaseFactory.getRecipientDatabase(ConversationActivity.this)
.setMuted(recipient.getId(), until);
SignalDatabase.recipients().setMuted(recipient.getId(), until);
return null;
}
@@ -1222,9 +1221,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
DatabaseFactory.getRecipientDatabase(ConversationActivity.this)
.setMuted(recipient.getId(), 0);
SignalDatabase.recipients().setMuted(recipient.getId(), 0);
return null;
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
@@ -1358,8 +1355,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
DatabaseFactory.getThreadDatabase(ConversationActivity.this)
.setDistributionType(threadId, ThreadDatabase.DistributionTypes.BROADCAST);
SignalDatabase.threads().setDistributionType(threadId, ThreadDatabase.DistributionTypes.BROADCAST);
return null;
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
@@ -1374,8 +1370,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
DatabaseFactory.getThreadDatabase(ConversationActivity.this)
.setDistributionType(threadId, ThreadDatabase.DistributionTypes.CONVERSATION);
SignalDatabase.threads().setDistributionType(threadId, ThreadDatabase.DistributionTypes.CONVERSATION);
return null;
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
@@ -1692,7 +1687,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
@Override
protected Pair<Drafts, CharSequence> doInBackground(Void... params) {
Context context = ConversationActivity.this;
DraftDatabase draftDatabase = DatabaseFactory.getDraftDatabase(context);
DraftDatabase draftDatabase = SignalDatabase.drafts();
Drafts results = draftDatabase.getDrafts(threadId);
Draft mentionsDraft = results.getDraftOfType(Draft.MENTION);
Spannable updatedText = null;
@@ -1925,8 +1920,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
List<Recipient> recipients;
if (params[0].isGroup()) {
recipients = DatabaseFactory.getGroupDatabase(ConversationActivity.this)
.getGroupMembers(params[0].requireGroupId(), GroupDatabase.MemberSet.FULL_MEMBERS_EXCLUDING_SELF);
recipients = SignalDatabase.groups().getGroupMembers(params[0].requireGroupId(), GroupDatabase.MemberSet.FULL_MEMBERS_EXCLUDING_SELF);
} else {
recipients = Collections.singletonList(params[0]);
}
@@ -2456,7 +2450,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
}
if (this.threadId == -1) {
SimpleTask.run(() -> DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipient.getId()), threadId -> {
SimpleTask.run(() -> SignalDatabase.threads().getThreadIdIfExistsFor(recipient.getId()), threadId -> {
if (this.threadId != threadId) {
Log.d(TAG, "Thread id changed via recipient change");
this.threadId = threadId;
@@ -2636,8 +2630,8 @@ public class ConversationActivity extends PassphraseRequiredActivity
}
}
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(ConversationActivity.this);
DraftDatabase draftDatabase = DatabaseFactory.getDraftDatabase(ConversationActivity.this);
ThreadDatabase threadDatabase = SignalDatabase.threads();
DraftDatabase draftDatabase = SignalDatabase.drafts();
long threadId = params[0];
if (drafts.size() > 0) {
@@ -2732,7 +2726,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
private boolean isActiveGroup() {
if (!isGroupConversation()) return false;
Optional<GroupRecord> record = DatabaseFactory.getGroupDatabase(this).getGroup(getRecipient().getId());
Optional<GroupRecord> record = SignalDatabase.groups().getGroup(getRecipient().getId());
return record.isPresent() && record.get().isActive();
}
@@ -2779,7 +2773,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
new AsyncTask<Long, Void, Void>() {
@Override
protected Void doInBackground(Long... params) {
DatabaseFactory.getThreadDatabase(ConversationActivity.this).setLastSeen(params[0]);
SignalDatabase.threads().setLastSeen(params[0]);
return null;
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, threadId);
@@ -2885,7 +2879,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
SimpleTask.run(() -> {
long resultId = MessageSender.sendPushWithPreUploadedMedia(this, secureMessage, result.getPreUploadResults(), thread, null);
int deleted = DatabaseFactory.getAttachmentDatabase(this).deleteAbandonedPreuploadedAttachments();
int deleted = SignalDatabase.attachments().deleteAbandonedPreuploadedAttachments();
Log.i(TAG, "Deleted " + deleted + " abandoned attachments.");
return resultId;
@@ -3083,7 +3077,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(ConversationActivity.this);
RecipientDatabase recipientDatabase = SignalDatabase.recipients();
recipientDatabase.setDefaultSubscriptionId(recipient.getId(), transportOption.getSimSubscriptionId().or(-1));
@@ -3258,7 +3252,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
sendSticker(new StickerLocator(stickerRecord.getPackId(), stickerRecord.getPackKey(), stickerRecord.getStickerId(), stickerRecord.getEmoji()), stickerRecord.getContentType(), stickerRecord.getUri(), stickerRecord.getSize(), clearCompose);
SignalExecutors.BOUNDED.execute(() ->
DatabaseFactory.getStickerDatabase(getApplicationContext())
SignalDatabase.stickers()
.updateStickerLastUsedTime(stickerRecord.getRowId(), System.currentTimeMillis())
);
}
@@ -3688,8 +3682,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
SimpleTask.run(() -> {
//noinspection CodeBlock2Expr
return DatabaseFactory.getMmsSmsDatabase(this)
.checkMessageExists(reactionDelegate.getMessageRecord());
return SignalDatabase.mmsSms().checkMessageExists(reactionDelegate.getMessageRecord());
}, messageExists -> {
if (!messageExists) {
reactionDelegate.hide();
@@ -3956,7 +3949,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
Context context = getApplicationContext();
MessageRecord messageRecord = DatabaseFactory.getMmsSmsDatabase(context).getMessageFor(quoteId.getId(), quoteId.getAuthor());
MessageRecord messageRecord = SignalDatabase.mmsSms().getMessageFor(quoteId.getId(), quoteId.getAuthor());
if (messageRecord == null) {
return null;
}

View File

@@ -17,7 +17,7 @@ import org.thoughtcrime.securesms.components.AvatarImageView;
import org.thoughtcrime.securesms.components.emoji.EmojiTextView;
import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto;
import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.LongClickMovementMethod;
@@ -70,8 +70,7 @@ public class ConversationBannerView extends ConstraintLayout {
if (recipient != null && recipient.shouldBlurAvatar() && recipient.getContactPhoto() != null) {
tapToView.setVisibility(VISIBLE);
tapToView.setOnClickListener(v -> {
SignalExecutors.BOUNDED.execute(() -> DatabaseFactory.getRecipientDatabase(getContext().getApplicationContext())
.manuallyShowAvatar(recipient.getId()));
SignalExecutors.BOUNDED.execute(() -> SignalDatabase.recipients().manuallyShowAvatar(recipient.getId()));
});
} else {
tapToView.setVisibility(GONE);

View File

@@ -12,9 +12,9 @@ import org.signal.paging.PagedDataSource;
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
import org.thoughtcrime.securesms.conversation.ConversationData.MessageRequestData;
import org.thoughtcrime.securesms.conversation.ConversationMessage.ConversationMessageFactory;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessageDatabase;
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.InMemoryMessageRecord;
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
import org.thoughtcrime.securesms.database.model.Mention;
@@ -57,7 +57,7 @@ class ConversationDataSource implements PagedDataSource<MessageId, ConversationM
@Override
public int size() {
long startTime = System.currentTimeMillis();
int size = DatabaseFactory.getMmsSmsDatabase(context).getConversationCount(threadId) +
int size = SignalDatabase.mmsSms().getConversationCount(threadId) +
(messageRequestData.includeWarningUpdateMessage() ? 1 : 0) +
(showUniversalExpireTimerUpdate ? 1 : 0);
@@ -69,7 +69,7 @@ class ConversationDataSource implements PagedDataSource<MessageId, ConversationM
@Override
public @NonNull List<ConversationMessage> load(int start, int length, @NonNull CancellationSignal cancellationSignal) {
Stopwatch stopwatch = new Stopwatch("load(" + start + ", " + length + "), thread " + threadId);
MmsSmsDatabase db = DatabaseFactory.getMmsSmsDatabase(context);
MmsSmsDatabase db = SignalDatabase.mmsSms();
List<MessageRecord> records = new ArrayList<>(length);
MentionHelper mentionHelper = new MentionHelper();
AttachmentHelper attachmentHelper = new AttachmentHelper();
@@ -123,7 +123,7 @@ class ConversationDataSource implements PagedDataSource<MessageId, ConversationM
@Override
public @Nullable ConversationMessage load(@NonNull MessageId messageId) {
Stopwatch stopwatch = new Stopwatch("load(" + messageId + "), thread " + threadId);
MessageDatabase database = messageId.isMms() ? DatabaseFactory.getMmsDatabase(context) : DatabaseFactory.getSmsDatabase(context);
MessageDatabase database = messageId.isMms() ? SignalDatabase.mms() : SignalDatabase.sms();
MessageRecord record = database.getMessageRecordOrNull(messageId.getId());
stopwatch.split("message");
@@ -132,20 +132,20 @@ class ConversationDataSource implements PagedDataSource<MessageId, ConversationM
if (record != null) {
List<Mention> mentions;
if (messageId.isMms()) {
mentions = DatabaseFactory.getMentionDatabase(context).getMentionsForMessage(messageId.getId());
mentions = SignalDatabase.mentions().getMentionsForMessage(messageId.getId());
} else {
mentions = Collections.emptyList();
}
stopwatch.split("mentions");
List<ReactionRecord> reactions = DatabaseFactory.getReactionDatabase(context).getReactions(messageId);
List<ReactionRecord> reactions = SignalDatabase.reactions().getReactions(messageId);
record = ReactionHelper.recordWithReactions(record, reactions);
stopwatch.split("reactions");
if (messageId.isMms()) {
List<DatabaseAttachment> attachments = DatabaseFactory.getAttachmentDatabase(context).getAttachmentsForMessage(messageId.getId());
List<DatabaseAttachment> attachments = SignalDatabase.attachments().getAttachmentsForMessage(messageId.getId());
if (attachments.size() > 0) {
record = ((MediaMmsMessageRecord) record).withAttachments(context, attachments);
}
@@ -179,7 +179,7 @@ class ConversationDataSource implements PagedDataSource<MessageId, ConversationM
}
void fetchMentions(Context context) {
messageIdToMentions = DatabaseFactory.getMentionDatabase(context).getMentionsForMessages(messageIds);
messageIdToMentions = SignalDatabase.mentions().getMentionsForMessages(messageIds);
}
@Nullable List<Mention> getMentions(long id) {
@@ -199,7 +199,7 @@ class ConversationDataSource implements PagedDataSource<MessageId, ConversationM
}
void fetchAttachments(Context context) {
messageIdToAttachments = DatabaseFactory.getAttachmentDatabase(context).getAttachmentsForMessages(messageIds);
messageIdToAttachments = SignalDatabase.attachments().getAttachmentsForMessages(messageIds);
}
@NonNull List<MessageRecord> buildUpdatedModels(@NonNull Context context, @NonNull List<MessageRecord> records) {
@@ -229,7 +229,7 @@ class ConversationDataSource implements PagedDataSource<MessageId, ConversationM
}
void fetchReactions(Context context) {
messageIdToReactions = DatabaseFactory.getReactionDatabase(context).getReactionsForMessages(messageIds);
messageIdToReactions = SignalDatabase.reactions().getReactionsForMessages(messageIds);
}
@NonNull List<MessageRecord> buildUpdatedModels(@NonNull Context context, @NonNull List<MessageRecord> records) {

View File

@@ -94,9 +94,9 @@ import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectFor
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragmentArgs;
import org.thoughtcrime.securesms.conversation.ui.error.EnableCallNotificationSettingsDialog;
import org.thoughtcrime.securesms.conversation.ui.error.SafetyNumberChangeDialog;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessageDatabase;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.model.InMemoryMessageRecord;
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
@@ -439,7 +439,7 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
} else {
lastVisibleMessageTimestamp = 0;
}
SignalExecutors.BOUNDED.submit(() -> DatabaseFactory.getThreadDatabase(requireContext()).setLastScrolled(threadId, lastVisibleMessageTimestamp));
SignalExecutors.BOUNDED.submit(() -> SignalDatabase.threads().setLastScrolled(threadId, lastVisibleMessageTimestamp));
}
@Override
@@ -860,9 +860,9 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
boolean threadDeleted;
if (messageRecord.isMms()) {
threadDeleted = DatabaseFactory.getMmsDatabase(context).deleteMessage(messageRecord.getId());
threadDeleted = SignalDatabase.mms().deleteMessage(messageRecord.getId());
} else {
threadDeleted = DatabaseFactory.getSmsDatabase(context).deleteMessage(messageRecord.getId());
threadDeleted = SignalDatabase.sms().deleteMessage(messageRecord.getId());
}
if (threadDeleted) {
@@ -1076,8 +1076,7 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
@SuppressWarnings("CodeBlock2Expr")
public void jumpToMessage(@NonNull RecipientId author, long timestamp, @Nullable Runnable onMessageNotFound) {
SimpleTask.run(getLifecycle(), () -> {
return DatabaseFactory.getMmsSmsDatabase(getContext())
.getMessagePositionInConversation(threadId, timestamp, author);
return SignalDatabase.mmsSms().getMessagePositionInConversation(threadId, timestamp, author);
}, p -> moveToPosition(p + (isTypingIndicatorShowing() ? 1 : 0), onMessageNotFound));
}
@@ -1142,8 +1141,7 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
private void scrollToNextMention() {
SimpleTask.run(getViewLifecycleOwner().getLifecycle(), () -> {
MessageDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(ApplicationDependencies.getApplication());
return mmsDatabase.getOldestUnreadMentionDetails(threadId);
return SignalDatabase.mms().getOldestUnreadMentionDetails(threadId);
}, (pair) -> {
if (pair != null) {
jumpToMessage(pair.first(), pair.second(), () -> {});
@@ -1366,10 +1364,9 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
}
SimpleTask.run(getLifecycle(), () -> {
return DatabaseFactory.getMmsSmsDatabase(getContext())
.getQuotedMessagePosition(threadId,
messageRecord.getQuote().getId(),
messageRecord.getQuote().getAuthor());
return SignalDatabase.mmsSms().getQuotedMessagePosition(threadId,
messageRecord.getQuote().getId(),
messageRecord.getQuote().getAuthor());
}, p -> moveToPosition(p + (isTypingIndicatorShowing() ? 1 : 0), () -> {
Toast.makeText(getContext(), R.string.ConversationFragment_quoted_message_no_longer_available, Toast.LENGTH_SHORT).show();
}));
@@ -1419,7 +1416,7 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
.withMimeType(thumbnailSlide.getContentType())
.createForSingleSessionOnDisk(requireContext());
DatabaseFactory.getAttachmentDatabase(requireContext()).deleteAttachmentFilesForViewOnceMessage(messageRecord.getId());
SignalDatabase.attachments().deleteAttachmentFilesForViewOnceMessage(messageRecord.getId());
ApplicationDependencies.getViewOnceMessageManager().scheduleIfNecessary();
@@ -1435,7 +1432,7 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
} else {
Log.w(TAG, "Failed to open view-once photo. Showing a toast and deleting the attachments for the message just in case.");
Toast.makeText(requireContext(), R.string.ConversationFragment_failed_to_open_message, Toast.LENGTH_SHORT).show();
SignalExecutors.BOUNDED.execute(() -> DatabaseFactory.getAttachmentDatabase(requireContext()).deleteAttachmentFilesForViewOnceMessage(messageRecord.getId()));
SignalExecutors.BOUNDED.execute(() -> SignalDatabase.attachments().deleteAttachmentFilesForViewOnceMessage(messageRecord.getId()));
}
});
}

View File

@@ -15,9 +15,9 @@ import androidx.lifecycle.ViewModelProvider;
import com.annimon.stream.Stream;
import org.signal.core.util.concurrent.SignalExecutors;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupChangeBusyException;
import org.thoughtcrime.securesms.groups.GroupChangeFailedException;
@@ -81,7 +81,7 @@ final class ConversationGroupViewModel extends ViewModel {
void onSuggestedMembersBannerDismissed(@NonNull GroupId groupId, @NonNull List<RecipientId> suggestions) {
SignalExecutors.BOUNDED.execute(() -> {
if (groupId.isV2()) {
DatabaseFactory.getGroupDatabase(ApplicationDependencies.getApplication()).removeUnmigratedV1Members(groupId.requireV2(), suggestions);
SignalDatabase.groups().removeUnmigratedV1Members(groupId.requireV2(), suggestions);
liveRecipient.postValue(liveRecipient.getValue());
}
});
@@ -118,7 +118,7 @@ final class ConversationGroupViewModel extends ViewModel {
private static @Nullable GroupRecord getGroupRecordForRecipient(@Nullable Recipient recipient) {
if (recipient != null && recipient.isGroup()) {
Application context = ApplicationDependencies.getApplication();
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
GroupDatabase groupDatabase = SignalDatabase.groups();
return groupDatabase.getGroup(recipient.getId()).orNull();
} else {
return null;
@@ -198,9 +198,9 @@ final class ConversationGroupViewModel extends ViewModel {
firstTimeInviteFriendsTriggered = true;
SimpleTask.run(() -> DatabaseFactory.getGroupDatabase(ApplicationDependencies.getApplication())
.requireGroup(groupId)
.getMembers().equals(Collections.singletonList(Recipient.self().getId())),
SimpleTask.run(() -> SignalDatabase.groups()
.requireGroup(groupId)
.getMembers().equals(Collections.singletonList(Recipient.self().getId())),
justSelf -> {
if (justSelf) {
inviteFriends(supportFragmentManager, groupId);

View File

@@ -88,8 +88,8 @@ import org.thoughtcrime.securesms.conversation.colors.Colorizer;
import org.thoughtcrime.securesms.conversation.mutiselect.MultiselectCollection;
import org.thoughtcrime.securesms.conversation.mutiselect.MultiselectPart;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessageDatabase;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
@@ -184,7 +184,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
private AvatarImageView contactPhoto;
private AlertView alertView;
protected ReactionsConversationView reactionsView;
private BadgeImageView badgeImageView;
protected BadgeImageView badgeImageView;
private @NonNull Set<MultiselectPart> batchSelected = new HashSet<>();
private @NonNull Outliner outliner = new Outliner();
@@ -1258,6 +1258,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
contactPhoto.setAvatar(glideRequests, recipient, false);
badgeImageView.setBadgeFromRecipient(recipient, glideRequests);
badgeImageView.setClickable(false);
}
private void linkifyMessageBody(@NonNull Spannable messageBody,
@@ -2108,8 +2109,8 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
if (message > -1) builder.setMessage(message);
builder.setPositiveButton(R.string.yes, (dialogInterface, i) -> {
MessageDatabase db = messageRecord.isMms() ? DatabaseFactory.getMmsDatabase(context)
: DatabaseFactory.getSmsDatabase(context);
MessageDatabase db = messageRecord.isMms() ? SignalDatabase.mms()
: SignalDatabase.sms();
db.markAsInsecure(messageRecord.getId());
db.markAsOutbox(messageRecord.getId());
@@ -2127,9 +2128,9 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
builder.setNegativeButton(R.string.no, (dialogInterface, i) -> {
if (messageRecord.isMms()) {
DatabaseFactory.getMmsDatabase(context).markAsSentFailed(messageRecord.getId());
SignalDatabase.mms().markAsSentFailed(messageRecord.getId());
} else {
DatabaseFactory.getSmsDatabase(context).markAsSentFailed(messageRecord.getId());
SignalDatabase.sms().markAsSentFailed(messageRecord.getId());
}
});
builder.show();

View File

@@ -202,8 +202,4 @@ public class ConversationItemSwipeCallback extends ItemTouchHelper.SimpleCallbac
interface OnSwipeListener {
void onSwipe(ConversationMessage conversationMessage);
}
public interface OnViewHolderTranslated {
void onViewHolderTranslated(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder);
}
}

View File

@@ -12,8 +12,8 @@ import org.signal.core.util.Conversions;
import org.thoughtcrime.securesms.components.mention.MentionAnnotation;
import org.thoughtcrime.securesms.conversation.mutiselect.Multiselect;
import org.thoughtcrime.securesms.conversation.mutiselect.MultiselectCollection;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MentionUtil;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.Mention;
import org.thoughtcrime.securesms.database.model.MessageRecord;
@@ -149,7 +149,7 @@ public class ConversationMessage {
@WorkerThread
public static @NonNull ConversationMessage createWithUnresolvedData(@NonNull Context context, @NonNull MessageRecord messageRecord, @NonNull CharSequence body) {
if (messageRecord.isMms()) {
List<Mention> mentions = DatabaseFactory.getMentionDatabase(context).getMentionsForMessage(messageRecord.getId());
List<Mention> mentions = SignalDatabase.mentions().getMentionsForMessage(messageRecord.getId());
if (!mentions.isEmpty()) {
MentionUtil.UpdatedBodyAndMentions updated = MentionUtil.updateBodyAndMentionsWithDisplayNames(context, body, mentions);
return new ConversationMessage(messageRecord, updated.getBody(), updated.getMentions());

View File

@@ -9,8 +9,8 @@ import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import org.signal.core.util.concurrent.SignalExecutors;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
@@ -46,7 +46,7 @@ class ConversationRepository {
@WorkerThread
boolean canShowAsBubble(long threadId) {
if (Build.VERSION.SDK_INT >= ConversationUtil.CONVERSATION_SUPPORT_VERSION) {
Recipient recipient = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadId);
Recipient recipient = SignalDatabase.threads().getRecipientForThreadId(threadId);
return recipient != null && BubbleUtil.canBubble(context, recipient.getId(), threadId);
} else {
@@ -55,8 +55,8 @@ class ConversationRepository {
}
private @NonNull ConversationData getConversationDataInternal(long threadId, @NonNull Recipient conversationRecipient, int jumpToPosition) {
ThreadDatabase.ConversationMetadata metadata = DatabaseFactory.getThreadDatabase(context).getConversationMetadata(threadId);
int threadSize = DatabaseFactory.getMmsSmsDatabase(context).getConversationCount(threadId);
ThreadDatabase.ConversationMetadata metadata = SignalDatabase.threads().getConversationMetadata(threadId);
int threadSize = SignalDatabase.mmsSms().getConversationCount(threadId);
long lastSeen = metadata.getLastSeen();
boolean hasSent = metadata.hasSent();
int lastSeenPosition = 0;
@@ -67,7 +67,7 @@ class ConversationRepository {
boolean showUniversalExpireTimerUpdate = false;
if (lastSeen > 0) {
lastSeenPosition = DatabaseFactory.getMmsSmsDatabase(context).getMessagePositionOnOrAfterTimestamp(threadId, lastSeen);
lastSeenPosition = SignalDatabase.mmsSms().getMessagePositionOnOrAfterTimestamp(threadId, lastSeen);
}
if (lastSeenPosition <= 0) {
@@ -75,14 +75,14 @@ class ConversationRepository {
}
if (lastSeen == 0 && lastScrolled > 0) {
lastScrolledPosition = DatabaseFactory.getMmsSmsDatabase(context).getMessagePositionOnOrAfterTimestamp(threadId, lastScrolled);
lastScrolledPosition = SignalDatabase.mmsSms().getMessagePositionOnOrAfterTimestamp(threadId, lastScrolled);
}
if (!isMessageRequestAccepted) {
boolean isGroup = false;
boolean recipientIsKnownOrHasGroupsInCommon = false;
if (conversationRecipient.isGroup()) {
Optional<GroupDatabase.GroupRecord> group = DatabaseFactory.getGroupDatabase(context).getGroup(conversationRecipient.getId());
Optional<GroupDatabase.GroupRecord> group = SignalDatabase.groups().getGroup(conversationRecipient.getId());
if (group.isPresent()) {
List<Recipient> recipients = Recipient.resolvedList(group.get().getMembers());
for (Recipient recipient : recipients) {
@@ -103,7 +103,7 @@ class ConversationRepository {
conversationRecipient.getExpiresInSeconds() == 0 &&
!conversationRecipient.isGroup() &&
conversationRecipient.isRegistered() &&
(threadId == -1 || !DatabaseFactory.getMmsSmsDatabase(context).hasMeaningfulMessage(threadId)))
(threadId == -1 || !SignalDatabase.mmsSms().hasMeaningfulMessage(threadId)))
{
showUniversalExpireTimerUpdate = true;
}

View File

@@ -36,6 +36,7 @@ final class ConversationSwipeAnimationHelper {
updateReactionsTransition(conversationItem.reactionsView, dx, sign);
updateReplyIconTransition(conversationItem.reply, dx, progress, sign);
updateContactPhotoHolderTransition(conversationItem.contactPhotoHolder, progress, sign);
updateContactPhotoHolderTransition(conversationItem.badgeImageView, progress, sign);
}
public static void trigger(@NonNull ConversationItem conversationItem) {

View File

@@ -8,8 +8,8 @@ import androidx.lifecycle.LifecycleOwner;
import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessageDatabase;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
@@ -46,7 +46,7 @@ class MarkReadHelper {
debouncer.publish(() -> {
EXECUTOR.execute(() -> {
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
ThreadDatabase threadDatabase = SignalDatabase.threads();
List<MessageDatabase.MarkedMessageInfo> infos = threadDatabase.setReadSince(threadId, false, timestamp);
Log.d(TAG, "Marking " + infos.size() + " messages as read.");

View File

@@ -10,8 +10,8 @@ import androidx.lifecycle.Transformations;
import androidx.lifecycle.ViewModel;
import org.signal.core.util.concurrent.SignalExecutors;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.DatabaseObserver;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.util.concurrent.SerialMonoLifoExecutor;
@@ -80,12 +80,12 @@ public class MessageCountsViewModel extends ViewModel {
}
private int getUnreadCount(@NonNull Context context, long threadId) {
ThreadRecord threadRecord = DatabaseFactory.getThreadDatabase(context).getThreadRecord(threadId);
ThreadRecord threadRecord = SignalDatabase.threads().getThreadRecord(threadId);
return threadRecord != null ? threadRecord.getUnreadCount() : 0;
}
private int getUnreadMentionsCount(@NonNull Context context, long threadId) {
return DatabaseFactory.getMmsDatabase(context).getUnreadMentionCount(threadId);
return SignalDatabase.mms().getUnreadMentionCount(threadId);
}
@Override

View File

@@ -15,8 +15,8 @@ import androidx.fragment.app.FragmentManager;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.groups.ParcelableGroupId;
import org.thoughtcrime.securesms.groups.ui.GroupMemberListView;
@@ -93,9 +93,9 @@ public final class ShowAdminsBottomSheetDialog extends BottomSheetDialogFragment
@WorkerThread
private static @NonNull List<Recipient> getAdmins(@NonNull Context context, @NonNull GroupId groupId) {
return DatabaseFactory.getGroupDatabase(context)
.getGroup(groupId)
.transform(GroupDatabase.GroupRecord::getAdmins)
.or(Collections.emptyList());
return SignalDatabase.groups()
.getGroup(groupId)
.transform(GroupDatabase.GroupRecord::getAdmins)
.or(Collections.emptyList());
}
}

View File

@@ -4,7 +4,7 @@ import android.content.Context
import org.signal.core.util.concurrent.SignalExecutors
import org.thoughtcrime.securesms.conversation.colors.ChatColors
import org.thoughtcrime.securesms.conversation.colors.ChatColorsPalette
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
@@ -21,19 +21,19 @@ sealed class ChatColorSelectionRepository(context: Context) {
fun duplicate(chatColors: ChatColors) {
SignalExecutors.BOUNDED.execute {
val duplicate = chatColors.withId(ChatColors.Id.NotSet)
DatabaseFactory.getChatColorsDatabase(context).saveChatColors(duplicate)
SignalDatabase.chatColors.saveChatColors(duplicate)
}
}
fun getUsageCount(chatColorsId: ChatColors.Id, consumer: (Int) -> Unit) {
SignalExecutors.BOUNDED.execute {
consumer(DatabaseFactory.getRecipientDatabase(context).getColorUsageCount(chatColorsId))
consumer(SignalDatabase.recipients.getColorUsageCount(chatColorsId))
}
}
fun delete(chatColors: ChatColors, onDeleted: () -> Unit) {
SignalExecutors.BOUNDED.execute {
DatabaseFactory.getChatColorsDatabase(context).deleteChatColors(chatColors)
SignalDatabase.chatColors.deleteChatColors(chatColors)
onDeleted()
}
}
@@ -84,7 +84,7 @@ sealed class ChatColorSelectionRepository(context: Context) {
override fun save(chatColors: ChatColors, onSaved: () -> Unit) {
SignalExecutors.BOUNDED.execute {
val recipientDatabase = DatabaseFactory.getRecipientDatabase(context)
val recipientDatabase = SignalDatabase.recipients
recipientDatabase.setColor(recipientId, chatColors)
onSaved()
}

View File

@@ -5,14 +5,14 @@ import org.signal.core.util.concurrent.SignalExecutors
import org.thoughtcrime.securesms.conversation.colors.ChatColors
import org.thoughtcrime.securesms.conversation.colors.ChatColorsPalette
import org.thoughtcrime.securesms.database.ChatColorsDatabase
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.DatabaseObserver
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.util.concurrent.SerialMonoLifoExecutor
import java.util.concurrent.Executor
class ChatColorsOptionsLiveData : LiveData<List<ChatColors>>() {
private val chatColorsDatabase: ChatColorsDatabase = DatabaseFactory.getChatColorsDatabase(ApplicationDependencies.getApplication())
private val chatColorsDatabase: ChatColorsDatabase = SignalDatabase.chatColors
private val observer: DatabaseObserver.Observer = DatabaseObserver.Observer { refreshChatColors() }
private val executor: Executor = SerialMonoLifoExecutor(SignalExecutors.BOUNDED)

View File

@@ -3,7 +3,7 @@ package org.thoughtcrime.securesms.conversation.colors.ui.custom
import android.content.Context
import org.signal.core.util.concurrent.SignalExecutors
import org.thoughtcrime.securesms.conversation.colors.ChatColors
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
@@ -12,9 +12,7 @@ import org.thoughtcrime.securesms.wallpaper.ChatWallpaper
class CustomChatColorCreatorRepository(private val context: Context) {
fun loadColors(chatColorsId: ChatColors.Id, consumer: (ChatColors) -> Unit) {
SignalExecutors.BOUNDED.execute {
val chatColorsDatabase = DatabaseFactory.getChatColorsDatabase(context)
val chatColors = chatColorsDatabase.getById(chatColorsId)
val chatColors = SignalDatabase.chatColors.getById(chatColorsId)
consumer(chatColors)
}
}
@@ -32,16 +30,14 @@ class CustomChatColorCreatorRepository(private val context: Context) {
fun setChatColors(chatColors: ChatColors, consumer: (ChatColors) -> Unit) {
SignalExecutors.BOUNDED.execute {
val chatColorsDatabase = DatabaseFactory.getChatColorsDatabase(context)
val savedColors = chatColorsDatabase.saveChatColors(chatColors)
val savedColors = SignalDatabase.chatColors.saveChatColors(chatColors)
consumer(savedColors)
}
}
fun getUsageCount(chatColorsId: ChatColors.Id, consumer: (Int) -> Unit) {
SignalExecutors.BOUNDED.execute {
val recipientsDatabase = DatabaseFactory.getRecipientDatabase(context)
val recipientsDatabase = SignalDatabase.recipients
consumer(recipientsDatabase.getColorUsageCount(chatColorsId))
}

View File

@@ -4,7 +4,7 @@ import android.content.Context
import androidx.core.util.Consumer
import io.reactivex.rxjava3.core.Single
import org.signal.core.util.concurrent.SignalExecutors
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.ThreadDatabase
import org.thoughtcrime.securesms.database.identity.IdentityRecordList
import org.thoughtcrime.securesms.database.model.IdentityRecord
@@ -44,7 +44,7 @@ class MultiselectForwardRepository(context: Context) {
return Single.fromCallable {
val recipient = Recipient.resolved(recipientId.get())
if (recipient.isPushV2Group) {
val record = DatabaseFactory.getGroupDatabase(context).getGroup(recipient.requireGroupId())
val record = SignalDatabase.groups.getGroup(recipient.requireGroupId())
!(record.isPresent && record.get().isAnnouncementGroup && !record.get().isAdmin(Recipient.self()))
} else {
true
@@ -59,7 +59,7 @@ class MultiselectForwardRepository(context: Context) {
resultHandlers: MultiselectForwardResultHandlers
) {
SignalExecutors.BOUNDED.execute {
val threadDatabase: ThreadDatabase = DatabaseFactory.getThreadDatabase(context)
val threadDatabase: ThreadDatabase = SignalDatabase.threads
val sharedContactsAndThreads: Set<ShareContactAndThread> = shareContacts
.asSequence()

View File

@@ -15,8 +15,8 @@ import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.crypto.SessionUtil;
import org.thoughtcrime.securesms.crypto.ReentrantSessionLock;
import org.thoughtcrime.securesms.crypto.storage.TextSecureIdentityKeyStore;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.IdentityDatabase;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.IdentityRecord;
import org.thoughtcrime.securesms.database.MessageDatabase;
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
@@ -82,9 +82,9 @@ final class SafetyNumberChangeRepository {
try {
switch (messageType) {
case MmsSmsDatabase.SMS_TRANSPORT:
return DatabaseFactory.getSmsDatabase(context).getMessageRecord(messageId);
return SignalDatabase.sms().getMessageRecord(messageId);
case MmsSmsDatabase.MMS_TRANSPORT:
return DatabaseFactory.getMmsDatabase(context).getMessageRecord(messageId);
return SignalDatabase.mms().getMessageRecord(messageId);
default:
throw new AssertionError("no valid message type specified");
}
@@ -136,7 +136,7 @@ final class SafetyNumberChangeRepository {
Log.i(TAG, "Archiving sessions explicitly as they appear to be out of sync.");
SessionUtil.archiveSession(changedRecipient.getRecipient().getId(), SignalServiceAddress.DEFAULT_DEVICE_ID);
SessionUtil.archiveSiblingSessions(mismatchAddress);
DatabaseFactory.getSenderKeySharedDatabase(context).deleteAllFor(changedRecipient.getRecipient().getId());
SignalDatabase.senderKeyShared().deleteAllFor(changedRecipient.getRecipient().getId());
}
}
}
@@ -151,8 +151,8 @@ final class SafetyNumberChangeRepository {
@WorkerThread
private void processOutgoingMessageRecord(@NonNull List<ChangedRecipient> changedRecipients, @NonNull MessageRecord messageRecord) {
Log.d(TAG, "processOutgoingMessageRecord");
MessageDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
MessageDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context);
MessageDatabase smsDatabase = SignalDatabase.sms();
MessageDatabase mmsDatabase = SignalDatabase.mms();
for (ChangedRecipient changedRecipient : changedRecipients) {
RecipientId id = changedRecipient.getRecipient().getId();

View File

@@ -8,9 +8,9 @@ import androidx.annotation.WorkerThread;
import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
@@ -23,8 +23,8 @@ final class MentionsPickerRepository {
private final GroupDatabase groupDatabase;
MentionsPickerRepository(@NonNull Context context) {
recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
groupDatabase = DatabaseFactory.getGroupDatabase(context);
recipientDatabase = SignalDatabase.recipients();
groupDatabase = SignalDatabase.groups();
}
@WorkerThread

View File

@@ -36,7 +36,7 @@ import com.google.android.material.snackbar.Snackbar;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.registration.PulsingFloatingActionButton;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.util.task.SnackbarAsyncTask;
import org.thoughtcrime.securesms.util.views.Stub;
@@ -112,13 +112,13 @@ public class ConversationListArchiveFragment extends ConversationListFragment im
@Override
@WorkerThread
protected void archiveThreads(Set<Long> threadIds) {
DatabaseFactory.getThreadDatabase(getActivity()).setArchived(threadIds, false);
SignalDatabase.threads().setArchived(threadIds, false);
}
@Override
@WorkerThread
protected void reverseArchiveThreads(Set<Long> threadIds) {
DatabaseFactory.getThreadDatabase(getActivity()).setArchived(threadIds, true);
SignalDatabase.threads().setArchived(threadIds, true);
}
@SuppressLint("StaticFieldLeak")
@@ -137,12 +137,12 @@ public class ConversationListArchiveFragment extends ConversationListFragment im
{
@Override
protected void executeAction(@Nullable Long parameter) {
DatabaseFactory.getThreadDatabase(getActivity()).unarchiveConversation(threadId);
SignalDatabase.threads().unarchiveConversation(threadId);
}
@Override
protected void reverseAction(@Nullable Long parameter) {
DatabaseFactory.getThreadDatabase(getActivity()).archiveConversation(threadId);
SignalDatabase.threads().archiveConversation(threadId);
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, threadId);
}

View File

@@ -13,7 +13,7 @@ import org.signal.core.util.logging.Log;
import org.signal.paging.PagedDataSource;
import org.thoughtcrime.securesms.conversationlist.model.Conversation;
import org.thoughtcrime.securesms.conversationlist.model.ConversationReader;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
@@ -31,7 +31,7 @@ abstract class ConversationListDataSource implements PagedDataSource<Long, Conve
protected final ThreadDatabase threadDatabase;
protected ConversationListDataSource(@NonNull Context context) {
this.threadDatabase = DatabaseFactory.getThreadDatabase(context);
this.threadDatabase = SignalDatabase.threads();
}
public static ConversationListDataSource create(@NonNull Context context, boolean isArchived) {

View File

@@ -107,8 +107,8 @@ import org.thoughtcrime.securesms.components.voice.VoiceNotePlayerView;
import org.thoughtcrime.securesms.conversation.ConversationFragment;
import org.thoughtcrime.securesms.conversationlist.model.Conversation;
import org.thoughtcrime.securesms.conversationlist.model.UnreadPayments;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessageDatabase.MarkedMessageInfo;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
@@ -447,7 +447,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
@Override
public void onContactClicked(@NonNull Recipient contact) {
SimpleTask.run(getViewLifecycleOwner().getLifecycle(), () -> {
return DatabaseFactory.getThreadDatabase(getContext()).getThreadIdIfExistsFor(contact.getId());
return SignalDatabase.threads().getThreadIdIfExistsFor(contact.getId());
}, threadId -> {
hideKeyboard();
getNavigator().goToConversation(contact.getId(),
@@ -460,7 +460,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
@Override
public void onMessageClicked(@NonNull MessageResult message) {
SimpleTask.run(getViewLifecycleOwner().getLifecycle(), () -> {
int startingPosition = DatabaseFactory.getMmsSmsDatabase(getContext()).getMessagePositionInConversation(message.getThreadId(), message.getReceivedTimestampMs());
int startingPosition = SignalDatabase.mmsSms().getMessagePositionInConversation(message.getThreadId(), message.getReceivedTimestampMs());
return Math.max(0, startingPosition);
}, startingPosition -> {
hideKeyboard();
@@ -680,9 +680,10 @@ public class ConversationListFragment extends MainFragment implements ActionMode
int firstVisibleItem = layoutManager != null ? layoutManager.findFirstCompletelyVisibleItemPosition() : -1;
defaultAdapter.submitList(conversations, () -> {
if (firstVisibleItem == 0) {
list.scrollToPosition(0);
}
if (firstVisibleItem == 0) {
list.scrollToPosition(0);
}
onPostSubmitList(conversations.size());
});
}
@@ -798,7 +799,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
Context context = requireContext();
SignalExecutors.BOUNDED.execute(() -> {
List<MarkedMessageInfo> messageIds = DatabaseFactory.getThreadDatabase(context).setAllThreadsRead();
List<MarkedMessageInfo> messageIds = SignalDatabase.threads().setAllThreadsRead();
ApplicationDependencies.getMessageNotifier().updateNotification(context);
MarkReadReceiver.process(context, messageIds);
@@ -809,7 +810,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
Context context = requireContext();
SimpleTask.run(getViewLifecycleOwner().getLifecycle(), () -> {
List<MarkedMessageInfo> messageIds = DatabaseFactory.getThreadDatabase(context).setRead(ids, false);
List<MarkedMessageInfo> messageIds = SignalDatabase.threads().setRead(ids, false);
ApplicationDependencies.getMessageNotifier().updateNotification(context);
MarkReadReceiver.process(context, messageIds);
@@ -824,7 +825,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
Context context = requireContext();
SimpleTask.run(getViewLifecycleOwner().getLifecycle(), () -> {
DatabaseFactory.getThreadDatabase(context).setForcedUnread(ids);
SignalDatabase.threads().setForcedUnread(ids);
StorageSyncHelper.scheduleSyncForDataChange();
return null;
}, none -> {
@@ -902,7 +903,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
@Override
protected Void doInBackground(Void... params) {
DatabaseFactory.getThreadDatabase(getActivity()).deleteConversations(selectedConversations);
SignalDatabase.threads().deleteConversations(selectedConversations);
ApplicationDependencies.getMessageNotifier().updateNotification(requireActivity());
return null;
}
@@ -937,7 +938,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
}
SimpleTask.run(SignalExecutors.BOUNDED, () -> {
ThreadDatabase db = DatabaseFactory.getThreadDatabase(ApplicationDependencies.getApplication());
ThreadDatabase db = SignalDatabase.threads();
db.pinConversations(toPin);
@@ -949,7 +950,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
private void handleUnpin(@NonNull Collection<Long> ids) {
SimpleTask.run(SignalExecutors.BOUNDED, () -> {
ThreadDatabase db = DatabaseFactory.getThreadDatabase(ApplicationDependencies.getApplication());
ThreadDatabase db = SignalDatabase.threads();
db.unpinConversations(ids);
@@ -978,7 +979,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
.filter(r -> r.getMuteUntil() != until)
.map(Recipient::getId)
.collect(Collectors.toList());
DatabaseFactory.getRecipientDatabase(requireContext()).setMuted(recipientIds, until);
SignalDatabase.recipients().setMuted(recipientIds, until);
return null;
}, unused -> {
endActionModeIfActive();
@@ -1022,11 +1023,6 @@ public class ConversationListFragment extends MainFragment implements ActionMode
}
}
private void onSubmitList(@NonNull List<Conversation> conversationList) {
defaultAdapter.submitList(conversationList);
onPostSubmitList(conversationList.size());
}
void updateEmptyState(boolean isConversationEmpty) {
if (isConversationEmpty) {
Log.i(TAG, "Received an empty data set.");
@@ -1264,12 +1260,12 @@ public class ConversationListFragment extends MainFragment implements ActionMode
@WorkerThread
protected void archiveThreads(Set<Long> threadIds) {
DatabaseFactory.getThreadDatabase(getActivity()).setArchived(threadIds, true);
SignalDatabase.threads().setArchived(threadIds, true);
}
@WorkerThread
protected void reverseArchiveThreads(Set<Long> threadIds) {
DatabaseFactory.getThreadDatabase(getActivity()).setArchived(threadIds, false);
SignalDatabase.threads().setArchived(threadIds, false);
}
@SuppressLint("StaticFieldLeak")
@@ -1285,7 +1281,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
Snackbar.LENGTH_LONG,
false)
{
private final ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(getActivity());
private final ThreadDatabase threadDatabase = SignalDatabase.threads();
private List<Long> pinnedThreadIds;

View File

@@ -192,7 +192,7 @@ public final class ConversationListItem extends ConstraintLayout
if (highlightSubstring != null) {
String name = recipient.get().isSelf() ? getContext().getString(R.string.note_to_self) : recipient.get().getDisplayName(getContext());
this.fromView.setText(SearchUtil.getHighlightedSpan(locale, SpanUtil::getMediumBoldSpan, name, highlightSubstring, SearchUtil.MATCH_ALL));
this.fromView.setText(recipient.get(), SearchUtil.getHighlightedSpan(locale, SpanUtil::getMediumBoldSpan, name, highlightSubstring, SearchUtil.MATCH_ALL), false, null);
} else {
this.fromView.setText(recipient.get(), false);
}
@@ -226,6 +226,7 @@ public final class ConversationListItem extends ConstraintLayout
private void setBadgeFromRecipient(Recipient recipient) {
if (!recipient.isSelf()) {
badge.setBadgeFromRecipient(recipient);
badge.setClickable(false);
} else {
badge.setBadge(null);
}

View File

@@ -17,8 +17,8 @@ import org.signal.paging.PagingController;
import org.thoughtcrime.securesms.conversationlist.model.Conversation;
import org.thoughtcrime.securesms.conversationlist.model.UnreadPayments;
import org.thoughtcrime.securesms.conversationlist.model.UnreadPaymentsLiveData;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.DatabaseObserver;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.megaphone.Megaphone;
import org.thoughtcrime.securesms.megaphone.MegaphoneRepository;
@@ -88,12 +88,12 @@ class ConversationListViewModel extends ViewModel {
};
this.hasNoConversations = LiveDataUtil.mapAsync(pagedData.getData(), conversations -> {
pinnedCount = DatabaseFactory.getThreadDatabase(application).getPinnedConversationListCount();
pinnedCount = SignalDatabase.threads().getPinnedConversationListCount();
if (conversations.size() > 0) {
return false;
} else {
return DatabaseFactory.getThreadDatabase(application).getArchivedConversationListCount() == 0;
return SignalDatabase.threads().getArchivedConversationListCount() == 0;
}
});

View File

@@ -5,9 +5,9 @@ import androidx.annotation.WorkerThread;
import androidx.lifecycle.LiveData;
import org.signal.core.util.concurrent.SignalExecutors;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.DatabaseObserver;
import org.thoughtcrime.securesms.database.PaymentDatabase;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.concurrent.SerialMonoLifoExecutor;
@@ -28,7 +28,7 @@ public final class UnreadPaymentsLiveData extends LiveData<Optional<UnreadPaymen
private final Executor executor;
public UnreadPaymentsLiveData() {
this.paymentDatabase = DatabaseFactory.getPaymentDatabase(ApplicationDependencies.getApplication());
this.paymentDatabase = SignalDatabase.payments();
this.observer = this::refreshUnreadPayments;
this.executor = new SerialMonoLifoExecutor(SignalExecutors.BOUNDED);
}

View File

@@ -5,7 +5,7 @@ import android.content.Context;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.crypto.storage.SignalSenderKeyStore;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.whispersystems.libsignal.SignalProtocolAddress;
@@ -22,7 +22,7 @@ public final class SenderKeyUtil {
public static void rotateOurKey(@NonNull Context context, @NonNull DistributionId distributionId) {
try (SignalSessionLock.Lock unused = ReentrantSessionLock.INSTANCE.acquire()) {
ApplicationDependencies.getSenderKeyStore().deleteAllFor(Recipient.self().requireServiceId(), distributionId);
DatabaseFactory.getSenderKeySharedDatabase(context).deleteAllFor(distributionId);
SignalDatabase.senderKeyShared().deleteAllFor(distributionId);
}
}
@@ -31,7 +31,7 @@ public final class SenderKeyUtil {
*/
public static long getCreateTimeForOurKey(@NonNull Context context, @NonNull DistributionId distributionId) {
SignalProtocolAddress address = new SignalProtocolAddress(Recipient.self().requireServiceId(), SignalServiceAddress.DEFAULT_DEVICE_ID);
return DatabaseFactory.getSenderKeyDatabase(context).getCreatedTime(address, distributionId);
return SignalDatabase.senderKeys().getCreatedTime(address, distributionId);
}
/**
@@ -40,7 +40,7 @@ public final class SenderKeyUtil {
public static void clearAllState(@NonNull Context context) {
try (SignalSessionLock.Lock unused = ReentrantSessionLock.INSTANCE.acquire()) {
ApplicationDependencies.getSenderKeyStore().deleteAll();
DatabaseFactory.getSenderKeySharedDatabase(context).deleteAll();
SignalDatabase.senderKeyShared().deleteAll();
}
}
}

View File

@@ -2,7 +2,7 @@ package org.thoughtcrime.securesms.crypto.storage;
import android.content.Context;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.IdentityKey;
@@ -191,10 +191,10 @@ public class SignalProtocolStoreImpl implements SignalServiceDataStore {
@Override
public Transaction beginTransaction() {
DatabaseFactory.getInstance(context).getRawDatabase().beginTransaction();
SignalDatabase.getRawDatabase().beginTransaction();
return () -> {
DatabaseFactory.getInstance(context).getRawDatabase().setTransactionSuccessful();
DatabaseFactory.getInstance(context).getRawDatabase().endTransaction();
SignalDatabase.getRawDatabase().setTransactionSuccessful();
SignalDatabase.getRawDatabase().endTransaction();
};
}
}

View File

@@ -4,7 +4,7 @@ import android.content.Context;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.whispersystems.signalservice.api.SignalServiceSenderKeyStore;
import org.whispersystems.signalservice.api.push.DistributionId;
import org.thoughtcrime.securesms.recipients.RecipientId;
@@ -34,35 +34,35 @@ public final class SignalSenderKeyStore implements SignalServiceSenderKeyStore {
@Override
public void storeSenderKey(@NonNull SignalProtocolAddress sender, @NonNull UUID distributionId, @NonNull SenderKeyRecord record) {
synchronized (LOCK) {
DatabaseFactory.getSenderKeyDatabase(context).store(sender, DistributionId.from(distributionId), record);
SignalDatabase.senderKeys().store(sender, DistributionId.from(distributionId), record);
}
}
@Override
public @Nullable SenderKeyRecord loadSenderKey(@NonNull SignalProtocolAddress sender, @NonNull UUID distributionId) {
synchronized (LOCK) {
return DatabaseFactory.getSenderKeyDatabase(context).load(sender, DistributionId.from(distributionId));
return SignalDatabase.senderKeys().load(sender, DistributionId.from(distributionId));
}
}
@Override
public Set<SignalProtocolAddress> getSenderKeySharedWith(DistributionId distributionId) {
synchronized (LOCK) {
return DatabaseFactory.getSenderKeySharedDatabase(context).getSharedWith(distributionId);
return SignalDatabase.senderKeyShared().getSharedWith(distributionId);
}
}
@Override
public void markSenderKeySharedWith(DistributionId distributionId, Collection<SignalProtocolAddress> addresses) {
synchronized (LOCK) {
DatabaseFactory.getSenderKeySharedDatabase(context).markAsShared(distributionId, addresses);
SignalDatabase.senderKeyShared().markAsShared(distributionId, addresses);
}
}
@Override
public void clearSenderKeySharedWith(Collection<SignalProtocolAddress> addresses) {
synchronized (LOCK) {
DatabaseFactory.getSenderKeySharedDatabase(context).deleteAllFor(addresses);
SignalDatabase.senderKeyShared().deleteAllFor(addresses);
}
}
@@ -71,7 +71,7 @@ public final class SignalSenderKeyStore implements SignalServiceSenderKeyStore {
*/
public void deleteAllFor(@NonNull String addressName, @NonNull DistributionId distributionId) {
synchronized (LOCK) {
DatabaseFactory.getSenderKeyDatabase(context).deleteAllFor(addressName, distributionId);
SignalDatabase.senderKeys().deleteAllFor(addressName, distributionId);
}
}
@@ -80,7 +80,7 @@ public final class SignalSenderKeyStore implements SignalServiceSenderKeyStore {
*/
public void deleteAll() {
synchronized (LOCK) {
DatabaseFactory.getSenderKeyDatabase(context).deleteAll();
SignalDatabase.senderKeys().deleteAll();
}
}
}

View File

@@ -8,9 +8,9 @@ import androidx.annotation.Nullable;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.thoughtcrime.securesms.crypto.SessionUtil;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.IdentityDatabase;
import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.identity.IdentityRecordList;
import org.thoughtcrime.securesms.database.model.IdentityRecord;
import org.thoughtcrime.securesms.database.model.IdentityStoreRecord;
@@ -43,7 +43,7 @@ public class TextSecureIdentityKeyStore implements IdentityKeyStore {
private final Cache cache;
public TextSecureIdentityKeyStore(Context context) {
this(context, DatabaseFactory.getIdentityDatabase(context));
this(context, SignalDatabase.identities());
}
TextSecureIdentityKeyStore(@NonNull Context context, @NonNull IdentityDatabase identityDatabase) {
@@ -92,7 +92,7 @@ public class TextSecureIdentityKeyStore implements IdentityKeyStore {
cache.save(address.getName(), recipientId, identityKey, verifiedStatus, false, System.currentTimeMillis(), nonBlockingApproval);
IdentityUtil.markIdentityUpdate(context, recipientId);
SessionUtil.archiveSiblingSessions(address);
DatabaseFactory.getSenderKeySharedDatabase(context).deleteAllFor(recipientId);
SignalDatabase.senderKeyShared().deleteAllFor(recipientId);
return SaveResult.UPDATE;
}

Some files were not shown because too many files have changed in this diff Show More