Compare commits

...

43 Commits

Author SHA1 Message Date
Alex Hart ae8e050891 Bump version to 8.13.2 2026-06-03 15:35:01 -03:00
Michelle Tang 7e6ad92ca8 Turn off KT. 2026-06-02 23:58:41 -04:00
Michelle Tang ae2477356b Bump version to 8.13.1 2026-05-29 10:58:26 -04:00
Michelle Tang 8d992fa7db Update baseline profile. 2026-05-29 10:55:07 -04:00
Michelle Tang 71c33fc579 Update translations and other static files. 2026-05-29 10:37:38 -04:00
Greyson Parrelli bdefc274b9 Start using updated character set for AEP. 2026-05-29 09:54:58 -04:00
Greyson Parrelli f926d9c893 Update SVR2 enclave. 2026-05-28 00:23:29 -04:00
Michelle Tang ec02d802f2 Bump version to 8.13.0 2026-05-27 14:52:19 -04:00
Michelle Tang 7925041982 Update translations and other static files. 2026-05-27 14:44:57 -04:00
Michelle Tang de42d748dc Ignore tests. 2026-05-27 14:37:51 -04:00
Michelle Tang 3e1df5b8ac Turn on KT. 2026-05-27 14:08:54 -04:00
Greyson Parrelli f1d722c5cd Fix test dependency import. 2026-05-27 14:08:54 -04:00
Cody Henthorne abcd65603c Fix block/unblock causing ANR in recipient bottom sheet. 2026-05-27 14:08:54 -04:00
Greyson Parrelli 7b23110cac Improve instrumentation setup. 2026-05-27 14:08:54 -04:00
Greyson Parrelli baa4dd3c86 Improve handling of devices without Play Services. 2026-05-27 14:08:54 -04:00
Greyson Parrelli 0beda1e615 Cancel delayed notifications when notification state is empty. 2026-05-27 14:01:29 -04:00
Michelle Tang b3acefdb08 Fix instrumentation tests. 2026-05-27 14:01:29 -04:00
Greyson Parrelli 9600366422 Fix instrumentation tests. 2026-05-27 14:01:29 -04:00
jeffrey-signal d85a57adce Add adaptive layout support to regV5 PIN creation screen. 2026-05-27 14:01:29 -04:00
jeffrey-signal cb1b878198 Add string resources for the PIN entry screen. 2026-05-27 14:01:29 -04:00
jeffrey-signal 4a2e2e0137 Add string resources for the PIN creation screen. 2026-05-27 14:01:29 -04:00
Greyson Parrelli b2f8445e1d Upgrade CameraX to 1.6.1 2026-05-27 14:01:29 -04:00
Alex Hart f5f686fece Add call-link specific network error handling.
Co-authored-by: Greyson Parrelli <greyson@signal.org>
2026-05-27 14:01:29 -04:00
fm-sys 1864534174 Add long press to copy app version code.
Co-authored-by: jeffrey-signal <jeffrey@signal.org>

Closes signalapp/Signal-Android#14795
2026-05-27 14:01:29 -04:00
Alex Hart 54c4bda4f2 Allow local backup restore to proceed regardless of remote restore failure state.
Co-authored-by: Greyson Parrelli <greyson@signal.org>
2026-05-27 14:01:29 -04:00
Alex Hart 3cb61e3e8a Remove inaccurate recovery key mention from local backup restore dialog.
Co-authored-by: Greyson Parrelli <greyson@signal.org>
2026-05-27 14:01:29 -04:00
andrew-signal 83a83f65ef Prune some unused resources. 2026-05-27 14:01:29 -04:00
Greyson Parrelli 122e1770b7 Hide keyboard on contact selection when add-message is disabled. 2026-05-27 14:01:29 -04:00
Greyson Parrelli 68eb4d3c82 Propagate force flag to story attachment downloads. 2026-05-27 14:01:29 -04:00
Greyson Parrelli ce4d68a20f Filter shortcode numbers in externalContact to prevent E164 re-formatting crash. 2026-05-27 14:01:29 -04:00
Greyson Parrelli 5d45914a08 Improved send performance. 2026-05-27 14:01:29 -04:00
Greyson Parrelli b2f450d849 Add index to make marking unread faster. 2026-05-27 14:01:29 -04:00
Cody Henthorne ed73dc2c24 Remove system call triggering location request. 2026-05-27 14:01:28 -04:00
andrew-signal 71967b278e Remove cluster of orphaned code around CameraContactSelectionViewController. 2026-05-27 14:01:28 -04:00
andrew-signal faadc9854f Re-encode call ringing/busy tones from MP3 to opus. 2026-05-27 14:01:28 -04:00
Greyson Parrelli f206487ede Add MessageService and IndividualSendJobV2. 2026-05-27 14:01:28 -04:00
Greyson Parrelli 0284da2d0f Move a lot of utils into core. 2026-05-27 14:01:28 -04:00
Alex Hart 15a3a8efde Add debug log pop (8 clicks) for reg v5.
Co-authored-by: Michelle Tang <mtang@signal.org>
2026-05-27 14:01:28 -04:00
andrew-signal b429d08c5e Exclude libsignal-testing.md acknowledgment from non-test builds. 2026-05-27 14:01:28 -04:00
Jim Gustafson 3892113a0e Update to RingRTC v2.69.2 2026-05-27 14:01:28 -04:00
Cody Henthorne 41f52ed886 Fix thrashing storage service fields. 2026-05-27 14:01:28 -04:00
Cody Henthorne ec07b7805e Fix incorrect sync message being sent when network drops group send response. 2026-05-27 14:01:28 -04:00
Alexandria 333e514a2f Fix media review confirm button color.
Closes signalapp/Signal-Android#14683
2026-05-27 14:01:28 -04:00
620 changed files with 17139 additions and 14922 deletions
+19 -23
View File
@@ -27,9 +27,9 @@ plugins {
val staticIps = Properties().apply { file("static-ips.properties").reader().use { load(it) } }
staticIps.stringPropertyNames().forEach { rootProject.extra[it] = staticIps.getProperty(it) }
val canonicalVersionCode = 1697
val canonicalVersionName = "8.12.4"
val currentHotfixVersion = 0
val canonicalVersionCode = 1699
val canonicalVersionName = "8.13.2"
val currentHotfixVersion = 1
val maxHotfixVersions = 100
// We don't want versions to ever end in 0 so that they don't conflict with nightly versions
@@ -56,6 +56,11 @@ val localProperties: Properties? = if (localPropertiesFile.exists()) {
val quickstartCredentialsDir: String? = localProperties?.getProperty("quickstart.credentials.dir")
val benchmarkBackupFile: String? = localProperties?.getProperty("benchmark.backup.file")
val isInstrumentationTestRun = gradle.startParameter.taskNames.any { taskName ->
val lower = taskName.lowercase()
lower.contains("androidtest") || lower.contains("connectedcheck")
}
val selectableVariants = listOf(
"nightlyProdSpinner",
"nightlyProdPerf",
@@ -68,13 +73,11 @@ val selectableVariants = listOf(
"playProdMocked",
"playProdNonMinifiedMocked",
"playProdBenchmark",
"playProdInstrumentation",
"playProdRelease",
"playStagingDebug",
"playStagingCanary",
"playStagingSpinner",
"playStagingPerf",
"playStagingInstrumentation",
"playStagingRelease",
"playProdQuickstart",
"playStagingQuickstart",
@@ -132,7 +135,6 @@ android {
ndkVersion = libs.versions.ndk.get()
flavorDimensions += listOf("distribution", "environment")
testBuildType = "instrumentation"
android.bundle.language.enableSplit = false
@@ -219,6 +221,10 @@ android {
versionCode = (canonicalVersionCode * maxHotfixVersions) + possibleHotfixVersions[currentHotfixVersion]
versionName = canonicalVersionName
if (isInstrumentationTestRun) {
applicationIdSuffix = ".test_run"
}
minSdk = libs.versions.minSdk.get().toInt()
targetSdk = libs.versions.targetSdk.get().toInt()
@@ -253,8 +259,8 @@ android {
buildConfigField("String[]", "SIGNAL_CDSI_IPS", rootProject.extra["cdsi_ips"] as String)
buildConfigField("String[]", "SIGNAL_SVR2_IPS", rootProject.extra["svr2_ips"] as String)
buildConfigField("String", "SIGNAL_AGENT", "\"OWA\"")
buildConfigField("String", "SVR2_MRENCLAVE_LEGACY", "\"29cd63c87bea751e3bfd0fbd401279192e2e5c99948b4ee9437eafc4968355fb\"")
buildConfigField("String", "SVR2_MRENCLAVE", "\"1240acbd4aa26974184844c8a46b1022d3957ac8a76c1fd8f5b1a15141ee0708\"")
buildConfigField("String", "SVR2_MRENCLAVE_LEGACY", "\"1240acbd4aa26974184844c8a46b1022d3957ac8a76c1fd8f5b1a15141ee0708\"")
buildConfigField("String", "SVR2_MRENCLAVE", "\"ced8217b26228e4b210c985786999d095c4958a94faf37b14acaf25c4cbb02a4\"")
buildConfigField("String[]", "UNIDENTIFIED_SENDER_TRUST_ROOTS", "new String[]{ \"BXu6QIKVz5MA8gstzfOgRQGqyLqOwNKHL6INkv3IHWMF\", \"BUkY0I+9+oPgDCn4+Ac6Iu813yvqkDr/ga8DzLxFxuk6\"}")
buildConfigField("String", "ZKGROUP_SERVER_PUBLIC_PARAMS", "\"AMhf5ywVwITZMsff/eCyudZx9JDmkkkbV6PInzG4p8x3VqVJSFiMvnvlEKWuRob/1eaIetR31IYeAbm0NdOuHH8Qi+Rexi1wLlpzIo1gstHWBfZzy1+qHRV5A4TqPp15YzBPm0WSggW6PbSn+F4lf57VCnHF7p8SvzAA2ZZJPYJURt8X7bbg+H3i+PEjH9DXItNEqs2sNcug37xZQDLm7X36nOoGPs54XsEGzPdEV+itQNGUFEjY6X9Uv+Acuks7NpyGvCoKxGwgKgE5XyJ+nNKlyHHOLb6N1NuHyBrZrgtY/JYJHRooo5CEqYKBqdFnmbTVGEkCvJKxLnjwKWf+fEPoWeQFj5ObDjcKMZf2Jm2Ae69x+ikU5gBXsRmoF94GXTLfN0/vLt98KDPnxwAQL9j5V1jGOY8jQl6MLxEs56cwXN0dqCnImzVH3TZT1cJ8SW1BRX6qIVxEzjsSGx3yxF3suAilPMqGRp4ffyopjMD1JXiKR2RwLKzizUe5e8XyGOy9fplzhw3jVzTRyUZTRSZKkMLWcQ/gv0E4aONNqs4P+NameAZYOD12qRkxosQQP5uux6B2nRyZ7sAV54DgFyLiRcq1FvwKw2EPQdk4HDoePrO/RNUbyNddnM/mMgj4FW65xCoT1LmjrIjsv/Ggdlx46ueczhMgtBunx1/w8k8V+l8LVZ8gAT6wkU5J+DPQalQguMg12Jzug3q4TbdHiGCmD9EunCwOmsLuLJkz6EcSYXtrlDEnAM+hicw7iergYLLlMXpfTdGxJCWJmP4zqUFeTTmsmhsjGBt7NiEB/9pFFEB3pSbf4iiUukw63Eo8Aqnf4iwob6X1QviCWuc8t0LUlT9vALgh/f2DPVOOmR0RW6bgRvc7DSF20V/omg+YBw==\"")
buildConfigField("String", "GENERIC_SERVER_PUBLIC_PARAMS", "\"AByD873dTilmOSG0TjKrvpeaKEsUmIO8Vx9BeMmftwUs9v7ikPwM8P3OHyT0+X3EUMZrSe9VUp26Wai51Q9I8mdk0hX/yo7CeFGJyzoOqn8e/i4Ygbn5HoAyXJx5eXfIbqpc0bIxzju4H/HOQeOpt6h742qii5u/cbwOhFZCsMIbElZTaeU+BWMBQiZHIGHT5IE0qCordQKZ5iPZom0HeFa8Yq0ShuEyAl0WINBiY6xE3H/9WnvzXBbMuuk//eRxXgzO8ieCeK8FwQNxbfXqZm6Ro1cMhCOF3u7xoX83QhpN\"")
@@ -344,18 +350,6 @@ android {
buildConfigField("String", "BUILD_VARIANT_TYPE", "\"Release\"")
}
create("instrumentation") {
initWith(getByName("debug"))
isDefault = false
isMinifyEnabled = false
matchingFallbacks += "debug"
applicationIdSuffix = ".instrumentation"
buildConfigField("String", "BUILD_VARIANT_TYPE", "\"Instrumentation\"")
buildConfigField("String", "STRIPE_BASE_URL", "\"http://127.0.0.1:8080/stripe\"")
buildConfigField("String[]", "UNIDENTIFIED_SENDER_TRUST_ROOTS", "new String[]{ \"BVT/2gHqbrG1xzuIypLIOjFgMtihrMld1/5TGADL6Dhv\"}")
}
create("spinner") {
initWith(getByName("debug"))
isDefault = false
@@ -538,9 +532,10 @@ androidComponents {
transformationRequest.set(renameRequest)
}
// Include the test-only library on debug builds.
if (variant.buildType != "instrumentation") {
// Include the test-only library on non-release builds.
if (variant.buildType == "release") {
variant.packaging.jniLibs.excludes.add("**/libsignal_jni_testing.so")
variant.androidResources.ignoreAssetsPatterns.add("libsignal-testing.md")
}
// Starting with minSdk 23, Android leaves native libraries uncompressed, which is fine for the Play Store, but not for our self-distributed APKs.
@@ -726,6 +721,7 @@ dependencies {
}
implementation(libs.dnsjava)
implementation(libs.kotlinx.collections.immutable)
implementation(libs.arrow.core)
implementation(libs.accompanist.permissions)
implementation(libs.accompanist.drawablepainter)
implementation(libs.kotlin.stdlib.jdk8)
@@ -747,7 +743,7 @@ dependencies {
"canaryImplementation"(libs.square.leakcanary)
"instrumentationImplementation"(libs.androidx.fragment.testing) {
androidTestImplementation(libs.androidx.fragment.testing) {
exclude(group = "androidx.test", module = "core")
}
+22 -1055
View File
File diff suppressed because it is too large Load Diff
@@ -1,5 +1,6 @@
package org.thoughtcrime.securesms
import android.content.Context
import org.signal.core.util.concurrent.SignalExecutors
import org.signal.core.util.logging.AndroidLogger
import org.signal.core.util.logging.Log
@@ -11,6 +12,7 @@ import org.thoughtcrime.securesms.dependencies.InstrumentationApplicationDepende
import org.thoughtcrime.securesms.logging.CustomSignalProtocolLogger
import org.thoughtcrime.securesms.logging.PersistentLogger
import org.thoughtcrime.securesms.testing.InMemoryLogger
import org.thoughtcrime.securesms.util.Environment
/**
* Application context for running instrumentation tests (aka androidTests).
@@ -19,6 +21,11 @@ class SignalInstrumentationApplicationContext : ApplicationContext() {
val inMemoryLogger: InMemoryLogger = InMemoryLogger()
override fun attachBaseContext(base: Context?) {
Environment.IS_INSTRUMENTATION = true
super.attachBaseContext(base)
}
override fun initializeAppDependencies() {
val default = ApplicationDependencyProvider(this)
AppDependencies.init(this, InstrumentationApplicationDependencyProvider(this, default))
@@ -75,6 +75,14 @@ class EditMessageSyncProcessorTest {
.timestamp(originalTimestamp)
.expirationStartTimestamp(originalTimestamp)
.message(content.dataMessage)
.unidentifiedStatus(
listOf(
SyncMessage.Sent.UnidentifiedDeliveryStatus.Builder()
.destinationServiceIdBinary(toRecipient.requireServiceId().toByteString())
.unidentified(true)
.build()
)
)
.build()
).build()
).build()
@@ -100,6 +108,14 @@ class EditMessageSyncProcessorTest {
.targetSentTimestamp(originalTimestamp)
.build()
)
.unidentifiedStatus(
listOf(
SyncMessage.Sent.UnidentifiedDeliveryStatus.Builder()
.destinationServiceIdBinary(toRecipient.requireServiceId().toByteString())
.unidentified(true)
.build()
)
)
.build()
).build()
).build()
@@ -13,7 +13,6 @@ import assertk.assertions.isEqualTo
import assertk.assertions.isGreaterThan
import assertk.assertions.isNotEqualTo
import assertk.assertions.isNotNull
import assertk.assertions.isNull
import org.junit.After
import org.junit.Before
import org.junit.Ignore
@@ -166,7 +165,7 @@ class SyncMessageProcessorTest_synchronizeDeleteForMe {
assertThat(messageCount).isEqualTo(0)
val threadRecord = SignalDatabase.threads.getThreadRecord(threadId)
assertThat(threadRecord).isNull()
assertThat(threadRecord?.active).isEqualTo(false)
}
@Test
@@ -245,7 +244,7 @@ class SyncMessageProcessorTest_synchronizeDeleteForMe {
// THEN
assertThat(SignalDatabase.messages.getMessageCountForThread(threadId)).isEqualTo(0)
assertThat(SignalDatabase.threads.getThreadRecord(threadId)).isNull()
assertThat(SignalDatabase.threads.getThreadRecord(threadId)?.active).isEqualTo(false)
}
@Test
@@ -304,7 +303,7 @@ class SyncMessageProcessorTest_synchronizeDeleteForMe {
// THEN
assertThat(SignalDatabase.messages.getMessageCountForThread(threadId)).isEqualTo(0)
assertThat(SignalDatabase.threads.getThreadRecord(threadId)).isNull()
assertThat(SignalDatabase.threads.getThreadRecord(threadId)?.active).isEqualTo(false)
harness.inMemoryLogger.flush()
assertThat(harness.inMemoryLogger.entries().filter { it.message?.contains("Using backup non-expiring messages") == true }).hasSize(1)
@@ -344,7 +343,7 @@ class SyncMessageProcessorTest_synchronizeDeleteForMe {
// THEN
assertThat(SignalDatabase.messages.getMessageCountForThread(threadId)).isEqualTo(0)
assertThat(SignalDatabase.threads.getThreadRecord(threadId)).isNull()
assertThat(SignalDatabase.threads.getThreadRecord(threadId)?.active).isEqualTo(false)
}
@Test
@@ -376,7 +375,7 @@ class SyncMessageProcessorTest_synchronizeDeleteForMe {
// THEN
assertThat(SignalDatabase.messages.getMessageCountForThread(threadId)).isEqualTo(3)
assertThat(SignalDatabase.threads.getThreadRecord(threadId)).isNotNull()
assertThat(SignalDatabase.threads.getThreadRecord(threadId)?.active).isEqualTo(true)
}
@Test
@@ -405,7 +404,7 @@ class SyncMessageProcessorTest_synchronizeDeleteForMe {
// THEN
assertThat(SignalDatabase.messages.getMessageCountForThread(threadId)).isEqualTo(0)
assertThat(SignalDatabase.threads.getThreadRecord(threadId)).isNull()
assertThat(SignalDatabase.threads.getThreadRecord(threadId)?.active).isEqualTo(false)
}
@Test
@@ -435,7 +434,7 @@ class SyncMessageProcessorTest_synchronizeDeleteForMe {
// THEN
threadIds.forEach {
assertThat(SignalDatabase.messages.getMessageCountForThread(it)).isEqualTo(0)
assertThat(SignalDatabase.threads.getThreadRecord(it)).isNull()
assertThat(SignalDatabase.threads.getThreadRecord(it)?.active).isEqualTo(false)
}
}
@@ -463,7 +462,7 @@ class SyncMessageProcessorTest_synchronizeDeleteForMe {
// THEN
assertThat(SignalDatabase.messages.getMessageCountForThread(aliceThreadId)).isEqualTo(0)
assertThat(SignalDatabase.threads.getThreadRecord(aliceThreadId)).isNull()
assertThat(SignalDatabase.threads.getThreadRecord(aliceThreadId)?.active).isEqualTo(false)
}
@Ignore("counts are consistent for some reason")
@@ -527,10 +526,10 @@ class SyncMessageProcessorTest_synchronizeDeleteForMe {
// THEN
assertThat(SignalDatabase.messages.getMessageCountForThread(aliceThreadId)).isEqualTo(0)
assertThat(SignalDatabase.threads.getThreadRecord(aliceThreadId)).isNull()
assertThat(SignalDatabase.threads.getThreadRecord(aliceThreadId)?.active).isEqualTo(false)
assertThat(SignalDatabase.messages.getMessageCountForThread(groupThreadId)).isEqualTo(0)
assertThat(SignalDatabase.threads.getThreadRecord(groupThreadId)).isNull()
assertThat(SignalDatabase.threads.getThreadRecord(groupThreadId)?.active).isEqualTo(false)
}
@Test
@@ -551,7 +550,7 @@ class SyncMessageProcessorTest_synchronizeDeleteForMe {
// THEN
assertThat(SignalDatabase.messages.getMessageCountForThread(threadId)).isEqualTo(20)
assertThat(SignalDatabase.threads.getThreadRecord(threadId)).isNotNull()
assertThat(SignalDatabase.threads.getThreadRecord(threadId)?.active).isEqualTo(true)
harness.inMemoryLogger.flush()
assertThat(harness.inMemoryLogger.entries().filter { it.message?.contains("Thread is not local only") == true }).hasSize(1)
@@ -665,7 +664,7 @@ class SyncMessageProcessorTest_synchronizeDeleteForMe {
updatedAttachments = SignalDatabase.attachments.getAttachmentsForMessage(message1.messageId)
assertThat(updatedAttachments).isEmpty()
assertThat(SignalDatabase.threads.getThreadRecord(threadId)).isNull()
assertThat(SignalDatabase.threads.getThreadRecord(threadId)?.active).isEqualTo(false)
}
private fun DatabaseAttachment.copy(
@@ -8,9 +8,9 @@ package org.thoughtcrime.securesms.testing
import androidx.test.platform.app.InstrumentationRegistry
import io.mockk.every
import org.junit.rules.ExternalResource
import org.signal.core.util.JsonUtils
import org.signal.network.NetworkResult
import org.thoughtcrime.securesms.dependencies.AppDependencies
import org.thoughtcrime.securesms.util.JsonUtils
import org.whispersystems.signalservice.internal.push.SubscriptionsConfiguration
/**
@@ -2,13 +2,13 @@ package org.thoughtcrime.securesms.testing.incomingmessageobserver
import android.app.Application
import org.signal.benchmark.setup.NoOpJob
import org.signal.core.util.UptimeSleepTimer
import org.signal.libsignal.net.Network
import org.thoughtcrime.securesms.dependencies.AppDependencies
import org.thoughtcrime.securesms.dependencies.ApplicationDependencyProvider
import org.thoughtcrime.securesms.dependencies.InstrumentationApplicationDependencyProvider
import org.thoughtcrime.securesms.jobmanager.JobManager
import org.thoughtcrime.securesms.jobs.JobManagerFactories
import org.whispersystems.signalservice.api.util.UptimeSleepTimer
import org.whispersystems.signalservice.api.websocket.SignalWebSocket
import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration
import org.whispersystems.signalservice.internal.websocket.BenchmarkWebSocketConnection
@@ -7,13 +7,13 @@ package org.thoughtcrime.securesms
import android.app.Application
import org.signal.benchmark.setup.NoOpJob
import org.signal.core.util.UptimeSleepTimer
import org.signal.libsignal.net.Network
import org.thoughtcrime.securesms.dependencies.AppDependencies
import org.thoughtcrime.securesms.dependencies.ApplicationDependencyProvider
import org.thoughtcrime.securesms.jobmanager.JobManager
import org.thoughtcrime.securesms.jobs.JobManagerFactories
import org.thoughtcrime.securesms.net.DeviceTransferBlockingInterceptor
import org.whispersystems.signalservice.api.util.UptimeSleepTimer
import org.whispersystems.signalservice.api.websocket.SignalWebSocket
import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration
import org.whispersystems.signalservice.internal.websocket.BenchmarkWebSocketConnection
@@ -11,7 +11,7 @@ import io.reactivex.rxjava3.subjects.BehaviorSubject
import org.signal.network.websocket.WebSocketRequestMessage
import org.signal.network.websocket.WebSocketResponseMessage
import org.signal.network.websocket.WebsocketResponse
import org.thoughtcrime.securesms.util.JsonUtils
import org.signal.core.util.JsonUtils
import org.thoughtcrime.securesms.util.SignalTrace
import org.whispersystems.signalservice.api.websocket.WebSocketConnectionState
import org.whispersystems.signalservice.internal.push.SendMessageResponse
@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<application
android:usesCleartextTraffic="true"
tools:replace="android:usesCleartextTraffic"
tools:ignore="UnusedAttribute" />
</manifest>
@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/signal_accent_green"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>
@@ -1,4 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<string name="app_name">Signal (Instrumentation)</string>
</resources>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -18,6 +18,7 @@ package org.thoughtcrime.securesms;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -88,7 +89,9 @@ import org.thoughtcrime.securesms.keyvalue.KeepMessagesDuration;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.logging.CustomSignalProtocolLogger;
import org.thoughtcrime.securesms.logging.PersistentLogger;
import org.thoughtcrime.securesms.logsubmit.SubmitDebugLogActivity;
import org.thoughtcrime.securesms.messageprocessingalarm.RoutineMessageFetchReceiver;
import org.thoughtcrime.securesms.messages.IncomingMessageObserver;
import org.thoughtcrime.securesms.migrations.ApplicationMigrations;
import org.thoughtcrime.securesms.mms.SignalGlideModule;
import org.thoughtcrime.securesms.providers.BlobProvider;
@@ -107,7 +110,7 @@ import org.thoughtcrime.securesms.service.webrtc.ActiveCallManager;
import org.thoughtcrime.securesms.service.webrtc.CallingAssets;
import org.thoughtcrime.securesms.service.webrtc.AndroidTelecomUtil;
import org.thoughtcrime.securesms.storage.StorageSyncHelper;
import org.thoughtcrime.securesms.util.AppForegroundObserver;
import org.signal.core.util.AppForegroundObserver;
import org.thoughtcrime.securesms.util.AppStartup;
import org.thoughtcrime.securesms.util.DeviceProperties;
import org.thoughtcrime.securesms.util.DynamicTheme;
@@ -417,7 +420,11 @@ public class ApplicationContext extends Application implements AppForegroundObse
new org.signal.registration.RegistrationDependencies(
new org.thoughtcrime.securesms.registration.v2.AppRegistrationNetworkController(this, AppDependencies.getPushServiceSocket()),
new org.thoughtcrime.securesms.registration.v2.AppRegistrationStorageController(this),
null
null,
context -> {
context.startActivity(new Intent(context, SubmitDebugLogActivity.class));
return Unit.INSTANCE;
}
)
);
}
@@ -448,22 +455,27 @@ public class ApplicationContext extends Application implements AppForegroundObse
PlayServicesUtil.PlayServicesStatus playServicesStatus = PlayServicesUtil.getPlayServicesStatus(this);
if (playServicesStatus == PlayServicesUtil.PlayServicesStatus.SUCCESS && !SignalStore.account().isFcmEnabled()) {
Log.i(TAG, "Play Services are newly-available. Enabling FCM and updating server.");
Log.w(TAG, "Play Services are newly-available. Enabling FCM and updating server.");
SignalStore.account().setFcmEnabled(true);
AppDependencies.getJobManager().startChain(new FcmRefreshJob())
.then(new RefreshAttributesJob())
.enqueue();
AppDependencies.resetNetwork();
AppDependencies.startNetwork();
IncomingMessageObserver.stopForegroundService(this);
} else if (playServicesStatus == PlayServicesUtil.PlayServicesStatus.MISSING && SignalStore.account().isFcmEnabled()) {
Log.w(TAG, "Play Services are no longer available. Disabling FCM and updating server.");
SignalStore.account().setFcmEnabled(false);
SignalStore.account().setFcmToken(null);
AppDependencies.getJobManager().add(new RefreshAttributesJob());
Log.w(TAG, "Play Services are no longer available. Attempting to get an FCM token anyway.");
AppDependencies.getJobManager().add(new FcmRefreshJob());
} else if (playServicesStatus == PlayServicesUtil.PlayServicesStatus.MISSING && (System.currentTimeMillis() - SignalStore.misc().getLastMissingPlayServicesFcmVerificationTime()) > TimeUnit.DAYS.toMillis(3)) {
Log.i(TAG, "Play Services are unavailable, but it's been long enough that we should check and see if we can get an FCM token anyway.");
AppDependencies.getJobManager().add(new FcmRefreshJob());
} else if (SignalStore.account().isFcmEnabled()) {
long lastSetTime = SignalStore.account().getFcmTokenLastSetTime();
long nextSetTime = lastSetTime + TimeUnit.HOURS.toMillis(6);
long now = System.currentTimeMillis();
if (SignalStore.account().getFcmToken() == null || nextSetTime <= now || lastSetTime > now) {
Log.i(TAG, "Time for routine FCM token refresh.");
AppDependencies.getJobManager().add(new FcmRefreshJob());
}
} else {
@@ -16,7 +16,7 @@ import androidx.core.app.ActivityOptionsCompat;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.dependencies.AppDependencies;
import org.thoughtcrime.securesms.util.AppStartup;
import org.thoughtcrime.securesms.util.ConfigurationUtil;
import org.signal.core.util.ConfigurationUtil;
import org.thoughtcrime.securesms.util.WindowUtil;
import org.thoughtcrime.securesms.util.dynamiclanguage.DynamicLanguageContextWrapper;
@@ -6,7 +6,7 @@ import androidx.lifecycle.LifecycleOwner;
import com.bumptech.glide.RequestManager;
import org.thoughtcrime.securesms.conversationlist.model.ConversationSet;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.database.model.ThreadWithRecipient;
import java.util.Locale;
import java.util.Set;
@@ -14,7 +14,7 @@ import java.util.Set;
public interface BindableConversationListItem extends Unbindable {
void bind(@NonNull LifecycleOwner lifecycleOwner,
@NonNull ThreadRecord thread,
@NonNull ThreadWithRecipient thread,
@NonNull RequestManager requestManager, @NonNull Locale locale,
@NonNull Set<Long> typingThreads,
@NonNull ConversationSet selectedConversations,
@@ -10,8 +10,8 @@ import androidx.activity.result.contract.ActivityResultContract
import androidx.biometric.BiometricManager
import androidx.biometric.BiometricPrompt
import androidx.biometric.BiometricPrompt.PromptInfo
import org.signal.core.util.ServiceUtil
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.util.ServiceUtil
/**
* Authentication using phone biometric (face, fingerprint recognition) or device lock (pattern, pin or passphrase).
@@ -23,45 +23,25 @@ public final class BlockUnblockDialog {
private BlockUnblockDialog() {}
public static void showReportSpamFor(@NonNull Context context,
@NonNull Lifecycle lifecycle,
@NonNull Recipient recipient,
@NonNull Runnable onReportSpam,
@Nullable Runnable onBlockAndReportSpam)
{
SimpleTask.run(lifecycle,
() -> buildReportSpamFor(context, recipient, onReportSpam, onBlockAndReportSpam),
AlertDialog.Builder::show);
buildReportSpamFor(context, recipient, onReportSpam, onBlockAndReportSpam).show();
}
public static void showBlockFor(@NonNull Context context,
@NonNull Lifecycle lifecycle,
@NonNull Recipient recipient,
@NonNull Runnable onBlock)
{
SimpleTask.run(lifecycle,
() -> buildBlockFor(context, recipient, onBlock, null),
AlertDialog.Builder::show);
}
public static void showBlockAndReportSpamFor(@NonNull Context context,
@NonNull Lifecycle lifecycle,
@NonNull Recipient recipient,
@NonNull Runnable onBlock,
@NonNull Runnable onBlockAndReportSpam)
{
SimpleTask.run(lifecycle,
() -> buildBlockFor(context, recipient, onBlock, onBlockAndReportSpam),
AlertDialog.Builder::show);
buildBlockFor(context, recipient, onBlock, null).show();
}
public static void showUnblockFor(@NonNull Context context,
@NonNull Lifecycle lifecycle,
@NonNull Recipient recipient,
@NonNull Runnable onUnblock)
{
SimpleTask.run(lifecycle,
() -> buildUnblockFor(context, recipient, onUnblock),
AlertDialog.Builder::show);
buildUnblockFor(context, recipient, onUnblock).show();
}
@WorkerThread
@@ -87,6 +87,7 @@ import org.signal.core.ui.compose.Snackbars
import org.signal.core.ui.compose.theme.SignalTheme
import org.signal.core.ui.permissions.Permissions
import org.signal.core.ui.rememberIsSplitPane
import org.signal.core.util.AppForegroundObserver
import org.signal.core.util.Util
import org.signal.core.util.concurrent.LifecycleDisposable
import org.signal.core.util.getParcelableCompat
@@ -179,7 +180,6 @@ import org.thoughtcrime.securesms.stories.Stories
import org.thoughtcrime.securesms.stories.archive.StoryArchiveActivity
import org.thoughtcrime.securesms.stories.landing.StoriesLandingFragment
import org.thoughtcrime.securesms.stories.settings.StorySettingsActivity
import org.thoughtcrime.securesms.util.AppForegroundObserver
import org.thoughtcrime.securesms.util.AppStartup
import org.thoughtcrime.securesms.util.CachedInflater
import org.thoughtcrime.securesms.util.CommunicationActions
@@ -33,7 +33,7 @@ import org.thoughtcrime.securesms.registration.ui.RegistrationActivity;
import org.thoughtcrime.securesms.util.Environment;
import org.thoughtcrime.securesms.restore.RestoreActivity;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.AppForegroundObserver;
import org.signal.core.util.AppForegroundObserver;
import org.thoughtcrime.securesms.util.AppStartup;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
@@ -11,6 +11,7 @@ import android.content.Context
import android.content.Intent
import android.content.pm.PackageInstaller
import android.os.Build
import org.signal.core.util.AppForegroundObserver
import org.signal.core.util.PendingIntentFlags
import org.signal.core.util.StreamUtil
import org.signal.core.util.getDownloadManager
@@ -18,7 +19,6 @@ import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.dependencies.AppDependencies
import org.thoughtcrime.securesms.jobs.ApkUpdateJob
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.util.AppForegroundObserver
import org.thoughtcrime.securesms.util.FileUtils
import java.io.FileInputStream
import java.io.IOException
@@ -12,12 +12,12 @@ import android.content.Intent
import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat
import org.signal.core.util.PendingIntentFlags
import org.signal.core.util.ServiceUtil
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.MainActivity
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.notifications.NotificationChannels
import org.thoughtcrime.securesms.notifications.NotificationIds
import org.thoughtcrime.securesms.util.ServiceUtil
object ApkUpdateNotifications {
@@ -10,11 +10,11 @@ import android.os.Parcelable
import androidx.core.os.ParcelCompat
import org.signal.blurhash.BlurHash
import org.signal.core.models.media.TransformProperties
import org.signal.core.util.ParcelUtil
import org.signal.core.util.UuidUtil
import org.thoughtcrime.securesms.audio.AudioHash
import org.thoughtcrime.securesms.database.AttachmentTable
import org.thoughtcrime.securesms.stickers.StickerLocator
import org.thoughtcrime.securesms.util.ParcelUtil
import java.util.UUID
import kotlin.time.Duration.Companion.days
import kotlin.time.Duration.Companion.milliseconds
@@ -5,11 +5,11 @@ import android.os.Parcel
import androidx.core.os.ParcelCompat
import org.signal.blurhash.BlurHash
import org.signal.core.models.media.TransformProperties
import org.signal.core.util.ParcelUtil
import org.thoughtcrime.securesms.audio.AudioHash
import org.thoughtcrime.securesms.database.AttachmentTable
import org.thoughtcrime.securesms.mms.PartAuthority
import org.thoughtcrime.securesms.stickers.StickerLocator
import org.thoughtcrime.securesms.util.ParcelUtil
import java.util.UUID
class DatabaseAttachment : Attachment {
@@ -7,7 +7,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.database.model.databaseprotos.AudioWaveFormData;
import org.thoughtcrime.securesms.util.ParcelUtil;
import org.signal.core.util.ParcelUtil;
import org.signal.core.util.Base64;
import java.io.IOException;
@@ -7,7 +7,7 @@ import android.media.AudioManager
import android.media.AudioManager.OnAudioFocusChangeListener
import android.os.Build
import androidx.annotation.RequiresApi
import org.thoughtcrime.securesms.util.ServiceUtil
import org.signal.core.util.ServiceUtil
abstract class AudioRecorderFocusManager(val context: Context) {
protected val audioManager: AudioManager = ServiceUtil.getAudioManager(context)
@@ -6,8 +6,8 @@
package org.thoughtcrime.securesms.backup.v2
import androidx.annotation.VisibleForTesting
import org.signal.core.util.ThrottledDebouncer
import org.thoughtcrime.securesms.dependencies.AppDependencies
import org.thoughtcrime.securesms.util.ThrottledDebouncer
import java.util.concurrent.ExecutionException
import java.util.concurrent.Executors
import kotlin.time.Duration.Companion.seconds
@@ -40,6 +40,7 @@ import org.signal.core.util.CursorUtil
import org.signal.core.util.DiskUtil
import org.signal.core.util.EventTimer
import org.signal.core.util.PendingIntentFlags.cancelCurrent
import org.signal.core.util.ServiceUtil
import org.signal.core.util.Stopwatch
import org.signal.core.util.bytes
import org.signal.core.util.concurrent.LimitedWorker
@@ -147,7 +148,6 @@ import org.thoughtcrime.securesms.service.BackupMediaRestoreService
import org.thoughtcrime.securesms.service.BackupProgressService
import org.thoughtcrime.securesms.storage.StorageSyncHelper
import org.thoughtcrime.securesms.util.RemoteConfig
import org.thoughtcrime.securesms.util.ServiceUtil
import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.thoughtcrime.securesms.util.toMillis
import org.whispersystems.signalservice.api.archive.ArchiveGetMediaItemsResponse
@@ -61,7 +61,7 @@ class ChatArchiveExporter(private val cursor: Cursor, private val db: SignalData
expirationTimerMs = cursor.requireLong(RecipientTable.MESSAGE_EXPIRATION_TIME).seconds.inWholeMilliseconds.takeIf { it > 0 },
expireTimerVersion = cursor.requireInt(RecipientTable.MESSAGE_EXPIRATION_TIME_VERSION),
muteUntilMs = cursor.requireLong(RecipientTable.MUTE_UNTIL).takeIf { it > 0 },
markedUnread = ThreadTable.ReadStatus.deserialize(cursor.requireInt(ThreadTable.READ)) == ThreadTable.ReadStatus.FORCED_UNREAD,
markedUnread = ThreadTable.ReadStatus.deserialize(cursor.requireInt(ThreadTable.READ)) == ThreadTable.ReadStatus.ForcedUnread,
dontNotifyForMentionsIfMuted = RecipientTable.NotificationSetting.DO_NOT_NOTIFY.id == cursor.requireInt(RecipientTable.MENTION_SETTING),
style = ChatStyleConverter.constructRemoteChatStyle(
db = db,
@@ -45,6 +45,7 @@ import org.signal.core.models.ServiceId
import org.signal.core.util.Base64
import org.signal.core.util.EventTimer
import org.signal.core.util.Hex
import org.signal.core.util.JsonUtils
import org.signal.core.util.ParallelEventTimer
import org.signal.core.util.StringUtil
import org.signal.core.util.UuidUtil
@@ -105,7 +106,6 @@ import org.thoughtcrime.securesms.payments.FailureReason
import org.thoughtcrime.securesms.payments.State
import org.thoughtcrime.securesms.polls.PollRecord
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.util.JsonUtils
import org.thoughtcrime.securesms.util.MediaUtil
import org.thoughtcrime.securesms.util.mb
import java.io.Closeable
@@ -46,7 +46,7 @@ object ChatArchiveImporter {
ThreadTable.RECIPIENT_ID to recipientId.serialize(),
ThreadTable.PINNED_ORDER to chat.pinnedOrder,
ThreadTable.ARCHIVED to chat.archived.toInt(),
ThreadTable.READ to if (chat.markedUnread) ThreadTable.ReadStatus.FORCED_UNREAD.serialize() else ThreadTable.ReadStatus.READ.serialize(),
ThreadTable.READ to if (chat.markedUnread) ThreadTable.ReadStatus.ForcedUnread.serialize() else ThreadTable.ReadStatus.Read.serialize(),
ThreadTable.ACTIVE to 1
)
.run()
@@ -27,6 +27,7 @@ import org.signal.archive.proto.ViewOnceMessage
import org.signal.core.models.ServiceId
import org.signal.core.util.Base64
import org.signal.core.util.Hex
import org.signal.core.util.JsonUtils
import org.signal.core.util.SqlUtil
import org.signal.core.util.UuidUtil
import org.signal.core.util.asList
@@ -81,7 +82,6 @@ import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.stickers.StickerLocator
import org.thoughtcrime.securesms.util.Environment
import org.thoughtcrime.securesms.util.JsonUtils
import org.thoughtcrime.securesms.util.MessageUtil
import org.whispersystems.signalservice.api.payments.Money
import org.whispersystems.signalservice.internal.push.DataMessage
@@ -52,6 +52,7 @@ import org.signal.core.ui.compose.Previews
import org.signal.core.ui.compose.Scaffolds
import org.signal.core.ui.compose.SignalIcons
import org.signal.core.ui.compose.theme.SignalTheme
import org.signal.core.util.ByteUnit
import org.signal.core.util.billing.BillingResponseCode
import org.signal.core.util.bytes
import org.signal.core.util.money.FiatMoney
@@ -60,7 +61,6 @@ import org.thoughtcrime.securesms.backup.v2.MessageBackupTier
import org.thoughtcrime.securesms.fonts.SignalSymbols
import org.thoughtcrime.securesms.fonts.SignalSymbols.signalSymbolText
import org.thoughtcrime.securesms.payments.FiatMoneyUtil
import org.thoughtcrime.securesms.util.ByteUnit
import java.math.BigDecimal
import java.util.Currency
import kotlin.time.Duration.Companion.days
@@ -13,6 +13,7 @@ import com.google.android.material.button.MaterialButton
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.subjects.PublishSubject
import org.signal.core.util.Debouncer
import org.signal.core.util.concurrent.LifecycleDisposable
import org.signal.core.util.getParcelableCompat
import org.signal.core.util.money.FiatMoney
@@ -39,7 +40,6 @@ import org.thoughtcrime.securesms.keyboard.emoji.EmojiKeyboardPageFragment
import org.thoughtcrime.securesms.keyboard.emoji.search.EmojiSearchFragment
import org.thoughtcrime.securesms.payments.FiatMoneyUtil
import org.thoughtcrime.securesms.payments.currency.CurrencyUtil
import org.thoughtcrime.securesms.util.Debouncer
import org.thoughtcrime.securesms.util.activityViewModel
import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter
import org.thoughtcrime.securesms.util.navigation.safeNavigate
@@ -14,13 +14,13 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import org.signal.core.ui.compose.DayNightPreviews
import org.signal.core.ui.compose.Previews
import org.signal.core.util.ServiceUtil
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.banner.Banner
import org.thoughtcrime.securesms.banner.ui.compose.Action
import org.thoughtcrime.securesms.banner.ui.compose.DefaultBanner
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.util.PowerManagerCompat
import org.thoughtcrime.securesms.util.ServiceUtil
import org.thoughtcrime.securesms.util.TextSecurePreferences
class DozeBanner(private val context: Context, private val onDismissListener: () -> Unit) : Banner<Unit>() {
@@ -78,7 +78,7 @@ public class BlockedUsersFragment extends Fragment {
}
private void handleRecipientClicked(@NonNull Recipient recipient) {
BlockUnblockDialog.showUnblockFor(requireContext(), getViewLifecycleOwner().getLifecycle(), recipient, () -> {
BlockUnblockDialog.showUnblockFor(requireContext(), recipient, () -> {
viewModel.unblock(recipient.getId());
});
}
@@ -9,14 +9,14 @@ import android.os.Bundle
import android.os.ResultReceiver
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import org.signal.core.util.ThrottledDebouncer
import org.signal.core.util.concurrent.SerialExecutor
import org.signal.core.util.concurrent.SignalExecutors
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.dependencies.AppDependencies
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.service.webrtc.ActiveCallData
import org.thoughtcrime.securesms.service.webrtc.links.CallLinkRoomId
import org.thoughtcrime.securesms.util.ThrottledDebouncer
import org.thoughtcrime.securesms.util.concurrent.SerialExecutor
import java.util.concurrent.Executor
import kotlin.time.Duration.Companion.days
import kotlin.time.Duration.Companion.seconds
@@ -21,7 +21,7 @@ import androidx.core.widget.TextViewCompat;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.util.EditTextExtensionsKt;
import org.thoughtcrime.securesms.util.ServiceUtil;
import org.signal.core.util.ServiceUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.ViewUtil;
@@ -13,7 +13,7 @@ import org.signal.core.util.ContextUtil;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.emoji.SimpleEmojiTextView;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.DrawableUtil;
import org.signal.core.util.DrawableUtil;
import org.thoughtcrime.securesms.util.RemoteConfig;
import org.thoughtcrime.securesms.util.SpanUtil;
import org.thoughtcrime.securesms.util.ViewUtil;
@@ -8,7 +8,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout.OnKeyboardShownListener;
import org.thoughtcrime.securesms.util.ServiceUtil;
import org.signal.core.util.ServiceUtil;
public class InputAwareLayout extends KeyboardAwareLinearLayout implements OnKeyboardShownListener {
private InputView current;
@@ -30,7 +30,7 @@ import androidx.appcompat.widget.LinearLayoutCompat;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.util.ServiceUtil;
import org.signal.core.util.ServiceUtil;
import org.signal.core.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
@@ -12,8 +12,11 @@ import androidx.appcompat.app.AlertDialog
import androidx.constraintlayout.widget.ConstraintLayout
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.progressindicator.CircularProgressIndicator
import org.signal.core.util.dp
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.util.ViewUtil
import org.thoughtcrime.securesms.util.padding
import org.thoughtcrime.securesms.util.visible
/**
* Wraps a normal progress dialog for showing blocking in-progress UI.
@@ -81,9 +84,15 @@ class SignalProgressDialog private constructor(
val progressView: CircularProgressIndicator = customView.findViewById(R.id.progress_dialog_progressbar)
titleView.text = title
titleView.visible = title != null
messageView.text = message
messageView.visible = message != null
progressView.isIndeterminate = indeterminate
if (title == null && message == null) {
progressView.padding(top = 32.dp, bottom = 32.dp)
}
builder.setView(customView)
val dialog = builder.show()
@@ -18,7 +18,7 @@ import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.emoji.EmojiProvider.EmojiDrawable;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.util.EditTextExtensionsKt;
import org.thoughtcrime.securesms.util.ServiceUtil;
import org.signal.core.util.ServiceUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.signal.core.util.Util;
@@ -20,7 +20,7 @@ import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.emoji.EmojiPageViewGridAdapter.EmojiHeader;
import org.thoughtcrime.securesms.components.emoji.EmojiPageViewGridAdapter.EmojiNoResultsModel;
import org.thoughtcrime.securesms.components.emoji.EmojiPageViewGridAdapter.VariationSelectorListener;
import org.thoughtcrime.securesms.util.DrawableUtil;
import org.signal.core.util.DrawableUtil;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.adapter.mapping.MappingModel;
@@ -16,7 +16,7 @@ import com.fasterxml.jackson.databind.type.TypeFactory;
import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.util.JsonUtils;
import org.signal.core.util.JsonUtils;
import java.io.IOException;
import java.util.ArrayList;
@@ -6,9 +6,9 @@ import android.text.Spanned
import android.text.TextUtils
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatTextView
import org.signal.core.util.ThrottledDebouncer
import org.thoughtcrime.securesms.components.spoiler.SpoilerRendererDelegate
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.util.ThrottledDebouncer
import java.util.Optional
open class SimpleEmojiTextView @JvmOverloads constructor(
@@ -13,7 +13,7 @@ import com.google.android.gms.maps.model.LatLng;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.maps.AddressData;
import org.thoughtcrime.securesms.util.JsonUtils;
import org.signal.core.util.JsonUtils;
import java.io.IOException;
@@ -14,7 +14,7 @@ import androidx.core.graphics.drawable.DrawableCompat;
import org.signal.core.util.ContextUtil;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.spoiler.SpoilerAnnotation;
import org.thoughtcrime.securesms.util.DrawableUtil;
import org.signal.core.util.DrawableUtil;
import org.signal.core.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
@@ -45,6 +45,7 @@ import org.signal.core.ui.compose.Previews
import org.signal.core.ui.compose.Rows
import org.signal.core.ui.compose.Scaffolds
import org.signal.core.ui.compose.Texts
import org.signal.core.util.ServiceUtil
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.compose.rememberStatusBarColorNestedScrollModifier
import org.thoughtcrime.securesms.contactshare.SimpleTextWatcher
@@ -56,7 +57,6 @@ import org.thoughtcrime.securesms.lock.v2.SvrConstants
import org.thoughtcrime.securesms.pin.RegistrationLockV2Dialog
import org.thoughtcrime.securesms.registration.ui.RegistrationActivity
import org.thoughtcrime.securesms.util.PlayStoreUtil
import org.thoughtcrime.securesms.util.ServiceUtil
import org.thoughtcrime.securesms.util.ViewUtil
import org.thoughtcrime.securesms.util.navigation.safeNavigate
import org.whispersystems.signalservice.api.kbs.PinHashUtil
@@ -5,10 +5,10 @@ import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.node.ObjectNode
import io.reactivex.rxjava3.core.Single
import io.reactivex.rxjava3.schedulers.Schedulers
import org.signal.core.util.JsonUtils
import org.signal.network.NetworkResult
import org.thoughtcrime.securesms.net.SignalNetwork
import org.thoughtcrime.securesms.providers.BlobProvider
import org.thoughtcrime.securesms.util.JsonUtils
class ExportAccountDataRepository {
@@ -8,6 +8,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import org.signal.core.util.ThrottledDebouncer
import org.thoughtcrime.securesms.backup.LocalExportProgress
import org.thoughtcrime.securesms.components.settings.app.chats.folders.ChatFoldersRepository
import org.thoughtcrime.securesms.dependencies.AppDependencies
@@ -17,7 +18,6 @@ import org.thoughtcrime.securesms.util.BackupUtil
import org.thoughtcrime.securesms.util.ConversationUtil
import org.thoughtcrime.securesms.util.RemoteConfig
import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.thoughtcrime.securesms.util.ThrottledDebouncer
class ChatsSettingsViewModel @JvmOverloads constructor(
private val repository: ChatsSettingsRepository = ChatsSettingsRepository()
@@ -17,6 +17,7 @@ import androidx.navigation.fragment.findNavController
import androidx.preference.PreferenceManager
import org.signal.core.ui.compose.ComposeFragment
import org.signal.core.ui.compose.DayNightPreviews
import org.signal.core.ui.compose.Dialogs
import org.signal.core.ui.compose.Dividers
import org.signal.core.ui.compose.Previews
import org.signal.core.ui.compose.Rows
@@ -90,6 +91,18 @@ class DataAndStorageSettingsFragment : ComposeFragment() {
override fun onRoamingDataAutoDownloadSelectionChanged(selection: Array<String>) {
viewModel.setRoamingAutoDownloadValues(selection.toSet())
}
override fun onForceWebsocketModeChanged(enabled: Boolean) {
viewModel.onForceWebsocketModeToggled(enabled)
}
override fun onConfirmStayConnectedInBackground() {
viewModel.confirmStayConnectedInBackground()
}
override fun onDismissStayConnectedInBackgroundDialog() {
viewModel.dismissStayConnectedInBackgroundDialog()
}
}
}
@@ -102,6 +115,9 @@ private interface DataAndStorageSettingsCallbacks {
fun onMobileDataAutoDownloadSelectionChanged(selection: Array<String>) = Unit
fun onWifiDataAutoDownloadSelectionChanged(selection: Array<String>) = Unit
fun onRoamingDataAutoDownloadSelectionChanged(selection: Array<String>) = Unit
fun onForceWebsocketModeChanged(enabled: Boolean) = Unit
fun onConfirmStayConnectedInBackground() = Unit
fun onDismissStayConnectedInBackgroundDialog() = Unit
object Empty : DataAndStorageSettingsCallbacks
}
@@ -252,6 +268,19 @@ private fun DataAndStorageSettingsScreen(
Dividers.Default()
}
item {
Rows.ToggleRow(
checked = state.forceWebsocketMode || !state.playServicesAvailable,
text = stringResource(R.string.DataAndStorageSettingsFragment__stay_connected_in_background),
enabled = state.playServicesAvailable,
onCheckChanged = callbacks::onForceWebsocketModeChanged
)
}
item {
Dividers.Default()
}
item {
Texts.SectionHeader(stringResource(R.string.preferences_proxy))
}
@@ -264,6 +293,17 @@ private fun DataAndStorageSettingsScreen(
)
}
}
if (state.showStayConnectedDialog) {
Dialogs.SimpleAlertDialog(
title = "",
body = stringResource(R.string.DataAndStorageSettingsFragment__staying_connected_while_in_the_background_will_likely_result_in_increased_battery_usage),
confirm = stringResource(R.string.DataAndStorageSettingsFragment__enable),
dismiss = stringResource(android.R.string.cancel),
onConfirm = callbacks::onConfirmStayConnectedInBackground,
onDismiss = callbacks::onDismissStayConnectedInBackgroundDialog
)
}
}
}
@@ -279,7 +319,10 @@ private fun DataAndStorageSettingsScreenPreview() {
roamingAutoDownloadValues = setOf(),
callDataMode = CallDataMode.HIGH_ALWAYS,
isProxyEnabled = false,
sentMediaQuality = SentMediaQuality.STANDARD
sentMediaQuality = SentMediaQuality.STANDARD,
forceWebsocketMode = false,
playServicesAvailable = true,
showStayConnectedDialog = false
),
callbacks = DataAndStorageSettingsCallbacks.Empty
)
@@ -10,5 +10,8 @@ data class DataAndStorageSettingsState(
val roamingAutoDownloadValues: Set<String>,
val callDataMode: CallDataMode,
val isProxyEnabled: Boolean,
val sentMediaQuality: SentMediaQuality
val sentMediaQuality: SentMediaQuality,
val forceWebsocketMode: Boolean,
val playServicesAvailable: Boolean,
val showStayConnectedDialog: Boolean
)
@@ -7,8 +7,11 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
import org.thoughtcrime.securesms.dependencies.AppDependencies
import org.thoughtcrime.securesms.keyvalue.SettingsValues.ForceWebsocketMode
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.messages.IncomingMessageObserver
import org.thoughtcrime.securesms.mms.SentMediaQuality
import org.thoughtcrime.securesms.util.PlayServicesUtil
import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.thoughtcrime.securesms.webrtc.CallDataMode
@@ -23,7 +26,7 @@ class DataAndStorageSettingsViewModel(
fun refresh() {
repository.getTotalStorageUse { totalStorageUse ->
store.update { getState().copy(totalStorageUse = totalStorageUse) }
store.update { getState().copy(totalStorageUse = totalStorageUse, showStayConnectedDialog = it.showStayConnectedDialog) }
}
}
@@ -53,8 +56,34 @@ class DataAndStorageSettingsViewModel(
getStateAndCopyStorageUsage()
}
fun onForceWebsocketModeToggled(enabled: Boolean) {
if (enabled) {
store.update { it.copy(showStayConnectedDialog = true) }
} else {
applyForceWebsocketMode(false)
}
}
fun confirmStayConnectedInBackground() {
applyForceWebsocketMode(true)
}
fun dismissStayConnectedInBackgroundDialog() {
store.update { it.copy(showStayConnectedDialog = false) }
}
private fun applyForceWebsocketMode(enabled: Boolean) {
SignalStore.settings.forceWebsocketMode = if (enabled) ForceWebsocketMode.ENABLED_BY_USER else ForceWebsocketMode.DISABLED
if (!enabled) {
IncomingMessageObserver.stopForegroundService(AppDependencies.application)
}
AppDependencies.resetNetwork()
AppDependencies.startNetwork()
getStateAndCopyStorageUsage()
}
private fun getStateAndCopyStorageUsage() {
store.update { getState().copy(totalStorageUse = it.totalStorageUse) }
store.update { getState().copy(totalStorageUse = it.totalStorageUse, showStayConnectedDialog = it.showStayConnectedDialog) }
}
private fun getState() = DataAndStorageSettingsState(
@@ -70,7 +99,10 @@ class DataAndStorageSettingsViewModel(
),
callDataMode = SignalStore.settings.callDataMode,
isProxyEnabled = SignalStore.proxy.isProxyEnabled,
sentMediaQuality = SignalStore.settings.sentMediaQuality
sentMediaQuality = SignalStore.settings.sentMediaQuality,
forceWebsocketMode = SignalStore.settings.forceWebsocketMode.isEnabled,
playServicesAvailable = PlayServicesUtil.getPlayServicesStatus(AppDependencies.application) == PlayServicesUtil.PlayServicesStatus.SUCCESS,
showStayConnectedDialog = false
)
class Factory(
@@ -5,6 +5,7 @@
package org.thoughtcrime.securesms.components.settings.app.help
import android.widget.Toast
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
@@ -26,6 +27,7 @@ import org.signal.core.ui.compose.Rows.TextAndLabel
import org.signal.core.ui.compose.Rows.defaultPadding
import org.signal.core.ui.compose.Scaffolds
import org.signal.core.ui.compose.SignalIcons
import org.signal.core.util.Util
import org.thoughtcrime.securesms.BuildConfig
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.util.CommunicationActions
@@ -74,7 +76,11 @@ class HelpSettingsFragment : ComposeFragment() {
item {
Rows.TextRow(
text = stringResource(R.string.HelpSettingsFragment__version),
label = BuildConfig.VERSION_NAME
label = BuildConfig.VERSION_NAME,
onLongClick = {
Util.copyToClipboard(context, BuildConfig.VERSION_NAME)
Toast.makeText(context, R.string.HelpSettingsFragment__copied_to_clipboard, Toast.LENGTH_SHORT).show()
}
)
}
@@ -83,7 +83,6 @@ import java.util.concurrent.TimeUnit
import kotlin.math.max
import kotlin.random.Random
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
class InternalSettingsFragment : DSLSettingsFragment(R.string.preferences__internal_preferences) {
@@ -511,25 +510,6 @@ class InternalSettingsFragment : DSLSettingsFragment(R.string.preferences__inter
sectionHeaderPref(DSLSettingsText.from("Network"))
switchPref(
title = DSLSettingsText.from("Force websocket mode"),
summary = DSLSettingsText.from("Pretend you have no Play Services. Ignores websocket messages and keeps the websocket open in a foreground service. You have to manually force-stop the app for changes to take effect."),
isChecked = state.forceWebsocketMode,
onClick = {
viewModel.setForceWebsocketMode(!state.forceWebsocketMode)
SimpleTask.run({
val jobState = AppDependencies.jobManager.runSynchronously(RefreshAttributesJob(), 10.seconds.inWholeMilliseconds)
return@run jobState.isPresent && jobState.get().isComplete
}, { success ->
if (success) {
Toast.makeText(context, "Successfully refreshed attributes. Force-stop the app for changes to take effect.", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(context, "Failed to refresh attributes.", Toast.LENGTH_SHORT).show()
}
})
}
)
switchPref(
title = DSLSettingsText.from("Allow censorship circumvention toggle"),
summary = DSLSettingsText.from("Allow changing the censorship circumvention toggle regardless of network connectivity."),
@@ -10,7 +10,6 @@ data class InternalSettingsState(
val gv2forceInvites: Boolean,
val gv2ignoreP2PChanges: Boolean,
val allowCensorshipSetting: Boolean,
val forceWebsocketMode: Boolean,
val callingServer: String,
val callingDataMode: CallManager.DataMode,
val callingDisableTelecom: Boolean,
@@ -70,11 +70,6 @@ class InternalSettingsViewModel(private val repository: InternalSettingsReposito
refresh()
}
fun setForceWebsocketMode(enabled: Boolean) {
preferenceDataStore.putBoolean(InternalValues.FORCE_WEBSOCKET_MODE, enabled)
refresh()
}
fun resetPnpInitializedState() {
SignalStore.misc.hasPniInitializedDevices = false
refresh()
@@ -182,7 +177,6 @@ class InternalSettingsViewModel(private val repository: InternalSettingsReposito
gv2forceInvites = SignalStore.internal.gv2ForceInvites,
gv2ignoreP2PChanges = SignalStore.internal.gv2IgnoreP2PChanges,
allowCensorshipSetting = SignalStore.internal.allowChangingCensorshipSetting,
forceWebsocketMode = SignalStore.internal.isWebsocketModeForced,
callingServer = SignalStore.internal.groupCallingServer,
callingDataMode = SignalStore.internal.callingDataMode,
callingDisableTelecom = SignalStore.internal.callingDisableTelecom,
@@ -54,7 +54,7 @@ import org.signal.core.ui.compose.DayNightPreviews
import org.signal.core.ui.compose.Previews
import org.signal.core.ui.compose.Rows
import org.signal.core.ui.compose.SignalIcons
import org.thoughtcrime.securesms.database.model.ThreadRecord
import org.thoughtcrime.securesms.database.model.ThreadWithRecipient
class DataSeedingPlaygroundFragment : ComposeFragment() {
@@ -285,7 +285,7 @@ fun Screen(
@Composable
private fun ThreadSelectionRow(
thread: ThreadRecord,
thread: ThreadWithRecipient,
isSelected: Boolean,
onSelectionChanged: (Boolean) -> Unit
) {
@@ -25,7 +25,7 @@ import org.thoughtcrime.securesms.attachments.Attachment
import org.thoughtcrime.securesms.attachments.UriAttachment
import org.thoughtcrime.securesms.database.AttachmentTable
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.model.ThreadRecord
import org.thoughtcrime.securesms.database.model.ThreadWithRecipient
import org.thoughtcrime.securesms.mms.OutgoingMessage
import org.thoughtcrime.securesms.sms.MessageSender
import org.thoughtcrime.securesms.util.MediaUtil
@@ -43,7 +43,7 @@ class DataSeedingPlaygroundViewModel(application: Application) : AndroidViewMode
fun loadThreads() {
viewModelScope.launch(Dispatchers.IO) {
try {
val threads = mutableListOf<ThreadRecord>()
val threads = mutableListOf<ThreadWithRecipient>()
val cursor: Cursor = SignalDatabase.threads.getRecentConversationList(
limit = MAX_RECENT_THREADS,
includeInactiveGroups = false,
@@ -213,7 +213,7 @@ class DataSeedingPlaygroundViewModel(application: Application) : AndroidViewMode
}
data class DataSeedingPlaygroundState(
val threads: List<ThreadRecord> = emptyList(),
val threads: List<ThreadWithRecipient> = emptyList(),
val selectedThreads: Set<Long> = emptySet(),
val mediaFiles: List<String> = emptyList(),
val selectedFolderPath: String = ""
@@ -22,6 +22,7 @@ import androidx.navigation.fragment.navArgs
import androidx.preference.PreferenceManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
import org.signal.core.util.ServiceUtil
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.BiometricDeviceAuthentication
import org.thoughtcrime.securesms.BiometricDeviceLockContract
@@ -41,7 +42,6 @@ import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.service.KeyCachingService
import org.thoughtcrime.securesms.util.CommunicationActions
import org.thoughtcrime.securesms.util.ExpirationUtil
import org.thoughtcrime.securesms.util.ServiceUtil
import org.thoughtcrime.securesms.util.SpanUtil
import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.thoughtcrime.securesms.util.adapter.mapping.LayoutFactory
@@ -1041,11 +1041,11 @@ class ConversationSettingsFragment :
isEnabled = !state.isDeprecatedOrUnregistered,
onClick = {
if (state.recipient.isBlocked) {
BlockUnblockDialog.showUnblockFor(requireContext(), viewLifecycleOwner.lifecycle, state.recipient) {
BlockUnblockDialog.showUnblockFor(requireContext(), state.recipient) {
viewModel.unblock()
}
} else {
BlockUnblockDialog.showBlockFor(requireContext(), viewLifecycleOwner.lifecycle, state.recipient) {
BlockUnblockDialog.showBlockFor(requireContext(), state.recipient) {
viewModel.block()
}
}
@@ -1061,7 +1061,6 @@ class ConversationSettingsFragment :
onClick = {
BlockUnblockDialog.showReportSpamFor(
requireContext(),
viewLifecycleOwner.lifecycle,
state.recipient,
{
viewModel
@@ -1110,7 +1109,6 @@ class ConversationSettingsFragment :
onClick = {
BlockUnblockDialog.showReportSpamFor(
requireContext(),
viewLifecycleOwner.lifecycle,
state.recipient,
{
viewModel
@@ -188,7 +188,7 @@ class InternalConversationSettingsFragment : ComposeFragment(), InternalConversa
message = OutgoingMessage(threadRecipient = recipient, sentTimeMillis = time, body = "Outgoing: $i"),
threadId = targetThread
).messageId
SignalDatabase.messages.markAsSent(id, true)
SignalDatabase.messages.markAsSent(id)
} else {
SignalDatabase.messages.insertMessageInbox(
retrieved = IncomingMessage(type = MessageType.NORMAL, from = recipient.id, sentTimeMillis = time, serverTimeMillis = time, receivedTimeMillis = System.currentTimeMillis(), body = "Incoming: $i"),
@@ -218,7 +218,7 @@ class InternalConversationSettingsFragment : ComposeFragment(), InternalConversa
message = OutgoingMessage(threadRecipient = recipient, sentTimeMillis = time, body = "Outgoing: $i", attachments = listOf(attachment)),
threadId = targetThread
).messageId
SignalDatabase.messages.markAsSent(id, true)
SignalDatabase.messages.markAsSent(id)
SignalDatabase.attachments.getAttachmentsForMessage(id).forEach {
SignalDatabase.attachments.debugMakeValidForArchive(it.attachmentId)
SignalDatabase.attachments.createRemoteKeyIfNecessary(it.attachmentId)
@@ -252,7 +252,7 @@ class InternalConversationSettingsFragment : ComposeFragment(), InternalConversa
false,
null
).messageId
SignalDatabase.messages.markAsSent(messageId, true)
SignalDatabase.messages.markAsSent(messageId)
SignalDatabase.threads.update(splitThreadId, true)
@@ -6,11 +6,11 @@ import android.text.SpannableStringBuilder
import android.view.View
import android.widget.TextView
import android.widget.Toast
import org.signal.core.util.ServiceUtil
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.components.settings.PreferenceModel
import org.thoughtcrime.securesms.fonts.SignalSymbols
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.util.ServiceUtil
import org.thoughtcrime.securesms.util.adapter.mapping.LayoutFactory
import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter
import org.thoughtcrime.securesms.util.adapter.mapping.MappingViewHolder
@@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.components.settings.conversation.sounds.custo
import android.content.Context
import android.net.Uri
import androidx.annotation.WorkerThread
import org.signal.core.util.concurrent.SerialExecutor
import org.signal.core.util.concurrent.SignalExecutors
import org.thoughtcrime.securesms.database.RecipientTable
import org.thoughtcrime.securesms.database.SignalDatabase
@@ -10,7 +11,6 @@ import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.notifications.NotificationChannels
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.util.concurrent.SerialExecutor
class CustomNotificationsSettingsRepository(context: Context) {
@@ -11,9 +11,9 @@ import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import org.signal.core.util.AccessibilityUtil
import org.signal.core.util.dp
import org.thoughtcrime.securesms.components.spoiler.SpoilerAnnotation.SpoilerClickableSpan
import org.thoughtcrime.securesms.util.AccessibilityUtil
import org.thoughtcrime.securesms.util.getLifecycle
import org.signal.core.ui.R as CoreUiR
@@ -18,6 +18,7 @@ import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import org.signal.core.util.ByteSize
import org.signal.core.util.ThrottledDebouncer
import org.signal.core.util.bytes
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.R
@@ -29,7 +30,6 @@ import org.thoughtcrime.securesms.databinding.TransferControlsViewBinding
import org.thoughtcrime.securesms.events.PartProgressEvent
import org.thoughtcrime.securesms.mms.Slide
import org.thoughtcrime.securesms.util.MediaUtil
import org.thoughtcrime.securesms.util.ThrottledDebouncer
import org.thoughtcrime.securesms.util.ViewUtil
import org.thoughtcrime.securesms.util.visible
import java.util.UUID
@@ -14,9 +14,9 @@ import androidx.lifecycle.LifecycleOwner
import androidx.media3.common.Player
import androidx.media3.session.MediaController
import androidx.media3.session.SessionCommand
import org.signal.core.util.ServiceUtil
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.dependencies.AppDependencies
import org.thoughtcrime.securesms.util.ServiceUtil
import org.thoughtcrime.securesms.webrtc.audio.AudioManagerCompat
import java.util.concurrent.TimeUnit
@@ -49,7 +49,9 @@ import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import org.signal.core.ui.BottomSheetUtil
import org.signal.core.ui.permissions.Permissions
import org.signal.core.util.EllapsedTimeFormatter
import org.signal.core.util.ThreadUtil
import org.signal.core.util.ThrottledDebouncer
import org.signal.core.util.concurrent.LifecycleDisposable
import org.signal.core.util.concurrent.SignalExecutors
import org.signal.core.util.isInMultiWindowModeCompat
@@ -81,11 +83,9 @@ import org.thoughtcrime.securesms.safety.SafetyNumberBottomSheet
import org.thoughtcrime.securesms.service.webrtc.CallLinkDisconnectReason
import org.thoughtcrime.securesms.service.webrtc.SignalCallManager
import org.thoughtcrime.securesms.sms.MessageSender
import org.thoughtcrime.securesms.util.EllapsedTimeFormatter
import org.thoughtcrime.securesms.util.FullscreenHelper
import org.thoughtcrime.securesms.util.RemoteConfig
import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.thoughtcrime.securesms.util.ThrottledDebouncer
import org.thoughtcrime.securesms.util.VibrateUtil
import org.thoughtcrime.securesms.webrtc.CallParticipantsViewState
import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager
@@ -5,7 +5,7 @@ import androidx.annotation.VisibleForTesting
import org.thoughtcrime.securesms.contacts.HeaderAction
import org.thoughtcrime.securesms.database.model.DistributionListPrivacyMode
import org.thoughtcrime.securesms.database.model.GroupRecord
import org.thoughtcrime.securesms.database.model.ThreadRecord
import org.thoughtcrime.securesms.database.model.ThreadWithRecipient
import org.thoughtcrime.securesms.groups.GroupsInCommonSummary
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.search.MessageResult
@@ -49,8 +49,8 @@ sealed class ContactSearchData(val contactSearchKey: ContactSearchKey) {
*/
data class Thread(
val query: String,
val threadRecord: ThreadRecord
) : ContactSearchData(ContactSearchKey.Thread(threadRecord.threadId))
val threadWithRecipient: ThreadWithRecipient
) : ContactSearchData(ContactSearchKey.Thread(threadWithRecipient.threadId))
/**
* A row displaying a group which has members that match the given query.
@@ -14,7 +14,7 @@ import org.thoughtcrime.securesms.database.GroupTable
import org.thoughtcrime.securesms.database.RecipientTable
import org.thoughtcrime.securesms.database.model.DistributionListPrivacyMode
import org.thoughtcrime.securesms.database.model.GroupRecord
import org.thoughtcrime.securesms.database.model.ThreadRecord
import org.thoughtcrime.securesms.database.model.ThreadWithRecipient
import org.thoughtcrime.securesms.keyvalue.StorySend
import org.thoughtcrime.securesms.phonenumbers.NumberUtil
import org.thoughtcrime.securesms.recipients.Recipient
@@ -476,7 +476,7 @@ class ContactSearchPagedDataSource(
}
}
private fun getThreadData(query: String?, unreadOnly: Boolean): ContactSearchIterator<ThreadRecord> {
private fun getThreadData(query: String?, unreadOnly: Boolean): ContactSearchIterator<ThreadWithRecipient> {
check(searchRepository != null)
if (searchCache.threadSearchResult == null && query != null) {
searchCache = searchCache.copy(threadSearchResult = searchRepository.queryThreadsSync(query, unreadOnly))
@@ -16,7 +16,7 @@ import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.attachments.AttachmentId;
import org.thoughtcrime.securesms.attachments.UriAttachment;
import org.thoughtcrime.securesms.database.AttachmentTable;
import org.thoughtcrime.securesms.util.JsonUtils;
import org.signal.core.util.JsonUtils;
import org.thoughtcrime.securesms.util.MediaUtil;
import java.io.IOException;
@@ -13,8 +13,8 @@ import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;
import org.thoughtcrime.securesms.conversation.v2.items.InteractiveConversationElement;
import org.thoughtcrime.securesms.util.AccessibilityUtil;
import org.thoughtcrime.securesms.util.ServiceUtil;
import org.signal.core.util.AccessibilityUtil;
import org.signal.core.util.ServiceUtil;
import java.util.Objects;
@@ -9,7 +9,7 @@ import androidx.lifecycle.ViewModelProvider;
import org.signal.core.util.ThreadUtil;
import org.thoughtcrime.securesms.search.MessageResult;
import org.thoughtcrime.securesms.search.SearchRepository;
import org.thoughtcrime.securesms.util.Debouncer;
import org.signal.core.util.Debouncer;
import java.util.Collections;
import java.util.List;
@@ -23,7 +23,7 @@ import org.thoughtcrime.securesms.avatar.view.AvatarView;
import org.thoughtcrime.securesms.badges.BadgeImageView;
import org.thoughtcrime.securesms.database.model.StoryViewState;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.DrawableUtil;
import org.signal.core.util.DrawableUtil;
import org.thoughtcrime.securesms.util.ExpirationUtil;
import org.thoughtcrime.securesms.util.ViewUtil;
@@ -56,7 +56,7 @@ import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.recipients.LiveRecipient;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.DrawableUtil;
import org.signal.core.util.DrawableUtil;
import org.thoughtcrime.securesms.util.ExpirationUtil;
import org.thoughtcrime.securesms.util.IdentityUtil;
import org.thoughtcrime.securesms.util.MessageRecordUtil;
@@ -24,7 +24,7 @@ import org.thoughtcrime.securesms.database.model.ReactionRecord;
import org.thoughtcrime.securesms.dependencies.AppDependencies;
import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
import org.thoughtcrime.securesms.notifications.v2.ConversationId;
import org.thoughtcrime.securesms.util.Debouncer;
import org.signal.core.util.Debouncer;
import org.thoughtcrime.securesms.util.concurrent.SerialMonoLifoExecutor;
import java.util.Collections;
@@ -394,7 +394,7 @@ private fun UserMessagesHost(
is UserMessage.Prompt.ConfirmBlockRecipient -> {
val lifecycle = LocalLifecycleOwner.current.lifecycle
LaunchedEffect(userMessage.recipient) {
BlockUnblockDialog.showBlockFor(context, lifecycle, userMessage.recipient) {
BlockUnblockDialog.showBlockFor(context, userMessage.recipient) {
onBlockConfirmed(userMessage.recipient)
}
}
@@ -16,9 +16,9 @@ import androidx.core.os.bundleOf
import androidx.fragment.app.FragmentManager
import org.signal.core.ui.BottomSheetUtil
import org.signal.core.ui.FixedRoundedCornerBottomSheetDialogFragment
import org.signal.core.util.ServiceUtil
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.util.ServiceUtil
import org.thoughtcrime.securesms.util.fragments.findListener
/**
@@ -11,11 +11,11 @@ import android.view.ViewGroup
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import org.signal.core.ui.FixedRoundedCornerBottomSheetDialogFragment
import org.signal.core.util.ServiceUtil
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.components.ViewBinderDelegate
import org.thoughtcrime.securesms.databinding.ScheduleMessageFtuxBottomSheetBinding
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.util.ServiceUtil
import org.thoughtcrime.securesms.util.fragments.findListener
class ScheduleMessageFtuxBottomSheetDialog : FixedRoundedCornerBottomSheetDialogFragment() {
@@ -239,6 +239,9 @@ class MultiselectForwardFragment :
if (contactSelection.isNotEmpty() && !bottomBar.isVisible) {
bottomBar.animation = AnimationUtils.loadAnimation(requireContext(), R.anim.slide_fade_from_bottom)
bottomBar.visible = true
if (args.forceDisableAddMessage) {
ViewUtil.hideKeyboard(requireContext(), bottomBar)
}
} else if (contactSelection.isEmpty() && bottomBar.isVisible) {
bottomBar.animation = AnimationUtils.loadAnimation(requireContext(), R.anim.slide_fade_to_bottom)
bottomBar.visible = false
@@ -17,7 +17,7 @@ import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.AvatarImageView;
import org.thoughtcrime.securesms.components.FromTextView;
import org.thoughtcrime.securesms.database.model.IdentityRecord;
import org.thoughtcrime.securesms.util.DrawableUtil;
import org.signal.core.util.DrawableUtil;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.adapter.AlwaysChangedDiffUtil;
@@ -12,6 +12,8 @@ import androidx.lifecycle.lifecycleScope
import io.reactivex.rxjava3.subjects.PublishSubject
import io.reactivex.rxjava3.subjects.Subject
import kotlinx.coroutines.launch
import org.signal.core.util.ConfigurationUtil
import org.signal.core.util.Debouncer
import org.signal.core.util.logging.Log
import org.signal.core.util.logging.Log.tag
import org.thoughtcrime.securesms.MainActivity
@@ -27,8 +29,6 @@ import org.thoughtcrime.securesms.jobs.ConversationShortcutUpdateJob
import org.thoughtcrime.securesms.main.MainNavigationChatDetailRouter
import org.thoughtcrime.securesms.main.MainNavigationDetailLocation
import org.thoughtcrime.securesms.messagedetails.MessageDetailsFragment
import org.thoughtcrime.securesms.util.ConfigurationUtil
import org.thoughtcrime.securesms.util.Debouncer
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme
import java.util.concurrent.TimeUnit
@@ -120,6 +120,8 @@ import org.signal.core.ui.permissions.Permissions
import org.signal.core.ui.util.ThemeUtil
import org.signal.core.ui.view.Stub
import org.signal.core.util.ByteLimitInputFilter
import org.signal.core.util.Debouncer
import org.signal.core.util.DrawableUtil
import org.signal.core.util.PendingIntentFlags
import org.signal.core.util.Result
import org.signal.core.util.ThreadUtil
@@ -350,11 +352,9 @@ import org.thoughtcrime.securesms.util.BubbleUtil
import org.thoughtcrime.securesms.util.CommunicationActions
import org.thoughtcrime.securesms.util.ConversationUtil
import org.thoughtcrime.securesms.util.DateUtils
import org.thoughtcrime.securesms.util.Debouncer
import org.thoughtcrime.securesms.util.DeleteDialog
import org.thoughtcrime.securesms.util.Dialogs
import org.thoughtcrime.securesms.util.DoubleClickDebouncer
import org.thoughtcrime.securesms.util.DrawableUtil
import org.thoughtcrime.securesms.util.FileProviderUtil
import org.thoughtcrime.securesms.util.FullscreenHelper
import org.thoughtcrime.securesms.util.MediaUtil
@@ -2880,7 +2880,6 @@ class ConversationFragment :
BlockUnblockDialog.showReportSpamFor(
requireContext(),
lifecycle,
recipient,
{
messageRequestViewModel
@@ -2928,7 +2927,6 @@ class ConversationFragment :
BlockUnblockDialog.showBlockFor(
requireContext(),
lifecycle,
recipient
) {
messageRequestViewModel
@@ -2947,7 +2945,6 @@ class ConversationFragment :
BlockUnblockDialog.showUnblockFor(
requireContext(),
lifecycle,
recipient
) {
messageRequestViewModel
@@ -24,6 +24,7 @@ import io.reactivex.rxjava3.core.Maybe
import io.reactivex.rxjava3.core.Single
import io.reactivex.rxjava3.core.SingleEmitter
import io.reactivex.rxjava3.schedulers.Schedulers
import org.signal.core.util.DrawableUtil
import org.signal.core.util.StreamUtil
import org.signal.core.util.Util
import org.signal.core.util.concurrent.MaybeCompat
@@ -92,7 +93,7 @@ import org.thoughtcrime.securesms.recipients.RecipientUtil
import org.thoughtcrime.securesms.sms.MessageSender
import org.thoughtcrime.securesms.sms.MessageSender.PreUploadResult
import org.thoughtcrime.securesms.transport.UndeliverableMessageException
import org.thoughtcrime.securesms.util.DrawableUtil
import org.thoughtcrime.securesms.util.AdaptiveBitmapMetrics
import org.thoughtcrime.securesms.util.GroupUtil
import org.thoughtcrime.securesms.util.MediaUtil
import org.thoughtcrime.securesms.util.MessageUtil
@@ -272,7 +273,7 @@ class ConversationRepository(
Log.i(TAG, "Some recipients skipped when sending end poll. Resending to $filterRecipientIds")
MessageSender.resendGroupMessage(applicationContext, messageRecord, filterRecipientIds)
} else {
SignalDatabase.messages.markAsSent(messageId, true)
SignalDatabase.messages.markAsSent(messageId)
}
emitter.onComplete()
} else {
@@ -380,7 +381,7 @@ class ConversationRepository(
Log.i(TAG, "Some recipients skipped when sending pin message. Resending to $filterRecipientIds")
MessageSender.resendGroupMessage(applicationContext, messageRecord, filterRecipientIds)
} else {
SignalDatabase.messages.markAsSent(insertResult.messageId, true)
SignalDatabase.messages.markAsSent(insertResult.messageId)
}
emitter.onComplete()
} else {
@@ -898,9 +899,9 @@ class ConversationRepository(
override fun transformToFinalBitmap(): Single<Bitmap> {
return Single.create {
val bitmap = if (Build.VERSION.SDK_INT <= 25) {
DrawableUtil.wrapBitmapForShortcutInfo(DrawableUtil.toBitmap(drawable, SHORTCUT_ICON_SIZE, SHORTCUT_ICON_SIZE))
AdaptiveBitmapMetrics.wrapBitmap(DrawableUtil.toBitmap(drawable, SHORTCUT_ICON_SIZE, SHORTCUT_ICON_SIZE))
} else {
DrawableUtil.wrapBitmapForShortcutInfo(drawable.toBitmap(SHORTCUT_ICON_SIZE, SHORTCUT_ICON_SIZE))
AdaptiveBitmapMetrics.wrapBitmap(drawable.toBitmap(SHORTCUT_ICON_SIZE, SHORTCUT_ICON_SIZE))
}
it.setCancellable {
bitmap.recycle()
@@ -913,7 +914,7 @@ class ConversationRepository(
class BitmapResult(private val bitmap: Bitmap) : ContactPhotoResult {
override fun transformToFinalBitmap(): Single<Bitmap> {
return Single.create {
val bitmap = DrawableUtil.wrapBitmapForShortcutInfo(bitmap)
val bitmap = AdaptiveBitmapMetrics.wrapBitmap(bitmap)
it.setCancellable {
bitmap.recycle()
}
@@ -16,6 +16,7 @@ import io.reactivex.rxjava3.core.SingleObserver
import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.disposables.Disposable
import io.reactivex.rxjava3.schedulers.Schedulers
import org.signal.core.util.ServiceUtil
import org.signal.core.util.concurrent.LifecycleDisposable
import org.signal.core.util.concurrent.addTo
import org.signal.core.util.logging.Log
@@ -23,7 +24,6 @@ import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.audio.AudioRecorder
import org.thoughtcrime.securesms.components.voice.VoiceNoteDraft
import org.thoughtcrime.securesms.conversation.VoiceRecorderWakeLock
import org.thoughtcrime.securesms.util.ServiceUtil
import java.util.concurrent.TimeUnit
/**
@@ -19,7 +19,7 @@ import org.thoughtcrime.securesms.database.MessageTypes;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.ThreadTable;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.database.model.ThreadWithRecipient;
import org.thoughtcrime.securesms.database.model.UpdateDescription;
import org.thoughtcrime.securesms.dependencies.AppDependencies;
import org.thoughtcrime.securesms.recipients.Recipient;
@@ -79,7 +79,7 @@ abstract class ConversationListDataSource implements PagedDataSource<Long, Conve
Set<RecipientId> needsResolve = new HashSet<>();
try (ConversationReader reader = new ConversationReader(getCursor(start, length))) {
ThreadRecord record;
ThreadWithRecipient record;
while ((record = reader.getNext()) != null && !cancellationSignal.isCanceled()) {
conversations.add(new Conversation(record));
recipients.add(record.getRecipient());
@@ -135,7 +135,7 @@ import org.thoughtcrime.securesms.conversationlist.model.ConversationFilter;
import org.thoughtcrime.securesms.database.MessageTable.MarkedMessageInfo;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.ThreadTable;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.database.model.ThreadWithRecipient;
import org.thoughtcrime.securesms.dependencies.AppDependencies;
import org.thoughtcrime.securesms.groups.SelectionLimits;
import org.thoughtcrime.securesms.jobs.RefreshOwnProfileJob;
@@ -157,13 +157,13 @@ import org.thoughtcrime.securesms.search.MessageResult;
import org.thoughtcrime.securesms.search.SearchFilter;
import org.thoughtcrime.securesms.search.SearchFilterBottomSheet;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.util.AppForegroundObserver;
import org.signal.core.util.AppForegroundObserver;
import org.thoughtcrime.securesms.util.AppStartup;
import org.signal.core.ui.BottomSheetUtil;
import org.signal.core.ui.view.Stub;
import org.thoughtcrime.securesms.util.CachedInflater;
import org.thoughtcrime.securesms.util.ConversationUtil;
import org.thoughtcrime.securesms.util.ServiceUtil;
import org.signal.core.util.ServiceUtil;
import org.thoughtcrime.securesms.util.RemoteConfig;
import org.thoughtcrime.securesms.util.SignalLocalMetrics;
import org.thoughtcrime.securesms.util.SignalProxyUtil;
@@ -668,7 +668,7 @@ public class ConversationListFragment extends MainFragment implements Conversati
}
}
private void onConversationClicked(@NonNull ThreadRecord threadRecord) {
private void onConversationClicked(@NonNull ThreadWithRecipient threadRecord) {
hideKeyboard();
getNavigator().goToConversation(threadRecord.getRecipient().getId(),
threadRecord.getThreadId(),
@@ -1843,7 +1843,7 @@ public class ConversationListFragment extends MainFragment implements Conversati
}
private void onTrueSwipe(RecyclerView.ViewHolder viewHolder) {
ThreadRecord thread = ((ConversationListItem) viewHolder.itemView).getThread();
ThreadWithRecipient thread = ((ConversationListItem) viewHolder.itemView).getThread();
onItemSwiped(thread.getThreadId(), thread.getUnreadCount(), thread.getUnreadSelfMentionsCount());
}
@@ -1966,12 +1966,12 @@ public class ConversationListFragment extends MainFragment implements Conversati
@Override
public void onThreadClicked(@NonNull View view, @NonNull ContactSearchData.Thread thread, boolean isSelected) {
onConversationClicked(thread.getThreadRecord());
onConversationClicked(thread.getThreadWithRecipient());
}
@Override
public boolean onThreadLongClicked(@NonNull View view, @NonNull ContactSearchData.Thread thread) {
return showConversationContextMenu(new Conversation(thread.getThreadRecord()), view, true);
return showConversationContextMenu(new Conversation(thread.getThreadWithRecipient()), view, true);
}
@Override
@@ -70,7 +70,7 @@ import org.thoughtcrime.securesms.database.MessageTypes;
import org.thoughtcrime.securesms.database.ThreadTable;
import org.thoughtcrime.securesms.database.model.LiveUpdateMessage;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.database.model.ThreadWithRecipient;
import org.thoughtcrime.securesms.database.model.UpdateDescription;
import org.thoughtcrime.securesms.fonts.SignalSymbols.Glyph;
import org.thoughtcrime.securesms.glide.targets.GlideLiveDataTarget;
@@ -126,7 +126,7 @@ public final class ConversationListItem extends ConstraintLayout implements Bind
private AlertView alertView;
private TextView unreadIndicator;
private long lastSeen;
private ThreadRecord thread;
private ThreadWithRecipient thread;
private boolean batchMode;
private Locale locale;
private String highlightSubstring;
@@ -209,7 +209,7 @@ public final class ConversationListItem extends ConstraintLayout implements Bind
@Override
public void bind(@NonNull LifecycleOwner lifecycleOwner,
@NonNull ThreadRecord thread,
@NonNull ThreadWithRecipient thread,
@NonNull RequestManager glideRequests,
@NonNull Locale locale,
@NonNull Set<Long> typingThreads,
@@ -220,7 +220,7 @@ public final class ConversationListItem extends ConstraintLayout implements Bind
}
public void bindThread(@NonNull LifecycleOwner lifecycleOwner,
@NonNull ThreadRecord thread,
@NonNull ThreadWithRecipient thread,
@NonNull RequestManager requestManager,
@NonNull Locale locale,
@NonNull Set<Long> typingThreads,
@@ -467,7 +467,7 @@ public final class ConversationListItem extends ConstraintLayout implements Bind
return threadId;
}
public @NonNull ThreadRecord getThread() {
public @NonNull ThreadWithRecipient getThread() {
return thread;
}
@@ -516,7 +516,7 @@ public final class ConversationListItem extends ConstraintLayout implements Bind
}
}
private void setStatusIcons(ThreadRecord thread) {
private void setStatusIcons(ThreadWithRecipient thread) {
if (MessageTypes.isBadDecryptType(thread.getType())) {
deliveryStatusIndicator.setNone();
alertView.setFailed();
@@ -556,7 +556,7 @@ public final class ConversationListItem extends ConstraintLayout implements Bind
}
}
private void setUnreadIndicator(ThreadRecord thread) {
private void setUnreadIndicator(ThreadWithRecipient thread) {
if (thread.isRead()) {
unreadIndicator.setVisibility(View.GONE);
unreadMentions.setVisibility(View.GONE);
@@ -596,7 +596,7 @@ public final class ConversationListItem extends ConstraintLayout implements Bind
}
private static @NonNull LiveData<SpannableString> getThreadDisplayBody(@NonNull Context context,
@NonNull ThreadRecord thread,
@NonNull ThreadWithRecipient thread,
@NonNull RequestManager requestManager,
@Px int thumbSize,
@NonNull GlideLiveDataTarget thumbTarget)
@@ -734,7 +734,7 @@ public final class ConversationListItem extends ConstraintLayout implements Bind
private static LiveData<CharSequence> createFinalBodyWithMediaIcon(@NonNull Context context,
@NonNull CharSequence body,
@NonNull ThreadRecord thread,
@NonNull ThreadWithRecipient thread,
@NonNull RequestManager requestManager,
@Px int thumbSize,
@NonNull GlideLiveDataTarget thumbTarget)
@@ -13,7 +13,7 @@ import com.bumptech.glide.RequestManager;
import org.thoughtcrime.securesms.BindableConversationListItem;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.conversationlist.model.ConversationSet;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.database.model.ThreadWithRecipient;
import java.util.Locale;
import java.util.Set;
@@ -42,7 +42,7 @@ public class ConversationListItemAction extends FrameLayout implements BindableC
@Override
public void bind(@NonNull LifecycleOwner lifecycleOwner,
@NonNull ThreadRecord thread,
@NonNull ThreadWithRecipient thread,
@NonNull RequestManager requestManager,
@NonNull Locale locale,
@NonNull Set<Long> typingThreads,
@@ -221,7 +221,7 @@ object ConversationListSearchModels {
(itemView as ConversationListItem).bindThread(
lifecycleOwner,
model.thread.threadRecord,
model.thread.threadWithRecipient,
requestManager,
Locale.getDefault(),
emptySet(),
@@ -2,13 +2,13 @@ package org.thoughtcrime.securesms.conversationlist.model;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.database.model.ThreadWithRecipient;
public class Conversation {
private final ThreadRecord threadRecord;
private final Type type;
private final ThreadWithRecipient threadRecord;
private final Type type;
public Conversation(@NonNull ThreadRecord threadRecord) {
public Conversation(@NonNull ThreadWithRecipient threadRecord) {
this.threadRecord = threadRecord;
if (this.threadRecord.getThreadId() < 0) {
type = Type.valueOf(this.threadRecord.getBody());
@@ -17,7 +17,7 @@ public class Conversation {
}
}
public @NonNull ThreadRecord getThreadRecord() {
public @NonNull ThreadWithRecipient getThreadRecord() {
return threadRecord;
}
@@ -5,7 +5,7 @@ import android.database.Cursor;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.database.ThreadTable;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.database.model.ThreadWithRecipient;
import org.thoughtcrime.securesms.dependencies.AppDependencies;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.signal.core.util.CursorUtil;
@@ -37,7 +37,7 @@ public class ConversationReader extends ThreadTable.StaticReader {
}
@Override
public ThreadRecord getCurrent() {
public ThreadWithRecipient getCurrent() {
if (cursor.getColumnIndex(HEADER_COLUMN[0]) == -1) {
return super.getCurrent();
} else {
@@ -45,7 +45,7 @@ public class ConversationReader extends ThreadTable.StaticReader {
}
}
private ThreadRecord buildThreadRecordForHeader() {
private ThreadWithRecipient buildThreadRecordForHeader() {
Conversation.Type type = Conversation.Type.valueOf(CursorUtil.requireString(cursor, HEADER_COLUMN[0]));
int count = 0;
if (type == Conversation.Type.ARCHIVED_FOOTER) {
@@ -60,8 +60,8 @@ public class ConversationReader extends ThreadTable.StaticReader {
return buildThreadRecordForType(type, count, showTip);
}
public static ThreadRecord buildThreadRecordForType(@NonNull Conversation.Type type, int count, boolean showTip) {
return new ThreadRecord.Builder(-(100 + type.ordinal()))
public static ThreadWithRecipient buildThreadRecordForType(@NonNull Conversation.Type type, int count, boolean showTip) {
return new ThreadWithRecipient.Builder(-(100 + type.ordinal()))
.setBody(type.toString())
.setDate(100)
.setRecipient(Recipient.UNKNOWN)
@@ -8,10 +8,10 @@ package org.thoughtcrime.securesms.crash
import androidx.annotation.VisibleForTesting
import com.fasterxml.jackson.annotation.JsonProperty
import org.signal.core.models.ServiceId
import org.signal.core.util.BucketingUtil
import org.signal.core.util.JsonUtils
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.util.BucketingUtil
import org.thoughtcrime.securesms.util.JsonUtils
import org.thoughtcrime.securesms.util.RemoteConfig
import java.io.IOException
@@ -16,7 +16,7 @@ import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.thoughtcrime.securesms.util.JsonUtils;
import org.signal.core.util.JsonUtils;
import java.io.IOException;
@@ -21,7 +21,7 @@ import android.os.Parcelable;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.util.ParcelUtil;
import org.signal.core.util.ParcelUtil;
import org.signal.libsignal.protocol.IdentityKey;
import org.signal.libsignal.protocol.InvalidKeyException;
@@ -20,7 +20,7 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.util.JsonUtils;
import org.signal.core.util.JsonUtils;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
@@ -9,7 +9,7 @@ import org.thoughtcrime.securesms.database.model.MessageId;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.service.webrtc.links.CallLinkRoomId;
import org.thoughtcrime.securesms.util.concurrent.SerialExecutor;
import org.signal.core.util.concurrent.SerialExecutor;
import java.util.Collection;
import java.util.HashMap;
@@ -30,6 +30,7 @@ import org.json.JSONObject
import org.signal.core.models.ServiceId
import org.signal.core.util.Base64
import org.signal.core.util.CursorUtil
import org.signal.core.util.JsonUtils
import org.signal.core.util.SqlUtil
import org.signal.core.util.SqlUtil.buildArgs
import org.signal.core.util.SqlUtil.buildSingleCollectionQuery
@@ -141,7 +142,6 @@ import org.thoughtcrime.securesms.revealable.ViewOnceUtil
import org.thoughtcrime.securesms.sms.GroupV2UpdateMessageUtil
import org.thoughtcrime.securesms.stories.Stories.isFeatureEnabled
import org.thoughtcrime.securesms.util.DateUtils
import org.thoughtcrime.securesms.util.JsonUtils
import org.thoughtcrime.securesms.util.MediaUtil
import org.thoughtcrime.securesms.util.MessageConstraintsUtil
import org.thoughtcrime.securesms.util.RemoteConfig
@@ -310,6 +310,8 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
"""
private const val INDEX_THREAD_STORY_SCHEDULED_DATE_LATEST_REVISION_ID = "message_thread_story_parent_story_scheduled_date_latest_revision_id_index"
private const val INDEX_THREAD_DATE_RECEIVED_UNREAD = "message_thread_date_received_unread_index"
private const val INDEX_COLLAPSED_STATE = "message_collapsed_state_index"
private const val INDEX_DATE_SENT_FROM_TO_THREAD = "message_date_sent_from_to_thread_index"
private const val INDEX_THREAD_COUNT = "message_thread_count_index"
private const val INDEX_THREAD_UNREAD_COUNT = "message_thread_unread_count_index"
@@ -339,6 +341,8 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
"CREATE INDEX IF NOT EXISTS $INDEX_THREAD_COUNT ON $TABLE_NAME ($THREAD_ID) WHERE $STORY_TYPE = 0 AND $PARENT_STORY_ID <= 0 AND $SCHEDULED_DATE = -1 AND $LATEST_REVISION_ID IS NULL AND $COLLAPSED_STATE != ${CollapsedState.COLLAPSED.id}",
// This index is created specifically for getting the number of unread messages in a thread and therefore needs to be kept in sync with that query
"CREATE INDEX IF NOT EXISTS $INDEX_THREAD_UNREAD_COUNT ON $TABLE_NAME ($THREAD_ID) WHERE $STORY_TYPE = 0 AND $PARENT_STORY_ID <= 0 AND $SCHEDULED_DATE = -1 AND $ORIGINAL_MESSAGE_ID IS NULL AND $READ = 0",
// Partial index for marking messages read in a thread (see setMessagesReadSince). Only contains unread/unseen rows.
"CREATE INDEX IF NOT EXISTS $INDEX_THREAD_DATE_RECEIVED_UNREAD ON $TABLE_NAME ($THREAD_ID, $DATE_RECEIVED) WHERE $STORY_TYPE = 0 AND $PARENT_STORY_ID <= 0 AND ($READ = 0 OR $REACTIONS_UNREAD = 1 OR $VOTES_UNREAD = 1)",
"CREATE INDEX IF NOT EXISTS message_votes_unread_index ON $TABLE_NAME ($VOTES_UNREAD)",
"CREATE INDEX IF NOT EXISTS message_pinned_until_index ON $TABLE_NAME ($PINNED_UNTIL)",
"CREATE INDEX IF NOT EXISTS message_pinned_at_index ON $TABLE_NAME ($PINNED_AT)",
@@ -2312,9 +2316,27 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
AppDependencies.databaseObserver.notifyConversationListListeners()
}
fun markAsSent(messageId: Long, secure: Boolean) {
fun markAsSent(messageId: Long) {
val threadId = getThreadIdForMessage(messageId)
updateMailboxBitmask(messageId, MessageTypes.BASE_TYPE_MASK, MessageTypes.BASE_SENT_TYPE or if (secure) MessageTypes.PUSH_MESSAGE_BIT or MessageTypes.SECURE_MESSAGE_BIT else 0, Optional.of(threadId))
updateMailboxBitmask(messageId, MessageTypes.BASE_TYPE_MASK, MessageTypes.BASE_SENT_TYPE or MessageTypes.PUSH_MESSAGE_BIT or MessageTypes.SECURE_MESSAGE_BIT, Optional.of(threadId))
AppDependencies.databaseObserver.notifyMessageUpdateObservers(MessageId(messageId))
AppDependencies.databaseObserver.notifyConversationListListeners()
}
fun markAsSent(messageId: Long, sealedSender: Boolean) {
val maskOff = MessageTypes.BASE_TYPE_MASK
val maskOn = MessageTypes.BASE_SENT_TYPE or MessageTypes.PUSH_MESSAGE_BIT or MessageTypes.SECURE_MESSAGE_BIT
writableDatabase.execSQL(
"""
UPDATE $TABLE_NAME
SET
$TYPE = ($TYPE & ${MessageTypes.TOTAL_MASK - maskOff} | $maskOn ),
$UNIDENTIFIED = ${sealedSender.toInt()}
WHERE $ID = $messageId
"""
)
AppDependencies.databaseObserver.notifyMessageUpdateObservers(MessageId(messageId))
AppDependencies.databaseObserver.notifyConversationListListeners()
}
@@ -2505,22 +2527,26 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
}
fun setMessagesReadSince(threadId: Long, sinceTimestamp: Long): List<MarkedMessageInfo> {
// The standalone "($READ = 0 OR $REACTIONS_UNREAD = 1 OR $VOTES_UNREAD = 1)" term below is logically redundant -- it's implied by the
// larger read/reactions/votes clause that follows. We need it to satisfy the query planner so we can use INDEX_THREAD_DATE_RECEIVED_UNREAD.
// The index needs to appear exactly in the query.
var query = """
$THREAD_ID = ? AND
$STORY_TYPE = 0 AND
$PARENT_STORY_ID <= 0 AND
$THREAD_ID = ? AND
$STORY_TYPE = 0 AND
$PARENT_STORY_ID <= 0 AND
(
$ORIGINAL_MESSAGE_ID IS NULL OR
$LATEST_REVISION_ID IS NULL
) AND
) AND
($READ = 0 OR $REACTIONS_UNREAD = 1 OR $VOTES_UNREAD = 1) AND
(
$READ = 0 OR
$READ = 0 OR
(
$REACTIONS_UNREAD = 1 AND
$REACTIONS_UNREAD = 1 AND
($outgoingTypeClause)
) OR
(
$VOTES_UNREAD = 1 AND
$VOTES_UNREAD = 1 AND
($outgoingTypeClause)
)
)
@@ -2533,7 +2559,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
args += sinceTimestamp.toString()
}
return setMessagesRead(query, args.toTypedArray())
return setMessagesRead(query, args.toTypedArray(), index = INDEX_THREAD_DATE_RECEIVED_UNREAD)
}
fun setGroupStoryMessagesReadSince(threadId: Long, groupStoryId: Long, sinceTimestamp: Long): List<MarkedMessageInfo> {
@@ -2600,7 +2626,8 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
private fun setMessagesRead(where: String, arguments: Array<String>?, index: String = INDEX_THREAD_STORY_SCHEDULED_DATE_LATEST_REVISION_ID): List<MarkedMessageInfo> {
val releaseChannelId = SignalStore.releaseChannel.releaseChannelRecipientId
return writableDatabase.rawQuery(
val startTime = System.currentTimeMillis()
val results = writableDatabase.rawQuery(
"""
UPDATE $TABLE_NAME INDEXED BY $index
SET $READ = 1, $REACTIONS_UNREAD = 0, $REACTIONS_LAST_SEEN = ${System.currentTimeMillis()}, $VOTES_UNREAD = 0, $VOTES_LAST_SEEN = ${System.currentTimeMillis()}
@@ -2627,6 +2654,10 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
}
}
.filterNotNull()
Log.d(TAG, "[setMessagesRead] Updated ${results.size} messages in ${System.currentTimeMillis() - startTime} ms using index $index.")
return results
}
fun getOldestUnreadMentionDetails(threadId: Long): Pair<RecipientId, Long>? {
@@ -2693,6 +2724,18 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
}
}
fun getOutgoingMessageOrNull(messageId: Long): OutgoingMessage? {
return try {
getOutgoingMessage(messageId)
} catch (e: MmsException) {
Log.w(TAG, "Hit MmsException, returning null", e)
null
} catch (e: NoSuchMessageException) {
Log.w(TAG, "Hit NoSuchMessageException, returning null", e)
null
}
}
@Throws(MmsException::class, NoSuchMessageException::class)
fun getOutgoingMessage(messageId: Long): OutgoingMessage {
return queryMessages(RAW_ID_WHERE, arrayOf(messageId.toString())).readToSingleObject { cursor ->
@@ -4615,10 +4658,22 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
}
fun setReactionsSeen(threadId: Long, sinceTimestamp: Long) {
val where = "$THREAD_ID = ? AND $REACTIONS_UNREAD = ?" + if (sinceTimestamp > -1) " AND $DATE_RECEIVED <= $sinceTimestamp" else ""
// The $STORY_TYPE/$PARENT_STORY_ID and "(read=0 OR reactions_unread=1 OR votes_unread=1)" predicates are required for the
// query planner to recognize that $INDEX_THREAD_DATE_RECEIVED_UNREAD (a partial index) covers this query.
// They match exactly the WHERE clause used when defining that index.
var where = """
$THREAD_ID = ? AND
$STORY_TYPE = 0 AND
$PARENT_STORY_ID <= 0 AND
($READ = 0 OR $REACTIONS_UNREAD = 1 OR $VOTES_UNREAD = 1) AND
$REACTIONS_UNREAD = ?
"""
if (sinceTimestamp > -1) {
where += " AND $DATE_RECEIVED <= $sinceTimestamp"
}
writableDatabase
.update(TABLE_NAME)
.update("$TABLE_NAME INDEXED BY $INDEX_THREAD_DATE_RECEIVED_UNREAD")
.values(
REACTIONS_UNREAD to 0,
REACTIONS_LAST_SEEN to System.currentTimeMillis()
@@ -4639,14 +4694,20 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
}
fun setVoteSeen(threadId: Long, sinceTimestamp: Long) {
val where = if (sinceTimestamp > -1) {
"$THREAD_ID = ? AND $VOTES_UNREAD = ? AND $DATE_RECEIVED <= $sinceTimestamp"
} else {
"$THREAD_ID = ? AND $VOTES_UNREAD = ?"
// See setReactionsSeen for an explanation of the extra predicates / INDEXED BY hint.
var where = """
$THREAD_ID = ? AND
$STORY_TYPE = 0 AND
$PARENT_STORY_ID <= 0 AND
($READ = 0 OR $REACTIONS_UNREAD = 1 OR $VOTES_UNREAD = 1) AND
$VOTES_UNREAD = ?
"""
if (sinceTimestamp > -1) {
where += " AND $DATE_RECEIVED <= $sinceTimestamp"
}
writableDatabase
.update(TABLE_NAME)
.update("$TABLE_NAME INDEXED BY $INDEX_THREAD_DATE_RECEIVED_UNREAD")
.values(
VOTES_UNREAD to 0,
VOTES_LAST_SEEN to System.currentTimeMillis()
@@ -4667,6 +4728,8 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
}
fun collapsePendingCollapsibleEvents(threadId: Long, sinceTimestamp: Long) {
// Force INDEXED BY message_collapsed_state_index. COLLAPSED_STATE = PENDING_COLLAPSED is a transient state, so the index
// entries for it are typically near zero — much more selective than scanning the thread by date_received.
val where = if (sinceTimestamp > -1) {
"$THREAD_ID = ? AND $COLLAPSED_STATE = ? AND $DATE_RECEIVED <= $sinceTimestamp"
} else {
@@ -4674,7 +4737,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat
}
writableDatabase
.update(TABLE_NAME)
.update("$TABLE_NAME INDEXED BY $INDEX_COLLAPSED_STATE")
.values(COLLAPSED_STATE to CollapsedState.COLLAPSED.id)
.where(where, threadId, CollapsedState.PENDING_COLLAPSED.id)
.run()

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