diff --git a/app/build.gradle b/app/build.gradle deleted file mode 100644 index 6eb8a67b64..0000000000 --- a/app/build.gradle +++ /dev/null @@ -1,578 +0,0 @@ -import com.android.build.api.dsl.ManagedVirtualDevice - -plugins { - id 'com.android.application' - id 'kotlin-android' - id 'androidx.navigation.safeargs' - id 'org.jetbrains.kotlin.android' - id 'app.cash.exhaustive' - id 'kotlin-parcelize' - id 'com.squareup.wire' - id 'translations' - id 'licenses' -} - -// Sort baseline.profm for reproducible builds -// See issue: https://issuetracker.google.com/issues/231837768 -apply from: 'fix-profm.gradle' - -wire { - kotlin { - javaInterop = true - } - - sourcePath { - srcDir 'src/main/protowire' - } - - protoPath { - srcDir "${project.rootDir}/libsignal-service/src/main/protowire" - } -} - -def getEnv(String name) { - return project.hasProperty('CI') ? System.getenv(name) : null -} - -// Override build config via env vars when project property 'CI' is set -ext { - BASE_APP_TITLE = getEnv('CI_APP_TITLE') ?: baseAppTitle - BASE_APP_FILENAME = getEnv('CI_APP_FILENAME') ?: baseAppFileName - BASE_PACKAGE_ID = getEnv('CI_PACKAGE_ID') ?: basePackageId - BUILD_VARIANTS = getEnv('CI_BUILD_VARIANTS') ?: buildVariants - FORCE_INTERNAL_USER_FLAG = getEnv('CI_FORCE_INTERNAL_USER_FLAG') ?: forceInternalUserFlag - MAPS_API_KEY = getEnv('CI_MAPS_API_KEY') ?: mapsApiKey -} - -def canonicalVersionCode = 1365 -def canonicalVersionName = "6.41.3" -def mollyRevision = 1 - -def postFixSize = 100 - -def selectableVariants = [ - 'prodFossWebsiteDebug', - 'prodFossWebsiteRelease', - 'prodFossStoreDebug', - 'prodFossStoreRelease', - 'prodGmsWebsiteDebug', - 'prodGmsWebsiteRelease', - 'prodGmsWebsiteCanary', - 'prodGmsWebsiteInstrumentation', - 'prodGmsWebsiteSpinner', - 'stagingFossWebsiteDebug', - 'stagingFossWebsiteRelease', - 'stagingGmsWebsiteDebug', - 'stagingGmsWebsiteRelease', -] - -android { - namespace 'org.thoughtcrime.securesms' - - buildToolsVersion = signalBuildToolsVersion - compileSdkVersion = signalCompileSdkVersion - - flavorDimensions = ['environment', 'license', 'distribution'] - useLibrary 'org.apache.http.legacy' - testBuildType 'instrumentation' - - kotlinOptions { - jvmTarget = signalKotlinJvmTarget - freeCompilerArgs = ["-Xallow-result-return-type"] - } - - signingConfigs { - ci { - def storeFilePath = getEnv('CI_KEYSTORE_PATH') - if (storeFilePath) { - println("Signing release build with keystore: '$storeFilePath'") - storeFile file(storeFilePath) - storePassword "${System.env.CI_KEYSTORE_PASSWORD}" - keyAlias "${System.env.CI_KEYSTORE_ALIAS}" - keyPassword "${System.env.CI_KEYSTORE_PASSWORD}" - enableV4Signing false - } - } - } - - testOptions { - execution 'ANDROIDX_TEST_ORCHESTRATOR' - - unitTests { - includeAndroidResources = true - } - - managedDevices { - devices { - pixel3api30 (ManagedVirtualDevice) { - device = "Pixel 3" - apiLevel = 30 - systemImageSource = "google-atd" - require64Bit = false - } - } - } - } - - - sourceSets { - test { - java.srcDirs += "$projectDir/src/testShared" - } - - androidTest { - java.srcDirs += "$projectDir/src/testShared" - } - } - - compileOptions { - coreLibraryDesugaringEnabled true - sourceCompatibility signalJavaVersion - targetCompatibility signalJavaVersion - } - - packagingOptions { - resources { - excludes += ['LICENSE.txt', 'LICENSE', 'NOTICE', 'asm-license.txt', 'META-INF/LICENSE', 'META-INF/LICENSE.md', 'META-INF/NOTICE', 'META-INF/LICENSE-notice.md', 'META-INF/proguard/androidx-annotations.pro', 'libsignal_jni.dylib', 'signal_jni.dll', '**/*.proto'] - } - jniLibs { - // MOLLY: Compress native libs by default as APK is not split on ABIs - useLegacyPackaging true - } - } - - - buildFeatures { - viewBinding true - compose true - } - - composeOptions { - kotlinCompilerExtensionVersion = '1.4.4' - } - - if (mollyRevision < 0 || mollyRevision >= postFixSize) { - throw new GradleException("Molly revision $mollyRevision out of range") - } - - defaultConfig { - versionCode canonicalVersionCode * postFixSize + mollyRevision - versionName project.hasProperty('CI') ? getCommitTag() : canonicalVersionName - - minSdkVersion signalMinSdkVersion - targetSdkVersion signalTargetSdkVersion - - multiDexEnabled true - - applicationId BASE_PACKAGE_ID - - buildConfigField "String", "SIGNAL_PACKAGE_NAME", "\"org.thoughtcrime.securesms\"" - buildConfigField "String", "SIGNAL_CANONICAL_VERSION_NAME", "\"$canonicalVersionName\"" - buildConfigField "int", "SIGNAL_CANONICAL_VERSION_CODE", "$canonicalVersionCode" - buildConfigField "String", "BACKUP_FILENAME", "\"" + BASE_APP_FILENAME.toLowerCase() + "\"" - buildConfigField "boolean", "FORCE_INTERNAL_USER_FLAG", "$FORCE_INTERNAL_USER_FLAG" - - vectorDrawables.useSupportLibrary = true - - buildConfigField "long", "BUILD_TIMESTAMP", "${getBuildTimestamp()}L" - buildConfigField "String", "GIT_HASH", "\"${getCommitHashOrNull()}\"" - // MOLLY: Ensure to add any new URL to the SignalServiceNetworkAccess.HOSTNAMES list - buildConfigField "String", "SIGNAL_URL", "\"https://chat.signal.org\"" - buildConfigField "String", "STORAGE_URL", "\"https://storage.signal.org\"" - buildConfigField "String", "SIGNAL_CDN_URL", "\"https://cdn.signal.org\"" - buildConfigField "String", "SIGNAL_CDN2_URL", "\"https://cdn2.signal.org\"" - buildConfigField "String", "SIGNAL_CDN3_URL", "\"https://cdn3.signal.org\"" - buildConfigField "String", "SIGNAL_CDSI_URL", "\"https://cdsi.signal.org\"" - buildConfigField "String", "SIGNAL_SERVICE_STATUS_URL", "\"uptime.signal.org\"" - buildConfigField "String", "SIGNAL_KEY_BACKUP_URL", "\"https://api.backup.signal.org\"" - buildConfigField "String", "SIGNAL_SVR2_URL", "\"https://svr2.signal.org\"" - buildConfigField "String", "SIGNAL_SFU_URL", "\"https://sfu.voip.signal.org\"" - buildConfigField "String", "SIGNAL_STAGING_SFU_URL", "\"https://sfu.staging.voip.signal.org\"" - buildConfigField "String[]", "SIGNAL_SFU_INTERNAL_NAMES", "new String[]{\"Test\", \"Staging\", \"Development\"}" - buildConfigField "String[]", "SIGNAL_SFU_INTERNAL_URLS", "new String[]{\"https://sfu.test.voip.signal.org\", \"https://sfu.staging.voip.signal.org\", \"https://sfu.staging.test.voip.signal.org\"}" - buildConfigField "String", "CONTENT_PROXY_HOST", "\"contentproxy.signal.org\"" - buildConfigField "int", "CONTENT_PROXY_PORT", "443" - buildConfigField "String", "SIGNAL_AGENT", "\"OWA\"" - buildConfigField "String", "CDSI_MRENCLAVE", "\"0f6fd79cdfdaa5b2e6337f534d3baf999318b0c462a7ac1f41297a3e4b424a57\"" - buildConfigField "String", "SVR2_MRENCLAVE", "\"6ee1042f9e20f880326686dd4ba50c25359f01e9f733eeba4382bca001d45094\"" - buildConfigField "String", "UNIDENTIFIED_SENDER_TRUST_ROOT", "\"BXu6QIKVz5MA8gstzfOgRQGqyLqOwNKHL6INkv3IHWMF\"" - buildConfigField "String", "ZKGROUP_SERVER_PUBLIC_PARAMS", "\"AMhf5ywVwITZMsff/eCyudZx9JDmkkkbV6PInzG4p8x3VqVJSFiMvnvlEKWuRob/1eaIetR31IYeAbm0NdOuHH8Qi+Rexi1wLlpzIo1gstHWBfZzy1+qHRV5A4TqPp15YzBPm0WSggW6PbSn+F4lf57VCnHF7p8SvzAA2ZZJPYJURt8X7bbg+H3i+PEjH9DXItNEqs2sNcug37xZQDLm7X36nOoGPs54XsEGzPdEV+itQNGUFEjY6X9Uv+Acuks7NpyGvCoKxGwgKgE5XyJ+nNKlyHHOLb6N1NuHyBrZrgtY/JYJHRooo5CEqYKBqdFnmbTVGEkCvJKxLnjwKWf+fEPoWeQFj5ObDjcKMZf2Jm2Ae69x+ikU5gBXsRmoF94GXTLfN0/vLt98KDPnxwAQL9j5V1jGOY8jQl6MLxEs56cwXN0dqCnImzVH3TZT1cJ8SW1BRX6qIVxEzjsSGx3yxF3suAilPMqGRp4ffyopjMD1JXiKR2RwLKzizUe5e8XyGOy9fplzhw3jVzTRyUZTRSZKkMLWcQ/gv0E4aONNqs4P\"" - buildConfigField "String", "GENERIC_SERVER_PUBLIC_PARAMS", "\"AByD873dTilmOSG0TjKrvpeaKEsUmIO8Vx9BeMmftwUs9v7ikPwM8P3OHyT0+X3EUMZrSe9VUp26Wai51Q9I8mdk0hX/yo7CeFGJyzoOqn8e/i4Ygbn5HoAyXJx5eXfIbqpc0bIxzju4H/HOQeOpt6h742qii5u/cbwOhFZCsMIbElZTaeU+BWMBQiZHIGHT5IE0qCordQKZ5iPZom0HeFa8Yq0ShuEyAl0WINBiY6xE3H/9WnvzXBbMuuk//eRxXgzO8ieCeK8FwQNxbfXqZm6Ro1cMhCOF3u7xoX83QhpN\"" - buildConfigField "String[]", "LANGUAGES", "new String[]{\"" + autoResConfig().collect { s -> s.replace('-r', '_') }.join('", "') + '"}' - buildConfigField "String", "DEFAULT_CURRENCIES", "\"EUR,AUD,GBP,CAD,CNY\"" - buildConfigField "String", "GIPHY_API_KEY", "\"3o6ZsYH6U6Eri53TXy\"" - buildConfigField "String", "SIGNAL_CAPTCHA_URL", "\"https://signalcaptchas.org/registration/generate.html\"" - buildConfigField "String", "RECAPTCHA_PROOF_URL", "\"https://signalcaptchas.org/challenge/generate.html\"" - // MOLLY: Rely on the built-in variables FLAVOR and BUILD_TYPE instead of BUILD_*_TYPE - buildConfigField "String", "BADGE_STATIC_ROOT", "\"https://updates2.signal.org/static/badges/\"" - buildConfigField "boolean", "TRACING_ENABLED", "false" - - ndk { - //noinspection ChromeOsAbiSupport - abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86_64' - } - - resourceConfigurations += [] - - bundle { - language { - enableSplit = false - } - } - - testInstrumentationRunner "org.thoughtcrime.securesms.testing.SignalTestRunner" - testInstrumentationRunnerArguments clearPackageData: 'true' - } - - buildTypes { - debug { - isDefault true - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), - 'proguard/proguard-firebase-messaging.pro', - 'proguard/proguard-google-play-services.pro', - 'proguard/proguard-jackson.pro', - 'proguard/proguard-sqlite.pro', - 'proguard/proguard-appcompat-v7.pro', - 'proguard/proguard-square-okhttp.pro', - 'proguard/proguard-square-okio.pro', - 'proguard/proguard-rounded-image-view.pro', - 'proguard/proguard-glide.pro', - 'proguard/proguard-shortcutbadger.pro', - 'proguard/proguard-retrofit.pro', - 'proguard/proguard-webrtc.pro', - 'proguard/proguard-klinker.pro', - 'proguard/proguard-mobilecoin.pro', - 'proguard/proguard-retrolambda.pro', - 'proguard/proguard-okhttp.pro', - 'proguard/proguard-ez-vcard.pro', - 'proguard/proguard.cfg' - testProguardFiles 'proguard/proguard-automation.pro', - 'proguard/proguard.cfg' - } - - instrumentation { - initWith debug - isDefault false - minifyEnabled false - matchingFallbacks = ['debug'] - applicationIdSuffix ".instrumentation" - } - - spinner { - initWith debug - isDefault false - minifyEnabled false - matchingFallbacks = ['debug'] - } - - release { - signingConfig signingConfigs.ci.storeFile ? signingConfigs.ci : null - minifyEnabled true - shrinkResources true - proguardFiles = buildTypes.debug.proguardFiles - } - - canary { - initWith debug - isDefault false - minifyEnabled false - matchingFallbacks = ['debug'] - buildConfigField "String", "BUILD_VARIANT_TYPE", "\"Canary\"" - } - } - - productFlavors { - website { - dimension 'distribution' - isDefault true - buildConfigField "boolean", "MANAGES_MOLLY_UPDATES", "true" - } - - store { - dimension 'distribution' - buildConfigField "boolean", "MANAGES_MOLLY_UPDATES", "false" - } - - gms { - dimension 'license' - isDefault true - manifestPlaceholders = [mapsApiKey:MAPS_API_KEY] - buildConfigField "boolean", "USE_PLAY_SERVICES", "true" - buildConfigField "boolean", "USE_OSM", "false" - buildConfigField "String", "FDROID_UPDATE_URL", "\"https://molly.im/fdroid/repo\"" - } - - foss { - dimension 'license' - versionNameSuffix '-FOSS' - buildConfigField "boolean", "USE_PLAY_SERVICES", "false" - buildConfigField "boolean", "USE_OSM", "true" - buildConfigField "String", "FDROID_UPDATE_URL", "\"https://molly.im/fdroid/foss/repo\"" - } - - prod { - dimension 'environment' - - isDefault true - - buildConfigField "String", "MOBILE_COIN_ENVIRONMENT", "\"mainnet\"" - } - - staging { - dimension 'environment' - - applicationIdSuffix ".staging" - buildConfigField "String", "SIGNAL_PACKAGE_NAME", "\"org.thoughtcrime.securesms.staging\"" - - buildConfigField "String", "SIGNAL_URL", "\"https://chat.staging.signal.org\"" - buildConfigField "String", "STORAGE_URL", "\"https://storage-staging.signal.org\"" - buildConfigField "String", "SIGNAL_CDN_URL", "\"https://cdn-staging.signal.org\"" - buildConfigField "String", "SIGNAL_CDN2_URL", "\"https://cdn2-staging.signal.org\"" - buildConfigField "String", "SIGNAL_CDN3_URL", "\"https://cdn3-staging.signal.org\"" - buildConfigField "String", "SIGNAL_CDSI_URL", "\"https://cdsi.staging.signal.org\"" - buildConfigField "String", "SIGNAL_KEY_BACKUP_URL", "\"https://api-staging.backup.signal.org\"" - buildConfigField "String", "SIGNAL_SVR2_URL", "\"https://svr2.staging.signal.org\"" - buildConfigField "String", "SVR2_MRENCLAVE", "\"a8a261420a6bb9b61aa25bf8a79e8bd20d7652531feb3381cbffd446d270be95\"" - buildConfigField "String", "UNIDENTIFIED_SENDER_TRUST_ROOT", "\"BbqY1DzohE4NUZoVF+L18oUPrK3kILllLEJh2UnPSsEx\"" - buildConfigField "String", "ZKGROUP_SERVER_PUBLIC_PARAMS", "\"ABSY21VckQcbSXVNCGRYJcfWHiAMZmpTtTELcDmxgdFbtp/bWsSxZdMKzfCp8rvIs8ocCU3B37fT3r4Mi5qAemeGeR2X+/YmOGR5ofui7tD5mDQfstAI9i+4WpMtIe8KC3wU5w3Inq3uNWVmoGtpKndsNfwJrCg0Hd9zmObhypUnSkfYn2ooMOOnBpfdanRtrvetZUayDMSC5iSRcXKpdlukrpzzsCIvEwjwQlJYVPOQPj4V0F4UXXBdHSLK05uoPBCQG8G9rYIGedYsClJXnbrgGYG3eMTG5hnx4X4ntARBgELuMWWUEEfSK0mjXg+/2lPmWcTZWR9nkqgQQP0tbzuiPm74H2wMO4u1Wafe+UwyIlIT9L7KLS19Aw8r4sPrXZSSsOZ6s7M1+rTJN0bI5CKY2PX29y5Ok3jSWufIKcgKOnWoP67d5b2du2ZVJjpjfibNIHbT/cegy/sBLoFwtHogVYUewANUAXIaMPyCLRArsKhfJ5wBtTminG/PAvuBdJ70Z/bXVPf8TVsR292zQ65xwvWTejROW6AZX6aqucUj\"" - buildConfigField "String", "GENERIC_SERVER_PUBLIC_PARAMS", "\"AHILOIrFPXX9laLbalbA9+L1CXpSbM/bTJXZGZiuyK1JaI6dK5FHHWL6tWxmHKYAZTSYmElmJ5z2A5YcirjO/yfoemE03FItyaf8W1fE4p14hzb5qnrmfXUSiAIVrhaXVwIwSzH6RL/+EO8jFIjJ/YfExfJ8aBl48CKHgu1+A6kWynhttonvWWx6h7924mIzW0Czj2ROuh4LwQyZypex4GuOPW8sgIT21KNZaafgg+KbV7XM1x1tF3XA17B4uGUaDbDw2O+nR1+U5p6qHPzmJ7ggFjSN6Utu+35dS1sS0P9N\"" - buildConfigField "String", "MOBILE_COIN_ENVIRONMENT", "\"testnet\"" - buildConfigField "String", "SIGNAL_CAPTCHA_URL", "\"https://signalcaptchas.org/staging/registration/generate.html\"" - buildConfigField "String", "RECAPTCHA_PROOF_URL", "\"https://signalcaptchas.org/staging/challenge/generate.html\"" - } - } - - lint { - abortOnError true - baseline file('lint-baseline.xml') - disable 'LintError' - } - - android.applicationVariants.configureEach { variant -> - def isStaging = variant.productFlavors*.name.contains("staging") - def hasSigningConfig = buildType.signingConfig || variant.signingConfig - - variant.resValue 'string', 'app_name', BASE_APP_TITLE + (isStaging ? " Staging" : "") - variant.resValue "string", 'package_name', variant.applicationId - - variant.outputs.configureEach { - def flavors = "-${variant.baseName}" - ~/-prod/ - ~/-(foss|gms)/ - ~/-website/ - ~/-release/ - def unsigned = hasSigningConfig ? "" : "-unsigned" - outputFileName = "${BASE_APP_FILENAME}${flavors}${unsigned}-${versionName}.apk" - } - } - - android.variantFilter { variant -> - def matches = variant.name =~ BUILD_VARIANTS - if (!(selectableVariants.contains(variant.name) && matches)) { - setIgnore(true) - } - } -} - -dependencies { - implementation libs.androidx.fragment.ktx - lintChecks project(':lintchecks') - - coreLibraryDesugaring libs.android.tools.desugar - - implementation (libs.androidx.appcompat) { - version { - strictly '1.6.1' - } - } - implementation libs.androidx.window.window - implementation libs.androidx.window.java - implementation libs.androidx.recyclerview - implementation libs.material.material - implementation libs.androidx.legacy.support - implementation libs.androidx.preference - implementation libs.androidx.legacy.preference - implementation libs.androidx.gridlayout - implementation libs.androidx.exifinterface - implementation libs.androidx.compose.rxjava3 - implementation libs.androidx.compose.runtime.livedata - implementation libs.androidx.constraintlayout - implementation libs.androidx.multidex - implementation libs.androidx.navigation.fragment.ktx - implementation libs.androidx.navigation.ui.ktx - implementation libs.androidx.lifecycle.viewmodel.ktx - implementation libs.androidx.lifecycle.livedata.ktx - implementation libs.androidx.lifecycle.process - implementation libs.androidx.lifecycle.viewmodel.savedstate - implementation libs.androidx.lifecycle.common.java8 - implementation libs.androidx.lifecycle.reactivestreams.ktx - implementation libs.androidx.camera.core - implementation libs.androidx.camera.camera2 - implementation libs.androidx.camera.lifecycle - implementation libs.androidx.camera.view - implementation libs.androidx.concurrent.futures - implementation libs.androidx.autofill - implementation libs.androidx.biometric - implementation libs.androidx.sharetarget - implementation libs.androidx.profileinstaller - implementation libs.androidx.asynclayoutinflater - implementation libs.androidx.asynclayoutinflater.appcompat - implementation libs.androidx.webkit - - gmsImplementation (libs.firebase.messaging) { - exclude group: 'com.google.firebase', module: 'firebase-core' - exclude group: 'com.google.firebase', module: 'firebase-analytics' - exclude group: 'com.google.firebase', module: 'firebase-measurement-connector' - } - - gmsImplementation libs.google.play.services.maps - gmsImplementation libs.google.play.services.auth - - implementation libs.bundles.media3 - - implementation libs.conscrypt.android - implementation libs.signal.aesgcmprovider - - implementation project(':libsignal-service') - implementation project(':paging') - implementation project(':core-util') - implementation project(':glide-config') - implementation project(':video') - implementation project(':device-transfer') - implementation project(':image-editor') - implementation project(':contacts') - implementation project(':qr') - implementation project(':sticky-header-grid') - implementation project(':photoview') - implementation project(':glide-webp') - - implementation libs.libsignal.android - - implementation libs.mobilecoin - - implementation libs.molly.ringrtc - - implementation libs.leolin.shortcutbadger - implementation libs.emilsjolander.stickylistheaders - implementation libs.apache.httpclient.android - implementation libs.glide.glide - implementation libs.roundedimageview - implementation libs.materialish.progress - implementation libs.greenrobot.eventbus - implementation libs.google.zxing.android.integration - implementation libs.google.zxing.core - implementation libs.google.flexbox - implementation (libs.subsampling.scale.image.view) { - exclude group: 'com.android.support', module: 'support-annotations' - } - implementation (libs.android.tooltips) { - exclude group: 'com.android.support', module: 'appcompat-v7' - } - implementation libs.stream - - implementation libs.lottie - - implementation libs.signal.android.database.sqlcipher - implementation libs.androidx.sqlite - - implementation (libs.google.ez.vcard) { - exclude group: 'com.fasterxml.jackson.core' - exclude group: 'org.freemarker' - } - implementation libs.dnsjava - implementation libs.kotlinx.collections.immutable - implementation libs.accompanist.permissions - - implementation(libs.molly.argon2) { - artifact { - type = "aar" - } - } - - implementation project(":libnetcipher") - implementation libs.gosimple.nbvcxz - implementation libs.molly.native.utils - implementation libs.molly.glide.webp.decoder - - fossImplementation 'org.osmdroid:osmdroid-android:6.1.16' - fossImplementation project(':libfakegms') - - spinnerImplementation project(":spinner") - - canaryImplementation libs.square.leakcanary - - testImplementation testLibs.junit.junit - testImplementation testLibs.assertj.core - testImplementation testLibs.mockito.core - testImplementation testLibs.mockito.kotlin - - testImplementation testLibs.androidx.test.core - testImplementation (testLibs.robolectric.robolectric) { - exclude group: 'com.google.protobuf', module: 'protobuf-java' - } - testImplementation testLibs.robolectric.shadows.multidex - testImplementation (testLibs.bouncycastle.bcprov.jdk15on) { version { strictly "1.70" } } // Used by roboelectric - testImplementation (testLibs.bouncycastle.bcpkix.jdk15on) { version { strictly "1.70" } } // Used by roboelectric - testImplementation testLibs.conscrypt.openjdk.uber // Used by robolectric - testImplementation testLibs.hamcrest.hamcrest - testImplementation testLibs.mockk - - testImplementation(testFixtures(project(":libsignal-service"))) - - androidTestImplementation testLibs.androidx.test.ext.junit - androidTestImplementation testLibs.espresso.core - androidTestImplementation testLibs.androidx.test.core - androidTestImplementation testLibs.androidx.test.core.ktx - androidTestImplementation testLibs.androidx.test.ext.junit.ktx - androidTestImplementation testLibs.mockito.android - androidTestImplementation testLibs.mockito.kotlin - androidTestImplementation testLibs.mockk.android - androidTestImplementation testLibs.square.okhttp.mockserver - - instrumentationImplementation (libs.androidx.fragment.testing) { - exclude group: 'androidx.test', module: 'core' - } - - testImplementation testLibs.espresso.core - - implementation libs.kotlin.stdlib.jdk8 - - implementation libs.rxjava3.rxandroid - implementation libs.rxjava3.rxkotlin - implementation libs.rxdogtag - - androidTestUtil testLibs.androidx.test.orchestrator - - implementation project(':core-ui') -} - -static def getCommitTag() { - return 'git describe --tags --exact-match'.execute().text.trim() ?: 'untagged' -} - -static def getCommitTimestamp() { - return 'git log -1 --pretty=format:%ct000'.execute().text.trim() -} - -static def getCommitHashOrNull() { - try { - return 'git rev-parse --short=12 HEAD'.execute().text.trim() - } catch (ignored) { - return null - } -} - -static def getBuildTimestamp() { - try { - return getCommitTimestamp() - } catch (ignored) { - return new Date().getTime() - } -} - -tasks.withType(Test).configureEach { - testLogging { - events "failed" - exceptionFormat "full" - showCauses true - showExceptions true - showStackTraces true - } -} diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 0000000000..0166496e1d --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,621 @@ +import org.gradle.api.tasks.testing.logging.TestExceptionFormat +import java.io.ByteArrayOutputStream + +plugins { + id("com.android.application") + id("kotlin-android") + id("androidx.navigation.safeargs") + id("org.jetbrains.kotlin.android") + id("app.cash.exhaustive") + id("kotlin-parcelize") + id("com.squareup.wire") + id("translations") + id("licenses") +} + +// Sort baseline.profm for reproducible builds +// See issue: https://issuetracker.google.com/issues/231837768 +apply { + from("fix-profm.gradle") +} + +val canonicalVersionCode = 1376 +val canonicalVersionName = "6.44.2" +val mollyRevision = 1 + +val postFixSize = 100 + +val selectableVariants = listOf( + "prodFossWebsiteDebug", + "prodFossWebsiteRelease", + "prodFossStoreDebug", + "prodFossStoreRelease", + "prodGmsWebsiteDebug", + "prodGmsWebsiteRelease", + "prodGmsWebsiteCanary", + "prodGmsWebsiteInstrumentation", + "prodGmsWebsiteSpinner", + "stagingFossWebsiteDebug", + "stagingFossWebsiteRelease", + "stagingGmsWebsiteDebug", + "stagingGmsWebsiteRelease", +) + +val signalBuildToolsVersion: String by rootProject.extra +val signalCompileSdkVersion: String by rootProject.extra +val signalTargetSdkVersion: Int by rootProject.extra +val signalMinSdkVersion: Int by rootProject.extra +val signalJavaVersion: JavaVersion by rootProject.extra +val signalKotlinJvmTarget: String by rootProject.extra + +// Override build config via env vars when project property 'CI' is set +val ciEnabled = project.hasProperty("CI") + +val baseAppTitle = getCiEnv("CI_APP_TITLE") ?: properties["baseAppTitle"] as String +val baseAppFileName = getCiEnv("CI_APP_FILENAME") ?: properties["baseAppFileName"] as String +val basePackageId = getCiEnv("CI_PACKAGE_ID") ?: properties["basePackageId"] as String +val buildVariants = getCiEnv("CI_BUILD_VARIANTS") ?: properties["buildVariants"] as String +val forceInternalUserFlag = getCiEnv("CI_FORCE_INTERNAL_USER_FLAG") ?: properties["forceInternalUserFlag"] as String +val mapsApiKey = getCiEnv("CI_MAPS_API_KEY") ?: properties["mapsApiKey"] as String + +fun getCiEnv(name: String): String? = if (ciEnabled) System.getenv(name) else null + +wire { + kotlin { + javaInterop = true + } + + sourcePath { + srcDir("src/main/protowire") + } + + protoPath { + srcDir("${project.rootDir}/libsignal-service/src/main/protowire") + } +} + +android { + namespace = "org.thoughtcrime.securesms" + + buildToolsVersion = signalBuildToolsVersion + compileSdkVersion = signalCompileSdkVersion + + flavorDimensions += listOf("environment", "license", "distribution") + useLibrary("org.apache.http.legacy") + testBuildType = "instrumentation" + + kotlinOptions { + jvmTarget = signalKotlinJvmTarget + freeCompilerArgs = listOf("-Xallow-result-return-type") + } + + signingConfigs { + System.getenv("CI_KEYSTORE_PATH")?.let { path -> + create("ci") { + println("Signing release build with keystore: '$path'") + storeFile = file(path) + storePassword = System.getenv("CI_KEYSTORE_PASSWORD") + keyAlias = System.getenv("CI_KEYSTORE_ALIAS") + keyPassword = System.getenv("CI_KEYSTORE_PASSWORD") + enableV4Signing = false + } + } + } + + testOptions { + execution = "ANDROIDX_TEST_ORCHESTRATOR" + + unitTests { + isIncludeAndroidResources = true + } + } + + sourceSets { + getByName("test") { + java.srcDir("$projectDir/src/testShared") + } + + getByName("androidTest") { + java.srcDir("$projectDir/src/testShared") + } + } + + compileOptions { + isCoreLibraryDesugaringEnabled = true + sourceCompatibility = signalJavaVersion + targetCompatibility = signalJavaVersion + } + + packagingOptions { + resources { + excludes += setOf("LICENSE.txt", "LICENSE", "NOTICE", "asm-license.txt", "META-INF/LICENSE", "META-INF/LICENSE.md", "META-INF/NOTICE", "META-INF/LICENSE-notice.md", "META-INF/proguard/androidx-annotations.pro", "libsignal_jni.dylib", "signal_jni.dll") + } + jniLibs { + // MOLLY: Compress native libs by default as APK is not split on ABIs + useLegacyPackaging = true + } + } + + buildFeatures { + viewBinding = true + compose = true + } + + composeOptions { + kotlinCompilerExtensionVersion = "1.4.4" + } + + if (mollyRevision < 0 || mollyRevision >= postFixSize) { + throw GradleException("Molly revision $mollyRevision out of range") + } + + defaultConfig { + versionCode = canonicalVersionCode * postFixSize + mollyRevision + versionName = if (ciEnabled) getCommitTag() else canonicalVersionName + + minSdkVersion(signalMinSdkVersion) + targetSdkVersion(signalTargetSdkVersion) + + applicationId = basePackageId + + multiDexEnabled = true + + buildConfigField("String", "SIGNAL_PACKAGE_NAME", "\"org.thoughtcrime.securesms\"") + buildConfigField("String", "SIGNAL_CANONICAL_VERSION_NAME", "\"$canonicalVersionName\"") + buildConfigField("int", "SIGNAL_CANONICAL_VERSION_CODE", "$canonicalVersionCode") + buildConfigField("String", "BACKUP_FILENAME", "\"${baseAppFileName.lowercase()}\"") + buildConfigField("boolean", "FORCE_INTERNAL_USER_FLAG", forceInternalUserFlag) + + vectorDrawables.useSupportLibrary = true + + // MOLLY: Ensure to add any new URLs to SignalServiceNetworkAccess.HOSTNAMES list + buildConfigField("long", "BUILD_TIMESTAMP", getLastCommitTimestamp() + "L") + buildConfigField("String", "GIT_HASH", "\"${getGitHash()}\"") + buildConfigField("String", "SIGNAL_URL", "\"https://chat.signal.org\"") + buildConfigField("String", "STORAGE_URL", "\"https://storage.signal.org\"") + buildConfigField("String", "SIGNAL_CDN_URL", "\"https://cdn.signal.org\"") + buildConfigField("String", "SIGNAL_CDN2_URL", "\"https://cdn2.signal.org\"") + buildConfigField("String", "SIGNAL_CDN3_URL", "\"https://cdn3.signal.org\"") + buildConfigField("String", "SIGNAL_CDSI_URL", "\"https://cdsi.signal.org\"") + buildConfigField("String", "SIGNAL_SERVICE_STATUS_URL", "\"uptime.signal.org\"") + buildConfigField("String", "SIGNAL_KEY_BACKUP_URL", "\"https://api.backup.signal.org\"") + buildConfigField("String", "SIGNAL_SVR2_URL", "\"https://svr2.signal.org\"") + buildConfigField("String", "SIGNAL_SFU_URL", "\"https://sfu.voip.signal.org\"") + buildConfigField("String", "SIGNAL_STAGING_SFU_URL", "\"https://sfu.staging.voip.signal.org\"") + buildConfigField("String[]", "SIGNAL_SFU_INTERNAL_NAMES", "new String[]{\"Test\", \"Staging\", \"Development\"}") + buildConfigField("String[]", "SIGNAL_SFU_INTERNAL_URLS", "new String[]{\"https://sfu.test.voip.signal.org\", \"https://sfu.staging.voip.signal.org\", \"https://sfu.staging.test.voip.signal.org\"}") + buildConfigField("String", "CONTENT_PROXY_HOST", "\"contentproxy.signal.org\"") + buildConfigField("int", "CONTENT_PROXY_PORT", "443") + buildConfigField("String", "SIGNAL_AGENT", "\"OWA\"") + buildConfigField("String", "CDSI_MRENCLAVE", "\"0f6fd79cdfdaa5b2e6337f534d3baf999318b0c462a7ac1f41297a3e4b424a57\"") + buildConfigField("String", "SVR2_MRENCLAVE_DEPRECATED", "\"6ee1042f9e20f880326686dd4ba50c25359f01e9f733eeba4382bca001d45094\"") + buildConfigField("String", "SVR2_MRENCLAVE", "\"a6622ad4656e1abcd0bc0ff17c229477747d2ded0495c4ebee7ed35c1789fa97\"") + buildConfigField("String", "UNIDENTIFIED_SENDER_TRUST_ROOT", "\"BXu6QIKVz5MA8gstzfOgRQGqyLqOwNKHL6INkv3IHWMF\"") + 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/9pFFEB3pSbf4iiUukw63Eo8Aqnf4iwob6X1QviCWuc8t0I=\"") + buildConfigField("String", "GENERIC_SERVER_PUBLIC_PARAMS", "\"AByD873dTilmOSG0TjKrvpeaKEsUmIO8Vx9BeMmftwUs9v7ikPwM8P3OHyT0+X3EUMZrSe9VUp26Wai51Q9I8mdk0hX/yo7CeFGJyzoOqn8e/i4Ygbn5HoAyXJx5eXfIbqpc0bIxzju4H/HOQeOpt6h742qii5u/cbwOhFZCsMIbElZTaeU+BWMBQiZHIGHT5IE0qCordQKZ5iPZom0HeFa8Yq0ShuEyAl0WINBiY6xE3H/9WnvzXBbMuuk//eRxXgzO8ieCeK8FwQNxbfXqZm6Ro1cMhCOF3u7xoX83QhpN\"") + buildConfigField("String", "BACKUP_SERVER_PUBLIC_PARAMS", "\"AJwNSU55fsFCbgaxGRD11wO1juAs8Yr5GF8FPlGzzvdJJIKH5/4CC7ZJSOe3yL2vturVaRU2Cx0n751Vt8wkj1bozK3CBV1UokxV09GWf+hdVImLGjXGYLLhnI1J2TWEe7iWHyb553EEnRb5oxr9n3lUbNAJuRmFM7hrr0Al0F0wrDD4S8lo2mGaXe0MJCOM166F8oYRQqpFeEHfiLnxA1O8ZLh7vMdv4g9jI5phpRBTsJ5IjiJrWeP0zdIGHEssUeprDZ9OUJ14m0v61eYJMKsf59Bn+mAT2a7YfB+Don9O\"") + buildConfigField("String[]", "LANGUAGES", "new String[]{ ${languageList().map { "\"$it\"" }.joinToString(separator = ", ")} }") + buildConfigField("String", "DEFAULT_CURRENCIES", "\"EUR,AUD,GBP,CAD,CNY\"") + buildConfigField("String", "GIPHY_API_KEY", "\"3o6ZsYH6U6Eri53TXy\"") + buildConfigField("String", "SIGNAL_CAPTCHA_URL", "\"https://signalcaptchas.org/registration/generate.html\"") + buildConfigField("String", "RECAPTCHA_PROOF_URL", "\"https://signalcaptchas.org/challenge/generate.html\"") + + // MOLLY: Rely on the built-in variables FLAVOR and BUILD_TYPE instead of BUILD_*_TYPE + buildConfigField("String", "BADGE_STATIC_ROOT", "\"https://updates2.signal.org/static/badges/\"") + buildConfigField("boolean", "TRACING_ENABLED", "false") + + ndk { + //noinspection ChromeOsAbiSupport + abiFilters += listOf("armeabi-v7a", "arm64-v8a", "x86_64") + } + + resourceConfigurations += listOf() + + bundle { + language { + enableSplit = false + } + } + + testInstrumentationRunner = "org.thoughtcrime.securesms.testing.SignalTestRunner" + testInstrumentationRunnerArguments["clearPackageData"] = "true" + } + + buildTypes { + getByName("debug") { + isDefault = true + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android.txt"), + "proguard/proguard-firebase-messaging.pro", + "proguard/proguard-google-play-services.pro", + "proguard/proguard-jackson.pro", + "proguard/proguard-sqlite.pro", + "proguard/proguard-appcompat-v7.pro", + "proguard/proguard-square-okhttp.pro", + "proguard/proguard-square-okio.pro", + "proguard/proguard-rounded-image-view.pro", + "proguard/proguard-glide.pro", + "proguard/proguard-shortcutbadger.pro", + "proguard/proguard-retrofit.pro", + "proguard/proguard-webrtc.pro", + "proguard/proguard-klinker.pro", + "proguard/proguard-mobilecoin.pro", + "proguard/proguard-retrolambda.pro", + "proguard/proguard-okhttp.pro", + "proguard/proguard-ez-vcard.pro", + "proguard/proguard.cfg" + ) + testProguardFiles( + "proguard/proguard-automation.pro", + "proguard/proguard.cfg" + ) + } + + getByName("release") { + isMinifyEnabled = true + isShrinkResources = true + signingConfig = signingConfigs.findByName("ci") + proguardFiles(*buildTypes["debug"].proguardFiles.toTypedArray()) + } + + create("instrumentation") { + initWith(getByName("debug")) + isDefault = false + isMinifyEnabled = false + matchingFallbacks += "debug" + applicationIdSuffix = ".instrumentation" + } + + create("spinner") { + initWith(getByName("debug")) + isDefault = false + isMinifyEnabled = false + matchingFallbacks += "debug" + } + + create("canary") { + initWith(getByName("debug")) + isDefault = false + isMinifyEnabled = false + matchingFallbacks += "debug" + } + } + + productFlavors { + create("store") { + dimension = "distribution" + buildConfigField("boolean", "MANAGES_MOLLY_UPDATES", "false") + } + + create("website") { + dimension = "distribution" + isDefault = true + buildConfigField("boolean", "MANAGES_MOLLY_UPDATES", "true") + } + + create("gms") { + dimension = "license" + isDefault = true + manifestPlaceholders["mapsApiKey"] = mapsApiKey + buildConfigField("boolean", "USE_PLAY_SERVICES", "true") + buildConfigField("boolean", "USE_OSM", "false") + buildConfigField("String", "FDROID_UPDATE_URL", "\"https://molly.im/fdroid/repo\"") + } + + create("foss") { + dimension = "license" + versionNameSuffix = "-FOSS" + buildConfigField("boolean", "USE_PLAY_SERVICES", "false") + buildConfigField("boolean", "USE_OSM", "true") + buildConfigField("String", "FDROID_UPDATE_URL", "\"https://molly.im/fdroid/foss/repo\"") + } + + create("prod") { + dimension = "environment" + + isDefault = true + + buildConfigField("String", "MOBILE_COIN_ENVIRONMENT", "\"mainnet\"") + } + + create("staging") { + dimension = "environment" + + applicationIdSuffix = ".staging" + + buildConfigField("String", "SIGNAL_PACKAGE_NAME", "\"org.thoughtcrime.securesms.staging\"") + + buildConfigField("String", "SIGNAL_URL", "\"https://chat.staging.signal.org\"") + buildConfigField("String", "STORAGE_URL", "\"https://storage-staging.signal.org\"") + buildConfigField("String", "SIGNAL_CDN_URL", "\"https://cdn-staging.signal.org\"") + buildConfigField("String", "SIGNAL_CDN2_URL", "\"https://cdn2-staging.signal.org\"") + buildConfigField("String", "SIGNAL_CDN3_URL", "\"https://cdn3-staging.signal.org\"") + buildConfigField("String", "SIGNAL_CDSI_URL", "\"https://cdsi.staging.signal.org\"") + buildConfigField("String", "SIGNAL_KEY_BACKUP_URL", "\"https://api-staging.backup.signal.org\"") + buildConfigField("String", "SIGNAL_SVR2_URL", "\"https://svr2.staging.signal.org\"") + buildConfigField("String", "SVR2_MRENCLAVE_DEPRECATED", "\"a8a261420a6bb9b61aa25bf8a79e8bd20d7652531feb3381cbffd446d270be95\"") + buildConfigField("String", "SVR2_MRENCLAVE", "\"acb1973aa0bbbd14b3b4e06f145497d948fd4a98efc500fcce363b3b743ec482\"") + buildConfigField("String", "UNIDENTIFIED_SENDER_TRUST_ROOT", "\"BbqY1DzohE4NUZoVF+L18oUPrK3kILllLEJh2UnPSsEx\"") + buildConfigField("String", "ZKGROUP_SERVER_PUBLIC_PARAMS", "\"ABSY21VckQcbSXVNCGRYJcfWHiAMZmpTtTELcDmxgdFbtp/bWsSxZdMKzfCp8rvIs8ocCU3B37fT3r4Mi5qAemeGeR2X+/YmOGR5ofui7tD5mDQfstAI9i+4WpMtIe8KC3wU5w3Inq3uNWVmoGtpKndsNfwJrCg0Hd9zmObhypUnSkfYn2ooMOOnBpfdanRtrvetZUayDMSC5iSRcXKpdlukrpzzsCIvEwjwQlJYVPOQPj4V0F4UXXBdHSLK05uoPBCQG8G9rYIGedYsClJXnbrgGYG3eMTG5hnx4X4ntARBgELuMWWUEEfSK0mjXg+/2lPmWcTZWR9nkqgQQP0tbzuiPm74H2wMO4u1Wafe+UwyIlIT9L7KLS19Aw8r4sPrXZSSsOZ6s7M1+rTJN0bI5CKY2PX29y5Ok3jSWufIKcgKOnWoP67d5b2du2ZVJjpjfibNIHbT/cegy/sBLoFwtHogVYUewANUAXIaMPyCLRArsKhfJ5wBtTminG/PAvuBdJ70Z/bXVPf8TVsR292zQ65xwvWTejROW6AZX6aqucUjlENAErBme1YHmOSpU6tr6doJ66dPzVAWIanmO/5mgjNEDeK7DDqQdB1xd03HT2Qs2TxY3kCK8aAb/0iM0HQiXjxZ9HIgYhbtvGEnDKW5ILSUydqH/KBhW4Pb0jZWnqN/YgbWDKeJxnDbYcUob5ZY5Lt5ZCMKuaGUvCJRrCtuugSMaqjowCGRempsDdJEt+cMaalhZ6gczklJB/IbdwENW9KeVFPoFNFzhxWUIS5ML9riVYhAtE6JE5jX0xiHNVIIPthb458cfA8daR0nYfYAUKogQArm0iBezOO+mPk5vCM=\"") + buildConfigField("String", "GENERIC_SERVER_PUBLIC_PARAMS", "\"AHILOIrFPXX9laLbalbA9+L1CXpSbM/bTJXZGZiuyK1JaI6dK5FHHWL6tWxmHKYAZTSYmElmJ5z2A5YcirjO/yfoemE03FItyaf8W1fE4p14hzb5qnrmfXUSiAIVrhaXVwIwSzH6RL/+EO8jFIjJ/YfExfJ8aBl48CKHgu1+A6kWynhttonvWWx6h7924mIzW0Czj2ROuh4LwQyZypex4GuOPW8sgIT21KNZaafgg+KbV7XM1x1tF3XA17B4uGUaDbDw2O+nR1+U5p6qHPzmJ7ggFjSN6Utu+35dS1sS0P9N\"") + buildConfigField("String", "BACKUP_SERVER_PUBLIC_PARAMS", "\"AHYrGb9IfugAAJiPKp+mdXUx+OL9zBolPYHYQz6GI1gWjpEu5me3zVNSvmYY4zWboZHif+HG1sDHSuvwFd0QszSwuSF4X4kRP3fJREdTZ5MCR0n55zUppTwfHRW2S4sdQ0JGz7YDQIJCufYSKh0pGNEHL6hv79Agrdnr4momr3oXdnkpVBIp3HWAQ6IbXQVSG18X36GaicI1vdT0UFmTwU2KTneluC2eyL9c5ff8PcmiS+YcLzh0OKYQXB5ZfQ06d6DiINvDQLy75zcfUOniLAj0lGJiHxGczin/RXisKSR8\"") + buildConfigField("String", "MOBILE_COIN_ENVIRONMENT", "\"testnet\"") + buildConfigField("String", "SIGNAL_CAPTCHA_URL", "\"https://signalcaptchas.org/staging/registration/generate.html\"") + buildConfigField("String", "RECAPTCHA_PROOF_URL", "\"https://signalcaptchas.org/staging/challenge/generate.html\"") + + buildConfigField("String", "BUILD_ENVIRONMENT_TYPE", "\"Staging\"") + buildConfigField("String", "STRIPE_PUBLISHABLE_KEY", "\"pk_test_sngOd8FnXNkpce9nPXawKrJD00kIDngZkD\"") + } + } + + lint { + abortOnError = true + baseline = file("lint-baseline.xml") + disable += "LintError" + } + + applicationVariants.all { + val isStaging = productFlavors.any { it.name == "staging" } + + resValue("string", "app_name", baseAppTitle + if (isStaging) " Staging" else "") + resValue("string", "package_name", applicationId) + + outputs + .map { it as com.android.build.gradle.internal.api.ApkVariantOutputImpl } + .forEach { output -> + val flavors = "-$baseName" + .replace("-prod", "") + .replace(Regex("-(foss|gms)"), "") + .replace("-website", "") + .replace("-release", "") + val unsigned = if (isSigningReady) "" else "-unsigned" + + output.outputFileName = "${baseAppFileName}${flavors}${unsigned}-${versionName}.apk" + } + } + + android.buildTypes.forEach { + val path: String = if (it.name == "release") { + "$projectDir/src/release/java" + } else { + "$projectDir/src/debug/java" + } + + sourceSets.findByName(it.name)!!.java.srcDir(path) + } +} + +androidComponents { + beforeVariants { variantBuilder -> + val name = variantBuilder.name + val selected = selectableVariants.contains(name) + if (!(selected && buildVariants.toRegex().containsMatchIn(name))) { + variantBuilder.enable = false + } + } +} + +dependencies { + lintChecks(project(":lintchecks")) + coreLibraryDesugaring(libs.android.tools.desugar) + + implementation(project(":libsignal-service")) + implementation(project(":paging")) + implementation(project(":core-util")) + implementation(project(":glide-config")) + implementation(project(":video")) + implementation(project(":device-transfer")) + implementation(project(":image-editor")) + implementation(project(":contacts")) + implementation(project(":qr")) + implementation(project(":sticky-header-grid")) + implementation(project(":photoview")) + implementation(project(":glide-webp")) + implementation(project(":core-ui")) + + implementation(libs.androidx.fragment.ktx) + implementation(libs.androidx.appcompat) { + version { + strictly("1.6.1") + } + } + implementation(libs.androidx.window.window) + implementation(libs.androidx.window.java) + implementation(libs.androidx.recyclerview) + implementation(libs.material.material) + implementation(libs.androidx.legacy.support) + implementation(libs.androidx.preference) + implementation(libs.androidx.legacy.preference) + implementation(libs.androidx.gridlayout) + implementation(libs.androidx.exifinterface) + implementation(libs.androidx.compose.rxjava3) + implementation(libs.androidx.compose.runtime.livedata) + implementation(libs.androidx.constraintlayout) + implementation(libs.androidx.multidex) + implementation(libs.androidx.navigation.fragment.ktx) + implementation(libs.androidx.navigation.ui.ktx) + implementation(libs.androidx.lifecycle.viewmodel.ktx) + implementation(libs.androidx.lifecycle.livedata.ktx) + implementation(libs.androidx.lifecycle.process) + implementation(libs.androidx.lifecycle.viewmodel.savedstate) + implementation(libs.androidx.lifecycle.common.java8) + implementation(libs.androidx.lifecycle.reactivestreams.ktx) + implementation(libs.androidx.camera.core) + implementation(libs.androidx.camera.camera2) + implementation(libs.androidx.camera.lifecycle) + implementation(libs.androidx.camera.view) + implementation(libs.androidx.concurrent.futures) + implementation(libs.androidx.autofill) + implementation(libs.androidx.biometric) + implementation(libs.androidx.sharetarget) + implementation(libs.androidx.profileinstaller) + implementation(libs.androidx.asynclayoutinflater) + implementation(libs.androidx.asynclayoutinflater.appcompat) + implementation(libs.androidx.webkit) + "gmsImplementation"(libs.firebase.messaging) { + exclude(group = "com.google.firebase", module = "firebase-core") + exclude(group = "com.google.firebase", module = "firebase-analytics") + exclude(group = "com.google.firebase", module = "firebase-measurement-connector") + } + "gmsImplementation"(libs.google.play.services.maps) + "gmsImplementation"(libs.google.play.services.auth) + implementation(libs.bundles.media3) + implementation(libs.conscrypt.android) + implementation(libs.signal.aesgcmprovider) + implementation(libs.libsignal.android) + implementation(libs.mobilecoin) + implementation(libs.molly.ringrtc) + implementation(libs.leolin.shortcutbadger) + implementation(libs.emilsjolander.stickylistheaders) + implementation(libs.apache.httpclient.android) + implementation(libs.glide.glide) + implementation(libs.roundedimageview) + implementation(libs.materialish.progress) + implementation(libs.greenrobot.eventbus) + implementation(libs.google.zxing.android.integration) + implementation(libs.google.zxing.core) + implementation(libs.google.flexbox) + implementation(libs.subsampling.scale.image.view) { + exclude(group = "com.android.support", module = "support-annotations") + } + implementation(libs.android.tooltips) { + exclude(group = "com.android.support", module = "appcompat-v7") + } + implementation(libs.stream) + implementation(libs.lottie) + implementation(libs.signal.android.database.sqlcipher) + implementation(libs.androidx.sqlite) + implementation(libs.google.ez.vcard) { + exclude(group = "com.fasterxml.jackson.core") + exclude(group = "org.freemarker") + } + implementation(libs.dnsjava) + implementation(libs.kotlinx.collections.immutable) + implementation(libs.accompanist.permissions) + implementation(libs.kotlin.stdlib.jdk8) + implementation(libs.rxjava3.rxandroid) + implementation(libs.rxjava3.rxkotlin) + implementation(libs.rxdogtag) + + implementation(project(":libnetcipher")) + implementation(libs.molly.argon2) { artifact { type = "aar" } } + implementation(libs.molly.native.utils) + implementation(libs.molly.glide.webp.decoder) + implementation(libs.gosimple.nbvcxz) + "fossImplementation"("org.osmdroid:osmdroid-android:6.1.16") + "fossImplementation"(project(":libfakegms")) + + "spinnerImplementation"(project(":spinner")) + + "canaryImplementation"(libs.square.leakcanary) + + "instrumentationImplementation"(libs.androidx.fragment.testing) { + exclude(group = "androidx.test", module = "core") + } + + testImplementation(testLibs.junit.junit) + testImplementation(testLibs.assertj.core) + testImplementation(testLibs.mockito.core) + testImplementation(testLibs.mockito.kotlin) + testImplementation(testLibs.androidx.test.core) + testImplementation(testLibs.robolectric.robolectric) { + exclude(group = "com.google.protobuf", module = "protobuf-java") + } + testImplementation(testLibs.robolectric.shadows.multidex) + testImplementation(testLibs.bouncycastle.bcprov.jdk15on) { + version { + strictly("1.70") + } + } + testImplementation(testLibs.bouncycastle.bcpkix.jdk15on) { + version { + strictly("1.70") + } + } + testImplementation(testLibs.conscrypt.openjdk.uber) + testImplementation(testLibs.hamcrest.hamcrest) + testImplementation(testLibs.mockk) + testImplementation(testFixtures(project(":libsignal-service"))) + testImplementation(testLibs.espresso.core) + + androidTestImplementation(testLibs.androidx.test.ext.junit) + androidTestImplementation(testLibs.espresso.core) + androidTestImplementation(testLibs.androidx.test.core) + androidTestImplementation(testLibs.androidx.test.core.ktx) + androidTestImplementation(testLibs.androidx.test.ext.junit.ktx) + androidTestImplementation(testLibs.mockito.android) + androidTestImplementation(testLibs.mockito.kotlin) + androidTestImplementation(testLibs.mockk.android) + androidTestImplementation(testLibs.square.okhttp.mockserver) + + androidTestUtil(testLibs.androidx.test.orchestrator) +} + +fun assertIsGitRepo() { + if (!file("${project.rootDir}/.git").exists()) { + throw IllegalStateException("Must be a git repository to guarantee reproducible builds! (git hash is part of APK)") + } +} + +fun getLastCommitTimestamp(): String { + assertIsGitRepo() + + ByteArrayOutputStream().use { stdout -> + exec { + commandLine = listOf("git", "log", "-1", "--pretty=format:%ct000") + standardOutput = stdout + } + + return stdout.toString().trim() + } +} + +fun getGitHash(): String { + assertIsGitRepo() + + ByteArrayOutputStream().use { stdout -> + exec { + commandLine = listOf("git", "rev-parse", "--short=12", "HEAD") + standardOutput = stdout + } + + return stdout.toString().trim() + } +} + +fun getCommitTag(): String { + assertIsGitRepo() + + ByteArrayOutputStream().use { stdout -> + exec { + commandLine = listOf("git", "describe", "--tags", "--exact-match") + standardOutput = stdout + } + + return stdout.toString().trim().takeIf { it.isNotEmpty() } ?: "untagged" + } +} + +tasks.withType().configureEach { + testLogging { + events("failed") + exceptionFormat = TestExceptionFormat.FULL + showCauses = true + showExceptions = true + showStackTraces = true + } +} + +fun Project.languageList(): List { + return fileTree("src/main/res") { include("**/strings.xml") } + .map { stringFile -> stringFile.parentFile.name } + .map { valuesFolderName -> valuesFolderName.replace("values-", "") } + .filter { valuesFolderName -> valuesFolderName != "values" } + .map { languageCode -> languageCode.replace("-r", "_") } + .distinct() + "en" +} + +fun String.capitalize(): String { + return this.replaceFirstChar { it.uppercase() } +} diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/BackupTest.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/BackupTest.kt new file mode 100644 index 0000000000..07c5024bbd --- /dev/null +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/BackupTest.kt @@ -0,0 +1,675 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2 + +import android.content.ContentValues +import android.database.Cursor +import androidx.core.content.contentValuesOf +import net.zetetic.database.sqlcipher.SQLiteDatabase +import org.junit.Before +import org.junit.Test +import org.signal.core.util.Hex +import org.signal.core.util.SqlUtil +import org.signal.core.util.insertInto +import org.signal.core.util.readToList +import org.signal.core.util.readToSingleObject +import org.signal.core.util.requireBlob +import org.signal.core.util.requireLong +import org.signal.core.util.requireString +import org.signal.core.util.select +import org.signal.core.util.toInt +import org.signal.core.util.withinTransaction +import org.signal.libsignal.zkgroup.profiles.ProfileKey +import org.thoughtcrime.securesms.backup.v2.database.clearAllDataForBackupRestore +import org.thoughtcrime.securesms.database.CallTable +import org.thoughtcrime.securesms.database.EmojiSearchTable +import org.thoughtcrime.securesms.database.MessageTable +import org.thoughtcrime.securesms.database.MessageTypes +import org.thoughtcrime.securesms.database.RecipientTable +import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList +import org.thoughtcrime.securesms.dependencies.ApplicationDependencies +import org.thoughtcrime.securesms.keyvalue.PhoneNumberPrivacyValues +import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.mms.QuoteModel +import org.thoughtcrime.securesms.profiles.ProfileName +import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.recipients.RecipientId +import org.thoughtcrime.securesms.subscription.Subscriber +import org.thoughtcrime.securesms.testing.assertIs +import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.whispersystems.signalservice.api.push.ServiceId.ACI +import org.whispersystems.signalservice.api.push.ServiceId.PNI +import org.whispersystems.signalservice.api.subscriptions.SubscriberId +import java.io.ByteArrayInputStream +import java.util.UUID +import kotlin.random.Random + +typealias DatabaseData = Map>> + +class BackupTest { + companion object { + val SELF_ACI = ACI.from(UUID.fromString("77770000-b477-4f35-a824-d92987a63641")) + val SELF_PNI = PNI.from(UUID.fromString("77771111-b014-41fb-bf73-05cb2ec52910")) + const val SELF_E164 = "+10000000000" + val SELF_PROFILE_KEY = ProfileKey(Random.nextBytes(32)) + + val ALICE_ACI = ACI.from(UUID.fromString("aaaa0000-5a76-47fa-a98a-7e72c948a82e")) + val ALICE_PNI = PNI.from(UUID.fromString("aaaa1111-c960-4f6c-8385-671ad2ffb999")) + val ALICE_E164 = "+12222222222" + + /** Columns that we don't need to check equality of */ + private val IGNORED_COLUMNS: Map> = mapOf( + RecipientTable.TABLE_NAME to setOf(RecipientTable.STORAGE_SERVICE_ID), + MessageTable.TABLE_NAME to setOf(MessageTable.FROM_DEVICE_ID) + ) + + /** Tables we don't need to check equality of */ + private val IGNORED_TABLES: Set = setOf( + EmojiSearchTable.TABLE_NAME, + "sqlite_sequence", + "message_fts_data", + "message_fts_idx", + "message_fts_docsize" + ) + } + + @Before + fun setup() { + SignalStore.account().setE164(SELF_E164) + SignalStore.account().setAci(SELF_ACI) + SignalStore.account().setPni(SELF_PNI) + SignalStore.account().generateAciIdentityKeyIfNecessary() + SignalStore.account().generatePniIdentityKeyIfNecessary() + } + + @Test + fun emptyDatabase() { + backupTest { } + } + + @Test + fun noteToSelf() { + backupTest { + individualChat(aci = SELF_ACI, givenName = "Note to Self") { + standardMessage(outgoing = true, body = "A") + standardMessage(outgoing = true, body = "B") + standardMessage(outgoing = true, body = "C") + } + } + } + + @Test + fun individualChat() { + backupTest { + individualChat(aci = ALICE_ACI, givenName = "Alice") { + val m1 = standardMessage(outgoing = true, body = "Outgoing 1") + val m2 = standardMessage(outgoing = false, body = "Incoming 1", read = true) + standardMessage(outgoing = true, body = "Outgoing 2", quotes = m2) + standardMessage(outgoing = false, body = "Incoming 2", quotes = m1, quoteTargetMissing = true, read = false) + standardMessage(outgoing = true, body = "Outgoing 3, with mention", randomMention = true) + standardMessage(outgoing = false, body = "Incoming 3, with style", read = false, randomStyling = true) + remoteDeletedMessage(outgoing = true) + remoteDeletedMessage(outgoing = false) + } + } + } + + @Test + fun individualRecipients() { + backupTest { + // Comprehensive example + individualRecipient( + aci = ALICE_ACI, + pni = ALICE_PNI, + e164 = ALICE_E164, + givenName = "Alice", + familyName = "Smith", + username = "alice.99", + hidden = false, + registeredState = RecipientTable.RegisteredState.REGISTERED, + profileKey = ProfileKey(Random.nextBytes(32)), + profileSharing = true, + hideStory = false + ) + + // Trying to get coverage of all the various values + individualRecipient(aci = ACI.from(UUID.randomUUID()), registeredState = RecipientTable.RegisteredState.NOT_REGISTERED) + individualRecipient(aci = ACI.from(UUID.randomUUID()), registeredState = RecipientTable.RegisteredState.UNKNOWN) + individualRecipient(pni = PNI.from(UUID.randomUUID())) + individualRecipient(e164 = "+15551234567") + individualRecipient(aci = ACI.from(UUID.randomUUID()), givenName = "Bob") + individualRecipient(aci = ACI.from(UUID.randomUUID()), familyName = "Smith") + individualRecipient(aci = ACI.from(UUID.randomUUID()), profileSharing = false) + individualRecipient(aci = ACI.from(UUID.randomUUID()), hideStory = true) + individualRecipient(aci = ACI.from(UUID.randomUUID()), hidden = true) + } + } + + @Test + fun individualCallLogs() { + backupTest { + val aliceId = individualRecipient( + aci = ALICE_ACI, + pni = ALICE_PNI, + e164 = ALICE_E164, + givenName = "Alice", + familyName = "Smith", + username = "alice.99", + hidden = false, + registeredState = RecipientTable.RegisteredState.REGISTERED, + profileKey = ProfileKey(Random.nextBytes(32)), + profileSharing = true, + hideStory = false + ) + insertOneToOneCallVariations(1, 1, aliceId) + } + } + + private fun insertOneToOneCallVariations(callId: Long, timestamp: Long, id: RecipientId): Long { + val directions = arrayOf(CallTable.Direction.INCOMING, CallTable.Direction.OUTGOING) + val callTypes = arrayOf(CallTable.Type.AUDIO_CALL, CallTable.Type.VIDEO_CALL) + val events = arrayOf( + CallTable.Event.MISSED, + CallTable.Event.OUTGOING_RING, + CallTable.Event.ONGOING, + CallTable.Event.ACCEPTED, + CallTable.Event.NOT_ACCEPTED + ) + var callTimestamp: Long = timestamp + var currentCallId = callId + for (direction in directions) { + for (event in events) { + for (type in callTypes) { + insertOneToOneCall(callId = currentCallId, callTimestamp, id, type, direction, event) + callTimestamp++ + currentCallId++ + } + } + } + + return currentCallId + } + + private fun insertOneToOneCall(callId: Long, timestamp: Long, peer: RecipientId, type: CallTable.Type, direction: CallTable.Direction, event: CallTable.Event) { + val messageType: Long = CallTable.Call.getMessageType(type, direction, event) + + SignalDatabase.rawDatabase.withinTransaction { + val recipient = Recipient.resolved(peer) + val threadId = SignalDatabase.threads.getOrCreateThreadIdFor(recipient) + val outgoing = direction == CallTable.Direction.OUTGOING + + val messageValues = contentValuesOf( + MessageTable.FROM_RECIPIENT_ID to if (outgoing) Recipient.self().id.serialize() else peer.serialize(), + MessageTable.FROM_DEVICE_ID to 1, + MessageTable.TO_RECIPIENT_ID to if (outgoing) peer.serialize() else Recipient.self().id.serialize(), + MessageTable.DATE_RECEIVED to timestamp, + MessageTable.DATE_SENT to timestamp, + MessageTable.READ to 1, + MessageTable.TYPE to messageType, + MessageTable.THREAD_ID to threadId + ) + + val messageId = SignalDatabase.rawDatabase.insert(MessageTable.TABLE_NAME, null, messageValues) + + val values = contentValuesOf( + CallTable.CALL_ID to callId, + CallTable.MESSAGE_ID to messageId, + CallTable.PEER to peer.serialize(), + CallTable.TYPE to CallTable.Type.serialize(type), + CallTable.DIRECTION to CallTable.Direction.serialize(direction), + CallTable.EVENT to CallTable.Event.serialize(event), + CallTable.TIMESTAMP to timestamp + ) + + SignalDatabase.rawDatabase.insert(CallTable.TABLE_NAME, null, values) + + SignalDatabase.threads.update(threadId, true) + } + } + + @Test + fun accountData() { + val context = ApplicationDependencies.getApplication() + + backupTest(validateKeyValue = true) { + val self = Recipient.self() + + // TODO note-to-self archived + // TODO note-to-self unread + + SignalStore.account().setAci(SELF_ACI) + SignalStore.account().setPni(SELF_PNI) + SignalStore.account().setE164(SELF_E164) + SignalStore.account().generateAciIdentityKeyIfNecessary() + SignalStore.account().generatePniIdentityKeyIfNecessary() + + SignalDatabase.recipients.setProfileKey(self.id, ProfileKey(Random.nextBytes(32))) + SignalDatabase.recipients.setProfileName(self.id, ProfileName.fromParts("Peter", "Parker")) + SignalDatabase.recipients.setProfileAvatar(self.id, "https://example.com/") + + SignalStore.donationsValues().markUserManuallyCancelled() + SignalStore.donationsValues().setSubscriber(Subscriber(SubscriberId.generate(), "USD")) + SignalStore.donationsValues().setDisplayBadgesOnProfile(false) + + SignalStore.phoneNumberPrivacy().phoneNumberListingMode = PhoneNumberPrivacyValues.PhoneNumberListingMode.UNLISTED + SignalStore.phoneNumberPrivacy().phoneNumberSharingMode = PhoneNumberPrivacyValues.PhoneNumberSharingMode.NOBODY + + SignalStore.settings().isLinkPreviewsEnabled = false + SignalStore.settings().isPreferSystemContactPhotos = true + SignalStore.settings().universalExpireTimer = 42 + SignalStore.settings().setKeepMutedChatsArchived(true) + + SignalStore.storyValues().viewedReceiptsEnabled = false + SignalStore.storyValues().userHasViewedOnboardingStory = true + SignalStore.storyValues().isFeatureDisabled = false + SignalStore.storyValues().userHasBeenNotifiedAboutStories = true + SignalStore.storyValues().userHasSeenGroupStoryEducationSheet = true + + SignalStore.emojiValues().reactions = listOf("a", "b", "c") + + TextSecurePreferences.setTypingIndicatorsEnabled(context, false) + TextSecurePreferences.setReadReceiptsEnabled(context, false) + TextSecurePreferences.setShowUnidentifiedDeliveryIndicatorsEnabled(context, true) + } + + // Have to check TextSecurePreferences ourselves, since they're not in a database + TextSecurePreferences.isTypingIndicatorsEnabled(context) assertIs false + TextSecurePreferences.isReadReceiptsEnabled(context) assertIs false + TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(context) assertIs true + } + + /** + * Sets up the database, then executes your setup code, then compares snapshots of the database + * before an after an import to ensure that no data was lost/changed. + * + * @param validateKeyValue If true, this will also validate the KeyValueDatabase. You only want to do this if you + * intend on setting most of the values. Otherwise stuff tends to not match since values are lazily written. + */ + private fun backupTest(validateKeyValue: Boolean = false, content: () -> Unit) { + // Under normal circumstances, My Story ends up being the first recipient in the table, and is added automatically. + // This screws with the tests by offsetting all the recipientIds in the initial state. + // Easiest way to get around this is to make the DB a true clean slate by clearing everything. + // (We only really need to clear Recipient/dlists, but doing everything to be consistent.) + SignalDatabase.distributionLists.clearAllDataForBackupRestore() + SignalDatabase.recipients.clearAllDataForBackupRestore() + SignalDatabase.messages.clearAllDataForBackupRestore() + SignalDatabase.threads.clearAllDataForBackupRestore() + + // Again, for comparison purposes, because we always import self first, we want to ensure it's the first item + // in the table when we export. + individualRecipient( + aci = SELF_ACI, + pni = SELF_PNI, + e164 = SELF_E164, + profileKey = SELF_PROFILE_KEY, + profileSharing = true + ) + + content() + + val startingMainData: DatabaseData = SignalDatabase.rawDatabase.readAllContents() + val startingKeyValueData: DatabaseData = if (validateKeyValue) SignalDatabase.rawDatabase.readAllContents() else emptyMap() + + val exported: ByteArray = BackupRepository.export() + BackupRepository.import(length = exported.size.toLong(), inputStreamFactory = { ByteArrayInputStream(exported) }, selfData = BackupRepository.SelfData(SELF_ACI, SELF_PNI, SELF_E164, SELF_PROFILE_KEY)) + + val endingData: DatabaseData = SignalDatabase.rawDatabase.readAllContents() + val endingKeyValueData: DatabaseData = if (validateKeyValue) SignalDatabase.rawDatabase.readAllContents() else emptyMap() + + assertDatabaseMatches(startingMainData, endingData) + assertDatabaseMatches(startingKeyValueData, endingKeyValueData) + } + + private fun individualChat(aci: ACI, givenName: String, familyName: String? = null, init: IndividualChatCreator.() -> Unit) { + val recipientId = individualRecipient(aci = aci, givenName = givenName, familyName = familyName, profileSharing = true) + + val threadId: Long = SignalDatabase.threads.getOrCreateThreadIdFor(recipientId, false) + + IndividualChatCreator(SignalDatabase.rawDatabase, recipientId, threadId).init() + + SignalDatabase.threads.update(threadId, false) + } + + private fun individualRecipient( + aci: ACI? = null, + pni: PNI? = null, + e164: String? = null, + givenName: String? = null, + familyName: String? = null, + username: String? = null, + hidden: Boolean = false, + registeredState: RecipientTable.RegisteredState = RecipientTable.RegisteredState.UNKNOWN, + profileKey: ProfileKey? = null, + profileSharing: Boolean = false, + hideStory: Boolean = false + ): RecipientId { + check(aci != null || pni != null || e164 != null) + + val recipientId = SignalDatabase.recipients.getAndPossiblyMerge(aci, pni, e164, pniVerified = true, changeSelf = true) + + if (givenName != null || familyName != null) { + SignalDatabase.recipients.setProfileName(recipientId, ProfileName.fromParts(givenName, familyName)) + } + + if (username != null) { + SignalDatabase.recipients.setUsername(recipientId, username) + } + + if (registeredState == RecipientTable.RegisteredState.REGISTERED) { + SignalDatabase.recipients.markRegistered(recipientId, aci ?: pni!!) + } else if (registeredState == RecipientTable.RegisteredState.NOT_REGISTERED) { + SignalDatabase.recipients.markUnregistered(recipientId) + } + + if (profileKey != null) { + SignalDatabase.recipients.setProfileKey(recipientId, profileKey) + } + + SignalDatabase.recipients.setProfileSharing(recipientId, profileSharing) + SignalDatabase.recipients.setHideStory(recipientId, hideStory) + + if (hidden) { + SignalDatabase.recipients.markHidden(recipientId) + } + + return recipientId + } + + private inner class IndividualChatCreator( + private val db: SQLiteDatabase, + private val recipientId: RecipientId, + private val threadId: Long + ) { + fun standardMessage( + outgoing: Boolean, + sentTimestamp: Long = System.currentTimeMillis(), + receivedTimestamp: Long = if (outgoing) sentTimestamp else sentTimestamp + 1, + serverTimestamp: Long = sentTimestamp, + body: String? = null, + read: Boolean = true, + quotes: Long? = null, + quoteTargetMissing: Boolean = false, + randomMention: Boolean = false, + randomStyling: Boolean = false + ): Long { + return db.insertMessage( + from = if (outgoing) Recipient.self().id else recipientId, + to = if (outgoing) recipientId else Recipient.self().id, + outgoing = outgoing, + threadId = threadId, + sentTimestamp = sentTimestamp, + receivedTimestamp = receivedTimestamp, + serverTimestamp = serverTimestamp, + body = body, + read = read, + quotes = quotes, + quoteTargetMissing = quoteTargetMissing, + randomMention = randomMention, + randomStyling = randomStyling + ) + } + + fun remoteDeletedMessage( + outgoing: Boolean, + sentTimestamp: Long = System.currentTimeMillis(), + receivedTimestamp: Long = if (outgoing) sentTimestamp else sentTimestamp + 1, + serverTimestamp: Long = sentTimestamp + ): Long { + return db.insertMessage( + from = if (outgoing) Recipient.self().id else recipientId, + to = if (outgoing) recipientId else Recipient.self().id, + outgoing = outgoing, + threadId = threadId, + sentTimestamp = sentTimestamp, + receivedTimestamp = receivedTimestamp, + serverTimestamp = serverTimestamp, + remoteDeleted = true + ) + } + } + + private fun SQLiteDatabase.insertMessage( + from: RecipientId, + to: RecipientId, + outgoing: Boolean, + threadId: Long, + sentTimestamp: Long = System.currentTimeMillis(), + receivedTimestamp: Long = if (outgoing) sentTimestamp else sentTimestamp + 1, + serverTimestamp: Long = sentTimestamp, + body: String? = null, + read: Boolean = true, + quotes: Long? = null, + quoteTargetMissing: Boolean = false, + randomMention: Boolean = false, + randomStyling: Boolean = false, + remoteDeleted: Boolean = false + ): Long { + val type = if (outgoing) { + MessageTypes.BASE_SENT_TYPE + } else { + MessageTypes.BASE_INBOX_TYPE + } or MessageTypes.SECURE_MESSAGE_BIT or MessageTypes.PUSH_MESSAGE_BIT + + val contentValues = ContentValues() + contentValues.put(MessageTable.DATE_SENT, sentTimestamp) + contentValues.put(MessageTable.DATE_RECEIVED, receivedTimestamp) + contentValues.put(MessageTable.FROM_RECIPIENT_ID, from.serialize()) + contentValues.put(MessageTable.TO_RECIPIENT_ID, to.serialize()) + contentValues.put(MessageTable.THREAD_ID, threadId) + contentValues.put(MessageTable.BODY, body) + contentValues.put(MessageTable.TYPE, type) + contentValues.put(MessageTable.READ, if (read) 1 else 0) + + if (!outgoing) { + contentValues.put(MessageTable.DATE_SERVER, serverTimestamp) + } + + if (remoteDeleted) { + contentValues.put(MessageTable.REMOTE_DELETED, 1) + return this + .insertInto(MessageTable.TABLE_NAME) + .values(contentValues) + .run() + } + + if (quotes != null) { + val quoteDetails = this.getQuoteDetailsFor(quotes) + contentValues.put(MessageTable.QUOTE_ID, if (quoteTargetMissing) MessageTable.QUOTE_TARGET_MISSING_ID else quoteDetails.quotedSentTimestamp) + contentValues.put(MessageTable.QUOTE_AUTHOR, quoteDetails.authorId.serialize()) + contentValues.put(MessageTable.QUOTE_BODY, quoteDetails.body) + contentValues.put(MessageTable.QUOTE_BODY_RANGES, quoteDetails.bodyRanges) + contentValues.put(MessageTable.QUOTE_TYPE, quoteDetails.type) + contentValues.put(MessageTable.QUOTE_MISSING, quoteTargetMissing.toInt()) + } + + if (body != null && (randomMention || randomStyling)) { + val ranges: MutableList = mutableListOf() + + if (randomMention) { + ranges += BodyRangeList.BodyRange( + start = 0, + length = Random.nextInt(body.length), + mentionUuid = if (outgoing) Recipient.resolved(to).requireAci().toString() else Recipient.resolved(from).requireAci().toString() + ) + } + + if (randomStyling) { + ranges += BodyRangeList.BodyRange( + start = 0, + length = Random.nextInt(body.length), + style = BodyRangeList.BodyRange.Style.fromValue(Random.nextInt(BodyRangeList.BodyRange.Style.values().size)) + ) + } + + contentValues.put(MessageTable.MESSAGE_RANGES, BodyRangeList(ranges = ranges).encode()) + } + + return this + .insertInto(MessageTable.TABLE_NAME) + .values(contentValues) + .run() + } + + private fun assertDatabaseMatches(expected: DatabaseData, actual: DatabaseData) { + assert(expected.keys.size == actual.keys.size) { "Mismatched table count! Expected: ${expected.keys} || Actual: ${actual.keys}" } + assert(expected.keys.containsAll(actual.keys)) { "Table names differ! Expected: ${expected.keys} || Actual: ${actual.keys}" } + + val tablesToCheck = expected.keys.filter { !IGNORED_TABLES.contains(it) } + + for (table in tablesToCheck) { + val expectedTable: List> = expected[table]!! + val actualTable: List> = actual[table]!! + + assert(expectedTable.size == actualTable.size) { "Mismatched number of rows for table '$table'! Expected: ${expectedTable.size} || Actual: ${actualTable.size}\n $actualTable" } + + val expectedFiltered: List> = expectedTable.withoutExcludedColumns(IGNORED_COLUMNS[table]) + val actualFiltered: List> = actualTable.withoutExcludedColumns(IGNORED_COLUMNS[table]) + + assert(contentEquals(expectedFiltered, actualFiltered)) { "Data did not match for table '$table'!\n${prettyDiff(expectedFiltered, actualFiltered)}" } + } + } + + private fun contentEquals(expectedRows: List>, actualRows: List>): Boolean { + if (expectedRows == actualRows) { + return true + } + + assert(expectedRows.size == actualRows.size) + + for (i in expectedRows.indices) { + val expectedRow = expectedRows[i] + val actualRow = actualRows[i] + + for (key in expectedRow.keys) { + val expectedValue = expectedRow[key] + val actualValue = actualRow[key] + + if (!contentEquals(expectedValue, actualValue)) { + return false + } + } + } + + return true + } + + private fun contentEquals(lhs: Any?, rhs: Any?): Boolean { + return if (lhs is ByteArray && rhs is ByteArray) { + lhs.contentEquals(rhs) + } else { + lhs == rhs + } + } + + private fun prettyDiff(expectedRows: List>, actualRows: List>): String { + val builder = StringBuilder() + + assert(expectedRows.size == actualRows.size) + + for (i in expectedRows.indices) { + val expectedRow = expectedRows[i] + val actualRow = actualRows[i] + var describedRow = false + + for (key in expectedRow.keys) { + val expectedValue = expectedRow[key] + val actualValue = actualRow[key] + + if (!contentEquals(expectedValue, actualValue)) { + if (!describedRow) { + builder.append("-- ROW ${i + 1}\n") + describedRow = true + } + builder.append("  [$key] Expected: ${expectedValue.prettyPrint()} || Actual: ${actualValue.prettyPrint()} \n") + } + } + + if (describedRow) { + builder.append("\n") + builder.append("Expected: $expectedRow\n") + builder.append("Actual: $actualRow\n") + } + } + + return builder.toString() + } + + private fun Any?.prettyPrint(): String { + return when (this) { + is ByteArray -> "Bytes(${Hex.toString(this)})" + else -> this.toString() + } + } + + private fun List>.withoutExcludedColumns(ignored: Set?): List> { + return if (ignored != null) { + this.map { row -> + row.filterKeys { !ignored.contains(it) } + } + } else { + this + } + } + + private fun SQLiteDatabase.getQuoteDetailsFor(messageId: Long): QuoteDetails { + return this + .select( + MessageTable.DATE_SENT, + MessageTable.FROM_RECIPIENT_ID, + MessageTable.BODY, + MessageTable.MESSAGE_RANGES + ) + .from(MessageTable.TABLE_NAME) + .where("${MessageTable.ID} = ?", messageId) + .run() + .readToSingleObject { cursor -> + QuoteDetails( + quotedSentTimestamp = cursor.requireLong(MessageTable.DATE_SENT), + authorId = RecipientId.from(cursor.requireLong(MessageTable.FROM_RECIPIENT_ID)), + body = cursor.requireString(MessageTable.BODY), + bodyRanges = cursor.requireBlob(MessageTable.MESSAGE_RANGES), + type = QuoteModel.Type.NORMAL.code + ) + }!! + } + + private fun SQLiteDatabase.readAllContents(): DatabaseData { + return SqlUtil.getAllTables(this).associateWith { table -> this.getAllTableData(table) } + } + + private fun SQLiteDatabase.getAllTableData(table: String): List> { + return this + .select() + .from(table) + .run() + .readToList { cursor -> + val map: MutableMap = mutableMapOf() + + for (i in 0 until cursor.columnCount) { + val column = cursor.getColumnName(i) + + when (cursor.getType(i)) { + Cursor.FIELD_TYPE_INTEGER -> map[column] = cursor.getInt(i) + Cursor.FIELD_TYPE_FLOAT -> map[column] = cursor.getFloat(i) + Cursor.FIELD_TYPE_STRING -> map[column] = cursor.getString(i) + Cursor.FIELD_TYPE_BLOB -> map[column] = cursor.getBlob(i) + Cursor.FIELD_TYPE_NULL -> map[column] = null + } + } + + map + } + } + + private data class QuoteDetails( + val quotedSentTimestamp: Long, + val authorId: RecipientId, + val body: String?, + val bodyRanges: ByteArray?, + val type: Int + ) +} diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/database/AttachmentTableTest.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/database/AttachmentTableTest.kt index 746e429dcb..af351f00ab 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/database/AttachmentTableTest.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/database/AttachmentTableTest.kt @@ -61,8 +61,8 @@ class AttachmentTableTest { false ) - val attachment1Info = SignalDatabase.attachments.getAttachmentDataFileInfo(attachment.attachmentId, AttachmentTable.DATA) - val attachment2Info = SignalDatabase.attachments.getAttachmentDataFileInfo(attachment2.attachmentId, AttachmentTable.DATA) + val attachment1Info = SignalDatabase.attachments.getAttachmentDataFileInfo(attachment.attachmentId, AttachmentTable.DATA_FILE) + val attachment2Info = SignalDatabase.attachments.getAttachmentDataFileInfo(attachment2.attachmentId, AttachmentTable.DATA_FILE) assertNotEquals(attachment1Info, attachment2Info) } @@ -89,8 +89,8 @@ class AttachmentTableTest { true ) - val attachment1Info = SignalDatabase.attachments.getAttachmentDataFileInfo(attachment.attachmentId, AttachmentTable.DATA) - val attachment2Info = SignalDatabase.attachments.getAttachmentDataFileInfo(attachment2.attachmentId, AttachmentTable.DATA) + val attachment1Info = SignalDatabase.attachments.getAttachmentDataFileInfo(attachment.attachmentId, AttachmentTable.DATA_FILE) + val attachment2Info = SignalDatabase.attachments.getAttachmentDataFileInfo(attachment2.attachmentId, AttachmentTable.DATA_FILE) assertNotEquals(attachment1Info, attachment2Info) } @@ -124,9 +124,9 @@ class AttachmentTableTest { SignalDatabase.attachments.updateAttachmentData(standardDatabaseAttachment, createMediaStream(compressedData), false) // THEN - val previousInfo = SignalDatabase.attachments.getAttachmentDataFileInfo(previousDatabaseAttachmentId, AttachmentTable.DATA)!! - val standardInfo = SignalDatabase.attachments.getAttachmentDataFileInfo(standardDatabaseAttachment.attachmentId, AttachmentTable.DATA)!! - val highInfo = SignalDatabase.attachments.getAttachmentDataFileInfo(highDatabaseAttachment.attachmentId, AttachmentTable.DATA)!! + val previousInfo = SignalDatabase.attachments.getAttachmentDataFileInfo(previousDatabaseAttachmentId, AttachmentTable.DATA_FILE)!! + val standardInfo = SignalDatabase.attachments.getAttachmentDataFileInfo(standardDatabaseAttachment.attachmentId, AttachmentTable.DATA_FILE)!! + val highInfo = SignalDatabase.attachments.getAttachmentDataFileInfo(highDatabaseAttachment.attachmentId, AttachmentTable.DATA_FILE)!! assertNotEquals(standardInfo, highInfo) standardInfo.file assertIs previousInfo.file @@ -158,9 +158,9 @@ class AttachmentTableTest { val secondHighDatabaseAttachment = SignalDatabase.attachments.insertAttachmentForPreUpload(secondHighQualityPreUpload) // THEN - val standardInfo = SignalDatabase.attachments.getAttachmentDataFileInfo(standardDatabaseAttachment.attachmentId, AttachmentTable.DATA)!! - val highInfo = SignalDatabase.attachments.getAttachmentDataFileInfo(highDatabaseAttachment.attachmentId, AttachmentTable.DATA)!! - val secondHighInfo = SignalDatabase.attachments.getAttachmentDataFileInfo(secondHighDatabaseAttachment.attachmentId, AttachmentTable.DATA)!! + val standardInfo = SignalDatabase.attachments.getAttachmentDataFileInfo(standardDatabaseAttachment.attachmentId, AttachmentTable.DATA_FILE)!! + val highInfo = SignalDatabase.attachments.getAttachmentDataFileInfo(highDatabaseAttachment.attachmentId, AttachmentTable.DATA_FILE)!! + val secondHighInfo = SignalDatabase.attachments.getAttachmentDataFileInfo(secondHighDatabaseAttachment.attachmentId, AttachmentTable.DATA_FILE)!! highInfo.file assertIsNot standardInfo.file secondHighInfo.file assertIs highInfo.file diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/database/CallTableTest.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/database/CallTableTest.kt index 19254bbfc4..38c63ce8b5 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/database/CallTableTest.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/database/CallTableTest.kt @@ -214,6 +214,175 @@ class CallTableTest { assertEquals(CallTable.Event.JOINED, acceptedCall?.event) } + @Test + fun givenAnOutgoingRingCall_whenIAcceptedOutgoingGroupCall_thenIExpectOutgoingRing() { + val callId = 1L + SignalDatabase.calls.insertAcceptedGroupCall( + callId = callId, + recipientId = groupRecipientId, + direction = CallTable.Direction.OUTGOING, + timestamp = 1 + ) + + val call = SignalDatabase.calls.getCallById(callId, groupRecipientId) + assertNotNull(call) + assertEquals(CallTable.Event.OUTGOING_RING, call?.event) + + SignalDatabase.calls.acceptOutgoingGroupCall( + call!! + ) + + val acceptedCall = SignalDatabase.calls.getCallById(callId, groupRecipientId) + assertEquals(CallTable.Event.OUTGOING_RING, acceptedCall?.event) + } + + @Test + fun givenARingingCall_whenIAcceptedOutgoingGroupCall_thenIExpectAccepted() { + val callId = 1L + SignalDatabase.calls.insertOrUpdateGroupCallFromRingState( + ringId = callId, + groupRecipientId = groupRecipientId, + ringerRecipient = harness.others[1], + dateReceived = System.currentTimeMillis(), + ringState = CallManager.RingUpdate.REQUESTED + ) + + val call = SignalDatabase.calls.getCallById(callId, groupRecipientId) + assertNotNull(call) + assertEquals(CallTable.Event.RINGING, call?.event) + + SignalDatabase.calls.acceptOutgoingGroupCall( + call!! + ) + + val acceptedCall = SignalDatabase.calls.getCallById(callId, groupRecipientId) + assertEquals(CallTable.Event.ACCEPTED, acceptedCall?.event) + } + + @Test + fun givenAMissedCall_whenIAcceptedOutgoingGroupCall_thenIExpectAccepted() { + val callId = 1L + SignalDatabase.calls.insertOrUpdateGroupCallFromRingState( + ringId = callId, + groupRecipientId = groupRecipientId, + ringerRecipient = harness.others[1], + dateReceived = System.currentTimeMillis(), + ringState = CallManager.RingUpdate.EXPIRED_REQUEST + ) + + val call = SignalDatabase.calls.getCallById(callId, groupRecipientId) + assertNotNull(call) + assertEquals(CallTable.Event.MISSED, call?.event) + + SignalDatabase.calls.acceptOutgoingGroupCall( + call!! + ) + + val acceptedCall = SignalDatabase.calls.getCallById(callId, groupRecipientId) + assertEquals(CallTable.Event.ACCEPTED, acceptedCall?.event) + } + + @Test + fun givenADeclinedCall_whenIAcceptedOutgoingGroupCall_thenIExpectAccepted() { + val callId = 1L + SignalDatabase.calls.insertOrUpdateGroupCallFromRingState( + ringId = callId, + groupRecipientId = groupRecipientId, + ringerRecipient = harness.others[1], + dateReceived = System.currentTimeMillis(), + ringState = CallManager.RingUpdate.DECLINED_ON_ANOTHER_DEVICE + ) + + val call = SignalDatabase.calls.getCallById(callId, groupRecipientId) + assertNotNull(call) + assertEquals(CallTable.Event.DECLINED, call?.event) + + SignalDatabase.calls.acceptOutgoingGroupCall( + call!! + ) + + val acceptedCall = SignalDatabase.calls.getCallById(callId, groupRecipientId) + assertEquals(CallTable.Event.ACCEPTED, acceptedCall?.event) + } + + @Test + fun givenAnAcceptedCall_whenIAcceptedOutgoingGroupCall_thenIExpectAccepted() { + val callId = 1L + SignalDatabase.calls.insertOrUpdateGroupCallFromRingState( + ringId = callId, + groupRecipientId = groupRecipientId, + ringerRecipient = harness.others[1], + dateReceived = System.currentTimeMillis(), + ringState = CallManager.RingUpdate.REQUESTED + ) + + val call = SignalDatabase.calls.getCallById(callId, groupRecipientId) + assertNotNull(call) + assertEquals(CallTable.Event.RINGING, call?.event) + + SignalDatabase.calls.acceptIncomingGroupCall( + call!! + ) + + SignalDatabase.calls.acceptOutgoingGroupCall( + SignalDatabase.calls.getCallById(callId, groupRecipientId)!! + ) + + val acceptedCall = SignalDatabase.calls.getCallById(callId, groupRecipientId) + assertEquals(CallTable.Event.ACCEPTED, acceptedCall?.event) + } + + @Test + fun givenAGenericGroupCall_whenIAcceptedOutgoingGroupCall_thenIExpectOutgoingRing() { + val era = "aaa" + val callId = CallId.fromEra(era).longValue() + SignalDatabase.calls.insertOrUpdateGroupCallFromLocalEvent( + groupRecipientId = groupRecipientId, + sender = harness.others[1], + timestamp = System.currentTimeMillis(), + peekGroupCallEraId = "aaa", + peekJoinedUuids = emptyList(), + isCallFull = false + ) + + val call = SignalDatabase.calls.getCallById(callId, groupRecipientId) + assertNotNull(call) + assertEquals(CallTable.Event.GENERIC_GROUP_CALL, call?.event) + + SignalDatabase.calls.acceptOutgoingGroupCall( + call!! + ) + + val acceptedCall = SignalDatabase.calls.getCallById(callId, groupRecipientId) + assertEquals(CallTable.Event.OUTGOING_RING, acceptedCall?.event) + } + + @Test + fun givenAJoinedGroupCall_whenIAcceptedOutgoingGroupCall_thenIExpectOutgoingRing() { + val era = "aaa" + val callId = CallId.fromEra(era).longValue() + SignalDatabase.calls.insertOrUpdateGroupCallFromLocalEvent( + groupRecipientId = groupRecipientId, + sender = harness.others[1], + timestamp = System.currentTimeMillis(), + peekGroupCallEraId = "aaa", + peekJoinedUuids = emptyList(), + isCallFull = false + ) + + val call = SignalDatabase.calls.getCallById(callId, groupRecipientId) + assertNotNull(call) + assertEquals(CallTable.Event.GENERIC_GROUP_CALL, call?.event) + + SignalDatabase.calls.acceptIncomingGroupCall( + call!! + ) + + SignalDatabase.calls.acceptOutgoingGroupCall(SignalDatabase.calls.getCallById(callId, groupRecipientId)!!) + val acceptedCall = SignalDatabase.calls.getCallById(callId, groupRecipientId) + assertEquals(CallTable.Event.OUTGOING_RING, acceptedCall?.event) + } + @Test fun givenNoPriorCallEvent_whenIReceiveAGroupCallUpdateMessage_thenIExpectAGenericGroupCall() { val era = "aaa" diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/dependencies/InstrumentationApplicationDependencyProvider.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/dependencies/InstrumentationApplicationDependencyProvider.kt index 72cd218af3..59c80b39f3 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/dependencies/InstrumentationApplicationDependencyProvider.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/dependencies/InstrumentationApplicationDependencyProvider.kt @@ -81,7 +81,8 @@ class InstrumentationApplicationDependencyProvider(application: Application, def proxySelector = Network.proxySelectorForSocks, dns = Network.dns, zkGroupServerPublicParams = Base64.decode(BuildConfig.ZKGROUP_SERVER_PUBLIC_PARAMS), - genericServerPublicParams = Base64.decode(BuildConfig.GENERIC_SERVER_PUBLIC_PARAMS) + genericServerPublicParams = Base64.decode(BuildConfig.GENERIC_SERVER_PUBLIC_PARAMS), + backupServerPublicParams = Base64.decode(BuildConfig.BACKUP_SERVER_PUBLIC_PARAMS) ) serviceNetworkAccessMock = mock { diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/messages/EditMessageSyncProcessorTest.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/messages/EditMessageSyncProcessorTest.kt index 82bb5ccc70..6875886c7c 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/messages/EditMessageSyncProcessorTest.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/messages/EditMessageSyncProcessorTest.kt @@ -39,7 +39,6 @@ class EditMessageSyncProcessorTest { ) private val IGNORE_ATTACHMENT_COLUMNS = listOf( - AttachmentTable.UNIQUE_ID, AttachmentTable.TRANSFER_FILE ) } diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/profiles/manage/UsernameEditFragmentTest.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/profiles/manage/UsernameEditFragmentTest.kt index 47de2aa06c..c545416a14 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/profiles/manage/UsernameEditFragmentTest.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/profiles/manage/UsernameEditFragmentTest.kt @@ -33,6 +33,7 @@ import org.thoughtcrime.securesms.testing.SignalActivityRule import org.thoughtcrime.securesms.testing.assertIsNotNull import org.thoughtcrime.securesms.testing.assertIsNull import org.thoughtcrime.securesms.testing.success +import org.whispersystems.signalservice.api.util.Usernames import org.whispersystems.signalservice.internal.push.ReserveUsernameResponse import java.util.concurrent.TimeUnit @@ -96,7 +97,7 @@ class UsernameEditFragmentTest { fun testNicknameUpdateHappyPath() { val nickname = "Spiderman" val discriminator = "4578" - val username = "$nickname${UsernameState.DELIMITER}$discriminator" + val username = "$nickname${Usernames.DELIMITER}$discriminator" InstrumentationApplicationDependencyProvider.addMockWebRequestHandlers( Put("/v1/accounts/username/reserved") { diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/testing/BobClient.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/testing/BobClient.kt index 49d7b78ba2..b79713ff46 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/testing/BobClient.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/testing/BobClient.kt @@ -165,7 +165,7 @@ class BobClient(val serviceId: ServiceId, val e164: String, val identityKeyPair: override fun storeSenderKey(sender: SignalProtocolAddress?, distributionId: UUID?, record: SenderKeyRecord?) = throw UnsupportedOperationException() override fun loadSenderKey(sender: SignalProtocolAddress?, distributionId: UUID?): SenderKeyRecord = throw UnsupportedOperationException() override fun archiveSession(address: SignalProtocolAddress?) = throw UnsupportedOperationException() - override fun getAllAddressesWithActiveSessions(addressNames: MutableList?): MutableSet = throw UnsupportedOperationException() + override fun getAllAddressesWithActiveSessions(addressNames: MutableList?): MutableMap = throw UnsupportedOperationException() override fun getSenderKeySharedWith(distributionId: DistributionId?): MutableSet = throw UnsupportedOperationException() override fun markSenderKeySharedWith(distributionId: DistributionId?, addresses: MutableCollection?) = throw UnsupportedOperationException() override fun clearSenderKeySharedWith(addresses: MutableCollection?) = throw UnsupportedOperationException() diff --git a/app/src/debug/java/org/thoughtcrime/securesms/components/settings/app/internal/conversation/test/ConversationElementGenerator.kt b/app/src/debug/java/org/thoughtcrime/securesms/components/settings/app/internal/conversation/test/ConversationElementGenerator.kt index 9fb49d98ef..2a0b90f4ce 100644 --- a/app/src/debug/java/org/thoughtcrime/securesms/components/settings/app/internal/conversation/test/ConversationElementGenerator.kt +++ b/app/src/debug/java/org/thoughtcrime/securesms/components/settings/app/internal/conversation/test/ConversationElementGenerator.kt @@ -117,7 +117,8 @@ class ConversationElementGenerator { -1, null, null, - 0 + 0, + false ) val conversationMessage = ConversationMessageFactory.createWithUnresolvedData( diff --git a/app/src/gms/AndroidManifest.xml b/app/src/gms/AndroidManifest.xml index b7129ff375..1e3d567b48 100644 --- a/app/src/gms/AndroidManifest.xml +++ b/app/src/gms/AndroidManifest.xml @@ -1,10 +1,12 @@ - + + android:exported="true" + tools:ignore="ExportedService"> @@ -17,10 +19,6 @@ - - diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e2623e3182..9e9d03f4f4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -58,8 +58,6 @@ - - @@ -106,6 +104,9 @@ + + @@ -938,7 +939,7 @@ android:exported="false"/> (Landroid/content/Context;Landroid/util/AttributeSet;I)V -HPLandroidx/appcompat/view/menu/ActionMenuItemView;->initialize(Landroidx/appcompat/view/menu/MenuItemImpl;I)V -HPLandroidx/appcompat/view/menu/ActionMenuItemView;->onMeasure(II)V -HPLandroidx/appcompat/view/menu/ActionMenuItemView;->setIcon(Landroid/graphics/drawable/Drawable;)V -HPLandroidx/appcompat/view/menu/ActionMenuItemView;->setPopupCallback(Landroidx/appcompat/view/menu/ActionMenuItemView$PopupCallback;)V -HPLandroidx/appcompat/view/menu/ActionMenuItemView;->setTitle(Ljava/lang/CharSequence;)V -HPLandroidx/appcompat/view/menu/ActionMenuItemView;->shouldAllowTextWithIcon()Z -HPLandroidx/appcompat/view/menu/ActionMenuItemView;->updateTextButtonVisibility()V -HPLandroidx/appcompat/view/menu/BaseMenuPresenter;->createItemView(Landroid/view/ViewGroup;)Landroidx/appcompat/view/menu/MenuView$ItemView; -HPLandroidx/appcompat/view/menu/BaseMenuPresenter;->getItemView(Landroidx/appcompat/view/menu/MenuItemImpl;Landroid/view/View;Landroid/view/ViewGroup;)Landroid/view/View; -HPLandroidx/appcompat/view/menu/MenuItemImpl;->applyIconTintIfNecessary(Landroid/graphics/drawable/Drawable;)Landroid/graphics/drawable/Drawable; -HPLandroidx/appcompat/view/menu/MenuItemImpl;->getActionView()Landroid/view/View; -HPLandroidx/appcompat/view/menu/MenuItemImpl;->getContentDescription()Ljava/lang/CharSequence; -HPLandroidx/appcompat/view/menu/MenuItemImpl;->getIcon()Landroid/graphics/drawable/Drawable; -HPLandroidx/appcompat/view/menu/MenuItemImpl;->getSupportActionProvider()Landroidx/core/view/ActionProvider; -HPLandroidx/appcompat/view/menu/MenuItemImpl;->getTitle()Ljava/lang/CharSequence; -HPLandroidx/appcompat/view/menu/MenuItemImpl;->getTitleForItemView(Landroidx/appcompat/view/menu/MenuView$ItemView;)Ljava/lang/CharSequence; -HPLandroidx/appcompat/view/menu/MenuItemImpl;->getTooltipText()Ljava/lang/CharSequence; -HPLandroidx/appcompat/view/menu/MenuItemImpl;->isEnabled()Z -HPLandroidx/appcompat/view/menu/MenuItemImpl;->showsTextAsAction()Z -HPLandroidx/appcompat/widget/ActionMenuPresenter;->bindItemView(Landroidx/appcompat/view/menu/MenuItemImpl;Landroidx/appcompat/view/menu/MenuView$ItemView;)V -HPLandroidx/appcompat/widget/ActionMenuPresenter;->getItemView(Landroidx/appcompat/view/menu/MenuItemImpl;Landroid/view/View;Landroid/view/ViewGroup;)Landroid/view/View; -HPLandroidx/appcompat/widget/ActionMenuView;->generateLayoutParams(Landroid/util/AttributeSet;)Landroidx/appcompat/widget/ActionMenuView$LayoutParams; -HPLandroidx/appcompat/widget/SearchView;->(Landroid/content/Context;Landroid/util/AttributeSet;I)V -HPLandroidx/core/view/ViewGroupKt$descendants$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation; +HPLandroidx/appcompat/app/AppCompatViewInflater;->themifyContext(Landroid/content/Context;Landroid/util/AttributeSet;ZZ)Landroid/content/Context; HPLandroidx/core/view/ViewGroupKt$descendants$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object; HPLandroidx/core/view/ViewGroupKt;->getDescendants(Landroid/view/ViewGroup;)Lkotlin/sequences/Sequence; +HPLandroidx/customview/poolingcontainer/PoolingContainer;->callPoolingContainerOnRelease(Landroid/view/View;)V +HPLandroidx/customview/poolingcontainer/PoolingContainer;->getPoolingContainerListenerHolder(Landroid/view/View;)Landroidx/customview/poolingcontainer/PoolingContainerListenerHolder; HPLandroidx/customview/poolingcontainer/PoolingContainerListenerHolder;->()V -HPLandroidx/fragment/app/FragmentManager;->saveAllStateInternal()Landroid/os/Bundle; -HPLandroidx/fragment/app/FragmentStateManager;->saveState()Landroid/os/Bundle; +HPLandroidx/customview/poolingcontainer/PoolingContainerListenerHolder;->onRelease()V HPLandroidx/recyclerview/widget/ViewInfoStore;->addToPreLayout(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;Landroidx/recyclerview/widget/RecyclerView$ItemAnimator$ItemHolderInfo;)V HPLandroidx/recyclerview/widget/ViewInfoStore;->popFromLayoutStep(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;I)Landroidx/recyclerview/widget/RecyclerView$ItemAnimator$ItemHolderInfo; -HPLandroidx/savedstate/SavedStateRegistry;->performSave(Landroid/os/Bundle;)V -HPLcom/fasterxml/jackson/core/base/ParserBase;->_parseNumericValue(I)V -HPLcom/fasterxml/jackson/core/json/JsonReadContext;->(Lcom/fasterxml/jackson/core/json/JsonReadContext;Lcom/fasterxml/jackson/core/json/DupDetector;III)V -HPLj$/util/Optional;->(Ljava/lang/Object;)V +HPLcom/fasterxml/jackson/databind/deser/BasicDeserializerFactory;->createMapDeserializer(Lcom/fasterxml/jackson/databind/DeserializationContext;Lcom/fasterxml/jackson/databind/type/MapType;Lcom/fasterxml/jackson/databind/BeanDescription;)Lcom/fasterxml/jackson/databind/JsonDeserializer; +HPLcom/fasterxml/jackson/databind/deser/std/MapDeserializer;->deserialize(Lcom/fasterxml/jackson/core/JsonParser;Lcom/fasterxml/jackson/databind/DeserializationContext;)Ljava/util/Map; +HPLcom/squareup/wire/internal/Internal;->countNonNull(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;)I +HPLkotlin/collections/AbstractList$IteratorImpl;->next()Ljava/lang/Object; +HPLkotlin/collections/CollectionsKt__IterablesKt;->collectionSizeOrDefault(Ljava/lang/Iterable;I)I HPLkotlin/sequences/SequenceBuilderIterator;->yieldAll(Ljava/util/Iterator;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; -HPLkotlin/sequences/SequencesKt__SequenceBuilderKt;->sequence(Lkotlin/jvm/functions/Function2;)Lkotlin/sequences/Sequence; -HPLkotlin/text/StringsKt__StringsKt;->indexOf(Ljava/lang/CharSequence;CIZ)I -HPLnet/zetetic/database/sqlcipher/SQLiteConnection;->bindArguments(Lnet/zetetic/database/sqlcipher/SQLiteConnection$PreparedStatement;[Ljava/lang/Object;)V -HPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->beginTransaction(Lnet/zetetic/database/sqlcipher/SQLiteTransactionListener;Z)V -HPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->getPath()Ljava/lang/String; -HPLnet/zetetic/database/sqlcipher/SQLiteStatementInfo;->()V -HPLorg/conscrypt/OpenSSLCipher;->engineGetOutputSize(I)I -HPLorg/signal/libsignal/protocol/ecc/ECPublicKey;->equals(Ljava/lang/Object;)Z -HPLorg/thoughtcrime/securesms/components/ScrollToPositionDelegate;->isListCommitted()Z +HPLkotlin/sequences/SequenceScope;->yieldAll(Lkotlin/sequences/Sequence;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +HPLnet/zetetic/database/sqlcipher/SQLiteProgram;->clearBindings()V +HPLnet/zetetic/database/sqlcipher/SQLiteProgram;->getSession()Lnet/zetetic/database/sqlcipher/SQLiteSession; +HPLorg/signal/core/util/concurrent/SignalExecutors;->lambda$newCachedBoundedExecutor$1(Ljava/lang/Runnable;Ljava/util/concurrent/ThreadPoolExecutor;)V +HPLorg/thoughtcrime/securesms/attachments/DatabaseAttachment;->(Lorg/thoughtcrime/securesms/attachments/AttachmentId;JZZLjava/lang/String;IJLjava/lang/String;ILjava/lang/String;Ljava/lang/String;[B[BILjava/lang/String;ZZZIIZLjava/lang/String;Lorg/thoughtcrime/securesms/stickers/StickerLocator;Lorg/thoughtcrime/securesms/blurhash/BlurHash;Lorg/thoughtcrime/securesms/audio/AudioHash;Lorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties;IJ)V HPLorg/thoughtcrime/securesms/conversation/mutiselect/ConversationItemAnimator;->animateChange(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;Landroidx/recyclerview/widget/RecyclerView$ViewHolder;Landroidx/recyclerview/widget/RecyclerView$ItemAnimator$ItemHolderInfo;Landroidx/recyclerview/widget/RecyclerView$ItemAnimator$ItemHolderInfo;)Z HPLorg/thoughtcrime/securesms/conversation/mutiselect/ConversationItemAnimator;->animatePersistence(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;Landroidx/recyclerview/widget/RecyclerView$ItemAnimator$ItemHolderInfo;Landroidx/recyclerview/widget/RecyclerView$ItemAnimator$ItemHolderInfo;)Z HPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$initializeConversationThreadUi$6;->invoke()Ljava/lang/Boolean; -HPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$canShowAsBubble$1;->apply(Lorg/thoughtcrime/securesms/recipients/Recipient;)Ljava/lang/Boolean; HPLorg/thoughtcrime/securesms/conversation/v2/IdentityRecordsState;->equals(Ljava/lang/Object;)Z -HPLorg/thoughtcrime/securesms/database/EmojiSearchTable$setSearchIndex$1;->invoke(Lorg/thoughtcrime/securesms/database/SQLiteDatabase;)V +HPLorg/thoughtcrime/securesms/conversation/v2/InputReadyState;->equals(Ljava/lang/Object;)Z +HPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$unregisterObserver$17(Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HPLorg/thoughtcrime/securesms/database/model/IdentityRecord;->equals(Ljava/lang/Object;)Z -HPLorg/thoughtcrime/securesms/database/model/LogEntry;->(JZLjava/lang/String;)V +HPLorg/thoughtcrime/securesms/database/model/ThreadRecord;->equals(Ljava/lang/Object;)Z +HPLorg/thoughtcrime/securesms/logging/PersistentLogger$LogRequest;->getCreateTime()J HPLorg/thoughtcrime/securesms/mms/Slide;->equals(Ljava/lang/Object;)Z -HPLorg/thoughtcrime/securesms/util/BubbleUtil;->canBubble(Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/Recipient;Ljava/lang/Long;)Z +HPLorg/thoughtcrime/securesms/profiles/ProfileName;->equals(Ljava/lang/Object;)Z +HPLorg/thoughtcrime/securesms/recipients/Recipient;->hasSameContent(Lorg/thoughtcrime/securesms/recipients/Recipient;)Z HSPLandroid/support/v4/media/MediaBrowserCompat$MediaBrowserImplApi21$$ExternalSyntheticThrowCCEIfNotNull0;->m(Ljava/lang/Object;)V HSPLandroid/support/v4/media/session/IMediaSession$Stub;->()V HSPLandroid/support/v4/media/session/MediaControllerCompat$MediaControllerImplApi21;->(Landroid/content/Context;Landroid/support/v4/media/session/MediaSessionCompat$Token;)V @@ -128,6 +104,7 @@ HSPLandroidx/activity/ComponentActivity$$ExternalSyntheticLambda0;->(Landr HSPLandroidx/activity/ComponentActivity$$ExternalSyntheticLambda0;->run()V HSPLandroidx/activity/ComponentActivity$$ExternalSyntheticLambda1;->(Landroidx/activity/ComponentActivity;)V HSPLandroidx/activity/ComponentActivity$$ExternalSyntheticLambda2;->(Landroidx/activity/ComponentActivity;)V +HSPLandroidx/activity/ComponentActivity$$ExternalSyntheticLambda2;->saveState()Landroid/os/Bundle; HSPLandroidx/activity/ComponentActivity$$ExternalSyntheticLambda3;->(Landroidx/activity/ComponentActivity;)V HSPLandroidx/activity/ComponentActivity$$ExternalSyntheticLambda3;->onContextAvailable(Landroid/content/Context;)V HSPLandroidx/activity/ComponentActivity$1;->(Landroidx/activity/ComponentActivity;)V @@ -141,6 +118,7 @@ HSPLandroidx/activity/ComponentActivity$5;->onStateChanged(Landroidx/lifecycle/L HSPLandroidx/activity/ComponentActivity$Api19Impl;->cancelPendingInputEvents(Landroid/view/View;)V HSPLandroidx/activity/ComponentActivity$ReportFullyDrawnExecutorApi16Impl;->(Landroidx/activity/ComponentActivity;)V HSPLandroidx/activity/ComponentActivity$ReportFullyDrawnExecutorApi16Impl;->activityDestroyed()V +HSPLandroidx/activity/ComponentActivity;->$r8$lambda$OnwlVMZzrLePIRy-6IUDTtLLUV0(Landroidx/activity/ComponentActivity;)Landroid/os/Bundle; HSPLandroidx/activity/ComponentActivity;->$r8$lambda$h2i_RK2mddCIbAsGubaI4eL8_cU(Landroidx/activity/ComponentActivity;Landroid/content/Context;)V HSPLandroidx/activity/ComponentActivity;->()V HSPLandroidx/activity/ComponentActivity;->addMenuProvider(Landroidx/core/view/MenuProvider;)V @@ -160,10 +138,12 @@ HSPLandroidx/activity/ComponentActivity;->getOnBackPressedDispatcher()Landroidx/ HSPLandroidx/activity/ComponentActivity;->getSavedStateRegistry()Landroidx/savedstate/SavedStateRegistry; HSPLandroidx/activity/ComponentActivity;->getViewModelStore()Landroidx/lifecycle/ViewModelStore; HSPLandroidx/activity/ComponentActivity;->invalidateMenu()V +HSPLandroidx/activity/ComponentActivity;->lambda$new$1()Landroid/os/Bundle; HSPLandroidx/activity/ComponentActivity;->lambda$new$2(Landroid/content/Context;)V HSPLandroidx/activity/ComponentActivity;->onCreate(Landroid/os/Bundle;)V HSPLandroidx/activity/ComponentActivity;->onCreatePanelMenu(ILandroid/view/Menu;)Z HSPLandroidx/activity/ComponentActivity;->onPreparePanel(ILandroid/view/View;Landroid/view/Menu;)Z +HSPLandroidx/activity/ComponentActivity;->onSaveInstanceState(Landroid/os/Bundle;)V HSPLandroidx/activity/ComponentActivity;->removeMenuProvider(Landroidx/core/view/MenuProvider;)V HSPLandroidx/activity/ComponentActivity;->removeOnConfigurationChangedListener(Landroidx/core/util/Consumer;)V HSPLandroidx/activity/ComponentActivity;->removeOnMultiWindowModeChangedListener(Landroidx/core/util/Consumer;)V @@ -204,6 +184,7 @@ HSPLandroidx/activity/result/ActivityResultRegistry$LifecycleContainer;->addObse HSPLandroidx/activity/result/ActivityResultRegistry;->()V HSPLandroidx/activity/result/ActivityResultRegistry;->bindRcKey(ILjava/lang/String;)V HSPLandroidx/activity/result/ActivityResultRegistry;->generateRandomNumber()I +HSPLandroidx/activity/result/ActivityResultRegistry;->onSaveInstanceState(Landroid/os/Bundle;)V HSPLandroidx/activity/result/ActivityResultRegistry;->register(Ljava/lang/String;Landroidx/activity/result/contract/ActivityResultContract;Landroidx/activity/result/ActivityResultCallback;)Landroidx/activity/result/ActivityResultLauncher; HSPLandroidx/activity/result/ActivityResultRegistry;->register(Ljava/lang/String;Landroidx/lifecycle/LifecycleOwner;Landroidx/activity/result/contract/ActivityResultContract;Landroidx/activity/result/ActivityResultCallback;)Landroidx/activity/result/ActivityResultLauncher; HSPLandroidx/activity/result/ActivityResultRegistry;->registerKey(Ljava/lang/String;)V @@ -222,6 +203,7 @@ HSPLandroidx/appcompat/app/ActionBar$LayoutParams;->(II)V HSPLandroidx/appcompat/app/ActionBar$LayoutParams;->(Landroid/content/Context;Landroid/util/AttributeSet;)V HSPLandroidx/appcompat/app/ActionBar;->()V HSPLandroidx/appcompat/app/AppCompatActivity$1;->(Landroidx/appcompat/app/AppCompatActivity;)V +HSPLandroidx/appcompat/app/AppCompatActivity$1;->saveState()Landroid/os/Bundle; HSPLandroidx/appcompat/app/AppCompatActivity$2;->(Landroidx/appcompat/app/AppCompatActivity;)V HSPLandroidx/appcompat/app/AppCompatActivity$2;->onContextAvailable(Landroid/content/Context;)V HSPLandroidx/appcompat/app/AppCompatActivity;->()V @@ -313,6 +295,7 @@ HSPLandroidx/appcompat/app/AppCompatDelegateImpl;->onCreateView(Landroid/view/Vi HSPLandroidx/appcompat/app/AppCompatDelegateImpl;->onDestroy()V HSPLandroidx/appcompat/app/AppCompatDelegateImpl;->onPostCreate(Landroid/os/Bundle;)V HSPLandroidx/appcompat/app/AppCompatDelegateImpl;->onPostResume()V +HSPLandroidx/appcompat/app/AppCompatDelegateImpl;->onSaveInstanceState(Landroid/os/Bundle;)V HSPLandroidx/appcompat/app/AppCompatDelegateImpl;->onStart()V HSPLandroidx/appcompat/app/AppCompatDelegateImpl;->onStop()V HSPLandroidx/appcompat/app/AppCompatDelegateImpl;->onSubDecorInstalled(Landroid/view/ViewGroup;)V @@ -337,7 +320,6 @@ HSPLandroidx/appcompat/app/AppCompatViewInflater;->createImageView(Landroid/cont HSPLandroidx/appcompat/app/AppCompatViewInflater;->createTextView(Landroid/content/Context;Landroid/util/AttributeSet;)Landroidx/appcompat/widget/AppCompatTextView; HSPLandroidx/appcompat/app/AppCompatViewInflater;->createView(Landroid/content/Context;Ljava/lang/String;Landroid/util/AttributeSet;)Landroid/view/View; HSPLandroidx/appcompat/app/AppCompatViewInflater;->createView(Landroid/view/View;Ljava/lang/String;Landroid/content/Context;Landroid/util/AttributeSet;ZZZZ)Landroid/view/View; -HSPLandroidx/appcompat/app/AppCompatViewInflater;->themifyContext(Landroid/content/Context;Landroid/util/AttributeSet;ZZ)Landroid/content/Context; HSPLandroidx/appcompat/app/AppCompatViewInflater;->verifyNotNull(Landroid/view/View;Ljava/lang/String;)V HSPLandroidx/appcompat/app/AppLocalesMetadataHolderService$Api24Impl;->getDisabledComponentFlag()I HSPLandroidx/appcompat/app/AppLocalesMetadataHolderService;->getServiceInfo(Landroid/content/Context;)Landroid/content/pm/ServiceInfo; @@ -374,8 +356,10 @@ HSPLandroidx/appcompat/view/ContextThemeWrapper;->isEmptyConfiguration(Landroid/ HSPLandroidx/appcompat/view/ContextThemeWrapper;->onApplyThemeResource(Landroid/content/res/Resources$Theme;IZ)V HSPLandroidx/appcompat/view/SupportMenuInflater$MenuState;->(Landroidx/appcompat/view/SupportMenuInflater;Landroid/view/Menu;)V HSPLandroidx/appcompat/view/SupportMenuInflater$MenuState;->addItem()V +HSPLandroidx/appcompat/view/SupportMenuInflater$MenuState;->addSubMenuItem()Landroid/view/SubMenu; HSPLandroidx/appcompat/view/SupportMenuInflater$MenuState;->getShortcut(Ljava/lang/String;)C HSPLandroidx/appcompat/view/SupportMenuInflater$MenuState;->hasAddedItem()Z +HSPLandroidx/appcompat/view/SupportMenuInflater$MenuState;->newInstance(Ljava/lang/String;[Ljava/lang/Class;[Ljava/lang/Object;)Ljava/lang/Object; HSPLandroidx/appcompat/view/SupportMenuInflater$MenuState;->readItem(Landroid/util/AttributeSet;)V HSPLandroidx/appcompat/view/SupportMenuInflater$MenuState;->resetGroup()V HSPLandroidx/appcompat/view/SupportMenuInflater$MenuState;->setItem(Landroid/view/MenuItem;)V @@ -394,7 +378,25 @@ HSPLandroidx/appcompat/view/WindowCallbackWrapper;->onPreparePanel(ILandroid/vie HSPLandroidx/appcompat/view/WindowCallbackWrapper;->onWindowAttributesChanged(Landroid/view/WindowManager$LayoutParams;)V HSPLandroidx/appcompat/view/WindowCallbackWrapper;->onWindowFocusChanged(Z)V HSPLandroidx/appcompat/view/menu/ActionMenuItem;->(Landroid/content/Context;IIIILjava/lang/CharSequence;)V +HSPLandroidx/appcompat/view/menu/ActionMenuItemView$PopupCallback;->()V +HSPLandroidx/appcompat/view/menu/ActionMenuItemView;->(Landroid/content/Context;Landroid/util/AttributeSet;)V +HSPLandroidx/appcompat/view/menu/ActionMenuItemView;->(Landroid/content/Context;Landroid/util/AttributeSet;I)V +HSPLandroidx/appcompat/view/menu/ActionMenuItemView;->getItemData()Landroidx/appcompat/view/menu/MenuItemImpl; +HSPLandroidx/appcompat/view/menu/ActionMenuItemView;->hasText()Z +HSPLandroidx/appcompat/view/menu/ActionMenuItemView;->initialize(Landroidx/appcompat/view/menu/MenuItemImpl;I)V +HSPLandroidx/appcompat/view/menu/ActionMenuItemView;->onMeasure(II)V +HSPLandroidx/appcompat/view/menu/ActionMenuItemView;->prefersCondensedTitle()Z +HSPLandroidx/appcompat/view/menu/ActionMenuItemView;->setIcon(Landroid/graphics/drawable/Drawable;)V +HSPLandroidx/appcompat/view/menu/ActionMenuItemView;->setItemInvoker(Landroidx/appcompat/view/menu/MenuBuilder$ItemInvoker;)V +HSPLandroidx/appcompat/view/menu/ActionMenuItemView;->setPopupCallback(Landroidx/appcompat/view/menu/ActionMenuItemView$PopupCallback;)V +HSPLandroidx/appcompat/view/menu/ActionMenuItemView;->setTitle(Ljava/lang/CharSequence;)V +HSPLandroidx/appcompat/view/menu/ActionMenuItemView;->shouldAllowTextWithIcon()Z +HSPLandroidx/appcompat/view/menu/ActionMenuItemView;->updateTextButtonVisibility()V HSPLandroidx/appcompat/view/menu/BaseMenuPresenter;->(Landroid/content/Context;II)V +HSPLandroidx/appcompat/view/menu/BaseMenuPresenter;->addItemView(Landroid/view/View;I)V +HSPLandroidx/appcompat/view/menu/BaseMenuPresenter;->createItemView(Landroid/view/ViewGroup;)Landroidx/appcompat/view/menu/MenuView$ItemView; +HSPLandroidx/appcompat/view/menu/BaseMenuPresenter;->filterLeftoverView(Landroid/view/ViewGroup;I)Z +HSPLandroidx/appcompat/view/menu/BaseMenuPresenter;->getItemView(Landroidx/appcompat/view/menu/MenuItemImpl;Landroid/view/View;Landroid/view/ViewGroup;)Landroid/view/View; HSPLandroidx/appcompat/view/menu/BaseMenuPresenter;->initForMenu(Landroid/content/Context;Landroidx/appcompat/view/menu/MenuBuilder;)V HSPLandroidx/appcompat/view/menu/BaseMenuPresenter;->setCallback(Landroidx/appcompat/view/menu/MenuPresenter$Callback;)V HSPLandroidx/appcompat/view/menu/BaseMenuPresenter;->updateMenuView(Z)V @@ -403,34 +405,59 @@ HSPLandroidx/appcompat/view/menu/MenuBuilder;->(Landroid/content/Context;) HSPLandroidx/appcompat/view/menu/MenuBuilder;->add(IIILjava/lang/CharSequence;)Landroid/view/MenuItem; HSPLandroidx/appcompat/view/menu/MenuBuilder;->addInternal(IIILjava/lang/CharSequence;)Landroid/view/MenuItem; HSPLandroidx/appcompat/view/menu/MenuBuilder;->addMenuPresenter(Landroidx/appcompat/view/menu/MenuPresenter;Landroid/content/Context;)V +HSPLandroidx/appcompat/view/menu/MenuBuilder;->addSubMenu(IIILjava/lang/CharSequence;)Landroid/view/SubMenu; HSPLandroidx/appcompat/view/menu/MenuBuilder;->clear()V +HSPLandroidx/appcompat/view/menu/MenuBuilder;->clearHeader()V HSPLandroidx/appcompat/view/menu/MenuBuilder;->createNewMenuItem(IIIILjava/lang/CharSequence;I)Landroidx/appcompat/view/menu/MenuItemImpl; HSPLandroidx/appcompat/view/menu/MenuBuilder;->dispatchPresenterUpdate(Z)V HSPLandroidx/appcompat/view/menu/MenuBuilder;->findInsertIndex(Ljava/util/ArrayList;I)I HSPLandroidx/appcompat/view/menu/MenuBuilder;->findItem(I)Landroid/view/MenuItem; +HSPLandroidx/appcompat/view/menu/MenuBuilder;->findItemIndex(I)I HSPLandroidx/appcompat/view/menu/MenuBuilder;->flagActionItems()V HSPLandroidx/appcompat/view/menu/MenuBuilder;->getActionItems()Ljava/util/ArrayList; +HSPLandroidx/appcompat/view/menu/MenuBuilder;->getContext()Landroid/content/Context; +HSPLandroidx/appcompat/view/menu/MenuBuilder;->getItem(I)Landroid/view/MenuItem; HSPLandroidx/appcompat/view/menu/MenuBuilder;->getNonActionItems()Ljava/util/ArrayList; HSPLandroidx/appcompat/view/menu/MenuBuilder;->getOrdering(I)I +HSPLandroidx/appcompat/view/menu/MenuBuilder;->getResources()Landroid/content/res/Resources; HSPLandroidx/appcompat/view/menu/MenuBuilder;->getVisibleItems()Ljava/util/ArrayList; HSPLandroidx/appcompat/view/menu/MenuBuilder;->hasVisibleItems()Z +HSPLandroidx/appcompat/view/menu/MenuBuilder;->onItemActionRequestChanged(Landroidx/appcompat/view/menu/MenuItemImpl;)V HSPLandroidx/appcompat/view/menu/MenuBuilder;->onItemVisibleChanged(Landroidx/appcompat/view/menu/MenuItemImpl;)V HSPLandroidx/appcompat/view/menu/MenuBuilder;->onItemsChanged(Z)V +HSPLandroidx/appcompat/view/menu/MenuBuilder;->removeItem(I)V +HSPLandroidx/appcompat/view/menu/MenuBuilder;->removeItemAtInt(IZ)V HSPLandroidx/appcompat/view/menu/MenuBuilder;->setCallback(Landroidx/appcompat/view/menu/MenuBuilder$Callback;)V +HSPLandroidx/appcompat/view/menu/MenuBuilder;->setHeaderInternal(ILjava/lang/CharSequence;ILandroid/graphics/drawable/Drawable;Landroid/view/View;)V +HSPLandroidx/appcompat/view/menu/MenuBuilder;->setHeaderTitleInt(Ljava/lang/CharSequence;)Landroidx/appcompat/view/menu/MenuBuilder; HSPLandroidx/appcompat/view/menu/MenuBuilder;->setOverrideVisibleItems(Z)V HSPLandroidx/appcompat/view/menu/MenuBuilder;->setShortcutsVisibleInner(Z)V HSPLandroidx/appcompat/view/menu/MenuBuilder;->size()I HSPLandroidx/appcompat/view/menu/MenuBuilder;->startDispatchingItemsChanged()V HSPLandroidx/appcompat/view/menu/MenuBuilder;->stopDispatchingItemsChanged()V HSPLandroidx/appcompat/view/menu/MenuItemImpl;->(Landroidx/appcompat/view/menu/MenuBuilder;IIIILjava/lang/CharSequence;I)V +HSPLandroidx/appcompat/view/menu/MenuItemImpl;->applyIconTintIfNecessary(Landroid/graphics/drawable/Drawable;)Landroid/graphics/drawable/Drawable; +HSPLandroidx/appcompat/view/menu/MenuItemImpl;->getActionView()Landroid/view/View; +HSPLandroidx/appcompat/view/menu/MenuItemImpl;->getContentDescription()Ljava/lang/CharSequence; +HSPLandroidx/appcompat/view/menu/MenuItemImpl;->getGroupId()I +HSPLandroidx/appcompat/view/menu/MenuItemImpl;->getIcon()Landroid/graphics/drawable/Drawable; HSPLandroidx/appcompat/view/menu/MenuItemImpl;->getItemId()I HSPLandroidx/appcompat/view/menu/MenuItemImpl;->getOrdering()I +HSPLandroidx/appcompat/view/menu/MenuItemImpl;->getSubMenu()Landroid/view/SubMenu; +HSPLandroidx/appcompat/view/menu/MenuItemImpl;->getSupportActionProvider()Landroidx/core/view/ActionProvider; +HSPLandroidx/appcompat/view/menu/MenuItemImpl;->getTitle()Ljava/lang/CharSequence; +HSPLandroidx/appcompat/view/menu/MenuItemImpl;->getTitleCondensed()Ljava/lang/CharSequence; +HSPLandroidx/appcompat/view/menu/MenuItemImpl;->getTitleForItemView(Landroidx/appcompat/view/menu/MenuView$ItemView;)Ljava/lang/CharSequence; +HSPLandroidx/appcompat/view/menu/MenuItemImpl;->getTooltipText()Ljava/lang/CharSequence; HSPLandroidx/appcompat/view/menu/MenuItemImpl;->hasSubMenu()Z HSPLandroidx/appcompat/view/menu/MenuItemImpl;->isActionButton()Z HSPLandroidx/appcompat/view/menu/MenuItemImpl;->isActionViewExpanded()Z +HSPLandroidx/appcompat/view/menu/MenuItemImpl;->isEnabled()Z HSPLandroidx/appcompat/view/menu/MenuItemImpl;->isVisible()Z HSPLandroidx/appcompat/view/menu/MenuItemImpl;->requestsActionButton()Z HSPLandroidx/appcompat/view/menu/MenuItemImpl;->requiresActionButton()Z +HSPLandroidx/appcompat/view/menu/MenuItemImpl;->setActionView(Landroid/view/View;)Landroid/view/MenuItem; +HSPLandroidx/appcompat/view/menu/MenuItemImpl;->setActionView(Landroid/view/View;)Landroidx/core/internal/view/SupportMenuItem; HSPLandroidx/appcompat/view/menu/MenuItemImpl;->setAlphabeticShortcut(CI)Landroid/view/MenuItem; HSPLandroidx/appcompat/view/menu/MenuItemImpl;->setCheckable(Z)Landroid/view/MenuItem; HSPLandroidx/appcompat/view/menu/MenuItemImpl;->setChecked(Z)Landroid/view/MenuItem; @@ -438,20 +465,34 @@ HSPLandroidx/appcompat/view/menu/MenuItemImpl;->setCheckedInt(Z)V HSPLandroidx/appcompat/view/menu/MenuItemImpl;->setContentDescription(Ljava/lang/CharSequence;)Landroidx/core/internal/view/SupportMenuItem; HSPLandroidx/appcompat/view/menu/MenuItemImpl;->setEnabled(Z)Landroid/view/MenuItem; HSPLandroidx/appcompat/view/menu/MenuItemImpl;->setIcon(I)Landroid/view/MenuItem; +HSPLandroidx/appcompat/view/menu/MenuItemImpl;->setIconTintList(Landroid/content/res/ColorStateList;)Landroid/view/MenuItem; HSPLandroidx/appcompat/view/menu/MenuItemImpl;->setIsActionButton(Z)V HSPLandroidx/appcompat/view/menu/MenuItemImpl;->setNumericShortcut(CI)Landroid/view/MenuItem; +HSPLandroidx/appcompat/view/menu/MenuItemImpl;->setOnActionExpandListener(Landroid/view/MenuItem$OnActionExpandListener;)Landroid/view/MenuItem; +HSPLandroidx/appcompat/view/menu/MenuItemImpl;->setShowAsAction(I)V +HSPLandroidx/appcompat/view/menu/MenuItemImpl;->setSubMenu(Landroidx/appcompat/view/menu/SubMenuBuilder;)V +HSPLandroidx/appcompat/view/menu/MenuItemImpl;->setTitle(Ljava/lang/CharSequence;)Landroid/view/MenuItem; HSPLandroidx/appcompat/view/menu/MenuItemImpl;->setTitleCondensed(Ljava/lang/CharSequence;)Landroid/view/MenuItem; HSPLandroidx/appcompat/view/menu/MenuItemImpl;->setTooltipText(Ljava/lang/CharSequence;)Landroidx/core/internal/view/SupportMenuItem; HSPLandroidx/appcompat/view/menu/MenuItemImpl;->setVisible(Z)Landroid/view/MenuItem; HSPLandroidx/appcompat/view/menu/MenuItemImpl;->setVisibleInt(Z)Z +HSPLandroidx/appcompat/view/menu/MenuItemImpl;->showsTextAsAction()Z +HSPLandroidx/appcompat/view/menu/SubMenuBuilder;->(Landroid/content/Context;Landroidx/appcompat/view/menu/MenuBuilder;Landroidx/appcompat/view/menu/MenuItemImpl;)V +HSPLandroidx/appcompat/view/menu/SubMenuBuilder;->getItem()Landroid/view/MenuItem; +HSPLandroidx/appcompat/view/menu/SubMenuBuilder;->setHeaderTitle(Ljava/lang/CharSequence;)Landroid/view/SubMenu; +HSPLandroidx/appcompat/widget/ActionMenuPresenter$ActionMenuPopupCallback;->(Landroidx/appcompat/widget/ActionMenuPresenter;)V HSPLandroidx/appcompat/widget/ActionMenuPresenter$OverflowMenuButton$1;->(Landroidx/appcompat/widget/ActionMenuPresenter$OverflowMenuButton;Landroid/view/View;Landroidx/appcompat/widget/ActionMenuPresenter;)V HSPLandroidx/appcompat/widget/ActionMenuPresenter$OverflowMenuButton;->(Landroidx/appcompat/widget/ActionMenuPresenter;Landroid/content/Context;)V HSPLandroidx/appcompat/widget/ActionMenuPresenter$OverflowMenuButton;->setFrame(IIII)Z HSPLandroidx/appcompat/widget/ActionMenuPresenter$PopupPresenterCallback;->(Landroidx/appcompat/widget/ActionMenuPresenter;)V HSPLandroidx/appcompat/widget/ActionMenuPresenter;->(Landroid/content/Context;)V +HSPLandroidx/appcompat/widget/ActionMenuPresenter;->bindItemView(Landroidx/appcompat/view/menu/MenuItemImpl;Landroidx/appcompat/view/menu/MenuView$ItemView;)V +HSPLandroidx/appcompat/widget/ActionMenuPresenter;->filterLeftoverView(Landroid/view/ViewGroup;I)Z HSPLandroidx/appcompat/widget/ActionMenuPresenter;->flagActionItems()Z +HSPLandroidx/appcompat/widget/ActionMenuPresenter;->getItemView(Landroidx/appcompat/view/menu/MenuItemImpl;Landroid/view/View;Landroid/view/ViewGroup;)Landroid/view/View; HSPLandroidx/appcompat/widget/ActionMenuPresenter;->getOverflowIcon()Landroid/graphics/drawable/Drawable; HSPLandroidx/appcompat/widget/ActionMenuPresenter;->initForMenu(Landroid/content/Context;Landroidx/appcompat/view/menu/MenuBuilder;)V +HSPLandroidx/appcompat/widget/ActionMenuPresenter;->isOverflowMenuShowing()Z HSPLandroidx/appcompat/widget/ActionMenuPresenter;->setExpandedActionViewsExclusive(Z)V HSPLandroidx/appcompat/widget/ActionMenuPresenter;->setMenuView(Landroidx/appcompat/widget/ActionMenuView;)V HSPLandroidx/appcompat/widget/ActionMenuPresenter;->setReserveOverflow(Z)V @@ -459,15 +500,19 @@ HSPLandroidx/appcompat/widget/ActionMenuPresenter;->shouldIncludeItem(ILandroidx HSPLandroidx/appcompat/widget/ActionMenuPresenter;->updateMenuView(Z)V HSPLandroidx/appcompat/widget/ActionMenuView$ActionMenuPresenterCallback;->()V HSPLandroidx/appcompat/widget/ActionMenuView$LayoutParams;->(II)V +HSPLandroidx/appcompat/widget/ActionMenuView$LayoutParams;->(Landroid/content/Context;Landroid/util/AttributeSet;)V HSPLandroidx/appcompat/widget/ActionMenuView$MenuBuilderCallback;->(Landroidx/appcompat/widget/ActionMenuView;)V HSPLandroidx/appcompat/widget/ActionMenuView;->(Landroid/content/Context;)V HSPLandroidx/appcompat/widget/ActionMenuView;->(Landroid/content/Context;Landroid/util/AttributeSet;)V HSPLandroidx/appcompat/widget/ActionMenuView;->checkLayoutParams(Landroid/view/ViewGroup$LayoutParams;)Z HSPLandroidx/appcompat/widget/ActionMenuView;->generateDefaultLayoutParams()Landroidx/appcompat/widget/ActionMenuView$LayoutParams; +HSPLandroidx/appcompat/widget/ActionMenuView;->generateLayoutParams(Landroid/util/AttributeSet;)Landroid/view/ViewGroup$LayoutParams; +HSPLandroidx/appcompat/widget/ActionMenuView;->generateLayoutParams(Landroid/util/AttributeSet;)Landroidx/appcompat/widget/ActionMenuView$LayoutParams; HSPLandroidx/appcompat/widget/ActionMenuView;->generateOverflowButtonLayoutParams()Landroidx/appcompat/widget/ActionMenuView$LayoutParams; HSPLandroidx/appcompat/widget/ActionMenuView;->getMenu()Landroid/view/Menu; HSPLandroidx/appcompat/widget/ActionMenuView;->getOverflowIcon()Landroid/graphics/drawable/Drawable; HSPLandroidx/appcompat/widget/ActionMenuView;->initialize(Landroidx/appcompat/view/menu/MenuBuilder;)V +HSPLandroidx/appcompat/widget/ActionMenuView;->isOverflowMenuShowing()Z HSPLandroidx/appcompat/widget/ActionMenuView;->onLayout(ZIIII)V HSPLandroidx/appcompat/widget/ActionMenuView;->onMeasure(II)V HSPLandroidx/appcompat/widget/ActionMenuView;->peekMenu()Landroidx/appcompat/view/menu/MenuBuilder; @@ -476,9 +521,12 @@ HSPLandroidx/appcompat/widget/ActionMenuView;->setMenuCallbacks(Landroidx/appcom HSPLandroidx/appcompat/widget/ActionMenuView;->setOnMenuItemClickListener(Landroidx/appcompat/widget/ActionMenuView$OnMenuItemClickListener;)V HSPLandroidx/appcompat/widget/ActionMenuView;->setOverflowReserved(Z)V HSPLandroidx/appcompat/widget/ActionMenuView;->setPopupTheme(I)V +HSPLandroidx/appcompat/widget/AppCompatAutoCompleteTextView;->()V +HSPLandroidx/appcompat/widget/AppCompatAutoCompleteTextView;->(Landroid/content/Context;Landroid/util/AttributeSet;I)V +HSPLandroidx/appcompat/widget/AppCompatAutoCompleteTextView;->initEmojiKeyListener(Landroidx/appcompat/widget/AppCompatEmojiEditTextHelper;)V +HSPLandroidx/appcompat/widget/AppCompatAutoCompleteTextView;->setCompoundDrawables(Landroid/graphics/drawable/Drawable;Landroid/graphics/drawable/Drawable;Landroid/graphics/drawable/Drawable;Landroid/graphics/drawable/Drawable;)V HSPLandroidx/appcompat/widget/AppCompatBackgroundHelper;->(Landroid/view/View;)V HSPLandroidx/appcompat/widget/AppCompatBackgroundHelper;->applySupportBackgroundTint()V -HSPLandroidx/appcompat/widget/AppCompatBackgroundHelper;->loadFromAttributes(Landroid/util/AttributeSet;I)V HSPLandroidx/appcompat/widget/AppCompatBackgroundHelper;->onSetBackgroundDrawable(Landroid/graphics/drawable/Drawable;)V HSPLandroidx/appcompat/widget/AppCompatBackgroundHelper;->onSetBackgroundResource(I)V HSPLandroidx/appcompat/widget/AppCompatBackgroundHelper;->setInternalBackgroundTint(Landroid/content/res/ColorStateList;)V @@ -505,6 +553,7 @@ HSPLandroidx/appcompat/widget/AppCompatDrawableManager$1;->()V HSPLandroidx/appcompat/widget/AppCompatDrawableManager$1;->arrayContains([II)Z HSPLandroidx/appcompat/widget/AppCompatDrawableManager$1;->createDrawableFor(Landroidx/appcompat/widget/ResourceManagerInternal;Landroid/content/Context;I)Landroid/graphics/drawable/Drawable; HSPLandroidx/appcompat/widget/AppCompatDrawableManager$1;->getTintListForDrawableRes(Landroid/content/Context;I)Landroid/content/res/ColorStateList; +HSPLandroidx/appcompat/widget/AppCompatDrawableManager$1;->getTintModeForDrawableRes(I)Landroid/graphics/PorterDuff$Mode; HSPLandroidx/appcompat/widget/AppCompatDrawableManager$1;->tintDrawable(Landroid/content/Context;ILandroid/graphics/drawable/Drawable;)Z HSPLandroidx/appcompat/widget/AppCompatDrawableManager$1;->tintDrawableUsingColorFilter(Landroid/content/Context;ILandroid/graphics/drawable/Drawable;)Z HSPLandroidx/appcompat/widget/AppCompatDrawableManager;->()V @@ -550,7 +599,6 @@ HSPLandroidx/appcompat/widget/AppCompatImageHelper;->(Landroid/widget/Imag HSPLandroidx/appcompat/widget/AppCompatImageHelper;->applyImageLevel()V HSPLandroidx/appcompat/widget/AppCompatImageHelper;->applySupportImageTint()V HSPLandroidx/appcompat/widget/AppCompatImageHelper;->hasOverlappingRendering()Z -HSPLandroidx/appcompat/widget/AppCompatImageHelper;->loadFromAttributes(Landroid/util/AttributeSet;I)V HSPLandroidx/appcompat/widget/AppCompatImageHelper;->obtainLevelFromDrawable(Landroid/graphics/drawable/Drawable;)V HSPLandroidx/appcompat/widget/AppCompatImageHelper;->setImageResource(I)V HSPLandroidx/appcompat/widget/AppCompatImageHelper;->shouldApplyFrameworkTintUsingColorFilter()Z @@ -588,7 +636,6 @@ HSPLandroidx/appcompat/widget/AppCompatTextHelper;->onSetCompoundDrawables()V HSPLandroidx/appcompat/widget/AppCompatTextHelper;->onSetTextAppearance(Landroid/content/Context;I)V HSPLandroidx/appcompat/widget/AppCompatTextHelper;->populateSurroundingTextIfNeeded(Landroid/widget/TextView;Landroid/view/inputmethod/InputConnection;Landroid/view/inputmethod/EditorInfo;)V HSPLandroidx/appcompat/widget/AppCompatTextHelper;->setCompoundDrawables(Landroid/graphics/drawable/Drawable;Landroid/graphics/drawable/Drawable;Landroid/graphics/drawable/Drawable;Landroid/graphics/drawable/Drawable;Landroid/graphics/drawable/Drawable;Landroid/graphics/drawable/Drawable;)V -HSPLandroidx/appcompat/widget/AppCompatTextHelper;->updateTypefaceAndStyle(Landroid/content/Context;Landroidx/appcompat/widget/TintTypedArray;)V HSPLandroidx/appcompat/widget/AppCompatTextView;->(Landroid/content/Context;)V HSPLandroidx/appcompat/widget/AppCompatTextView;->(Landroid/content/Context;Landroid/util/AttributeSet;)V HSPLandroidx/appcompat/widget/AppCompatTextView;->(Landroid/content/Context;Landroid/util/AttributeSet;I)V @@ -641,6 +688,7 @@ HSPLandroidx/appcompat/widget/FitWindowsFrameLayout;->(Landroid/content/Co HSPLandroidx/appcompat/widget/FitWindowsFrameLayout;->fitSystemWindows(Landroid/graphics/Rect;)Z HSPLandroidx/appcompat/widget/ForwardingListener;->(Landroid/view/View;)V HSPLandroidx/appcompat/widget/ForwardingListener;->onViewAttachedToWindow(Landroid/view/View;)V +HSPLandroidx/appcompat/widget/ForwardingListener;->onViewDetachedFromWindow(Landroid/view/View;)V HSPLandroidx/appcompat/widget/LinearLayoutCompat$LayoutParams;->(II)V HSPLandroidx/appcompat/widget/LinearLayoutCompat$LayoutParams;->(Landroid/content/Context;Landroid/util/AttributeSet;)V HSPLandroidx/appcompat/widget/LinearLayoutCompat;->(Landroid/content/Context;Landroid/util/AttributeSet;)V @@ -675,6 +723,7 @@ HSPLandroidx/appcompat/widget/ResourceManagerInternal$ColorFilterLruCache;->get( HSPLandroidx/appcompat/widget/ResourceManagerInternal$ColorFilterLruCache;->put(ILandroid/graphics/PorterDuff$Mode;Landroid/graphics/PorterDuffColorFilter;)Landroid/graphics/PorterDuffColorFilter; HSPLandroidx/appcompat/widget/ResourceManagerInternal;->()V HSPLandroidx/appcompat/widget/ResourceManagerInternal;->()V +HSPLandroidx/appcompat/widget/ResourceManagerInternal;->addTintListToCache(Landroid/content/Context;ILandroid/content/res/ColorStateList;)V HSPLandroidx/appcompat/widget/ResourceManagerInternal;->checkVectorDrawableSetup(Landroid/content/Context;)V HSPLandroidx/appcompat/widget/ResourceManagerInternal;->createCacheKey(Landroid/util/TypedValue;)J HSPLandroidx/appcompat/widget/ResourceManagerInternal;->createDrawableIfNeeded(Landroid/content/Context;I)Landroid/graphics/drawable/Drawable; @@ -686,6 +735,7 @@ HSPLandroidx/appcompat/widget/ResourceManagerInternal;->getDrawable(Landroid/con HSPLandroidx/appcompat/widget/ResourceManagerInternal;->getPorterDuffColorFilter(ILandroid/graphics/PorterDuff$Mode;)Landroid/graphics/PorterDuffColorFilter; HSPLandroidx/appcompat/widget/ResourceManagerInternal;->getTintList(Landroid/content/Context;I)Landroid/content/res/ColorStateList; HSPLandroidx/appcompat/widget/ResourceManagerInternal;->getTintListFromCache(Landroid/content/Context;I)Landroid/content/res/ColorStateList; +HSPLandroidx/appcompat/widget/ResourceManagerInternal;->getTintMode(I)Landroid/graphics/PorterDuff$Mode; HSPLandroidx/appcompat/widget/ResourceManagerInternal;->installDefaultInflateDelegates(Landroidx/appcompat/widget/ResourceManagerInternal;)V HSPLandroidx/appcompat/widget/ResourceManagerInternal;->isVectorDrawable(Landroid/graphics/drawable/Drawable;)Z HSPLandroidx/appcompat/widget/ResourceManagerInternal;->loadDrawableFromDelegates(Landroid/content/Context;I)Landroid/graphics/drawable/Drawable; @@ -699,6 +749,36 @@ HSPLandroidx/appcompat/widget/RtlSpacingHelper;->getStart()I HSPLandroidx/appcompat/widget/RtlSpacingHelper;->setAbsolute(II)V HSPLandroidx/appcompat/widget/RtlSpacingHelper;->setDirection(Z)V HSPLandroidx/appcompat/widget/RtlSpacingHelper;->setRelative(II)V +HSPLandroidx/appcompat/widget/SearchView$10;->(Landroidx/appcompat/widget/SearchView;)V +HSPLandroidx/appcompat/widget/SearchView$1;->(Landroidx/appcompat/widget/SearchView;)V +HSPLandroidx/appcompat/widget/SearchView$2;->(Landroidx/appcompat/widget/SearchView;)V +HSPLandroidx/appcompat/widget/SearchView$3;->(Landroidx/appcompat/widget/SearchView;)V +HSPLandroidx/appcompat/widget/SearchView$4;->(Landroidx/appcompat/widget/SearchView;)V +HSPLandroidx/appcompat/widget/SearchView$5;->(Landroidx/appcompat/widget/SearchView;)V +HSPLandroidx/appcompat/widget/SearchView$6;->(Landroidx/appcompat/widget/SearchView;)V +HSPLandroidx/appcompat/widget/SearchView$7;->(Landroidx/appcompat/widget/SearchView;)V +HSPLandroidx/appcompat/widget/SearchView$8;->(Landroidx/appcompat/widget/SearchView;)V +HSPLandroidx/appcompat/widget/SearchView$9;->(Landroidx/appcompat/widget/SearchView;)V +HSPLandroidx/appcompat/widget/SearchView$SearchAutoComplete$1;->(Landroidx/appcompat/widget/SearchView$SearchAutoComplete;)V +HSPLandroidx/appcompat/widget/SearchView$SearchAutoComplete;->(Landroid/content/Context;Landroid/util/AttributeSet;)V +HSPLandroidx/appcompat/widget/SearchView$SearchAutoComplete;->(Landroid/content/Context;Landroid/util/AttributeSet;I)V +HSPLandroidx/appcompat/widget/SearchView$SearchAutoComplete;->enoughToFilter()Z +HSPLandroidx/appcompat/widget/SearchView$SearchAutoComplete;->getSearchViewTextMinWidthDp()I +HSPLandroidx/appcompat/widget/SearchView$SearchAutoComplete;->onFinishInflate()V +HSPLandroidx/appcompat/widget/SearchView$SearchAutoComplete;->setSearchView(Landroidx/appcompat/widget/SearchView;)V +HSPLandroidx/appcompat/widget/SearchView;->()V +HSPLandroidx/appcompat/widget/SearchView;->(Landroid/content/Context;Landroid/util/AttributeSet;I)V +HSPLandroidx/appcompat/widget/SearchView;->getDecoratedHint(Ljava/lang/CharSequence;)Ljava/lang/CharSequence; +HSPLandroidx/appcompat/widget/SearchView;->getQueryHint()Ljava/lang/CharSequence; +HSPLandroidx/appcompat/widget/SearchView;->isSubmitAreaEnabled()Z +HSPLandroidx/appcompat/widget/SearchView;->setIconifiedByDefault(Z)V +HSPLandroidx/appcompat/widget/SearchView;->setMaxWidth(I)V +HSPLandroidx/appcompat/widget/SearchView;->updateCloseButton()V +HSPLandroidx/appcompat/widget/SearchView;->updateQueryHint()V +HSPLandroidx/appcompat/widget/SearchView;->updateSubmitArea()V +HSPLandroidx/appcompat/widget/SearchView;->updateSubmitButton(Z)V +HSPLandroidx/appcompat/widget/SearchView;->updateViewsVisibility(Z)V +HSPLandroidx/appcompat/widget/SearchView;->updateVoiceButton(Z)V HSPLandroidx/appcompat/widget/ThemeUtils;->()V HSPLandroidx/appcompat/widget/ThemeUtils;->checkAppCompatTheme(Landroid/view/View;Landroid/content/Context;)V HSPLandroidx/appcompat/widget/TintContextWrapper;->()V @@ -736,6 +816,9 @@ HSPLandroidx/appcompat/widget/Toolbar$ExpandedActionViewMenuPresenter;->initForM HSPLandroidx/appcompat/widget/Toolbar$ExpandedActionViewMenuPresenter;->updateMenuView(Z)V HSPLandroidx/appcompat/widget/Toolbar$LayoutParams;->(II)V HSPLandroidx/appcompat/widget/Toolbar$LayoutParams;->(Landroid/content/Context;Landroid/util/AttributeSet;)V +HSPLandroidx/appcompat/widget/Toolbar$SavedState$1;->()V +HSPLandroidx/appcompat/widget/Toolbar$SavedState;->()V +HSPLandroidx/appcompat/widget/Toolbar$SavedState;->(Landroid/os/Parcelable;)V HSPLandroidx/appcompat/widget/Toolbar;->(Landroid/content/Context;Landroid/util/AttributeSet;)V HSPLandroidx/appcompat/widget/Toolbar;->(Landroid/content/Context;Landroid/util/AttributeSet;I)V HSPLandroidx/appcompat/widget/Toolbar;->addCustomViewsWithGravity(Ljava/util/List;I)V @@ -771,6 +854,7 @@ HSPLandroidx/appcompat/widget/Toolbar;->getVerticalMargins(Landroid/view/View;)I HSPLandroidx/appcompat/widget/Toolbar;->getViewListMeasuredWidth(Ljava/util/List;[I)I HSPLandroidx/appcompat/widget/Toolbar;->invalidateMenu()V HSPLandroidx/appcompat/widget/Toolbar;->isChildOrHidden(Landroid/view/View;)Z +HSPLandroidx/appcompat/widget/Toolbar;->isOverflowMenuShowing()Z HSPLandroidx/appcompat/widget/Toolbar;->layoutChildLeft(Landroid/view/View;I[II)I HSPLandroidx/appcompat/widget/Toolbar;->layoutChildRight(Landroid/view/View;I[II)I HSPLandroidx/appcompat/widget/Toolbar;->measureChildCollapseMargins(Landroid/view/View;IIII[I)I @@ -780,6 +864,7 @@ HSPLandroidx/appcompat/widget/Toolbar;->onCreateMenu()V HSPLandroidx/appcompat/widget/Toolbar;->onLayout(ZIIII)V HSPLandroidx/appcompat/widget/Toolbar;->onMeasure(II)V HSPLandroidx/appcompat/widget/Toolbar;->onRtlPropertiesChanged(I)V +HSPLandroidx/appcompat/widget/Toolbar;->onSaveInstanceState()Landroid/os/Parcelable; HSPLandroidx/appcompat/widget/Toolbar;->setBackInvokedCallbackEnabled(Z)V HSPLandroidx/appcompat/widget/Toolbar;->setMenuCallbacks(Landroidx/appcompat/view/menu/MenuPresenter$Callback;Landroidx/appcompat/view/menu/MenuBuilder$Callback;)V HSPLandroidx/appcompat/widget/Toolbar;->setNavigationContentDescription(I)V @@ -1018,6 +1103,7 @@ HSPLandroidx/collection/SimpleArrayMap;->valueAt(I)Ljava/lang/Object; HSPLandroidx/collection/SparseArrayCompat;->()V HSPLandroidx/collection/SparseArrayCompat;->()V HSPLandroidx/collection/SparseArrayCompat;->(I)V +HSPLandroidx/collection/SparseArrayCompat;->append(ILjava/lang/Object;)V HSPLandroidx/collection/SparseArrayCompat;->containsValue(Ljava/lang/Object;)Z HSPLandroidx/collection/SparseArrayCompat;->get(I)Ljava/lang/Object; HSPLandroidx/collection/SparseArrayCompat;->get(ILjava/lang/Object;)Ljava/lang/Object; @@ -1051,7 +1137,6 @@ HSPLandroidx/constraintlayout/core/ArrayLinkedVariables;->getCurrentSize()I HSPLandroidx/constraintlayout/core/ArrayLinkedVariables;->getVariable(I)Landroidx/constraintlayout/core/SolverVariable; HSPLandroidx/constraintlayout/core/ArrayLinkedVariables;->getVariableValue(I)F HSPLandroidx/constraintlayout/core/ArrayLinkedVariables;->invert()V -HSPLandroidx/constraintlayout/core/ArrayLinkedVariables;->use(Landroidx/constraintlayout/core/ArrayRow;Z)F HSPLandroidx/constraintlayout/core/ArrayRow;->(Landroidx/constraintlayout/core/Cache;)V HSPLandroidx/constraintlayout/core/ArrayRow;->addError(Landroidx/constraintlayout/core/LinearSystem;I)Landroidx/constraintlayout/core/ArrayRow; HSPLandroidx/constraintlayout/core/ArrayRow;->addSingleError(Landroidx/constraintlayout/core/SolverVariable;I)Landroidx/constraintlayout/core/ArrayRow; @@ -1072,11 +1157,12 @@ HSPLandroidx/constraintlayout/core/ArrayRow;->isNew(Landroidx/constraintlayout/c HSPLandroidx/constraintlayout/core/ArrayRow;->pivot(Landroidx/constraintlayout/core/SolverVariable;)V HSPLandroidx/constraintlayout/core/ArrayRow;->reset()V HSPLandroidx/constraintlayout/core/ArrayRow;->updateFromFinalVariable(Landroidx/constraintlayout/core/LinearSystem;Landroidx/constraintlayout/core/SolverVariable;Z)V -HSPLandroidx/constraintlayout/core/ArrayRow;->updateFromSystem(Landroidx/constraintlayout/core/LinearSystem;)V HSPLandroidx/constraintlayout/core/Cache;->()V HSPLandroidx/constraintlayout/core/LinearSystem;->()V HSPLandroidx/constraintlayout/core/LinearSystem;->()V +HSPLandroidx/constraintlayout/core/LinearSystem;->acquireSolverVariable(Landroidx/constraintlayout/core/SolverVariable$Type;Ljava/lang/String;)Landroidx/constraintlayout/core/SolverVariable; HSPLandroidx/constraintlayout/core/LinearSystem;->addCentering(Landroidx/constraintlayout/core/SolverVariable;Landroidx/constraintlayout/core/SolverVariable;IFLandroidx/constraintlayout/core/SolverVariable;Landroidx/constraintlayout/core/SolverVariable;II)V +HSPLandroidx/constraintlayout/core/LinearSystem;->addConstraint(Landroidx/constraintlayout/core/ArrayRow;)V HSPLandroidx/constraintlayout/core/LinearSystem;->addEquality(Landroidx/constraintlayout/core/SolverVariable;I)V HSPLandroidx/constraintlayout/core/LinearSystem;->addEquality(Landroidx/constraintlayout/core/SolverVariable;Landroidx/constraintlayout/core/SolverVariable;II)Landroidx/constraintlayout/core/ArrayRow; HSPLandroidx/constraintlayout/core/LinearSystem;->addGreaterBarrier(Landroidx/constraintlayout/core/SolverVariable;Landroidx/constraintlayout/core/SolverVariable;IZ)V @@ -1087,6 +1173,7 @@ HSPLandroidx/constraintlayout/core/LinearSystem;->addRow(Landroidx/constraintlay HSPLandroidx/constraintlayout/core/LinearSystem;->addSingleError(Landroidx/constraintlayout/core/ArrayRow;II)V HSPLandroidx/constraintlayout/core/LinearSystem;->computeValues()V HSPLandroidx/constraintlayout/core/LinearSystem;->createObjectVariable(Ljava/lang/Object;)Landroidx/constraintlayout/core/SolverVariable; +HSPLandroidx/constraintlayout/core/LinearSystem;->createRow()Landroidx/constraintlayout/core/ArrayRow; HSPLandroidx/constraintlayout/core/LinearSystem;->createSlackVariable()Landroidx/constraintlayout/core/SolverVariable; HSPLandroidx/constraintlayout/core/LinearSystem;->enforceBFS(Landroidx/constraintlayout/core/LinearSystem$Row;)I HSPLandroidx/constraintlayout/core/LinearSystem;->getCache()Landroidx/constraintlayout/core/Cache; @@ -1116,7 +1203,6 @@ HSPLandroidx/constraintlayout/core/PriorityGoalRow;->clear()V HSPLandroidx/constraintlayout/core/PriorityGoalRow;->getPivotCandidate(Landroidx/constraintlayout/core/LinearSystem;[Z)Landroidx/constraintlayout/core/SolverVariable; HSPLandroidx/constraintlayout/core/PriorityGoalRow;->isEmpty()Z HSPLandroidx/constraintlayout/core/PriorityGoalRow;->removeGoal(Landroidx/constraintlayout/core/SolverVariable;)V -HSPLandroidx/constraintlayout/core/PriorityGoalRow;->updateFromRow(Landroidx/constraintlayout/core/LinearSystem;Landroidx/constraintlayout/core/ArrayRow;Z)V HSPLandroidx/constraintlayout/core/SolverVariable$Type;->()V HSPLandroidx/constraintlayout/core/SolverVariable$Type;->(Ljava/lang/String;I)V HSPLandroidx/constraintlayout/core/SolverVariable;->()V @@ -1127,7 +1213,6 @@ HSPLandroidx/constraintlayout/core/SolverVariable;->removeFromRow(Landroidx/cons HSPLandroidx/constraintlayout/core/SolverVariable;->reset()V HSPLandroidx/constraintlayout/core/SolverVariable;->setFinalValue(Landroidx/constraintlayout/core/LinearSystem;F)V HSPLandroidx/constraintlayout/core/SolverVariable;->setType(Landroidx/constraintlayout/core/SolverVariable$Type;Ljava/lang/String;)V -HSPLandroidx/constraintlayout/core/SolverVariable;->updateReferencesWithNewDefinition(Landroidx/constraintlayout/core/LinearSystem;Landroidx/constraintlayout/core/ArrayRow;)V HSPLandroidx/constraintlayout/core/state/WidgetFrame;->()V HSPLandroidx/constraintlayout/core/state/WidgetFrame;->(Landroidx/constraintlayout/core/widgets/ConstraintWidget;)V HSPLandroidx/constraintlayout/core/widgets/Barrier;->()V @@ -1271,7 +1356,6 @@ HSPLandroidx/constraintlayout/core/widgets/ConstraintWidgetContainer;->invalidat HSPLandroidx/constraintlayout/core/widgets/ConstraintWidgetContainer;->isHeightMeasuredTooSmall()Z HSPLandroidx/constraintlayout/core/widgets/ConstraintWidgetContainer;->isRtl()Z HSPLandroidx/constraintlayout/core/widgets/ConstraintWidgetContainer;->isWidthMeasuredTooSmall()Z -HSPLandroidx/constraintlayout/core/widgets/ConstraintWidgetContainer;->layout()V HSPLandroidx/constraintlayout/core/widgets/ConstraintWidgetContainer;->measure(IIIIIIIII)J HSPLandroidx/constraintlayout/core/widgets/ConstraintWidgetContainer;->measure(ILandroidx/constraintlayout/core/widgets/ConstraintWidget;Landroidx/constraintlayout/core/widgets/analyzer/BasicMeasure$Measurer;Landroidx/constraintlayout/core/widgets/analyzer/BasicMeasure$Measure;I)Z HSPLandroidx/constraintlayout/core/widgets/ConstraintWidgetContainer;->optimizeFor(I)Z @@ -1311,7 +1395,6 @@ HSPLandroidx/constraintlayout/core/widgets/WidgetContainer;->resetSolverVariable HSPLandroidx/constraintlayout/core/widgets/analyzer/BasicMeasure$Measure;->()V HSPLandroidx/constraintlayout/core/widgets/analyzer/BasicMeasure$Measure;->()V HSPLandroidx/constraintlayout/core/widgets/analyzer/BasicMeasure;->(Landroidx/constraintlayout/core/widgets/ConstraintWidgetContainer;)V -HSPLandroidx/constraintlayout/core/widgets/analyzer/BasicMeasure;->measure(Landroidx/constraintlayout/core/widgets/analyzer/BasicMeasure$Measurer;Landroidx/constraintlayout/core/widgets/ConstraintWidget;I)Z HSPLandroidx/constraintlayout/core/widgets/analyzer/BasicMeasure;->measureChildren(Landroidx/constraintlayout/core/widgets/ConstraintWidgetContainer;)V HSPLandroidx/constraintlayout/core/widgets/analyzer/BasicMeasure;->solveLinearSystem(Landroidx/constraintlayout/core/widgets/ConstraintWidgetContainer;Ljava/lang/String;III)V HSPLandroidx/constraintlayout/core/widgets/analyzer/BasicMeasure;->solverMeasure(Landroidx/constraintlayout/core/widgets/ConstraintWidgetContainer;IIIIIIIII)J @@ -1321,14 +1404,13 @@ HSPLandroidx/constraintlayout/core/widgets/analyzer/DependencyGraph;->invalidate HSPLandroidx/constraintlayout/core/widgets/analyzer/DependencyGraph;->invalidateMeasures()V HSPLandroidx/constraintlayout/core/widgets/analyzer/DependencyGraph;->setMeasurer(Landroidx/constraintlayout/core/widgets/analyzer/BasicMeasure$Measurer;)V HSPLandroidx/constraintlayout/core/widgets/analyzer/Direct;->()V -HSPLandroidx/constraintlayout/core/widgets/analyzer/Direct;->canMeasure(ILandroidx/constraintlayout/core/widgets/ConstraintWidget;)Z +HSPLandroidx/constraintlayout/core/widgets/analyzer/Direct;->horizontalSolvingPass(ILandroidx/constraintlayout/core/widgets/ConstraintWidget;Landroidx/constraintlayout/core/widgets/analyzer/BasicMeasure$Measurer;Z)V HSPLandroidx/constraintlayout/core/widgets/analyzer/Direct;->solveBarrier(ILandroidx/constraintlayout/core/widgets/Barrier;Landroidx/constraintlayout/core/widgets/analyzer/BasicMeasure$Measurer;IZ)V HSPLandroidx/constraintlayout/core/widgets/analyzer/Direct;->solveHorizontalCenterConstraints(ILandroidx/constraintlayout/core/widgets/analyzer/BasicMeasure$Measurer;Landroidx/constraintlayout/core/widgets/ConstraintWidget;Z)V HSPLandroidx/constraintlayout/core/widgets/analyzer/Direct;->solveHorizontalMatchConstraint(ILandroidx/constraintlayout/core/widgets/ConstraintWidget;Landroidx/constraintlayout/core/widgets/analyzer/BasicMeasure$Measurer;Landroidx/constraintlayout/core/widgets/ConstraintWidget;Z)V HSPLandroidx/constraintlayout/core/widgets/analyzer/Direct;->solveVerticalCenterConstraints(ILandroidx/constraintlayout/core/widgets/analyzer/BasicMeasure$Measurer;Landroidx/constraintlayout/core/widgets/ConstraintWidget;)V HSPLandroidx/constraintlayout/core/widgets/analyzer/Direct;->solveVerticalMatchConstraint(ILandroidx/constraintlayout/core/widgets/ConstraintWidget;Landroidx/constraintlayout/core/widgets/analyzer/BasicMeasure$Measurer;Landroidx/constraintlayout/core/widgets/ConstraintWidget;)V HSPLandroidx/constraintlayout/core/widgets/analyzer/Direct;->solvingPass(Landroidx/constraintlayout/core/widgets/ConstraintWidgetContainer;Landroidx/constraintlayout/core/widgets/analyzer/BasicMeasure$Measurer;)V -HSPLandroidx/constraintlayout/core/widgets/analyzer/Direct;->verticalSolvingPass(ILandroidx/constraintlayout/core/widgets/ConstraintWidget;Landroidx/constraintlayout/core/widgets/analyzer/BasicMeasure$Measurer;)V HSPLandroidx/constraintlayout/widget/Barrier;->(Landroid/content/Context;Landroid/util/AttributeSet;)V HSPLandroidx/constraintlayout/widget/Barrier;->init(Landroid/util/AttributeSet;)V HSPLandroidx/constraintlayout/widget/Barrier;->resolveRtl(Landroidx/constraintlayout/core/widgets/ConstraintWidget;Z)V @@ -1352,8 +1434,6 @@ HSPLandroidx/constraintlayout/widget/ConstraintHelper;->validateParams()V HSPLandroidx/constraintlayout/widget/ConstraintLayout$1;->()V HSPLandroidx/constraintlayout/widget/ConstraintLayout$LayoutParams$Table;->()V HSPLandroidx/constraintlayout/widget/ConstraintLayout$LayoutParams;->(Landroid/content/Context;Landroid/util/AttributeSet;)V -HSPLandroidx/constraintlayout/widget/ConstraintLayout$LayoutParams;->resolveLayoutDirection(I)V -HSPLandroidx/constraintlayout/widget/ConstraintLayout$LayoutParams;->validate()V HSPLandroidx/constraintlayout/widget/ConstraintLayout$Measurer;->(Landroidx/constraintlayout/widget/ConstraintLayout;Landroidx/constraintlayout/widget/ConstraintLayout;)V HSPLandroidx/constraintlayout/widget/ConstraintLayout$Measurer;->captureLayoutInfo(IIIIII)V HSPLandroidx/constraintlayout/widget/ConstraintLayout$Measurer;->didMeasures()V @@ -1382,7 +1462,6 @@ HSPLandroidx/constraintlayout/widget/ConstraintLayout;->onViewRemoved(Landroid/v HSPLandroidx/constraintlayout/widget/ConstraintLayout;->requestLayout()V HSPLandroidx/constraintlayout/widget/ConstraintLayout;->resolveMeasuredDimension(IIIIZZ)V HSPLandroidx/constraintlayout/widget/ConstraintLayout;->resolveSystem(Landroidx/constraintlayout/core/widgets/ConstraintWidgetContainer;III)V -HSPLandroidx/constraintlayout/widget/ConstraintLayout;->setChildrenConstraints()V HSPLandroidx/constraintlayout/widget/ConstraintLayout;->setConstraintSet(Landroidx/constraintlayout/widget/ConstraintSet;)V HSPLandroidx/constraintlayout/widget/ConstraintLayout;->setId(I)V HSPLandroidx/constraintlayout/widget/ConstraintLayout;->setMinHeight(I)V @@ -1408,7 +1487,6 @@ HSPLandroidx/constraintlayout/widget/ConstraintSet;->clone(Landroidx/constraintl HSPLandroidx/constraintlayout/widget/Guideline;->(Landroid/content/Context;Landroid/util/AttributeSet;)V HSPLandroidx/constraintlayout/widget/Guideline;->setGuidelineBegin(I)V HSPLandroidx/constraintlayout/widget/Guideline;->setGuidelineEnd(I)V -HSPLandroidx/constraintlayout/widget/Guideline;->setVisibility(I)V HSPLandroidx/constraintlayout/widget/R$styleable;->()V HSPLandroidx/coordinatorlayout/R$styleable;->()V HSPLandroidx/coordinatorlayout/widget/CoordinatorLayout$Behavior;->(Landroid/content/Context;Landroid/util/AttributeSet;)V @@ -1444,6 +1522,9 @@ HSPLandroidx/coordinatorlayout/widget/CoordinatorLayout$LayoutParams;->setNested HSPLandroidx/coordinatorlayout/widget/CoordinatorLayout$LayoutParams;->shouldDodge(Landroid/view/View;I)Z HSPLandroidx/coordinatorlayout/widget/CoordinatorLayout$OnPreDrawListener;->(Landroidx/coordinatorlayout/widget/CoordinatorLayout;)V HSPLandroidx/coordinatorlayout/widget/CoordinatorLayout$OnPreDrawListener;->onPreDraw()Z +HSPLandroidx/coordinatorlayout/widget/CoordinatorLayout$SavedState$1;->()V +HSPLandroidx/coordinatorlayout/widget/CoordinatorLayout$SavedState;->()V +HSPLandroidx/coordinatorlayout/widget/CoordinatorLayout$SavedState;->(Landroid/os/Parcelable;)V HSPLandroidx/coordinatorlayout/widget/CoordinatorLayout$ViewElevationComparator;->()V HSPLandroidx/coordinatorlayout/widget/CoordinatorLayout$ViewElevationComparator;->compare(Landroid/view/View;Landroid/view/View;)I HSPLandroidx/coordinatorlayout/widget/CoordinatorLayout$ViewElevationComparator;->compare(Ljava/lang/Object;Ljava/lang/Object;)I @@ -1479,6 +1560,7 @@ HSPLandroidx/coordinatorlayout/widget/CoordinatorLayout;->onLayoutChild(Landroid HSPLandroidx/coordinatorlayout/widget/CoordinatorLayout;->onMeasure(II)V HSPLandroidx/coordinatorlayout/widget/CoordinatorLayout;->onMeasureChild(Landroid/view/View;IIII)V HSPLandroidx/coordinatorlayout/widget/CoordinatorLayout;->onNestedScrollAccepted(Landroid/view/View;Landroid/view/View;II)V +HSPLandroidx/coordinatorlayout/widget/CoordinatorLayout;->onSaveInstanceState()Landroid/os/Parcelable; HSPLandroidx/coordinatorlayout/widget/CoordinatorLayout;->onStartNestedScroll(Landroid/view/View;Landroid/view/View;II)Z HSPLandroidx/coordinatorlayout/widget/CoordinatorLayout;->onStopNestedScroll(Landroid/view/View;I)V HSPLandroidx/coordinatorlayout/widget/CoordinatorLayout;->onTouchEvent(Landroid/view/MotionEvent;)Z @@ -1518,6 +1600,7 @@ HSPLandroidx/core/app/BundleCompat;->getBinder(Landroid/os/Bundle;Ljava/lang/Str HSPLandroidx/core/app/BundleCompat;->putBinder(Landroid/os/Bundle;Ljava/lang/String;Landroid/os/IBinder;)V HSPLandroidx/core/app/ComponentActivity;->()V HSPLandroidx/core/app/ComponentActivity;->onCreate(Landroid/os/Bundle;)V +HSPLandroidx/core/app/ComponentActivity;->onSaveInstanceState(Landroid/os/Bundle;)V HSPLandroidx/core/app/CoreComponentFactory;->()V HSPLandroidx/core/app/CoreComponentFactory;->checkCompatWrapper(Ljava/lang/Object;)Ljava/lang/Object; HSPLandroidx/core/app/CoreComponentFactory;->instantiateActivity(Ljava/lang/ClassLoader;Ljava/lang/String;Landroid/content/Intent;)Landroid/app/Activity; @@ -1633,15 +1716,13 @@ HSPLandroidx/core/os/BundleCompat$Api18Impl;->getBinder(Landroid/os/Bundle;Ljava HSPLandroidx/core/os/BundleCompat$Api18Impl;->putBinder(Landroid/os/Bundle;Ljava/lang/String;Landroid/os/IBinder;)V HSPLandroidx/core/os/BundleCompat;->getBinder(Landroid/os/Bundle;Ljava/lang/String;)Landroid/os/IBinder; HSPLandroidx/core/os/BundleCompat;->putBinder(Landroid/os/Bundle;Ljava/lang/String;Landroid/os/IBinder;)V +HSPLandroidx/core/os/BundleKt;->bundleOf([Lkotlin/Pair;)Landroid/os/Bundle; HSPLandroidx/core/os/CancellationSignal;->()V HSPLandroidx/core/os/CancellationSignal;->setOnCancelListener(Landroidx/core/os/CancellationSignal$OnCancelListener;)V HSPLandroidx/core/os/CancellationSignal;->waitForCancelFinishedLocked()V HSPLandroidx/core/os/ConfigurationCompat$Api24Impl$$ExternalSyntheticApiModelOutline0;->m(Ljava/lang/Object;)Landroid/os/LocaleList; HSPLandroidx/core/os/ConfigurationCompat$Api24Impl;->getLocales(Landroid/content/res/Configuration;)Landroid/os/LocaleList; HSPLandroidx/core/os/ConfigurationCompat;->getLocales(Landroid/content/res/Configuration;)Landroidx/core/os/LocaleListCompat; -HSPLandroidx/core/os/HandlerCompat$Api28Impl$$ExternalSyntheticApiModelOutline0;->m(Landroid/os/Handler;Ljava/lang/Runnable;Ljava/lang/Object;J)Z -HSPLandroidx/core/os/HandlerCompat$Api28Impl;->postDelayed(Landroid/os/Handler;Ljava/lang/Runnable;Ljava/lang/Object;J)Z -HSPLandroidx/core/os/HandlerCompat;->postDelayed(Landroid/os/Handler;Ljava/lang/Runnable;Ljava/lang/Object;J)Z HSPLandroidx/core/os/LocaleListCompat$Api21Impl;->()V HSPLandroidx/core/os/LocaleListCompat$Api21Impl;->forLanguageTag(Ljava/lang/String;)Ljava/util/Locale; HSPLandroidx/core/os/LocaleListCompat$Api24Impl;->createLocaleList([Ljava/util/Locale;)Landroid/os/LocaleList; @@ -1731,6 +1812,7 @@ HSPLandroidx/core/view/MenuHostHelper;->onPrepareMenu(Landroid/view/Menu;)V HSPLandroidx/core/view/MenuHostHelper;->removeMenuProvider(Landroidx/core/view/MenuProvider;)V HSPLandroidx/core/view/MenuItemCompat;->setAlphabeticShortcut(Landroid/view/MenuItem;CI)V HSPLandroidx/core/view/MenuItemCompat;->setContentDescription(Landroid/view/MenuItem;Ljava/lang/CharSequence;)V +HSPLandroidx/core/view/MenuItemCompat;->setIconTintList(Landroid/view/MenuItem;Landroid/content/res/ColorStateList;)V HSPLandroidx/core/view/MenuItemCompat;->setNumericShortcut(Landroid/view/MenuItem;CI)V HSPLandroidx/core/view/MenuItemCompat;->setTooltipText(Landroid/view/MenuItem;Ljava/lang/CharSequence;)V HSPLandroidx/core/view/NestedScrollingChildHelper;->(Landroid/view/View;)V @@ -1886,6 +1968,7 @@ HSPLandroidx/core/view/ViewGroupKt;->iterator(Landroid/view/ViewGroup;)Ljava/uti HSPLandroidx/core/view/ViewKt$doOnPreDraw$1;->(Lkotlin/jvm/functions/Function1;Landroid/view/View;)V HSPLandroidx/core/view/ViewKt$doOnPreDraw$1;->run()V HSPLandroidx/core/view/ViewKt;->doOnPreDraw(Landroid/view/View;Lkotlin/jvm/functions/Function1;)Landroidx/core/view/OneShotPreDrawListener; +HSPLandroidx/core/view/ViewKt;->isVisible(Landroid/view/View;)Z HSPLandroidx/core/view/ViewParentCompat;->onNestedScrollAccepted(Landroid/view/ViewParent;Landroid/view/View;Landroid/view/View;II)V HSPLandroidx/core/view/ViewParentCompat;->onStartNestedScroll(Landroid/view/ViewParent;Landroid/view/View;Landroid/view/View;II)Z HSPLandroidx/core/view/ViewParentCompat;->onStopNestedScroll(Landroid/view/ViewParent;Landroid/view/View;I)V @@ -2017,6 +2100,12 @@ HSPLandroidx/core/widget/TextViewCompat;->wrapCustomSelectionActionModeCallback( HSPLandroidx/core/widget/TextViewOnReceiveContentListener;->()V HSPLandroidx/customview/poolingcontainer/PoolingContainer;->()V HSPLandroidx/customview/poolingcontainer/PoolingContainer;->setPoolingContainer(Landroid/view/View;Z)V +HSPLandroidx/customview/view/AbsSavedState$1;->()V +HSPLandroidx/customview/view/AbsSavedState$2;->()V +HSPLandroidx/customview/view/AbsSavedState;->()V +HSPLandroidx/customview/view/AbsSavedState;->()V +HSPLandroidx/customview/view/AbsSavedState;->(Landroid/os/Parcelable;)V +HSPLandroidx/customview/view/AbsSavedState;->(Landroidx/customview/view/AbsSavedState$1;)V HSPLandroidx/customview/widget/ExploreByTouchHelper$1;->()V HSPLandroidx/customview/widget/ExploreByTouchHelper$2;->()V HSPLandroidx/customview/widget/ExploreByTouchHelper;->()V @@ -2087,6 +2176,7 @@ HSPLandroidx/emoji2/text/SpannableBuilder$WatcherWrapper;->(Ljava/lang/Obj HSPLandroidx/emoji2/text/SpannableBuilder$WatcherWrapper;->onSpanAdded(Landroid/text/Spannable;Ljava/lang/Object;II)V HSPLandroidx/emoji2/text/SpannableBuilder;->(Ljava/lang/Class;Ljava/lang/CharSequence;)V HSPLandroidx/emoji2/text/SpannableBuilder;->create(Ljava/lang/Class;Ljava/lang/CharSequence;)Landroidx/emoji2/text/SpannableBuilder; +HSPLandroidx/emoji2/text/SpannableBuilder;->getSpanEnd(Ljava/lang/Object;)I HSPLandroidx/emoji2/text/SpannableBuilder;->getSpanFlags(Ljava/lang/Object;)I HSPLandroidx/emoji2/text/SpannableBuilder;->getSpanStart(Ljava/lang/Object;)I HSPLandroidx/emoji2/text/SpannableBuilder;->getSpans(IILjava/lang/Class;)[Ljava/lang/Object; @@ -2177,6 +2267,7 @@ HSPLandroidx/fragment/app/Fragment$7;->apply(Ljava/lang/Void;)Landroidx/activity HSPLandroidx/fragment/app/Fragment$9;->(Landroidx/fragment/app/Fragment;Landroidx/arch/core/util/Function;Ljava/util/concurrent/atomic/AtomicReference;Landroidx/activity/result/contract/ActivityResultContract;Landroidx/activity/result/ActivityResultCallback;)V HSPLandroidx/fragment/app/Fragment$9;->onPreAttached()V HSPLandroidx/fragment/app/Fragment$AnimationInfo;->()V +HSPLandroidx/fragment/app/Fragment$Api19Impl;->cancelPendingInputEvents(Landroid/view/View;)V HSPLandroidx/fragment/app/Fragment$OnPreAttachedListener;->()V HSPLandroidx/fragment/app/Fragment$OnPreAttachedListener;->(Landroidx/fragment/app/Fragment$1;)V HSPLandroidx/fragment/app/Fragment;->$r8$lambda$Cl7MxTaA6NVZ8I5KAGBxRTLl1sc(Landroidx/fragment/app/Fragment;)V @@ -2193,6 +2284,7 @@ HSPLandroidx/fragment/app/Fragment;->getChildFragmentManager()Landroidx/fragment HSPLandroidx/fragment/app/Fragment;->getContext()Landroid/content/Context; HSPLandroidx/fragment/app/Fragment;->getDefaultViewModelCreationExtras()Landroidx/lifecycle/viewmodel/CreationExtras; HSPLandroidx/fragment/app/Fragment;->getFocusedView()Landroid/view/View; +HSPLandroidx/fragment/app/Fragment;->getHost()Ljava/lang/Object; HSPLandroidx/fragment/app/Fragment;->getId()I HSPLandroidx/fragment/app/Fragment;->getLayoutInflater(Landroid/os/Bundle;)Landroid/view/LayoutInflater; HSPLandroidx/fragment/app/Fragment;->getLifecycle()Landroidx/lifecycle/Lifecycle; @@ -2229,7 +2321,9 @@ HSPLandroidx/fragment/app/Fragment;->onInflate(Landroid/content/Context;Landroid HSPLandroidx/fragment/app/Fragment;->onPause()V HSPLandroidx/fragment/app/Fragment;->onPrimaryNavigationFragmentChanged(Z)V HSPLandroidx/fragment/app/Fragment;->onResume()V +HSPLandroidx/fragment/app/Fragment;->onSaveInstanceState(Landroid/os/Bundle;)V HSPLandroidx/fragment/app/Fragment;->onStart()V +HSPLandroidx/fragment/app/Fragment;->onStop()V HSPLandroidx/fragment/app/Fragment;->onViewCreated(Landroid/view/View;Landroid/os/Bundle;)V HSPLandroidx/fragment/app/Fragment;->onViewStateRestored(Landroid/os/Bundle;)V HSPLandroidx/fragment/app/Fragment;->performActivityCreated(Landroid/os/Bundle;)V @@ -2242,7 +2336,9 @@ HSPLandroidx/fragment/app/Fragment;->performPause()V HSPLandroidx/fragment/app/Fragment;->performPrepareOptionsMenu(Landroid/view/Menu;)Z HSPLandroidx/fragment/app/Fragment;->performPrimaryNavigationFragmentChanged()V HSPLandroidx/fragment/app/Fragment;->performResume()V +HSPLandroidx/fragment/app/Fragment;->performSaveInstanceState(Landroid/os/Bundle;)V HSPLandroidx/fragment/app/Fragment;->performStart()V +HSPLandroidx/fragment/app/Fragment;->performStop()V HSPLandroidx/fragment/app/Fragment;->performViewCreated()V HSPLandroidx/fragment/app/Fragment;->prepareCallInternal(Landroidx/activity/result/contract/ActivityResultContract;Landroidx/arch/core/util/Function;Landroidx/activity/result/ActivityResultCallback;)Landroidx/activity/result/ActivityResultLauncher; HSPLandroidx/fragment/app/Fragment;->registerForActivityResult(Landroidx/activity/result/contract/ActivityResultContract;Landroidx/activity/result/ActivityResultCallback;)Landroidx/activity/result/ActivityResultLauncher; @@ -2263,6 +2359,7 @@ HSPLandroidx/fragment/app/Fragment;->setPopDirection(Z)V HSPLandroidx/fragment/app/Fragment;->setPostOnViewCreatedAlpha(F)V HSPLandroidx/fragment/app/Fragment;->setSharedElementNames(Ljava/util/ArrayList;Ljava/util/ArrayList;)V HSPLandroidx/fragment/app/FragmentActivity$$ExternalSyntheticLambda0;->(Landroidx/fragment/app/FragmentActivity;)V +HSPLandroidx/fragment/app/FragmentActivity$$ExternalSyntheticLambda0;->saveState()Landroid/os/Bundle; HSPLandroidx/fragment/app/FragmentActivity$$ExternalSyntheticLambda1;->(Landroidx/fragment/app/FragmentActivity;)V HSPLandroidx/fragment/app/FragmentActivity$$ExternalSyntheticLambda2;->(Landroidx/fragment/app/FragmentActivity;)V HSPLandroidx/fragment/app/FragmentActivity$$ExternalSyntheticLambda3;->(Landroidx/fragment/app/FragmentActivity;)V @@ -2281,6 +2378,8 @@ HSPLandroidx/fragment/app/FragmentActivity$HostCallbacks;->getViewModelStore()La HSPLandroidx/fragment/app/FragmentActivity$HostCallbacks;->invalidateMenu()V HSPLandroidx/fragment/app/FragmentActivity$HostCallbacks;->onAttachFragment(Landroidx/fragment/app/FragmentManager;Landroidx/fragment/app/Fragment;)V HSPLandroidx/fragment/app/FragmentActivity$HostCallbacks;->onFindViewById(I)Landroid/view/View; +HSPLandroidx/fragment/app/FragmentActivity$HostCallbacks;->onGetHost()Landroidx/fragment/app/FragmentActivity; +HSPLandroidx/fragment/app/FragmentActivity$HostCallbacks;->onGetHost()Ljava/lang/Object; HSPLandroidx/fragment/app/FragmentActivity$HostCallbacks;->onGetLayoutInflater()Landroid/view/LayoutInflater; HSPLandroidx/fragment/app/FragmentActivity$HostCallbacks;->onHasView()Z HSPLandroidx/fragment/app/FragmentActivity$HostCallbacks;->onSupportInvalidateOptionsMenu()V @@ -2290,10 +2389,12 @@ HSPLandroidx/fragment/app/FragmentActivity$HostCallbacks;->removeOnMultiWindowMo HSPLandroidx/fragment/app/FragmentActivity$HostCallbacks;->removeOnPictureInPictureModeChangedListener(Landroidx/core/util/Consumer;)V HSPLandroidx/fragment/app/FragmentActivity$HostCallbacks;->removeOnTrimMemoryListener(Landroidx/core/util/Consumer;)V HSPLandroidx/fragment/app/FragmentActivity;->$r8$lambda$euPNEtWNfVdMY89Jt5kWt_WEHqw(Landroidx/fragment/app/FragmentActivity;Landroid/content/Context;)V +HSPLandroidx/fragment/app/FragmentActivity;->$r8$lambda$t3WwJ1XbNlapyNW0l552nMkkXdo(Landroidx/fragment/app/FragmentActivity;)Landroid/os/Bundle; HSPLandroidx/fragment/app/FragmentActivity;->()V HSPLandroidx/fragment/app/FragmentActivity;->dispatchFragmentsOnCreateView(Landroid/view/View;Ljava/lang/String;Landroid/content/Context;Landroid/util/AttributeSet;)Landroid/view/View; HSPLandroidx/fragment/app/FragmentActivity;->getSupportFragmentManager()Landroidx/fragment/app/FragmentManager; HSPLandroidx/fragment/app/FragmentActivity;->init()V +HSPLandroidx/fragment/app/FragmentActivity;->lambda$init$0()Landroid/os/Bundle; HSPLandroidx/fragment/app/FragmentActivity;->lambda$init$3(Landroid/content/Context;)V HSPLandroidx/fragment/app/FragmentActivity;->markFragmentsCreated()V HSPLandroidx/fragment/app/FragmentActivity;->markState(Landroidx/fragment/app/FragmentManager;Landroidx/lifecycle/Lifecycle$State;)Z @@ -2354,13 +2455,16 @@ HSPLandroidx/fragment/app/FragmentLifecycleCallbacksDispatcher;->dispatchOnFragm HSPLandroidx/fragment/app/FragmentLifecycleCallbacksDispatcher;->dispatchOnFragmentPreAttached(Landroidx/fragment/app/Fragment;Z)V HSPLandroidx/fragment/app/FragmentLifecycleCallbacksDispatcher;->dispatchOnFragmentPreCreated(Landroidx/fragment/app/Fragment;Landroid/os/Bundle;Z)V HSPLandroidx/fragment/app/FragmentLifecycleCallbacksDispatcher;->dispatchOnFragmentResumed(Landroidx/fragment/app/Fragment;Z)V +HSPLandroidx/fragment/app/FragmentLifecycleCallbacksDispatcher;->dispatchOnFragmentSaveInstanceState(Landroidx/fragment/app/Fragment;Landroid/os/Bundle;Z)V HSPLandroidx/fragment/app/FragmentLifecycleCallbacksDispatcher;->dispatchOnFragmentStarted(Landroidx/fragment/app/Fragment;Z)V +HSPLandroidx/fragment/app/FragmentLifecycleCallbacksDispatcher;->dispatchOnFragmentStopped(Landroidx/fragment/app/Fragment;Z)V HSPLandroidx/fragment/app/FragmentLifecycleCallbacksDispatcher;->dispatchOnFragmentViewCreated(Landroidx/fragment/app/Fragment;Landroid/view/View;Landroid/os/Bundle;Z)V HSPLandroidx/fragment/app/FragmentManager$$ExternalSyntheticLambda0;->(Landroidx/fragment/app/FragmentManager;)V HSPLandroidx/fragment/app/FragmentManager$$ExternalSyntheticLambda1;->(Landroidx/fragment/app/FragmentManager;)V HSPLandroidx/fragment/app/FragmentManager$$ExternalSyntheticLambda2;->(Landroidx/fragment/app/FragmentManager;)V HSPLandroidx/fragment/app/FragmentManager$$ExternalSyntheticLambda3;->(Landroidx/fragment/app/FragmentManager;)V HSPLandroidx/fragment/app/FragmentManager$$ExternalSyntheticLambda4;->(Landroidx/fragment/app/FragmentManager;)V +HSPLandroidx/fragment/app/FragmentManager$$ExternalSyntheticLambda4;->saveState()Landroid/os/Bundle; HSPLandroidx/fragment/app/FragmentManager$10;->(Landroidx/fragment/app/FragmentManager;)V HSPLandroidx/fragment/app/FragmentManager$1;->(Landroidx/fragment/app/FragmentManager;Z)V HSPLandroidx/fragment/app/FragmentManager$2;->(Landroidx/fragment/app/FragmentManager;)V @@ -2379,6 +2483,7 @@ HSPLandroidx/fragment/app/FragmentManager$8;->(Landroidx/fragment/app/Frag HSPLandroidx/fragment/app/FragmentManager$9;->(Landroidx/fragment/app/FragmentManager;)V HSPLandroidx/fragment/app/FragmentManager$FragmentIntentSenderContract;->()V HSPLandroidx/fragment/app/FragmentManager$LifecycleAwareResultListener;->(Landroidx/lifecycle/Lifecycle;Landroidx/fragment/app/FragmentResultListener;Landroidx/lifecycle/LifecycleEventObserver;)V +HSPLandroidx/fragment/app/FragmentManager;->$r8$lambda$sido8p6zuWx0PQxIkv4qM-BRiGM(Landroidx/fragment/app/FragmentManager;)Landroid/os/Bundle; HSPLandroidx/fragment/app/FragmentManager;->()V HSPLandroidx/fragment/app/FragmentManager;->()V HSPLandroidx/fragment/app/FragmentManager;->access$000(Landroidx/fragment/app/FragmentManager;)Ljava/util/Map; @@ -2421,6 +2526,7 @@ HSPLandroidx/fragment/app/FragmentManager;->findActiveFragment(Ljava/lang/String HSPLandroidx/fragment/app/FragmentManager;->findFragment(Landroid/view/View;)Landroidx/fragment/app/Fragment; HSPLandroidx/fragment/app/FragmentManager;->findFragmentById(I)Landroidx/fragment/app/Fragment; HSPLandroidx/fragment/app/FragmentManager;->findViewFragment(Landroid/view/View;)Landroidx/fragment/app/Fragment; +HSPLandroidx/fragment/app/FragmentManager;->forcePostponedTransactions()V HSPLandroidx/fragment/app/FragmentManager;->generateOpsForPendingActions(Ljava/util/ArrayList;Ljava/util/ArrayList;)Z HSPLandroidx/fragment/app/FragmentManager;->getBackStackEntryCount()I HSPLandroidx/fragment/app/FragmentManager;->getChildNonConfig(Landroidx/fragment/app/Fragment;)Landroidx/fragment/app/FragmentManagerViewModel; @@ -2443,11 +2549,13 @@ HSPLandroidx/fragment/app/FragmentManager;->isParentMenuVisible(Landroidx/fragme HSPLandroidx/fragment/app/FragmentManager;->isPrimaryNavigation(Landroidx/fragment/app/Fragment;)Z HSPLandroidx/fragment/app/FragmentManager;->isStateAtLeast(I)Z HSPLandroidx/fragment/app/FragmentManager;->isStateSaved()Z +HSPLandroidx/fragment/app/FragmentManager;->lambda$attachController$4()Landroid/os/Bundle; HSPLandroidx/fragment/app/FragmentManager;->moveToState(IZ)V HSPLandroidx/fragment/app/FragmentManager;->noteStateNotSaved()V HSPLandroidx/fragment/app/FragmentManager;->onContainerAvailable(Landroidx/fragment/app/FragmentContainerView;)V HSPLandroidx/fragment/app/FragmentManager;->performPendingDeferredStart(Landroidx/fragment/app/FragmentStateManager;)V HSPLandroidx/fragment/app/FragmentManager;->removeRedundantOperationsAndExecute(Ljava/util/ArrayList;Ljava/util/ArrayList;)V +HSPLandroidx/fragment/app/FragmentManager;->saveAllStateInternal()Landroid/os/Bundle; HSPLandroidx/fragment/app/FragmentManager;->scheduleCommit()V HSPLandroidx/fragment/app/FragmentManager;->setExitAnimationOrder(Landroidx/fragment/app/Fragment;Z)V HSPLandroidx/fragment/app/FragmentManager;->setFragmentResultListener(Ljava/lang/String;Landroidx/lifecycle/LifecycleOwner;Landroidx/fragment/app/FragmentResultListener;)V @@ -2455,6 +2563,9 @@ HSPLandroidx/fragment/app/FragmentManager;->setPrimaryNavigationFragment(Landroi HSPLandroidx/fragment/app/FragmentManager;->startPendingDeferredFragments()V HSPLandroidx/fragment/app/FragmentManager;->updateOnBackPressedCallbackEnabled()V HSPLandroidx/fragment/app/FragmentManagerImpl;->()V +HSPLandroidx/fragment/app/FragmentManagerState$1;->()V +HSPLandroidx/fragment/app/FragmentManagerState;->()V +HSPLandroidx/fragment/app/FragmentManagerState;->()V HSPLandroidx/fragment/app/FragmentManagerViewModel$1;->()V HSPLandroidx/fragment/app/FragmentManagerViewModel$1;->create(Ljava/lang/Class;)Landroidx/lifecycle/ViewModel; HSPLandroidx/fragment/app/FragmentManagerViewModel$1;->create(Ljava/lang/Class;Landroidx/lifecycle/viewmodel/CreationExtras;)Landroidx/lifecycle/ViewModel; @@ -2466,6 +2577,9 @@ HSPLandroidx/fragment/app/FragmentManagerViewModel;->getViewModelStore(Landroidx HSPLandroidx/fragment/app/FragmentManagerViewModel;->isCleared()Z HSPLandroidx/fragment/app/FragmentManagerViewModel;->onCleared()V HSPLandroidx/fragment/app/FragmentManagerViewModel;->setIsStateSaved(Z)V +HSPLandroidx/fragment/app/FragmentState$1;->()V +HSPLandroidx/fragment/app/FragmentState;->()V +HSPLandroidx/fragment/app/FragmentState;->(Landroidx/fragment/app/Fragment;)V HSPLandroidx/fragment/app/FragmentStateManager$1;->(Landroidx/fragment/app/FragmentStateManager;Landroid/view/View;)V HSPLandroidx/fragment/app/FragmentStateManager$1;->onViewAttachedToWindow(Landroid/view/View;)V HSPLandroidx/fragment/app/FragmentStateManager$2;->()V @@ -2483,8 +2597,11 @@ HSPLandroidx/fragment/app/FragmentStateManager;->moveToExpectedState()V HSPLandroidx/fragment/app/FragmentStateManager;->pause()V HSPLandroidx/fragment/app/FragmentStateManager;->restoreState(Ljava/lang/ClassLoader;)V HSPLandroidx/fragment/app/FragmentStateManager;->resume()V +HSPLandroidx/fragment/app/FragmentStateManager;->saveState()Landroid/os/Bundle; +HSPLandroidx/fragment/app/FragmentStateManager;->saveViewState()V HSPLandroidx/fragment/app/FragmentStateManager;->setFragmentManagerState(I)V HSPLandroidx/fragment/app/FragmentStateManager;->start()V +HSPLandroidx/fragment/app/FragmentStateManager;->stop()V HSPLandroidx/fragment/app/FragmentStore;->()V HSPLandroidx/fragment/app/FragmentStore;->addFragment(Landroidx/fragment/app/Fragment;)V HSPLandroidx/fragment/app/FragmentStore;->burpActive()V @@ -2495,11 +2612,14 @@ HSPLandroidx/fragment/app/FragmentStore;->findFragmentById(I)Landroidx/fragment/ HSPLandroidx/fragment/app/FragmentStore;->findFragmentIndexInContainer(Landroidx/fragment/app/Fragment;)I HSPLandroidx/fragment/app/FragmentStore;->getActiveFragmentStateManagers()Ljava/util/List; HSPLandroidx/fragment/app/FragmentStore;->getActiveFragments()Ljava/util/List; +HSPLandroidx/fragment/app/FragmentStore;->getAllSavedState()Ljava/util/HashMap; HSPLandroidx/fragment/app/FragmentStore;->getFragmentStateManager(Ljava/lang/String;)Landroidx/fragment/app/FragmentStateManager; HSPLandroidx/fragment/app/FragmentStore;->getFragments()Ljava/util/List; HSPLandroidx/fragment/app/FragmentStore;->getNonConfig()Landroidx/fragment/app/FragmentManagerViewModel; HSPLandroidx/fragment/app/FragmentStore;->makeActive(Landroidx/fragment/app/FragmentStateManager;)V HSPLandroidx/fragment/app/FragmentStore;->moveToExpectedState()V +HSPLandroidx/fragment/app/FragmentStore;->saveActiveFragments()Ljava/util/ArrayList; +HSPLandroidx/fragment/app/FragmentStore;->saveAddedFragments()Ljava/util/ArrayList; HSPLandroidx/fragment/app/FragmentStore;->setNonConfig(Landroidx/fragment/app/FragmentManagerViewModel;)V HSPLandroidx/fragment/app/FragmentStore;->setSavedState(Ljava/lang/String;Landroid/os/Bundle;)Landroid/os/Bundle; HSPLandroidx/fragment/app/FragmentTransaction$Op;->(ILandroidx/fragment/app/Fragment;)V @@ -2520,6 +2640,8 @@ HSPLandroidx/fragment/app/FragmentViewLifecycleOwner;->getSavedStateRegistry()La HSPLandroidx/fragment/app/FragmentViewLifecycleOwner;->handleLifecycleEvent(Landroidx/lifecycle/Lifecycle$Event;)V HSPLandroidx/fragment/app/FragmentViewLifecycleOwner;->initialize()V HSPLandroidx/fragment/app/FragmentViewLifecycleOwner;->performRestore(Landroid/os/Bundle;)V +HSPLandroidx/fragment/app/FragmentViewLifecycleOwner;->performSave(Landroid/os/Bundle;)V +HSPLandroidx/fragment/app/FragmentViewLifecycleOwner;->setCurrentState(Landroidx/lifecycle/Lifecycle$State;)V HSPLandroidx/fragment/app/FragmentViewModelLazyKt;->access$viewModels$lambda-1(Lkotlin/Lazy;)Landroidx/lifecycle/ViewModelStoreOwner; HSPLandroidx/fragment/app/FragmentViewModelLazyKt;->createViewModelLazy(Landroidx/fragment/app/Fragment;Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;)Lkotlin/Lazy; HSPLandroidx/fragment/app/FragmentViewModelLazyKt;->viewModels$lambda-1(Lkotlin/Lazy;)Landroidx/lifecycle/ViewModelStoreOwner; @@ -2572,6 +2694,7 @@ HSPLandroidx/fragment/app/SpecialEffectsController;->executePendingOperations()V HSPLandroidx/fragment/app/SpecialEffectsController;->findPendingOperation(Landroidx/fragment/app/Fragment;)Landroidx/fragment/app/SpecialEffectsController$Operation; HSPLandroidx/fragment/app/SpecialEffectsController;->findRunningOperation(Landroidx/fragment/app/Fragment;)Landroidx/fragment/app/SpecialEffectsController$Operation; HSPLandroidx/fragment/app/SpecialEffectsController;->forceCompleteAllOperations()V +HSPLandroidx/fragment/app/SpecialEffectsController;->forcePostponedExecutePendingOperations()V HSPLandroidx/fragment/app/SpecialEffectsController;->getAwaitingCompletionLifecycleImpact(Landroidx/fragment/app/FragmentStateManager;)Landroidx/fragment/app/SpecialEffectsController$Operation$LifecycleImpact; HSPLandroidx/fragment/app/SpecialEffectsController;->getOrCreateController(Landroid/view/ViewGroup;Landroidx/fragment/app/FragmentManager;)Landroidx/fragment/app/SpecialEffectsController; HSPLandroidx/fragment/app/SpecialEffectsController;->getOrCreateController(Landroid/view/ViewGroup;Landroidx/fragment/app/SpecialEffectsControllerFactory;)Landroidx/fragment/app/SpecialEffectsController; @@ -2622,6 +2745,7 @@ HSPLandroidx/lifecycle/EmptyActivityLifecycleCallbacks;->onActivityCreated(Landr HSPLandroidx/lifecycle/EmptyActivityLifecycleCallbacks;->onActivityDestroyed(Landroid/app/Activity;)V HSPLandroidx/lifecycle/EmptyActivityLifecycleCallbacks;->onActivityPaused(Landroid/app/Activity;)V HSPLandroidx/lifecycle/EmptyActivityLifecycleCallbacks;->onActivityResumed(Landroid/app/Activity;)V +HSPLandroidx/lifecycle/EmptyActivityLifecycleCallbacks;->onActivitySaveInstanceState(Landroid/app/Activity;Landroid/os/Bundle;)V HSPLandroidx/lifecycle/EmptyActivityLifecycleCallbacks;->onActivityStarted(Landroid/app/Activity;)V HSPLandroidx/lifecycle/EmptyActivityLifecycleCallbacks;->onActivityStopped(Landroid/app/Activity;)V HSPLandroidx/lifecycle/LegacySavedStateHandleController;->()V @@ -2664,9 +2788,11 @@ HSPLandroidx/lifecycle/LifecycleRegistry;->addObserver(Landroidx/lifecycle/Lifec HSPLandroidx/lifecycle/LifecycleRegistry;->backwardPass(Landroidx/lifecycle/LifecycleOwner;)V HSPLandroidx/lifecycle/LifecycleRegistry;->calculateTargetState(Landroidx/lifecycle/LifecycleObserver;)Landroidx/lifecycle/Lifecycle$State; HSPLandroidx/lifecycle/LifecycleRegistry;->enforceMainThreadIfNeeded(Ljava/lang/String;)V +HSPLandroidx/lifecycle/LifecycleRegistry;->forwardPass(Landroidx/lifecycle/LifecycleOwner;)V HSPLandroidx/lifecycle/LifecycleRegistry;->getCurrentState()Landroidx/lifecycle/Lifecycle$State; HSPLandroidx/lifecycle/LifecycleRegistry;->handleLifecycleEvent(Landroidx/lifecycle/Lifecycle$Event;)V HSPLandroidx/lifecycle/LifecycleRegistry;->isSynced()Z +HSPLandroidx/lifecycle/LifecycleRegistry;->markState(Landroidx/lifecycle/Lifecycle$State;)V HSPLandroidx/lifecycle/LifecycleRegistry;->moveToState(Landroidx/lifecycle/Lifecycle$State;)V HSPLandroidx/lifecycle/LifecycleRegistry;->popParentState()V HSPLandroidx/lifecycle/LifecycleRegistry;->pushParentState(Landroidx/lifecycle/Lifecycle$State;)V @@ -2714,6 +2840,7 @@ HSPLandroidx/lifecycle/MediatorLiveData$Source;->unplug()V HSPLandroidx/lifecycle/MediatorLiveData;->()V HSPLandroidx/lifecycle/MediatorLiveData;->addSource(Landroidx/lifecycle/LiveData;Landroidx/lifecycle/Observer;)V HSPLandroidx/lifecycle/MediatorLiveData;->onActive()V +HSPLandroidx/lifecycle/MediatorLiveData;->onInactive()V HSPLandroidx/lifecycle/MediatorLiveData;->removeSource(Landroidx/lifecycle/LiveData;)V HSPLandroidx/lifecycle/MutableLiveData;->()V HSPLandroidx/lifecycle/MutableLiveData;->(Ljava/lang/Object;)V @@ -2773,6 +2900,7 @@ HSPLandroidx/lifecycle/ReportFragment$LifecycleCallbacks;->onActivityPreDestroye HSPLandroidx/lifecycle/ReportFragment$LifecycleCallbacks;->onActivityPrePaused(Landroid/app/Activity;)V HSPLandroidx/lifecycle/ReportFragment$LifecycleCallbacks;->onActivityPreStopped(Landroid/app/Activity;)V HSPLandroidx/lifecycle/ReportFragment$LifecycleCallbacks;->onActivityResumed(Landroid/app/Activity;)V +HSPLandroidx/lifecycle/ReportFragment$LifecycleCallbacks;->onActivitySaveInstanceState(Landroid/app/Activity;Landroid/os/Bundle;)V HSPLandroidx/lifecycle/ReportFragment$LifecycleCallbacks;->onActivityStarted(Landroid/app/Activity;)V HSPLandroidx/lifecycle/ReportFragment$LifecycleCallbacks;->onActivityStopped(Landroid/app/Activity;)V HSPLandroidx/lifecycle/ReportFragment;->()V @@ -2792,10 +2920,13 @@ HSPLandroidx/lifecycle/SavedStateHandle$$ExternalSyntheticLambda0;->(Landr HSPLandroidx/lifecycle/SavedStateHandle$Companion;->()V HSPLandroidx/lifecycle/SavedStateHandle$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLandroidx/lifecycle/SavedStateHandle$Companion;->createHandle(Landroid/os/Bundle;Landroid/os/Bundle;)Landroidx/lifecycle/SavedStateHandle; +HSPLandroidx/lifecycle/SavedStateHandle$Companion;->validateValue(Ljava/lang/Object;)Z HSPLandroidx/lifecycle/SavedStateHandle;->()V HSPLandroidx/lifecycle/SavedStateHandle;->()V +HSPLandroidx/lifecycle/SavedStateHandle;->access$getACCEPTABLE_CLASSES$cp()[Ljava/lang/Class; HSPLandroidx/lifecycle/SavedStateHandle;->get(Ljava/lang/String;)Ljava/lang/Object; HSPLandroidx/lifecycle/SavedStateHandle;->savedStateProvider()Landroidx/savedstate/SavedStateRegistry$SavedStateProvider; +HSPLandroidx/lifecycle/SavedStateHandle;->set(Ljava/lang/String;Ljava/lang/Object;)V HSPLandroidx/lifecycle/SavedStateHandleAttacher;->(Landroidx/lifecycle/SavedStateHandlesProvider;)V HSPLandroidx/lifecycle/SavedStateHandleAttacher;->onStateChanged(Landroidx/lifecycle/LifecycleOwner;Landroidx/lifecycle/Lifecycle$Event;)V HSPLandroidx/lifecycle/SavedStateHandleController;->(Ljava/lang/String;Landroidx/lifecycle/SavedStateHandle;)V @@ -2818,7 +2949,9 @@ HSPLandroidx/lifecycle/SavedStateHandlesProvider$viewModel$2;->invoke()Ljava/lan HSPLandroidx/lifecycle/SavedStateHandlesProvider;->(Landroidx/savedstate/SavedStateRegistry;Landroidx/lifecycle/ViewModelStoreOwner;)V HSPLandroidx/lifecycle/SavedStateHandlesProvider;->getViewModel()Landroidx/lifecycle/SavedStateHandlesVM; HSPLandroidx/lifecycle/SavedStateHandlesProvider;->performRestore()V +HSPLandroidx/lifecycle/SavedStateHandlesProvider;->saveState()Landroid/os/Bundle; HSPLandroidx/lifecycle/SavedStateHandlesVM;->()V +HSPLandroidx/lifecycle/SavedStateHandlesVM;->getHandles()Ljava/util/Map; HSPLandroidx/lifecycle/SavedStateViewModelFactory;->(Landroid/app/Application;Landroidx/savedstate/SavedStateRegistryOwner;Landroid/os/Bundle;)V HSPLandroidx/lifecycle/SavedStateViewModelFactory;->create(Ljava/lang/Class;Landroidx/lifecycle/viewmodel/CreationExtras;)Landroidx/lifecycle/ViewModel; HSPLandroidx/lifecycle/SavedStateViewModelFactory;->onRequery(Landroidx/lifecycle/ViewModel;)V @@ -4597,8 +4730,14 @@ HSPLandroidx/navigation/NavBackStackEntry;->getSavedStateRegistry()Landroidx/sav HSPLandroidx/navigation/NavBackStackEntry;->getViewModelStore()Landroidx/lifecycle/ViewModelStore; HSPLandroidx/navigation/NavBackStackEntry;->handleLifecycleEvent(Landroidx/lifecycle/Lifecycle$Event;)V HSPLandroidx/navigation/NavBackStackEntry;->hashCode()I +HSPLandroidx/navigation/NavBackStackEntry;->saveState(Landroid/os/Bundle;)V HSPLandroidx/navigation/NavBackStackEntry;->setMaxLifecycle(Landroidx/lifecycle/Lifecycle$State;)V HSPLandroidx/navigation/NavBackStackEntry;->updateState()V +HSPLandroidx/navigation/NavBackStackEntryState$Companion$CREATOR$1;->()V +HSPLandroidx/navigation/NavBackStackEntryState$Companion;->()V +HSPLandroidx/navigation/NavBackStackEntryState$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLandroidx/navigation/NavBackStackEntryState;->()V +HSPLandroidx/navigation/NavBackStackEntryState;->(Landroidx/navigation/NavBackStackEntry;)V HSPLandroidx/navigation/NavController$$ExternalSyntheticLambda0;->(Landroidx/navigation/NavController;)V HSPLandroidx/navigation/NavController$$ExternalSyntheticLambda0;->onStateChanged(Landroidx/lifecycle/LifecycleOwner;Landroidx/lifecycle/Lifecycle$Event;)V HSPLandroidx/navigation/NavController$Companion;->()V @@ -4649,6 +4788,7 @@ HSPLandroidx/navigation/NavController;->navigateInternal(Landroidx/navigation/Na HSPLandroidx/navigation/NavController;->onGraphCreated(Landroid/os/Bundle;)V HSPLandroidx/navigation/NavController;->populateVisibleEntries$navigation_runtime_release()Ljava/util/List; HSPLandroidx/navigation/NavController;->removeOnDestinationChangedListener(Landroidx/navigation/NavController$OnDestinationChangedListener;)V +HSPLandroidx/navigation/NavController;->saveState()Landroid/os/Bundle; HSPLandroidx/navigation/NavController;->setGraph(I)V HSPLandroidx/navigation/NavController;->setGraph(Landroidx/navigation/NavGraph;Landroid/os/Bundle;)V HSPLandroidx/navigation/NavController;->setLifecycleOwner(Landroidx/lifecycle/LifecycleOwner;)V @@ -4781,6 +4921,7 @@ HSPLandroidx/navigation/Navigator;->()V HSPLandroidx/navigation/Navigator;->getState()Landroidx/navigation/NavigatorState; HSPLandroidx/navigation/Navigator;->isAttached()Z HSPLandroidx/navigation/Navigator;->onAttach(Landroidx/navigation/NavigatorState;)V +HSPLandroidx/navigation/Navigator;->onSaveState()Landroid/os/Bundle; HSPLandroidx/navigation/NavigatorProvider$Companion;->()V HSPLandroidx/navigation/NavigatorProvider$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLandroidx/navigation/NavigatorProvider$Companion;->getNameForNavigator$navigation_common_release(Ljava/lang/Class;)Ljava/lang/String; @@ -4869,11 +5010,18 @@ HSPLandroidx/navigation/fragment/FragmentNavigator;->navigate(Landroidx/navigati HSPLandroidx/navigation/fragment/FragmentNavigator;->navigate(Ljava/util/List;Landroidx/navigation/NavOptions;Landroidx/navigation/Navigator$Extras;)V HSPLandroidx/navigation/fragment/FragmentNavigator;->onAttach$lambda$4(Landroidx/navigation/NavigatorState;Landroidx/navigation/fragment/FragmentNavigator;Landroidx/fragment/app/FragmentManager;Landroidx/fragment/app/Fragment;)V HSPLandroidx/navigation/fragment/FragmentNavigator;->onAttach(Landroidx/navigation/NavigatorState;)V +HSPLandroidx/navigation/fragment/FragmentNavigator;->onSaveState()Landroid/os/Bundle; HSPLandroidx/navigation/fragment/NavHostFragment$Companion;->()V HSPLandroidx/navigation/fragment/NavHostFragment$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLandroidx/navigation/fragment/NavHostFragment$navHostController$2$$ExternalSyntheticLambda0;->(Landroidx/navigation/NavHostController;)V +HSPLandroidx/navigation/fragment/NavHostFragment$navHostController$2$$ExternalSyntheticLambda0;->saveState()Landroid/os/Bundle; HSPLandroidx/navigation/fragment/NavHostFragment$navHostController$2$$ExternalSyntheticLambda1;->(Landroidx/navigation/fragment/NavHostFragment;)V +HSPLandroidx/navigation/fragment/NavHostFragment$navHostController$2$$ExternalSyntheticLambda1;->saveState()Landroid/os/Bundle; +HSPLandroidx/navigation/fragment/NavHostFragment$navHostController$2;->$r8$lambda$S8rYz5PdxQ_qmpQw5Wg04g8YyYI(Landroidx/navigation/fragment/NavHostFragment;)Landroid/os/Bundle; +HSPLandroidx/navigation/fragment/NavHostFragment$navHostController$2;->$r8$lambda$yvpdz-7lzmuHOSkQDGqC7TUxHHI(Landroidx/navigation/NavHostController;)Landroid/os/Bundle; HSPLandroidx/navigation/fragment/NavHostFragment$navHostController$2;->(Landroidx/navigation/fragment/NavHostFragment;)V +HSPLandroidx/navigation/fragment/NavHostFragment$navHostController$2;->invoke$lambda$5$lambda$2(Landroidx/navigation/NavHostController;)Landroid/os/Bundle; +HSPLandroidx/navigation/fragment/NavHostFragment$navHostController$2;->invoke$lambda$5$lambda$4(Landroidx/navigation/fragment/NavHostFragment;)Landroid/os/Bundle; HSPLandroidx/navigation/fragment/NavHostFragment$navHostController$2;->invoke()Landroidx/navigation/NavHostController; HSPLandroidx/navigation/fragment/NavHostFragment$navHostController$2;->invoke()Ljava/lang/Object; HSPLandroidx/navigation/fragment/NavHostFragment;->()V @@ -4888,6 +5036,7 @@ HSPLandroidx/navigation/fragment/NavHostFragment;->onCreateNavController(Landroi HSPLandroidx/navigation/fragment/NavHostFragment;->onCreateNavHostController(Landroidx/navigation/NavHostController;)V HSPLandroidx/navigation/fragment/NavHostFragment;->onCreateView(Landroid/view/LayoutInflater;Landroid/view/ViewGroup;Landroid/os/Bundle;)Landroid/view/View; HSPLandroidx/navigation/fragment/NavHostFragment;->onInflate(Landroid/content/Context;Landroid/util/AttributeSet;Landroid/os/Bundle;)V +HSPLandroidx/navigation/fragment/NavHostFragment;->onSaveInstanceState(Landroid/os/Bundle;)V HSPLandroidx/navigation/fragment/NavHostFragment;->onViewCreated(Landroid/view/View;Landroid/os/Bundle;)V HSPLandroidx/navigation/fragment/R$styleable;->()V HSPLandroidx/preference/PreferenceManager;->getDefaultSharedPreferences(Landroid/content/Context;)Landroid/content/SharedPreferences; @@ -4938,11 +5087,8 @@ HSPLandroidx/recyclerview/widget/AsyncDifferConfig$Builder;->(Landroidx/re HSPLandroidx/recyclerview/widget/AsyncDifferConfig$Builder;->build()Landroidx/recyclerview/widget/AsyncDifferConfig; HSPLandroidx/recyclerview/widget/AsyncDifferConfig;->(Ljava/util/concurrent/Executor;Ljava/util/concurrent/Executor;Landroidx/recyclerview/widget/DiffUtil$ItemCallback;)V HSPLandroidx/recyclerview/widget/AsyncDifferConfig;->getBackgroundThreadExecutor()Ljava/util/concurrent/Executor; -HSPLandroidx/recyclerview/widget/AsyncDifferConfig;->getDiffCallback()Landroidx/recyclerview/widget/DiffUtil$ItemCallback; HSPLandroidx/recyclerview/widget/AsyncDifferConfig;->getMainThreadExecutor()Ljava/util/concurrent/Executor; HSPLandroidx/recyclerview/widget/AsyncListDiffer$1$1;->(Landroidx/recyclerview/widget/AsyncListDiffer$1;)V -HSPLandroidx/recyclerview/widget/AsyncListDiffer$1$1;->areContentsTheSame(II)Z -HSPLandroidx/recyclerview/widget/AsyncListDiffer$1$1;->areItemsTheSame(II)Z HSPLandroidx/recyclerview/widget/AsyncListDiffer$1$1;->getNewListSize()I HSPLandroidx/recyclerview/widget/AsyncListDiffer$1$1;->getOldListSize()I HSPLandroidx/recyclerview/widget/AsyncListDiffer$1$2;->(Landroidx/recyclerview/widget/AsyncListDiffer$1;Landroidx/recyclerview/widget/DiffUtil$DiffResult;)V @@ -5047,8 +5193,6 @@ HSPLandroidx/recyclerview/widget/DiffUtil$1;->()V HSPLandroidx/recyclerview/widget/DiffUtil$Callback;->()V HSPLandroidx/recyclerview/widget/DiffUtil$CenteredArray;->(I)V HSPLandroidx/recyclerview/widget/DiffUtil$CenteredArray;->backingData()[I -HSPLandroidx/recyclerview/widget/DiffUtil$CenteredArray;->get(I)I -HSPLandroidx/recyclerview/widget/DiffUtil$CenteredArray;->set(II)V HSPLandroidx/recyclerview/widget/DiffUtil$Diagonal;->(III)V HSPLandroidx/recyclerview/widget/DiffUtil$Diagonal;->endX()I HSPLandroidx/recyclerview/widget/DiffUtil$Diagonal;->endY()I @@ -5058,19 +5202,11 @@ HSPLandroidx/recyclerview/widget/DiffUtil$DiffResult;->dispatchUpdatesTo(Landroi HSPLandroidx/recyclerview/widget/DiffUtil$DiffResult;->findMatchingItems()V HSPLandroidx/recyclerview/widget/DiffUtil$DiffResult;->findMoveMatches()V HSPLandroidx/recyclerview/widget/DiffUtil$ItemCallback;->()V -HSPLandroidx/recyclerview/widget/DiffUtil$Range;->()V HSPLandroidx/recyclerview/widget/DiffUtil$Range;->(IIII)V -HSPLandroidx/recyclerview/widget/DiffUtil$Range;->newSize()I HSPLandroidx/recyclerview/widget/DiffUtil$Range;->oldSize()I -HSPLandroidx/recyclerview/widget/DiffUtil$Snake;->()V -HSPLandroidx/recyclerview/widget/DiffUtil$Snake;->diagonalSize()I -HSPLandroidx/recyclerview/widget/DiffUtil$Snake;->hasAdditionOrRemoval()Z -HSPLandroidx/recyclerview/widget/DiffUtil$Snake;->toDiagonal()Landroidx/recyclerview/widget/DiffUtil$Diagonal; HSPLandroidx/recyclerview/widget/DiffUtil;->()V -HSPLandroidx/recyclerview/widget/DiffUtil;->backward(Landroidx/recyclerview/widget/DiffUtil$Range;Landroidx/recyclerview/widget/DiffUtil$Callback;Landroidx/recyclerview/widget/DiffUtil$CenteredArray;Landroidx/recyclerview/widget/DiffUtil$CenteredArray;I)Landroidx/recyclerview/widget/DiffUtil$Snake; HSPLandroidx/recyclerview/widget/DiffUtil;->calculateDiff(Landroidx/recyclerview/widget/DiffUtil$Callback;)Landroidx/recyclerview/widget/DiffUtil$DiffResult; HSPLandroidx/recyclerview/widget/DiffUtil;->calculateDiff(Landroidx/recyclerview/widget/DiffUtil$Callback;Z)Landroidx/recyclerview/widget/DiffUtil$DiffResult; -HSPLandroidx/recyclerview/widget/DiffUtil;->forward(Landroidx/recyclerview/widget/DiffUtil$Range;Landroidx/recyclerview/widget/DiffUtil$Callback;Landroidx/recyclerview/widget/DiffUtil$CenteredArray;Landroidx/recyclerview/widget/DiffUtil$CenteredArray;I)Landroidx/recyclerview/widget/DiffUtil$Snake; HSPLandroidx/recyclerview/widget/DiffUtil;->midPoint(Landroidx/recyclerview/widget/DiffUtil$Range;Landroidx/recyclerview/widget/DiffUtil$Callback;Landroidx/recyclerview/widget/DiffUtil$CenteredArray;Landroidx/recyclerview/widget/DiffUtil$CenteredArray;)Landroidx/recyclerview/widget/DiffUtil$Snake; HSPLandroidx/recyclerview/widget/GapWorker$1;->()V HSPLandroidx/recyclerview/widget/GapWorker$LayoutPrefetchRegistryImpl;->()V @@ -5110,6 +5246,9 @@ HSPLandroidx/recyclerview/widget/LinearLayoutManager$LayoutChunkResult;->resetIn HSPLandroidx/recyclerview/widget/LinearLayoutManager$LayoutState;->()V HSPLandroidx/recyclerview/widget/LinearLayoutManager$LayoutState;->hasMore(Landroidx/recyclerview/widget/RecyclerView$State;)Z HSPLandroidx/recyclerview/widget/LinearLayoutManager$LayoutState;->next(Landroidx/recyclerview/widget/RecyclerView$Recycler;)Landroid/view/View; +HSPLandroidx/recyclerview/widget/LinearLayoutManager$SavedState$1;->()V +HSPLandroidx/recyclerview/widget/LinearLayoutManager$SavedState;->()V +HSPLandroidx/recyclerview/widget/LinearLayoutManager$SavedState;->()V HSPLandroidx/recyclerview/widget/LinearLayoutManager;->(Landroid/content/Context;)V HSPLandroidx/recyclerview/widget/LinearLayoutManager;->(Landroid/content/Context;IZ)V HSPLandroidx/recyclerview/widget/LinearLayoutManager;->(Landroid/content/Context;Landroid/util/AttributeSet;II)V @@ -5135,6 +5274,7 @@ HSPLandroidx/recyclerview/widget/LinearLayoutManager;->findOneVisibleChild(IIZZ) HSPLandroidx/recyclerview/widget/LinearLayoutManager;->findReferenceChild(Landroidx/recyclerview/widget/RecyclerView$Recycler;Landroidx/recyclerview/widget/RecyclerView$State;ZZ)Landroid/view/View; HSPLandroidx/recyclerview/widget/LinearLayoutManager;->fixLayoutEndGap(ILandroidx/recyclerview/widget/RecyclerView$Recycler;Landroidx/recyclerview/widget/RecyclerView$State;Z)I HSPLandroidx/recyclerview/widget/LinearLayoutManager;->fixLayoutStartGap(ILandroidx/recyclerview/widget/RecyclerView$Recycler;Landroidx/recyclerview/widget/RecyclerView$State;Z)I +HSPLandroidx/recyclerview/widget/LinearLayoutManager;->getChildClosestToStart()Landroid/view/View; HSPLandroidx/recyclerview/widget/LinearLayoutManager;->getExtraLayoutSpace(Landroidx/recyclerview/widget/RecyclerView$State;)I HSPLandroidx/recyclerview/widget/LinearLayoutManager;->getReverseLayout()Z HSPLandroidx/recyclerview/widget/LinearLayoutManager;->isAutoMeasureEnabled()Z @@ -5145,6 +5285,7 @@ HSPLandroidx/recyclerview/widget/LinearLayoutManager;->onAnchorReady(Landroidx/r HSPLandroidx/recyclerview/widget/LinearLayoutManager;->onInitializeAccessibilityEvent(Landroid/view/accessibility/AccessibilityEvent;)V HSPLandroidx/recyclerview/widget/LinearLayoutManager;->onLayoutChildren(Landroidx/recyclerview/widget/RecyclerView$Recycler;Landroidx/recyclerview/widget/RecyclerView$State;)V HSPLandroidx/recyclerview/widget/LinearLayoutManager;->onLayoutCompleted(Landroidx/recyclerview/widget/RecyclerView$State;)V +HSPLandroidx/recyclerview/widget/LinearLayoutManager;->onSaveInstanceState()Landroid/os/Parcelable; HSPLandroidx/recyclerview/widget/LinearLayoutManager;->resolveIsInfinite()Z HSPLandroidx/recyclerview/widget/LinearLayoutManager;->resolveShouldLayoutReverse()V HSPLandroidx/recyclerview/widget/LinearLayoutManager;->scrollToPositionWithOffset(II)V @@ -5182,6 +5323,7 @@ HSPLandroidx/recyclerview/widget/OpReorderer;->reorderOps(Ljava/util/List;)V HSPLandroidx/recyclerview/widget/OrientationHelper$1;->(Landroidx/recyclerview/widget/RecyclerView$LayoutManager;)V HSPLandroidx/recyclerview/widget/OrientationHelper$1;->getDecoratedMeasurement(Landroid/view/View;)I HSPLandroidx/recyclerview/widget/OrientationHelper$1;->getDecoratedMeasurementInOther(Landroid/view/View;)I +HSPLandroidx/recyclerview/widget/OrientationHelper$1;->getDecoratedStart(Landroid/view/View;)I HSPLandroidx/recyclerview/widget/OrientationHelper$1;->getEndAfterPadding()I HSPLandroidx/recyclerview/widget/OrientationHelper$1;->getEndPadding()I HSPLandroidx/recyclerview/widget/OrientationHelper$1;->getMode()I @@ -5232,7 +5374,6 @@ HSPLandroidx/recyclerview/widget/RecyclerView$Adapter;->getStateRestorationPolic HSPLandroidx/recyclerview/widget/RecyclerView$Adapter;->hasObservers()Z HSPLandroidx/recyclerview/widget/RecyclerView$Adapter;->hasStableIds()Z HSPLandroidx/recyclerview/widget/RecyclerView$Adapter;->notifyDataSetChanged()V -HSPLandroidx/recyclerview/widget/RecyclerView$Adapter;->notifyItemRangeChanged(II)V HSPLandroidx/recyclerview/widget/RecyclerView$Adapter;->notifyItemRangeChanged(IILjava/lang/Object;)V HSPLandroidx/recyclerview/widget/RecyclerView$Adapter;->notifyItemRangeInserted(II)V HSPLandroidx/recyclerview/widget/RecyclerView$Adapter;->onAttachedToRecyclerView(Landroidx/recyclerview/widget/RecyclerView;)V @@ -5244,7 +5385,6 @@ HSPLandroidx/recyclerview/widget/RecyclerView$Adapter;->unregisterAdapterDataObs HSPLandroidx/recyclerview/widget/RecyclerView$AdapterDataObservable;->()V HSPLandroidx/recyclerview/widget/RecyclerView$AdapterDataObservable;->hasObservers()Z HSPLandroidx/recyclerview/widget/RecyclerView$AdapterDataObservable;->notifyChanged()V -HSPLandroidx/recyclerview/widget/RecyclerView$AdapterDataObservable;->notifyItemRangeChanged(II)V HSPLandroidx/recyclerview/widget/RecyclerView$AdapterDataObservable;->notifyItemRangeChanged(IILjava/lang/Object;)V HSPLandroidx/recyclerview/widget/RecyclerView$AdapterDataObservable;->notifyItemRangeInserted(II)V HSPLandroidx/recyclerview/widget/RecyclerView$AdapterDataObserver;->()V @@ -5302,6 +5442,7 @@ HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getChildMeasureSpe HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getColumnCountForAccessibility(Landroidx/recyclerview/widget/RecyclerView$Recycler;Landroidx/recyclerview/widget/RecyclerView$State;)I HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getDecoratedBottom(Landroid/view/View;)I HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getDecoratedBoundsWithMargins(Landroid/view/View;Landroid/graphics/Rect;)V +HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getDecoratedLeft(Landroid/view/View;)I HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getDecoratedMeasuredHeight(Landroid/view/View;)I HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getDecoratedMeasuredWidth(Landroid/view/View;)I HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getDecoratedTop(Landroid/view/View;)I @@ -5310,6 +5451,7 @@ HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getHeight()I HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getHeightMode()I HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getItemCount()I HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getLayoutDirection()I +HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getLeftDecorationWidth(Landroid/view/View;)I HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getMinimumHeight()I HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getMinimumWidth()I HSPLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getPaddingBottom()I @@ -5402,6 +5544,9 @@ HSPLandroidx/recyclerview/widget/RecyclerView$RecyclerViewDataObserver;->onChang HSPLandroidx/recyclerview/widget/RecyclerView$RecyclerViewDataObserver;->onItemRangeChanged(IILjava/lang/Object;)V HSPLandroidx/recyclerview/widget/RecyclerView$RecyclerViewDataObserver;->onItemRangeInserted(II)V HSPLandroidx/recyclerview/widget/RecyclerView$RecyclerViewDataObserver;->triggerUpdateProcessor()V +HSPLandroidx/recyclerview/widget/RecyclerView$SavedState$1;->()V +HSPLandroidx/recyclerview/widget/RecyclerView$SavedState;->()V +HSPLandroidx/recyclerview/widget/RecyclerView$SavedState;->(Landroid/os/Parcelable;)V HSPLandroidx/recyclerview/widget/RecyclerView$SimpleOnItemTouchListener;->()V HSPLandroidx/recyclerview/widget/RecyclerView$State;->()V HSPLandroidx/recyclerview/widget/RecyclerView$State;->assertLayoutStep(I)V @@ -5474,6 +5619,7 @@ HSPLandroidx/recyclerview/widget/RecyclerView;->dispatchLayoutStep2()V HSPLandroidx/recyclerview/widget/RecyclerView;->dispatchLayoutStep3()V HSPLandroidx/recyclerview/widget/RecyclerView;->dispatchOnScrolled(II)V HSPLandroidx/recyclerview/widget/RecyclerView;->dispatchPendingImportantForAccessibilityChanges()V +HSPLandroidx/recyclerview/widget/RecyclerView;->dispatchSaveInstanceState(Landroid/util/SparseArray;)V HSPLandroidx/recyclerview/widget/RecyclerView;->draw(Landroid/graphics/Canvas;)V HSPLandroidx/recyclerview/widget/RecyclerView;->drawChild(Landroid/graphics/Canvas;Landroid/view/View;J)Z HSPLandroidx/recyclerview/widget/RecyclerView;->fillRemainingScrollValues(Landroidx/recyclerview/widget/RecyclerView$State;)V @@ -5519,6 +5665,7 @@ HSPLandroidx/recyclerview/widget/RecyclerView;->onExitLayoutOrScroll(Z)V HSPLandroidx/recyclerview/widget/RecyclerView;->onInterceptTouchEvent(Landroid/view/MotionEvent;)Z HSPLandroidx/recyclerview/widget/RecyclerView;->onLayout(ZIIII)V HSPLandroidx/recyclerview/widget/RecyclerView;->onMeasure(II)V +HSPLandroidx/recyclerview/widget/RecyclerView;->onSaveInstanceState()Landroid/os/Parcelable; HSPLandroidx/recyclerview/widget/RecyclerView;->onScrolled(II)V HSPLandroidx/recyclerview/widget/RecyclerView;->onSizeChanged(IIII)V HSPLandroidx/recyclerview/widget/RecyclerView;->postAnimationRunner()V @@ -5625,6 +5772,7 @@ HSPLandroidx/savedstate/SavedStateRegistry;->getSavedStateProvider(Ljava/lang/St HSPLandroidx/savedstate/SavedStateRegistry;->performAttach$lambda$4(Landroidx/savedstate/SavedStateRegistry;Landroidx/lifecycle/LifecycleOwner;Landroidx/lifecycle/Lifecycle$Event;)V HSPLandroidx/savedstate/SavedStateRegistry;->performAttach$savedstate_release(Landroidx/lifecycle/Lifecycle;)V HSPLandroidx/savedstate/SavedStateRegistry;->performRestore$savedstate_release(Landroid/os/Bundle;)V +HSPLandroidx/savedstate/SavedStateRegistry;->performSave(Landroid/os/Bundle;)V HSPLandroidx/savedstate/SavedStateRegistry;->registerSavedStateProvider(Ljava/lang/String;Landroidx/savedstate/SavedStateRegistry$SavedStateProvider;)V HSPLandroidx/savedstate/SavedStateRegistry;->runOnNextRecreation(Ljava/lang/Class;)V HSPLandroidx/savedstate/SavedStateRegistryController$Companion;->()V @@ -5637,6 +5785,7 @@ HSPLandroidx/savedstate/SavedStateRegistryController;->create(Landroidx/savedsta HSPLandroidx/savedstate/SavedStateRegistryController;->getSavedStateRegistry()Landroidx/savedstate/SavedStateRegistry; HSPLandroidx/savedstate/SavedStateRegistryController;->performAttach()V HSPLandroidx/savedstate/SavedStateRegistryController;->performRestore(Landroid/os/Bundle;)V +HSPLandroidx/savedstate/SavedStateRegistryController;->performSave(Landroid/os/Bundle;)V HSPLandroidx/savedstate/ViewTreeSavedStateRegistryOwner;->set(Landroid/view/View;Landroidx/savedstate/SavedStateRegistryOwner;)V HSPLandroidx/sqlite/db/SimpleSQLiteQuery;->(Ljava/lang/String;[Ljava/lang/Object;)V HSPLandroidx/sqlite/db/SimpleSQLiteQuery;->bind(Landroidx/sqlite/db/SupportSQLiteProgram;ILjava/lang/Object;)V @@ -5680,6 +5829,9 @@ HSPLcom/airbnb/lottie/LottieAnimationView$$ExternalSyntheticLambda1;->onResult(L HSPLcom/airbnb/lottie/LottieAnimationView$$ExternalSyntheticLambda2;->()V HSPLcom/airbnb/lottie/LottieAnimationView$1;->(Lcom/airbnb/lottie/LottieAnimationView;)V HSPLcom/airbnb/lottie/LottieAnimationView$2;->(Lcom/airbnb/lottie/LottieAnimationView;Lcom/airbnb/lottie/value/SimpleLottieValueCallback;)V +HSPLcom/airbnb/lottie/LottieAnimationView$SavedState$1;->()V +HSPLcom/airbnb/lottie/LottieAnimationView$SavedState;->()V +HSPLcom/airbnb/lottie/LottieAnimationView$SavedState;->(Landroid/os/Parcelable;)V HSPLcom/airbnb/lottie/LottieAnimationView$UserActionTaken;->()V HSPLcom/airbnb/lottie/LottieAnimationView$UserActionTaken;->(Ljava/lang/String;I)V HSPLcom/airbnb/lottie/LottieAnimationView;->()V @@ -5694,6 +5846,7 @@ HSPLcom/airbnb/lottie/LottieAnimationView;->init(Landroid/util/AttributeSet;I)V HSPLcom/airbnb/lottie/LottieAnimationView;->invalidate()V HSPLcom/airbnb/lottie/LottieAnimationView;->invalidateDrawable(Landroid/graphics/drawable/Drawable;)V HSPLcom/airbnb/lottie/LottieAnimationView;->onAttachedToWindow()V +HSPLcom/airbnb/lottie/LottieAnimationView;->onSaveInstanceState()Landroid/os/Parcelable; HSPLcom/airbnb/lottie/LottieAnimationView;->pauseAnimation()V HSPLcom/airbnb/lottie/LottieAnimationView;->setAnimation(I)V HSPLcom/airbnb/lottie/LottieAnimationView;->setComposition(Lcom/airbnb/lottie/LottieComposition;)V @@ -5762,11 +5915,16 @@ HSPLcom/airbnb/lottie/LottieDrawable;->draw(Landroid/graphics/Canvas;)V HSPLcom/airbnb/lottie/LottieDrawable;->drawDirectlyToCanvas(Landroid/graphics/Canvas;)V HSPLcom/airbnb/lottie/LottieDrawable;->enableMergePathsForKitKatAndAbove(Z)V HSPLcom/airbnb/lottie/LottieDrawable;->getComposition()Lcom/airbnb/lottie/LottieComposition; +HSPLcom/airbnb/lottie/LottieDrawable;->getImageAssetsFolder()Ljava/lang/String; HSPLcom/airbnb/lottie/LottieDrawable;->getIntrinsicHeight()I HSPLcom/airbnb/lottie/LottieDrawable;->getIntrinsicWidth()I HSPLcom/airbnb/lottie/LottieDrawable;->getOpacity()I +HSPLcom/airbnb/lottie/LottieDrawable;->getProgress()F HSPLcom/airbnb/lottie/LottieDrawable;->getRenderMode()Lcom/airbnb/lottie/RenderMode; +HSPLcom/airbnb/lottie/LottieDrawable;->getRepeatCount()I +HSPLcom/airbnb/lottie/LottieDrawable;->getRepeatMode()I HSPLcom/airbnb/lottie/LottieDrawable;->invalidateSelf()V +HSPLcom/airbnb/lottie/LottieDrawable;->isAnimatingOrWillAnimateOnVisible()Z HSPLcom/airbnb/lottie/LottieDrawable;->isApplyingOpacityToLayersEnabled()Z HSPLcom/airbnb/lottie/LottieDrawable;->lambda$addValueCallback$14(Lcom/airbnb/lottie/model/KeyPath;Ljava/lang/Object;Lcom/airbnb/lottie/value/LottieValueCallback;Lcom/airbnb/lottie/LottieComposition;)V HSPLcom/airbnb/lottie/LottieDrawable;->lambda$setProgress$13(FLcom/airbnb/lottie/LottieComposition;)V @@ -5903,6 +6061,7 @@ HSPLcom/airbnb/lottie/animation/keyframe/TransformKeyframeAnimation;->addListene HSPLcom/airbnb/lottie/animation/keyframe/TransformKeyframeAnimation;->applyValueCallback(Ljava/lang/Object;Lcom/airbnb/lottie/value/LottieValueCallback;)Z HSPLcom/airbnb/lottie/animation/keyframe/TransformKeyframeAnimation;->getMatrix()Landroid/graphics/Matrix; HSPLcom/airbnb/lottie/animation/keyframe/TransformKeyframeAnimation;->getOpacity()Lcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation; +HSPLcom/airbnb/lottie/animation/keyframe/TransformKeyframeAnimation;->setProgress(F)V HSPLcom/airbnb/lottie/animation/keyframe/ValueCallbackKeyframeAnimation;->(Lcom/airbnb/lottie/value/LottieValueCallback;)V HSPLcom/airbnb/lottie/animation/keyframe/ValueCallbackKeyframeAnimation;->(Lcom/airbnb/lottie/value/LottieValueCallback;Ljava/lang/Object;)V HSPLcom/airbnb/lottie/model/CubicCurveData;->()V @@ -6173,6 +6332,7 @@ HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->nextQuotedValue(Lokio/ByteSt HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->nextString()Ljava/lang/String; HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->peek()Lcom/airbnb/lottie/parser/moshi/JsonReader$Token; HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->peekKeyword()I +HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->selectName(Lcom/airbnb/lottie/parser/moshi/JsonReader$Options;)I HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->skipName()V HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->skipQuotedValue(Lokio/ByteString;)V HSPLcom/airbnb/lottie/parser/moshi/JsonUtf8Reader;->skipValue()V @@ -7275,13 +7435,9 @@ HSPLcom/fasterxml/jackson/annotation/JsonFormat$Value;->hasShape()Z HSPLcom/fasterxml/jackson/annotation/JsonFormat$Value;->withOverrides(Lcom/fasterxml/jackson/annotation/JsonFormat$Value;)Lcom/fasterxml/jackson/annotation/JsonFormat$Value; HSPLcom/fasterxml/jackson/annotation/JsonIgnoreProperties$Value;->()V HSPLcom/fasterxml/jackson/annotation/JsonIgnoreProperties$Value;->(Ljava/util/Set;ZZZZ)V -HSPLcom/fasterxml/jackson/annotation/JsonIgnoreProperties$Value;->_asSet([Ljava/lang/String;)Ljava/util/Set; -HSPLcom/fasterxml/jackson/annotation/JsonIgnoreProperties$Value;->_empty(Ljava/util/Set;ZZZZ)Z -HSPLcom/fasterxml/jackson/annotation/JsonIgnoreProperties$Value;->construct(Ljava/util/Set;ZZZZ)Lcom/fasterxml/jackson/annotation/JsonIgnoreProperties$Value; HSPLcom/fasterxml/jackson/annotation/JsonIgnoreProperties$Value;->empty()Lcom/fasterxml/jackson/annotation/JsonIgnoreProperties$Value; HSPLcom/fasterxml/jackson/annotation/JsonIgnoreProperties$Value;->findIgnoredForDeserialization()Ljava/util/Set; HSPLcom/fasterxml/jackson/annotation/JsonIgnoreProperties$Value;->findIgnoredForSerialization()Ljava/util/Set; -HSPLcom/fasterxml/jackson/annotation/JsonIgnoreProperties$Value;->from(Lcom/fasterxml/jackson/annotation/JsonIgnoreProperties;)Lcom/fasterxml/jackson/annotation/JsonIgnoreProperties$Value; HSPLcom/fasterxml/jackson/annotation/JsonIgnoreProperties$Value;->getIgnoreUnknown()Z HSPLcom/fasterxml/jackson/annotation/JsonIgnoreProperties$Value;->merge(Lcom/fasterxml/jackson/annotation/JsonIgnoreProperties$Value;Lcom/fasterxml/jackson/annotation/JsonIgnoreProperties$Value;)Lcom/fasterxml/jackson/annotation/JsonIgnoreProperties$Value; HSPLcom/fasterxml/jackson/annotation/JsonIgnoreProperties$Value;->withOverrides(Lcom/fasterxml/jackson/annotation/JsonIgnoreProperties$Value;)Lcom/fasterxml/jackson/annotation/JsonIgnoreProperties$Value; @@ -7322,13 +7478,8 @@ HSPLcom/fasterxml/jackson/core/Base64Variant;->(Lcom/fasterxml/jackson/cor HSPLcom/fasterxml/jackson/core/Base64Variant;->(Lcom/fasterxml/jackson/core/Base64Variant;Ljava/lang/String;ZCI)V HSPLcom/fasterxml/jackson/core/Base64Variant;->(Lcom/fasterxml/jackson/core/Base64Variant;Ljava/lang/String;ZCLcom/fasterxml/jackson/core/Base64Variant$PaddingReadBehaviour;I)V HSPLcom/fasterxml/jackson/core/Base64Variant;->(Ljava/lang/String;Ljava/lang/String;ZCI)V -HSPLcom/fasterxml/jackson/core/Base64Variant;->encodeBase64Chunk(I[CI)I -HSPLcom/fasterxml/jackson/core/Base64Variant;->encodeBase64Partial(II[CI)I -HSPLcom/fasterxml/jackson/core/Base64Variant;->getMaxLineLength()I -HSPLcom/fasterxml/jackson/core/Base64Variant;->usesPadding()Z HSPLcom/fasterxml/jackson/core/Base64Variants;->()V HSPLcom/fasterxml/jackson/core/Base64Variants;->getDefaultVariant()Lcom/fasterxml/jackson/core/Base64Variant; -HSPLcom/fasterxml/jackson/core/JacksonException;->(Ljava/lang/String;Ljava/lang/Throwable;)V HSPLcom/fasterxml/jackson/core/JsonEncoding;->()V HSPLcom/fasterxml/jackson/core/JsonEncoding;->(Ljava/lang/String;ILjava/lang/String;ZI)V HSPLcom/fasterxml/jackson/core/JsonFactory$Feature;->()V @@ -7370,8 +7521,6 @@ HSPLcom/fasterxml/jackson/core/JsonGenerator;->()V HSPLcom/fasterxml/jackson/core/JsonGenerator;->canWriteBinaryNatively()Z HSPLcom/fasterxml/jackson/core/JsonLocation;->()V HSPLcom/fasterxml/jackson/core/JsonLocation;->(Ljava/lang/Object;JJII)V -HSPLcom/fasterxml/jackson/core/JsonParseException;->(Lcom/fasterxml/jackson/core/JsonParser;Ljava/lang/String;)V -HSPLcom/fasterxml/jackson/core/JsonParseException;->withRequestPayload(Lcom/fasterxml/jackson/core/util/RequestPayload;)Lcom/fasterxml/jackson/core/JsonParseException; HSPLcom/fasterxml/jackson/core/JsonParser$Feature;->()V HSPLcom/fasterxml/jackson/core/JsonParser$Feature;->(Ljava/lang/String;IZ)V HSPLcom/fasterxml/jackson/core/JsonParser$Feature;->collectDefaults()I @@ -7383,11 +7532,8 @@ HSPLcom/fasterxml/jackson/core/JsonParser$NumberType;->()V HSPLcom/fasterxml/jackson/core/JsonParser$NumberType;->(Ljava/lang/String;I)V HSPLcom/fasterxml/jackson/core/JsonParser;->()V HSPLcom/fasterxml/jackson/core/JsonParser;->(I)V -HSPLcom/fasterxml/jackson/core/JsonParser;->_constructError(Ljava/lang/String;)Lcom/fasterxml/jackson/core/JsonParseException; HSPLcom/fasterxml/jackson/core/JsonParser;->currentName()Ljava/lang/String; HSPLcom/fasterxml/jackson/core/JsonParser;->isEnabled(Lcom/fasterxml/jackson/core/JsonParser$Feature;)Z -HSPLcom/fasterxml/jackson/core/JsonProcessingException;->(Ljava/lang/String;Lcom/fasterxml/jackson/core/JsonLocation;)V -HSPLcom/fasterxml/jackson/core/JsonProcessingException;->(Ljava/lang/String;Lcom/fasterxml/jackson/core/JsonLocation;Ljava/lang/Throwable;)V HSPLcom/fasterxml/jackson/core/JsonStreamContext;->()V HSPLcom/fasterxml/jackson/core/JsonStreamContext;->(II)V HSPLcom/fasterxml/jackson/core/JsonStreamContext;->inArray()Z @@ -7422,13 +7568,11 @@ HSPLcom/fasterxml/jackson/core/base/GeneratorBase;->getOutputContext()Lcom/faste HSPLcom/fasterxml/jackson/core/base/GeneratorBase;->isEnabled(Lcom/fasterxml/jackson/core/JsonGenerator$Feature;)Z HSPLcom/fasterxml/jackson/core/base/ParserBase;->()V HSPLcom/fasterxml/jackson/core/base/ParserBase;->(Lcom/fasterxml/jackson/core/io/IOContext;I)V -HSPLcom/fasterxml/jackson/core/base/ParserBase;->_getSourceReference()Ljava/lang/Object; -HSPLcom/fasterxml/jackson/core/base/ParserBase;->_parseIntValue()I +HSPLcom/fasterxml/jackson/core/base/ParserBase;->_parseNumericValue(I)V HSPLcom/fasterxml/jackson/core/base/ParserBase;->_releaseBuffers()V -HSPLcom/fasterxml/jackson/core/base/ParserBase;->_validJsonTokenList()Ljava/lang/String; -HSPLcom/fasterxml/jackson/core/base/ParserBase;->_validJsonValueList()Ljava/lang/String; HSPLcom/fasterxml/jackson/core/base/ParserBase;->close()V HSPLcom/fasterxml/jackson/core/base/ParserBase;->convertNumberToLong()V +HSPLcom/fasterxml/jackson/core/base/ParserBase;->getCurrentName()Ljava/lang/String; HSPLcom/fasterxml/jackson/core/base/ParserBase;->getIntValue()I HSPLcom/fasterxml/jackson/core/base/ParserBase;->getLongValue()J HSPLcom/fasterxml/jackson/core/base/ParserBase;->getNumberType()Lcom/fasterxml/jackson/core/JsonParser$NumberType; @@ -7437,7 +7581,6 @@ HSPLcom/fasterxml/jackson/core/base/ParserBase;->resetInt(ZI)Lcom/fasterxml/jack HSPLcom/fasterxml/jackson/core/base/ParserBase;->setCurrentValue(Ljava/lang/Object;)V HSPLcom/fasterxml/jackson/core/base/ParserMinimalBase;->()V HSPLcom/fasterxml/jackson/core/base/ParserMinimalBase;->(I)V -HSPLcom/fasterxml/jackson/core/base/ParserMinimalBase;->_reportError(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)V HSPLcom/fasterxml/jackson/core/base/ParserMinimalBase;->currentToken()Lcom/fasterxml/jackson/core/JsonToken; HSPLcom/fasterxml/jackson/core/base/ParserMinimalBase;->currentTokenId()I HSPLcom/fasterxml/jackson/core/base/ParserMinimalBase;->getValueAsString()Ljava/lang/String; @@ -7448,7 +7591,6 @@ HSPLcom/fasterxml/jackson/core/base/ParserMinimalBase;->isExpectedNumberIntToken HSPLcom/fasterxml/jackson/core/base/ParserMinimalBase;->isExpectedStartArrayToken()Z HSPLcom/fasterxml/jackson/core/base/ParserMinimalBase;->isExpectedStartObjectToken()Z HSPLcom/fasterxml/jackson/core/base/ParserMinimalBase;->skipChildren()Lcom/fasterxml/jackson/core/JsonParser; -HSPLcom/fasterxml/jackson/core/exc/StreamReadException;->(Lcom/fasterxml/jackson/core/JsonParser;Ljava/lang/String;)V HSPLcom/fasterxml/jackson/core/io/CharTypes;->()V HSPLcom/fasterxml/jackson/core/io/CharTypes;->copyHexBytes()[B HSPLcom/fasterxml/jackson/core/io/CharTypes;->copyHexChars()[C @@ -7465,7 +7607,6 @@ HSPLcom/fasterxml/jackson/core/io/IOContext;->allocTokenBuffer()[C HSPLcom/fasterxml/jackson/core/io/IOContext;->allocTokenBuffer(I)[C HSPLcom/fasterxml/jackson/core/io/IOContext;->allocWriteEncodingBuffer()[B HSPLcom/fasterxml/jackson/core/io/IOContext;->constructTextBuffer()Lcom/fasterxml/jackson/core/util/TextBuffer; -HSPLcom/fasterxml/jackson/core/io/IOContext;->getSourceReference()Ljava/lang/Object; HSPLcom/fasterxml/jackson/core/io/IOContext;->isResourceManaged()Z HSPLcom/fasterxml/jackson/core/io/IOContext;->releaseConcatBuffer([C)V HSPLcom/fasterxml/jackson/core/io/IOContext;->releaseReadIOBuffer([B)V @@ -7480,11 +7621,7 @@ HSPLcom/fasterxml/jackson/core/io/JsonStringEncoder;->quoteAsUTF8(Ljava/lang/Str HSPLcom/fasterxml/jackson/core/io/NumberInput;->()V HSPLcom/fasterxml/jackson/core/io/NumberInput;->parseInt([CII)I HSPLcom/fasterxml/jackson/core/io/NumberOutput;->()V -HSPLcom/fasterxml/jackson/core/io/NumberOutput;->_full3(I[CI)I HSPLcom/fasterxml/jackson/core/io/NumberOutput;->_leading3(I[CI)I -HSPLcom/fasterxml/jackson/core/io/NumberOutput;->_outputFullBillion(I[CI)I -HSPLcom/fasterxml/jackson/core/io/NumberOutput;->_outputUptoBillion(I[CI)I -HSPLcom/fasterxml/jackson/core/io/NumberOutput;->_outputUptoMillion([CIII)I HSPLcom/fasterxml/jackson/core/io/NumberOutput;->outputInt(I[BI)I HSPLcom/fasterxml/jackson/core/io/NumberOutput;->outputInt(I[CI)I HSPLcom/fasterxml/jackson/core/io/NumberOutput;->outputLong(J[BI)I @@ -7507,6 +7644,7 @@ HSPLcom/fasterxml/jackson/core/json/ByteSourceJsonBootstrapper;->ensureLoaded(I) HSPLcom/fasterxml/jackson/core/json/ByteSourceJsonBootstrapper;->handleBOM(I)Z HSPLcom/fasterxml/jackson/core/json/JsonGeneratorImpl;->()V HSPLcom/fasterxml/jackson/core/json/JsonGeneratorImpl;->(Lcom/fasterxml/jackson/core/io/IOContext;ILcom/fasterxml/jackson/core/ObjectCodec;)V +HSPLcom/fasterxml/jackson/core/json/JsonReadContext;->(Lcom/fasterxml/jackson/core/json/JsonReadContext;Lcom/fasterxml/jackson/core/json/DupDetector;III)V HSPLcom/fasterxml/jackson/core/json/JsonReadContext;->clearAndGetParent()Lcom/fasterxml/jackson/core/json/JsonReadContext; HSPLcom/fasterxml/jackson/core/json/JsonReadContext;->createChildArrayContext(II)Lcom/fasterxml/jackson/core/json/JsonReadContext; HSPLcom/fasterxml/jackson/core/json/JsonReadContext;->createChildObjectContext(II)Lcom/fasterxml/jackson/core/json/JsonReadContext; @@ -7529,20 +7667,22 @@ HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->()V HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->(Lcom/fasterxml/jackson/core/io/IOContext;ILjava/io/Reader;Lcom/fasterxml/jackson/core/ObjectCodec;Lcom/fasterxml/jackson/core/sym/CharsToNameCanonicalizer;)V HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->(Lcom/fasterxml/jackson/core/io/IOContext;ILjava/io/Reader;Lcom/fasterxml/jackson/core/ObjectCodec;Lcom/fasterxml/jackson/core/sym/CharsToNameCanonicalizer;[CIIZ)V HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_closeInput()V -HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_closeScope(I)V +HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_finishString()V HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_finishString2()V HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_loadMore()Z -HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_matchFalse()V HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_matchNull()V -HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_matchTrue()V +HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_nextAfterName()Lcom/fasterxml/jackson/core/JsonToken; +HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_parseName()Ljava/lang/String; HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_parseName2(III)Ljava/lang/String; HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_parseNumber2(ZI)Lcom/fasterxml/jackson/core/JsonToken; +HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_parsePosNumber(I)Lcom/fasterxml/jackson/core/JsonToken; HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_releaseBuffers()V HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_skipAfterComma2()I HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_skipColon()I HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_skipColon2(Z)I HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_skipComma(I)I HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_skipString()V +HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_skipWSOrEnd()I HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_skipWSOrEnd2()I HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_updateLocation()V HSPLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_updateNameLocation()V @@ -7562,8 +7702,6 @@ HSPLcom/fasterxml/jackson/core/json/UTF8JsonGenerator;->close()V HSPLcom/fasterxml/jackson/core/json/UTF8JsonGenerator;->writeEndArray()V HSPLcom/fasterxml/jackson/core/json/UTF8JsonGenerator;->writeEndObject()V HSPLcom/fasterxml/jackson/core/json/UTF8JsonGenerator;->writeFieldName(Lcom/fasterxml/jackson/core/SerializableString;)V -HSPLcom/fasterxml/jackson/core/json/UTF8JsonGenerator;->writeFieldName(Ljava/lang/String;)V -HSPLcom/fasterxml/jackson/core/json/UTF8JsonGenerator;->writeNumber(I)V HSPLcom/fasterxml/jackson/core/json/UTF8JsonGenerator;->writeNumber(J)V HSPLcom/fasterxml/jackson/core/json/UTF8JsonGenerator;->writeStartArray(Ljava/lang/Object;I)V HSPLcom/fasterxml/jackson/core/json/UTF8JsonGenerator;->writeStartObject(Ljava/lang/Object;)V @@ -7573,9 +7711,7 @@ HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->()V HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->(Lcom/fasterxml/jackson/core/io/IOContext;ILjava/io/InputStream;Lcom/fasterxml/jackson/core/ObjectCodec;Lcom/fasterxml/jackson/core/sym/ByteQuadsCanonicalizer;[BIIIZ)V HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->_closeInput()V HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->_closeObjectScope()V -HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->_decodeCharForError(I)I HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->_finishString2([CI)V -HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->_handleUnexpectedValue(I)Lcom/fasterxml/jackson/core/JsonToken; HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->_loadMore()Z HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->_loadMoreGuaranteed()V HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->_nextAfterName()Lcom/fasterxml/jackson/core/JsonToken; @@ -7583,11 +7719,11 @@ HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->_padLastQuad(II)I HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->_parseName(I)Ljava/lang/String; HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->_parsePosNumber(I)Lcom/fasterxml/jackson/core/JsonToken; HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->_releaseBuffers()V -HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->_reportInvalidToken(Ljava/lang/String;Ljava/lang/String;)V HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->_skipColon()I HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->_skipColon2(Z)I HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->_skipWS()I HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->_skipWS2()I +HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->_skipWSOrEnd()I HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->_updateLocation()V HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->_updateNameLocation()V HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->addName([III)Ljava/lang/String; @@ -7595,7 +7731,6 @@ HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->findName(II)Ljava/lan HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->findName(III)Ljava/lang/String; HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->findName(IIII)Ljava/lang/String; HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->findName([IIII)Ljava/lang/String; -HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->getCurrentLocation()Lcom/fasterxml/jackson/core/JsonLocation; HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->getReadCapabilities()Lcom/fasterxml/jackson/core/util/JacksonFeatureSet; HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->getText()Ljava/lang/String; HSPLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->nextFieldName()Ljava/lang/String; @@ -7609,21 +7744,18 @@ HSPLcom/fasterxml/jackson/core/json/WriterBasedJsonGenerator;->(Lcom/faste HSPLcom/fasterxml/jackson/core/json/WriterBasedJsonGenerator;->_flushBuffer()V HSPLcom/fasterxml/jackson/core/json/WriterBasedJsonGenerator;->_releaseBuffers()V HSPLcom/fasterxml/jackson/core/json/WriterBasedJsonGenerator;->_verifyValueWrite(Ljava/lang/String;)V -HSPLcom/fasterxml/jackson/core/json/WriterBasedJsonGenerator;->_writeBinary(Lcom/fasterxml/jackson/core/Base64Variant;[BII)V HSPLcom/fasterxml/jackson/core/json/WriterBasedJsonGenerator;->_writeFieldName(Lcom/fasterxml/jackson/core/SerializableString;Z)V HSPLcom/fasterxml/jackson/core/json/WriterBasedJsonGenerator;->_writeFieldName(Ljava/lang/String;Z)V HSPLcom/fasterxml/jackson/core/json/WriterBasedJsonGenerator;->_writeNull()V HSPLcom/fasterxml/jackson/core/json/WriterBasedJsonGenerator;->_writeString(Ljava/lang/String;)V HSPLcom/fasterxml/jackson/core/json/WriterBasedJsonGenerator;->_writeString2(I)V HSPLcom/fasterxml/jackson/core/json/WriterBasedJsonGenerator;->close()V -HSPLcom/fasterxml/jackson/core/json/WriterBasedJsonGenerator;->writeBinary(Lcom/fasterxml/jackson/core/Base64Variant;[BII)V HSPLcom/fasterxml/jackson/core/json/WriterBasedJsonGenerator;->writeBoolean(Z)V HSPLcom/fasterxml/jackson/core/json/WriterBasedJsonGenerator;->writeEndArray()V HSPLcom/fasterxml/jackson/core/json/WriterBasedJsonGenerator;->writeEndObject()V HSPLcom/fasterxml/jackson/core/json/WriterBasedJsonGenerator;->writeFieldName(Lcom/fasterxml/jackson/core/SerializableString;)V HSPLcom/fasterxml/jackson/core/json/WriterBasedJsonGenerator;->writeFieldName(Ljava/lang/String;)V HSPLcom/fasterxml/jackson/core/json/WriterBasedJsonGenerator;->writeNull()V -HSPLcom/fasterxml/jackson/core/json/WriterBasedJsonGenerator;->writeNumber(I)V HSPLcom/fasterxml/jackson/core/json/WriterBasedJsonGenerator;->writeNumber(J)V HSPLcom/fasterxml/jackson/core/json/WriterBasedJsonGenerator;->writeStartArray(Ljava/lang/Object;I)V HSPLcom/fasterxml/jackson/core/json/WriterBasedJsonGenerator;->writeStartObject(Ljava/lang/Object;)V @@ -7759,7 +7891,6 @@ HSPLcom/fasterxml/jackson/databind/AnnotationIntrospector;->findFormat(Lcom/fast HSPLcom/fasterxml/jackson/databind/AnnotationIntrospector;->findImplicitPropertyName(Lcom/fasterxml/jackson/databind/introspect/AnnotatedMember;)Ljava/lang/String; HSPLcom/fasterxml/jackson/databind/AnnotationIntrospector;->findInjectableValue(Lcom/fasterxml/jackson/databind/introspect/AnnotatedMember;)Lcom/fasterxml/jackson/annotation/JacksonInject$Value; HSPLcom/fasterxml/jackson/databind/AnnotationIntrospector;->findInjectableValueId(Lcom/fasterxml/jackson/databind/introspect/AnnotatedMember;)Ljava/lang/Object; -HSPLcom/fasterxml/jackson/databind/AnnotationIntrospector;->findKeyDeserializer(Lcom/fasterxml/jackson/databind/introspect/Annotated;)Ljava/lang/Object; HSPLcom/fasterxml/jackson/databind/AnnotationIntrospector;->findKeySerializer(Lcom/fasterxml/jackson/databind/introspect/Annotated;)Ljava/lang/Object; HSPLcom/fasterxml/jackson/databind/AnnotationIntrospector;->findMergeInfo(Lcom/fasterxml/jackson/databind/introspect/Annotated;)Ljava/lang/Boolean; HSPLcom/fasterxml/jackson/databind/AnnotationIntrospector;->findNameForDeserialization(Lcom/fasterxml/jackson/databind/introspect/Annotated;)Lcom/fasterxml/jackson/databind/PropertyName; @@ -7836,9 +7967,7 @@ HSPLcom/fasterxml/jackson/databind/DeserializationConfig;->without(Lcom/fasterxm HSPLcom/fasterxml/jackson/databind/DeserializationContext;->(Lcom/fasterxml/jackson/databind/DeserializationContext;Lcom/fasterxml/jackson/databind/DeserializationConfig;Lcom/fasterxml/jackson/core/JsonParser;Lcom/fasterxml/jackson/databind/InjectableValues;)V HSPLcom/fasterxml/jackson/databind/DeserializationContext;->(Lcom/fasterxml/jackson/databind/DeserializationContext;Lcom/fasterxml/jackson/databind/deser/DeserializerFactory;)V HSPLcom/fasterxml/jackson/databind/DeserializationContext;->(Lcom/fasterxml/jackson/databind/deser/DeserializerFactory;Lcom/fasterxml/jackson/databind/deser/DeserializerCache;)V -HSPLcom/fasterxml/jackson/databind/DeserializationContext;->constructType(Ljava/lang/Class;)Lcom/fasterxml/jackson/databind/JavaType; HSPLcom/fasterxml/jackson/databind/DeserializationContext;->findContextualValueDeserializer(Lcom/fasterxml/jackson/databind/JavaType;Lcom/fasterxml/jackson/databind/BeanProperty;)Lcom/fasterxml/jackson/databind/JsonDeserializer; -HSPLcom/fasterxml/jackson/databind/DeserializationContext;->findKeyDeserializer(Lcom/fasterxml/jackson/databind/JavaType;Lcom/fasterxml/jackson/databind/BeanProperty;)Lcom/fasterxml/jackson/databind/KeyDeserializer; HSPLcom/fasterxml/jackson/databind/DeserializationContext;->findNonContextualValueDeserializer(Lcom/fasterxml/jackson/databind/JavaType;)Lcom/fasterxml/jackson/databind/JsonDeserializer; HSPLcom/fasterxml/jackson/databind/DeserializationContext;->findRootValueDeserializer(Lcom/fasterxml/jackson/databind/JavaType;)Lcom/fasterxml/jackson/databind/JsonDeserializer; HSPLcom/fasterxml/jackson/databind/DeserializationContext;->getAnnotationIntrospector()Lcom/fasterxml/jackson/databind/AnnotationIntrospector; @@ -7892,7 +8021,6 @@ HSPLcom/fasterxml/jackson/databind/JsonNode;->()V HSPLcom/fasterxml/jackson/databind/JsonNode;->iterator()Ljava/util/Iterator; HSPLcom/fasterxml/jackson/databind/JsonSerializable$Base;->()V HSPLcom/fasterxml/jackson/databind/JsonSerializer;->()V -HSPLcom/fasterxml/jackson/databind/KeyDeserializer;->()V HSPLcom/fasterxml/jackson/databind/MapperFeature;->()V HSPLcom/fasterxml/jackson/databind/MapperFeature;->(Ljava/lang/String;IZ)V HSPLcom/fasterxml/jackson/databind/MapperFeature;->enabledByDefault()Z @@ -8014,7 +8142,6 @@ HSPLcom/fasterxml/jackson/databind/cfg/BaseSettings;->()V HSPLcom/fasterxml/jackson/databind/cfg/BaseSettings;->(Lcom/fasterxml/jackson/databind/introspect/ClassIntrospector;Lcom/fasterxml/jackson/databind/AnnotationIntrospector;Lcom/fasterxml/jackson/databind/PropertyNamingStrategy;Lcom/fasterxml/jackson/databind/type/TypeFactory;Lcom/fasterxml/jackson/databind/jsontype/TypeResolverBuilder;Ljava/text/DateFormat;Lcom/fasterxml/jackson/databind/cfg/HandlerInstantiator;Ljava/util/Locale;Ljava/util/TimeZone;Lcom/fasterxml/jackson/core/Base64Variant;Lcom/fasterxml/jackson/databind/jsontype/PolymorphicTypeValidator;Lcom/fasterxml/jackson/databind/introspect/AccessorNamingStrategy$Provider;)V HSPLcom/fasterxml/jackson/databind/cfg/BaseSettings;->getAccessorNaming()Lcom/fasterxml/jackson/databind/introspect/AccessorNamingStrategy$Provider; HSPLcom/fasterxml/jackson/databind/cfg/BaseSettings;->getAnnotationIntrospector()Lcom/fasterxml/jackson/databind/AnnotationIntrospector; -HSPLcom/fasterxml/jackson/databind/cfg/BaseSettings;->getBase64Variant()Lcom/fasterxml/jackson/core/Base64Variant; HSPLcom/fasterxml/jackson/databind/cfg/BaseSettings;->getClassIntrospector()Lcom/fasterxml/jackson/databind/introspect/ClassIntrospector; HSPLcom/fasterxml/jackson/databind/cfg/BaseSettings;->getHandlerInstantiator()Lcom/fasterxml/jackson/databind/cfg/HandlerInstantiator; HSPLcom/fasterxml/jackson/databind/cfg/BaseSettings;->getLocale()Ljava/util/Locale; @@ -8077,9 +8204,7 @@ HSPLcom/fasterxml/jackson/databind/cfg/DeserializerFactoryConfig;->([Lcom/ HSPLcom/fasterxml/jackson/databind/cfg/DeserializerFactoryConfig;->deserializers()Ljava/lang/Iterable; HSPLcom/fasterxml/jackson/databind/cfg/DeserializerFactoryConfig;->hasAbstractTypeResolvers()Z HSPLcom/fasterxml/jackson/databind/cfg/DeserializerFactoryConfig;->hasDeserializerModifiers()Z -HSPLcom/fasterxml/jackson/databind/cfg/DeserializerFactoryConfig;->hasKeyDeserializers()Z HSPLcom/fasterxml/jackson/databind/cfg/DeserializerFactoryConfig;->hasValueInstantiators()Z -HSPLcom/fasterxml/jackson/databind/cfg/DeserializerFactoryConfig;->keyDeserializers()Ljava/lang/Iterable; HSPLcom/fasterxml/jackson/databind/cfg/DeserializerFactoryConfig;->valueInstantiators()Ljava/lang/Iterable; HSPLcom/fasterxml/jackson/databind/cfg/DeserializerFactoryConfig;->withAdditionalDeserializers(Lcom/fasterxml/jackson/databind/deser/Deserializers;)Lcom/fasterxml/jackson/databind/cfg/DeserializerFactoryConfig; HSPLcom/fasterxml/jackson/databind/cfg/DeserializerFactoryConfig;->withValueInstantiators(Lcom/fasterxml/jackson/databind/deser/ValueInstantiators;)Lcom/fasterxml/jackson/databind/cfg/DeserializerFactoryConfig; @@ -8092,7 +8217,6 @@ HSPLcom/fasterxml/jackson/databind/cfg/MapperConfig;->collectFeatureDefaults(Lja HSPLcom/fasterxml/jackson/databind/cfg/MapperConfig;->constructType(Ljava/lang/Class;)Lcom/fasterxml/jackson/databind/JavaType; HSPLcom/fasterxml/jackson/databind/cfg/MapperConfig;->getAccessorNaming()Lcom/fasterxml/jackson/databind/introspect/AccessorNamingStrategy$Provider; HSPLcom/fasterxml/jackson/databind/cfg/MapperConfig;->getAnnotationIntrospector()Lcom/fasterxml/jackson/databind/AnnotationIntrospector; -HSPLcom/fasterxml/jackson/databind/cfg/MapperConfig;->getBase64Variant()Lcom/fasterxml/jackson/core/Base64Variant; HSPLcom/fasterxml/jackson/databind/cfg/MapperConfig;->getClassIntrospector()Lcom/fasterxml/jackson/databind/introspect/ClassIntrospector; HSPLcom/fasterxml/jackson/databind/cfg/MapperConfig;->getDefaultInclusion(Ljava/lang/Class;Ljava/lang/Class;Lcom/fasterxml/jackson/annotation/JsonInclude$Value;)Lcom/fasterxml/jackson/annotation/JsonInclude$Value; HSPLcom/fasterxml/jackson/databind/cfg/MapperConfig;->getDefaultPropertyInclusion(Ljava/lang/Class;Lcom/fasterxml/jackson/annotation/JsonInclude$Value;)Lcom/fasterxml/jackson/annotation/JsonInclude$Value; @@ -8139,7 +8263,6 @@ HSPLcom/fasterxml/jackson/databind/cfg/SerializerFactoryConfig;->withAdditionalS HSPLcom/fasterxml/jackson/databind/deser/BasicDeserializerFactory$1;->()V HSPLcom/fasterxml/jackson/databind/deser/BasicDeserializerFactory$ContainerDefaultMappings;->()V HSPLcom/fasterxml/jackson/databind/deser/BasicDeserializerFactory$ContainerDefaultMappings;->findCollectionFallback(Lcom/fasterxml/jackson/databind/JavaType;)Ljava/lang/Class; -HSPLcom/fasterxml/jackson/databind/deser/BasicDeserializerFactory$ContainerDefaultMappings;->findMapFallback(Lcom/fasterxml/jackson/databind/JavaType;)Ljava/lang/Class; HSPLcom/fasterxml/jackson/databind/deser/BasicDeserializerFactory$CreatorCollectionState;->(Lcom/fasterxml/jackson/databind/DeserializationContext;Lcom/fasterxml/jackson/databind/BeanDescription;Lcom/fasterxml/jackson/databind/introspect/VisibilityChecker;Lcom/fasterxml/jackson/databind/deser/impl/CreatorCollector;Ljava/util/Map;)V HSPLcom/fasterxml/jackson/databind/deser/BasicDeserializerFactory$CreatorCollectionState;->addImplicitConstructorCandidate(Lcom/fasterxml/jackson/databind/deser/impl/CreatorCandidate;)V HSPLcom/fasterxml/jackson/databind/deser/BasicDeserializerFactory$CreatorCollectionState;->addImplicitFactoryCandidate(Lcom/fasterxml/jackson/databind/deser/impl/CreatorCandidate;)V @@ -8165,18 +8288,14 @@ HSPLcom/fasterxml/jackson/databind/deser/BasicDeserializerFactory;->_findCreator HSPLcom/fasterxml/jackson/databind/deser/BasicDeserializerFactory;->_findCustomArrayDeserializer(Lcom/fasterxml/jackson/databind/type/ArrayType;Lcom/fasterxml/jackson/databind/DeserializationConfig;Lcom/fasterxml/jackson/databind/BeanDescription;Lcom/fasterxml/jackson/databind/jsontype/TypeDeserializer;Lcom/fasterxml/jackson/databind/JsonDeserializer;)Lcom/fasterxml/jackson/databind/JsonDeserializer; HSPLcom/fasterxml/jackson/databind/deser/BasicDeserializerFactory;->_findCustomBeanDeserializer(Lcom/fasterxml/jackson/databind/JavaType;Lcom/fasterxml/jackson/databind/DeserializationConfig;Lcom/fasterxml/jackson/databind/BeanDescription;)Lcom/fasterxml/jackson/databind/JsonDeserializer; HSPLcom/fasterxml/jackson/databind/deser/BasicDeserializerFactory;->_findCustomCollectionDeserializer(Lcom/fasterxml/jackson/databind/type/CollectionType;Lcom/fasterxml/jackson/databind/DeserializationConfig;Lcom/fasterxml/jackson/databind/BeanDescription;Lcom/fasterxml/jackson/databind/jsontype/TypeDeserializer;Lcom/fasterxml/jackson/databind/JsonDeserializer;)Lcom/fasterxml/jackson/databind/JsonDeserializer; -HSPLcom/fasterxml/jackson/databind/deser/BasicDeserializerFactory;->_findCustomMapDeserializer(Lcom/fasterxml/jackson/databind/type/MapType;Lcom/fasterxml/jackson/databind/DeserializationConfig;Lcom/fasterxml/jackson/databind/BeanDescription;Lcom/fasterxml/jackson/databind/KeyDeserializer;Lcom/fasterxml/jackson/databind/jsontype/TypeDeserializer;Lcom/fasterxml/jackson/databind/JsonDeserializer;)Lcom/fasterxml/jackson/databind/JsonDeserializer; HSPLcom/fasterxml/jackson/databind/deser/BasicDeserializerFactory;->_findCustomTreeNodeDeserializer(Ljava/lang/Class;Lcom/fasterxml/jackson/databind/DeserializationConfig;Lcom/fasterxml/jackson/databind/BeanDescription;)Lcom/fasterxml/jackson/databind/JsonDeserializer; HSPLcom/fasterxml/jackson/databind/deser/BasicDeserializerFactory;->_getSetterInfo(Lcom/fasterxml/jackson/databind/DeserializationContext;Lcom/fasterxml/jackson/databind/BeanProperty;Lcom/fasterxml/jackson/databind/PropertyMetadata;)Lcom/fasterxml/jackson/databind/PropertyMetadata; HSPLcom/fasterxml/jackson/databind/deser/BasicDeserializerFactory;->_handleSingleArgumentCreator(Lcom/fasterxml/jackson/databind/deser/impl/CreatorCollector;Lcom/fasterxml/jackson/databind/introspect/AnnotatedWithParams;ZZ)Z HSPLcom/fasterxml/jackson/databind/deser/BasicDeserializerFactory;->_mapAbstractCollectionType(Lcom/fasterxml/jackson/databind/JavaType;Lcom/fasterxml/jackson/databind/DeserializationConfig;)Lcom/fasterxml/jackson/databind/type/CollectionType; -HSPLcom/fasterxml/jackson/databind/deser/BasicDeserializerFactory;->_mapAbstractMapType(Lcom/fasterxml/jackson/databind/JavaType;Lcom/fasterxml/jackson/databind/DeserializationConfig;)Lcom/fasterxml/jackson/databind/type/MapType; HSPLcom/fasterxml/jackson/databind/deser/BasicDeserializerFactory;->_mapAbstractType2(Lcom/fasterxml/jackson/databind/DeserializationConfig;Lcom/fasterxml/jackson/databind/JavaType;)Lcom/fasterxml/jackson/databind/JavaType; HSPLcom/fasterxml/jackson/databind/deser/BasicDeserializerFactory;->constructCreatorProperty(Lcom/fasterxml/jackson/databind/DeserializationContext;Lcom/fasterxml/jackson/databind/BeanDescription;Lcom/fasterxml/jackson/databind/PropertyName;ILcom/fasterxml/jackson/databind/introspect/AnnotatedParameter;Lcom/fasterxml/jackson/annotation/JacksonInject$Value;)Lcom/fasterxml/jackson/databind/deser/SettableBeanProperty; HSPLcom/fasterxml/jackson/databind/deser/BasicDeserializerFactory;->createArrayDeserializer(Lcom/fasterxml/jackson/databind/DeserializationContext;Lcom/fasterxml/jackson/databind/type/ArrayType;Lcom/fasterxml/jackson/databind/BeanDescription;)Lcom/fasterxml/jackson/databind/JsonDeserializer; HSPLcom/fasterxml/jackson/databind/deser/BasicDeserializerFactory;->createCollectionDeserializer(Lcom/fasterxml/jackson/databind/DeserializationContext;Lcom/fasterxml/jackson/databind/type/CollectionType;Lcom/fasterxml/jackson/databind/BeanDescription;)Lcom/fasterxml/jackson/databind/JsonDeserializer; -HSPLcom/fasterxml/jackson/databind/deser/BasicDeserializerFactory;->createKeyDeserializer(Lcom/fasterxml/jackson/databind/DeserializationContext;Lcom/fasterxml/jackson/databind/JavaType;)Lcom/fasterxml/jackson/databind/KeyDeserializer; -HSPLcom/fasterxml/jackson/databind/deser/BasicDeserializerFactory;->createMapDeserializer(Lcom/fasterxml/jackson/databind/DeserializationContext;Lcom/fasterxml/jackson/databind/type/MapType;Lcom/fasterxml/jackson/databind/BeanDescription;)Lcom/fasterxml/jackson/databind/JsonDeserializer; HSPLcom/fasterxml/jackson/databind/deser/BasicDeserializerFactory;->createTreeDeserializer(Lcom/fasterxml/jackson/databind/DeserializationConfig;Lcom/fasterxml/jackson/databind/JavaType;Lcom/fasterxml/jackson/databind/BeanDescription;)Lcom/fasterxml/jackson/databind/JsonDeserializer; HSPLcom/fasterxml/jackson/databind/deser/BasicDeserializerFactory;->findDefaultDeserializer(Lcom/fasterxml/jackson/databind/DeserializationContext;Lcom/fasterxml/jackson/databind/JavaType;Lcom/fasterxml/jackson/databind/BeanDescription;)Lcom/fasterxml/jackson/databind/JsonDeserializer; HSPLcom/fasterxml/jackson/databind/deser/BasicDeserializerFactory;->findDeserializerFromAnnotation(Lcom/fasterxml/jackson/databind/DeserializationContext;Lcom/fasterxml/jackson/databind/introspect/Annotated;)Lcom/fasterxml/jackson/databind/JsonDeserializer; @@ -8192,9 +8311,9 @@ HSPLcom/fasterxml/jackson/databind/deser/BasicDeserializerFactory;->withValueIns HSPLcom/fasterxml/jackson/databind/deser/BeanDeserializer;->(Lcom/fasterxml/jackson/databind/deser/BeanDeserializerBuilder;Lcom/fasterxml/jackson/databind/BeanDescription;Lcom/fasterxml/jackson/databind/deser/impl/BeanPropertyMap;Ljava/util/Map;Ljava/util/HashSet;ZLjava/util/Set;Z)V HSPLcom/fasterxml/jackson/databind/deser/BeanDeserializer;->_deserializeUsingPropertyBased(Lcom/fasterxml/jackson/core/JsonParser;Lcom/fasterxml/jackson/databind/DeserializationContext;)Ljava/lang/Object; HSPLcom/fasterxml/jackson/databind/deser/BeanDeserializer;->_deserializeWithErrorWrapping(Lcom/fasterxml/jackson/core/JsonParser;Lcom/fasterxml/jackson/databind/DeserializationContext;Lcom/fasterxml/jackson/databind/deser/SettableBeanProperty;)Ljava/lang/Object; +HSPLcom/fasterxml/jackson/databind/deser/BeanDeserializer;->deserialize(Lcom/fasterxml/jackson/core/JsonParser;Lcom/fasterxml/jackson/databind/DeserializationContext;)Ljava/lang/Object; HSPLcom/fasterxml/jackson/databind/deser/BeanDeserializer;->deserialize(Lcom/fasterxml/jackson/core/JsonParser;Lcom/fasterxml/jackson/databind/DeserializationContext;Ljava/lang/Object;)Ljava/lang/Object; HSPLcom/fasterxml/jackson/databind/deser/BeanDeserializer;->deserializeFromObject(Lcom/fasterxml/jackson/core/JsonParser;Lcom/fasterxml/jackson/databind/DeserializationContext;)Ljava/lang/Object; -HSPLcom/fasterxml/jackson/databind/deser/BeanDeserializer;->vanillaDeserialize(Lcom/fasterxml/jackson/core/JsonParser;Lcom/fasterxml/jackson/databind/DeserializationContext;Lcom/fasterxml/jackson/core/JsonToken;)Ljava/lang/Object; HSPLcom/fasterxml/jackson/databind/deser/BeanDeserializerBase;->()V HSPLcom/fasterxml/jackson/databind/deser/BeanDeserializerBase;->(Lcom/fasterxml/jackson/databind/deser/BeanDeserializerBuilder;Lcom/fasterxml/jackson/databind/BeanDescription;Lcom/fasterxml/jackson/databind/deser/impl/BeanPropertyMap;Ljava/util/Map;Ljava/util/Set;ZLjava/util/Set;Z)V HSPLcom/fasterxml/jackson/databind/deser/BeanDeserializerBase;->_delegateDeserializer()Lcom/fasterxml/jackson/databind/JsonDeserializer; @@ -8231,7 +8350,6 @@ HSPLcom/fasterxml/jackson/databind/deser/BeanDeserializerBuilder;->setValueInsta HSPLcom/fasterxml/jackson/databind/deser/BeanDeserializerFactory;->()V HSPLcom/fasterxml/jackson/databind/deser/BeanDeserializerFactory;->(Lcom/fasterxml/jackson/databind/cfg/DeserializerFactoryConfig;)V HSPLcom/fasterxml/jackson/databind/deser/BeanDeserializerFactory;->_findUnsupportedTypeDeserializer(Lcom/fasterxml/jackson/databind/DeserializationContext;Lcom/fasterxml/jackson/databind/JavaType;Lcom/fasterxml/jackson/databind/BeanDescription;)Lcom/fasterxml/jackson/databind/JsonDeserializer; -HSPLcom/fasterxml/jackson/databind/deser/BeanDeserializerFactory;->_isSetterlessType(Ljava/lang/Class;)Z HSPLcom/fasterxml/jackson/databind/deser/BeanDeserializerFactory;->_validateSubType(Lcom/fasterxml/jackson/databind/DeserializationContext;Lcom/fasterxml/jackson/databind/JavaType;Lcom/fasterxml/jackson/databind/BeanDescription;)V HSPLcom/fasterxml/jackson/databind/deser/BeanDeserializerFactory;->addBackReferenceProperties(Lcom/fasterxml/jackson/databind/DeserializationContext;Lcom/fasterxml/jackson/databind/BeanDescription;Lcom/fasterxml/jackson/databind/deser/BeanDeserializerBuilder;)V HSPLcom/fasterxml/jackson/databind/deser/BeanDeserializerFactory;->addBeanProps(Lcom/fasterxml/jackson/databind/DeserializationContext;Lcom/fasterxml/jackson/databind/BeanDescription;Lcom/fasterxml/jackson/databind/deser/BeanDeserializerBuilder;)V @@ -8267,7 +8385,6 @@ HSPLcom/fasterxml/jackson/databind/deser/DefaultDeserializationContext;->( HSPLcom/fasterxml/jackson/databind/deser/DefaultDeserializationContext;->(Lcom/fasterxml/jackson/databind/deser/DeserializerFactory;Lcom/fasterxml/jackson/databind/deser/DeserializerCache;)V HSPLcom/fasterxml/jackson/databind/deser/DefaultDeserializationContext;->checkUnresolvedObjectId()V HSPLcom/fasterxml/jackson/databind/deser/DefaultDeserializationContext;->deserializerInstance(Lcom/fasterxml/jackson/databind/introspect/Annotated;Ljava/lang/Object;)Lcom/fasterxml/jackson/databind/JsonDeserializer; -HSPLcom/fasterxml/jackson/databind/deser/DefaultDeserializationContext;->keyDeserializerInstance(Lcom/fasterxml/jackson/databind/introspect/Annotated;Ljava/lang/Object;)Lcom/fasterxml/jackson/databind/KeyDeserializer; HSPLcom/fasterxml/jackson/databind/deser/DefaultDeserializationContext;->readRootValue(Lcom/fasterxml/jackson/core/JsonParser;Lcom/fasterxml/jackson/databind/JavaType;Lcom/fasterxml/jackson/databind/JsonDeserializer;Ljava/lang/Object;)Ljava/lang/Object; HSPLcom/fasterxml/jackson/databind/deser/DeserializerCache;->()V HSPLcom/fasterxml/jackson/databind/deser/DeserializerCache;->(I)V @@ -8278,7 +8395,6 @@ HSPLcom/fasterxml/jackson/databind/deser/DeserializerCache;->_createDeserializer HSPLcom/fasterxml/jackson/databind/deser/DeserializerCache;->_findCachedDeserializer(Lcom/fasterxml/jackson/databind/JavaType;)Lcom/fasterxml/jackson/databind/JsonDeserializer; HSPLcom/fasterxml/jackson/databind/deser/DeserializerCache;->_hasCustomHandlers(Lcom/fasterxml/jackson/databind/JavaType;)Z HSPLcom/fasterxml/jackson/databind/deser/DeserializerCache;->findDeserializerFromAnnotation(Lcom/fasterxml/jackson/databind/DeserializationContext;Lcom/fasterxml/jackson/databind/introspect/Annotated;)Lcom/fasterxml/jackson/databind/JsonDeserializer; -HSPLcom/fasterxml/jackson/databind/deser/DeserializerCache;->findKeyDeserializer(Lcom/fasterxml/jackson/databind/DeserializationContext;Lcom/fasterxml/jackson/databind/deser/DeserializerFactory;Lcom/fasterxml/jackson/databind/JavaType;)Lcom/fasterxml/jackson/databind/KeyDeserializer; HSPLcom/fasterxml/jackson/databind/deser/DeserializerCache;->findValueDeserializer(Lcom/fasterxml/jackson/databind/DeserializationContext;Lcom/fasterxml/jackson/databind/deser/DeserializerFactory;Lcom/fasterxml/jackson/databind/JavaType;)Lcom/fasterxml/jackson/databind/JsonDeserializer; HSPLcom/fasterxml/jackson/databind/deser/DeserializerCache;->modifyTypeByAnnotation(Lcom/fasterxml/jackson/databind/DeserializationContext;Lcom/fasterxml/jackson/databind/introspect/Annotated;Lcom/fasterxml/jackson/databind/JavaType;)Lcom/fasterxml/jackson/databind/JavaType; HSPLcom/fasterxml/jackson/databind/deser/DeserializerFactory;->()V @@ -8286,7 +8402,6 @@ HSPLcom/fasterxml/jackson/databind/deser/DeserializerFactory;->()V HSPLcom/fasterxml/jackson/databind/deser/Deserializers$Base;->()V HSPLcom/fasterxml/jackson/databind/deser/Deserializers$Base;->findArrayDeserializer(Lcom/fasterxml/jackson/databind/type/ArrayType;Lcom/fasterxml/jackson/databind/DeserializationConfig;Lcom/fasterxml/jackson/databind/BeanDescription;Lcom/fasterxml/jackson/databind/jsontype/TypeDeserializer;Lcom/fasterxml/jackson/databind/JsonDeserializer;)Lcom/fasterxml/jackson/databind/JsonDeserializer; HSPLcom/fasterxml/jackson/databind/deser/Deserializers$Base;->findCollectionDeserializer(Lcom/fasterxml/jackson/databind/type/CollectionType;Lcom/fasterxml/jackson/databind/DeserializationConfig;Lcom/fasterxml/jackson/databind/BeanDescription;Lcom/fasterxml/jackson/databind/jsontype/TypeDeserializer;Lcom/fasterxml/jackson/databind/JsonDeserializer;)Lcom/fasterxml/jackson/databind/JsonDeserializer; -HSPLcom/fasterxml/jackson/databind/deser/Deserializers$Base;->findMapDeserializer(Lcom/fasterxml/jackson/databind/type/MapType;Lcom/fasterxml/jackson/databind/DeserializationConfig;Lcom/fasterxml/jackson/databind/BeanDescription;Lcom/fasterxml/jackson/databind/KeyDeserializer;Lcom/fasterxml/jackson/databind/jsontype/TypeDeserializer;Lcom/fasterxml/jackson/databind/JsonDeserializer;)Lcom/fasterxml/jackson/databind/JsonDeserializer; HSPLcom/fasterxml/jackson/databind/deser/Deserializers$Base;->findTreeNodeDeserializer(Ljava/lang/Class;Lcom/fasterxml/jackson/databind/DeserializationConfig;Lcom/fasterxml/jackson/databind/BeanDescription;)Lcom/fasterxml/jackson/databind/JsonDeserializer; HSPLcom/fasterxml/jackson/databind/deser/SettableBeanProperty;->()V HSPLcom/fasterxml/jackson/databind/deser/SettableBeanProperty;->(Lcom/fasterxml/jackson/databind/PropertyName;Lcom/fasterxml/jackson/databind/JavaType;Lcom/fasterxml/jackson/databind/PropertyName;Lcom/fasterxml/jackson/databind/jsontype/TypeDeserializer;Lcom/fasterxml/jackson/databind/util/Annotations;Lcom/fasterxml/jackson/databind/PropertyMetadata;)V @@ -8305,11 +8420,7 @@ HSPLcom/fasterxml/jackson/databind/deser/SettableBeanProperty;->hasViews()Z HSPLcom/fasterxml/jackson/databind/deser/SettableBeanProperty;->setViews([Ljava/lang/Class;)V HSPLcom/fasterxml/jackson/databind/deser/ValueInstantiator$Base;->(Ljava/lang/Class;)V HSPLcom/fasterxml/jackson/databind/deser/ValueInstantiator;->()V -HSPLcom/fasterxml/jackson/databind/deser/ValueInstantiator;->canCreateFromObjectWith()Z -HSPLcom/fasterxml/jackson/databind/deser/ValueInstantiator;->canCreateUsingArrayDelegate()Z -HSPLcom/fasterxml/jackson/databind/deser/ValueInstantiator;->canCreateUsingDelegate()Z HSPLcom/fasterxml/jackson/databind/deser/ValueInstantiator;->createContextual(Lcom/fasterxml/jackson/databind/DeserializationContext;Lcom/fasterxml/jackson/databind/BeanDescription;)Lcom/fasterxml/jackson/databind/deser/ValueInstantiator; -HSPLcom/fasterxml/jackson/databind/deser/ValueInstantiator;->createFromObjectWith(Lcom/fasterxml/jackson/databind/DeserializationContext;[Lcom/fasterxml/jackson/databind/deser/SettableBeanProperty;Lcom/fasterxml/jackson/databind/deser/impl/PropertyValueBuffer;)Ljava/lang/Object; HSPLcom/fasterxml/jackson/databind/deser/ValueInstantiator;->getArrayDelegateCreator()Lcom/fasterxml/jackson/databind/introspect/AnnotatedWithParams; HSPLcom/fasterxml/jackson/databind/deser/ValueInstantiator;->getDelegateCreator()Lcom/fasterxml/jackson/databind/introspect/AnnotatedWithParams; HSPLcom/fasterxml/jackson/databind/deser/impl/BeanPropertyMap;->(ZLjava/util/Collection;Ljava/util/Map;Ljava/util/Locale;)V @@ -8353,7 +8464,6 @@ HSPLcom/fasterxml/jackson/databind/deser/impl/FailingDeserializer;->(Ljava HSPLcom/fasterxml/jackson/databind/deser/impl/FailingDeserializer;->(Ljava/lang/String;)V HSPLcom/fasterxml/jackson/databind/deser/impl/FieldProperty;->(Lcom/fasterxml/jackson/databind/deser/impl/FieldProperty;Lcom/fasterxml/jackson/databind/JsonDeserializer;Lcom/fasterxml/jackson/databind/deser/NullValueProvider;)V HSPLcom/fasterxml/jackson/databind/deser/impl/FieldProperty;->(Lcom/fasterxml/jackson/databind/introspect/BeanPropertyDefinition;Lcom/fasterxml/jackson/databind/JavaType;Lcom/fasterxml/jackson/databind/jsontype/TypeDeserializer;Lcom/fasterxml/jackson/databind/util/Annotations;Lcom/fasterxml/jackson/databind/introspect/AnnotatedField;)V -HSPLcom/fasterxml/jackson/databind/deser/impl/FieldProperty;->deserializeAndSet(Lcom/fasterxml/jackson/core/JsonParser;Lcom/fasterxml/jackson/databind/DeserializationContext;Ljava/lang/Object;)V HSPLcom/fasterxml/jackson/databind/deser/impl/FieldProperty;->fixAccess(Lcom/fasterxml/jackson/databind/DeserializationConfig;)V HSPLcom/fasterxml/jackson/databind/deser/impl/FieldProperty;->getMember()Lcom/fasterxml/jackson/databind/introspect/AnnotatedMember; HSPLcom/fasterxml/jackson/databind/deser/impl/FieldProperty;->withValueDeserializer(Lcom/fasterxml/jackson/databind/JsonDeserializer;)Lcom/fasterxml/jackson/databind/deser/SettableBeanProperty; @@ -8361,10 +8471,6 @@ HSPLcom/fasterxml/jackson/databind/deser/impl/JDKValueInstantiators$ArrayListIns HSPLcom/fasterxml/jackson/databind/deser/impl/JDKValueInstantiators$ArrayListInstantiator;->()V HSPLcom/fasterxml/jackson/databind/deser/impl/JDKValueInstantiators$ArrayListInstantiator;->canCreateUsingDefault()Z HSPLcom/fasterxml/jackson/databind/deser/impl/JDKValueInstantiators$ArrayListInstantiator;->createUsingDefault(Lcom/fasterxml/jackson/databind/DeserializationContext;)Ljava/lang/Object; -HSPLcom/fasterxml/jackson/databind/deser/impl/JDKValueInstantiators$LinkedHashMapInstantiator;->()V -HSPLcom/fasterxml/jackson/databind/deser/impl/JDKValueInstantiators$LinkedHashMapInstantiator;->()V -HSPLcom/fasterxml/jackson/databind/deser/impl/JDKValueInstantiators$LinkedHashMapInstantiator;->canCreateUsingDefault()Z -HSPLcom/fasterxml/jackson/databind/deser/impl/JDKValueInstantiators$LinkedHashMapInstantiator;->createUsingDefault(Lcom/fasterxml/jackson/databind/DeserializationContext;)Ljava/lang/Object; HSPLcom/fasterxml/jackson/databind/deser/impl/JDKValueInstantiators;->findStdValueInstantiator(Lcom/fasterxml/jackson/databind/DeserializationConfig;Ljava/lang/Class;)Lcom/fasterxml/jackson/databind/deser/ValueInstantiator; HSPLcom/fasterxml/jackson/databind/deser/impl/NullsConstantProvider;->()V HSPLcom/fasterxml/jackson/databind/deser/impl/NullsConstantProvider;->(Ljava/lang/Object;)V @@ -8378,7 +8484,6 @@ HSPLcom/fasterxml/jackson/databind/deser/impl/PropertyValueBuffer;->(Lcom/ HSPLcom/fasterxml/jackson/databind/deser/impl/PropertyValueBuffer;->assignParameter(Lcom/fasterxml/jackson/databind/deser/SettableBeanProperty;Ljava/lang/Object;)Z HSPLcom/fasterxml/jackson/databind/deser/impl/PropertyValueBuffer;->buffered()Lcom/fasterxml/jackson/databind/deser/impl/PropertyValue; HSPLcom/fasterxml/jackson/databind/deser/impl/PropertyValueBuffer;->getParameter(Lcom/fasterxml/jackson/databind/deser/SettableBeanProperty;)Ljava/lang/Object; -HSPLcom/fasterxml/jackson/databind/deser/impl/PropertyValueBuffer;->getParameters([Lcom/fasterxml/jackson/databind/deser/SettableBeanProperty;)[Ljava/lang/Object; HSPLcom/fasterxml/jackson/databind/deser/impl/PropertyValueBuffer;->handleIdValue(Lcom/fasterxml/jackson/databind/DeserializationContext;Ljava/lang/Object;)Ljava/lang/Object; HSPLcom/fasterxml/jackson/databind/deser/impl/PropertyValueBuffer;->hasParameter(Lcom/fasterxml/jackson/databind/deser/SettableBeanProperty;)Z HSPLcom/fasterxml/jackson/databind/deser/impl/PropertyValueBuffer;->readIdProperty(Ljava/lang/String;)Z @@ -8402,26 +8507,6 @@ HSPLcom/fasterxml/jackson/databind/deser/std/JsonNodeDeserializer;->deserialize( HSPLcom/fasterxml/jackson/databind/deser/std/JsonNodeDeserializer;->deserialize(Lcom/fasterxml/jackson/core/JsonParser;Lcom/fasterxml/jackson/databind/DeserializationContext;)Ljava/lang/Object; HSPLcom/fasterxml/jackson/databind/deser/std/JsonNodeDeserializer;->getDeserializer(Ljava/lang/Class;)Lcom/fasterxml/jackson/databind/JsonDeserializer; HSPLcom/fasterxml/jackson/databind/deser/std/JsonNodeDeserializer;->isCachable()Z -HSPLcom/fasterxml/jackson/databind/deser/std/MapDeserializer;->(Lcom/fasterxml/jackson/databind/JavaType;Lcom/fasterxml/jackson/databind/deser/ValueInstantiator;Lcom/fasterxml/jackson/databind/KeyDeserializer;Lcom/fasterxml/jackson/databind/JsonDeserializer;Lcom/fasterxml/jackson/databind/jsontype/TypeDeserializer;)V -HSPLcom/fasterxml/jackson/databind/deser/std/MapDeserializer;->(Lcom/fasterxml/jackson/databind/deser/std/MapDeserializer;Lcom/fasterxml/jackson/databind/KeyDeserializer;Lcom/fasterxml/jackson/databind/JsonDeserializer;Lcom/fasterxml/jackson/databind/jsontype/TypeDeserializer;Lcom/fasterxml/jackson/databind/deser/NullValueProvider;Ljava/util/Set;Ljava/util/Set;)V -HSPLcom/fasterxml/jackson/databind/deser/std/MapDeserializer;->_isStdKeyDeser(Lcom/fasterxml/jackson/databind/JavaType;Lcom/fasterxml/jackson/databind/KeyDeserializer;)Z -HSPLcom/fasterxml/jackson/databind/deser/std/MapDeserializer;->_readAndBindStringKeyMap(Lcom/fasterxml/jackson/core/JsonParser;Lcom/fasterxml/jackson/databind/DeserializationContext;Ljava/util/Map;)V -HSPLcom/fasterxml/jackson/databind/deser/std/MapDeserializer;->createContextual(Lcom/fasterxml/jackson/databind/DeserializationContext;Lcom/fasterxml/jackson/databind/BeanProperty;)Lcom/fasterxml/jackson/databind/JsonDeserializer; -HSPLcom/fasterxml/jackson/databind/deser/std/MapDeserializer;->deserialize(Lcom/fasterxml/jackson/core/JsonParser;Lcom/fasterxml/jackson/databind/DeserializationContext;)Ljava/lang/Object; -HSPLcom/fasterxml/jackson/databind/deser/std/MapDeserializer;->deserialize(Lcom/fasterxml/jackson/core/JsonParser;Lcom/fasterxml/jackson/databind/DeserializationContext;)Ljava/util/Map; -HSPLcom/fasterxml/jackson/databind/deser/std/MapDeserializer;->isCachable()Z -HSPLcom/fasterxml/jackson/databind/deser/std/MapDeserializer;->resolve(Lcom/fasterxml/jackson/databind/DeserializationContext;)V -HSPLcom/fasterxml/jackson/databind/deser/std/MapDeserializer;->setIgnorableProperties(Ljava/util/Set;)V -HSPLcom/fasterxml/jackson/databind/deser/std/MapDeserializer;->setIncludableProperties(Ljava/util/Set;)V -HSPLcom/fasterxml/jackson/databind/deser/std/MapDeserializer;->withResolved(Lcom/fasterxml/jackson/databind/KeyDeserializer;Lcom/fasterxml/jackson/databind/jsontype/TypeDeserializer;Lcom/fasterxml/jackson/databind/JsonDeserializer;Lcom/fasterxml/jackson/databind/deser/NullValueProvider;Ljava/util/Set;Ljava/util/Set;)Lcom/fasterxml/jackson/databind/deser/std/MapDeserializer; -HSPLcom/fasterxml/jackson/databind/deser/std/NumberDeserializers$BooleanDeserializer;->()V -HSPLcom/fasterxml/jackson/databind/deser/std/NumberDeserializers$BooleanDeserializer;->(Ljava/lang/Class;Ljava/lang/Boolean;)V -HSPLcom/fasterxml/jackson/databind/deser/std/NumberDeserializers$BooleanDeserializer;->deserialize(Lcom/fasterxml/jackson/core/JsonParser;Lcom/fasterxml/jackson/databind/DeserializationContext;)Ljava/lang/Boolean; -HSPLcom/fasterxml/jackson/databind/deser/std/NumberDeserializers$BooleanDeserializer;->deserialize(Lcom/fasterxml/jackson/core/JsonParser;Lcom/fasterxml/jackson/databind/DeserializationContext;)Ljava/lang/Object; -HSPLcom/fasterxml/jackson/databind/deser/std/NumberDeserializers$DoubleDeserializer;->()V -HSPLcom/fasterxml/jackson/databind/deser/std/NumberDeserializers$DoubleDeserializer;->(Ljava/lang/Class;Ljava/lang/Double;)V -HSPLcom/fasterxml/jackson/databind/deser/std/NumberDeserializers$FloatDeserializer;->()V -HSPLcom/fasterxml/jackson/databind/deser/std/NumberDeserializers$FloatDeserializer;->(Ljava/lang/Class;Ljava/lang/Float;)V HSPLcom/fasterxml/jackson/databind/deser/std/NumberDeserializers$IntegerDeserializer;->()V HSPLcom/fasterxml/jackson/databind/deser/std/NumberDeserializers$IntegerDeserializer;->(Ljava/lang/Class;Ljava/lang/Integer;)V HSPLcom/fasterxml/jackson/databind/deser/std/NumberDeserializers$IntegerDeserializer;->deserialize(Lcom/fasterxml/jackson/core/JsonParser;Lcom/fasterxml/jackson/databind/DeserializationContext;)Ljava/lang/Integer; @@ -8442,16 +8527,6 @@ HSPLcom/fasterxml/jackson/databind/deser/std/ObjectArrayDeserializer;->deseriali HSPLcom/fasterxml/jackson/databind/deser/std/ObjectArrayDeserializer;->deserialize(Lcom/fasterxml/jackson/core/JsonParser;Lcom/fasterxml/jackson/databind/DeserializationContext;)[Ljava/lang/Object; HSPLcom/fasterxml/jackson/databind/deser/std/ObjectArrayDeserializer;->isCachable()Z HSPLcom/fasterxml/jackson/databind/deser/std/ObjectArrayDeserializer;->withResolved(Lcom/fasterxml/jackson/databind/jsontype/TypeDeserializer;Lcom/fasterxml/jackson/databind/JsonDeserializer;Lcom/fasterxml/jackson/databind/deser/NullValueProvider;Ljava/lang/Boolean;)Lcom/fasterxml/jackson/databind/deser/std/ObjectArrayDeserializer; -HSPLcom/fasterxml/jackson/databind/deser/std/PrimitiveArrayDeserializers$BooleanDeser;->()V -HSPLcom/fasterxml/jackson/databind/deser/std/PrimitiveArrayDeserializers$DoubleDeser;->()V -HSPLcom/fasterxml/jackson/databind/deser/std/PrimitiveArrayDeserializers$FloatDeser;->()V -HSPLcom/fasterxml/jackson/databind/deser/std/PrimitiveArrayDeserializers$IntDeser;->()V -HSPLcom/fasterxml/jackson/databind/deser/std/PrimitiveArrayDeserializers$IntDeser;->()V -HSPLcom/fasterxml/jackson/databind/deser/std/PrimitiveArrayDeserializers$LongDeser;->()V -HSPLcom/fasterxml/jackson/databind/deser/std/PrimitiveArrayDeserializers$LongDeser;->()V -HSPLcom/fasterxml/jackson/databind/deser/std/PrimitiveArrayDeserializers;->(Ljava/lang/Class;)V -HSPLcom/fasterxml/jackson/databind/deser/std/PrimitiveArrayDeserializers;->createContextual(Lcom/fasterxml/jackson/databind/DeserializationContext;Lcom/fasterxml/jackson/databind/BeanProperty;)Lcom/fasterxml/jackson/databind/JsonDeserializer; -HSPLcom/fasterxml/jackson/databind/deser/std/PrimitiveArrayDeserializers;->forType(Ljava/lang/Class;)Lcom/fasterxml/jackson/databind/JsonDeserializer; HSPLcom/fasterxml/jackson/databind/deser/std/StdDeserializer;->()V HSPLcom/fasterxml/jackson/databind/deser/std/StdDeserializer;->(Lcom/fasterxml/jackson/databind/JavaType;)V HSPLcom/fasterxml/jackson/databind/deser/std/StdDeserializer;->(Ljava/lang/Class;)V @@ -8465,15 +8540,7 @@ HSPLcom/fasterxml/jackson/databind/deser/std/StdDeserializer;->findFormatOverrid HSPLcom/fasterxml/jackson/databind/deser/std/StdDeserializer;->findValueNullProvider(Lcom/fasterxml/jackson/databind/DeserializationContext;Lcom/fasterxml/jackson/databind/deser/SettableBeanProperty;Lcom/fasterxml/jackson/databind/PropertyMetadata;)Lcom/fasterxml/jackson/databind/deser/NullValueProvider; HSPLcom/fasterxml/jackson/databind/deser/std/StdDeserializer;->handleUnknownProperty(Lcom/fasterxml/jackson/core/JsonParser;Lcom/fasterxml/jackson/databind/DeserializationContext;Ljava/lang/Object;Ljava/lang/String;)V HSPLcom/fasterxml/jackson/databind/deser/std/StdDeserializer;->isDefaultDeserializer(Lcom/fasterxml/jackson/databind/JsonDeserializer;)Z -HSPLcom/fasterxml/jackson/databind/deser/std/StdDeserializer;->isDefaultKeyDeserializer(Lcom/fasterxml/jackson/databind/KeyDeserializer;)Z -HSPLcom/fasterxml/jackson/databind/deser/std/StdKeyDeserializer$StringKD;->()V -HSPLcom/fasterxml/jackson/databind/deser/std/StdKeyDeserializer$StringKD;->(Ljava/lang/Class;)V -HSPLcom/fasterxml/jackson/databind/deser/std/StdKeyDeserializer$StringKD;->forType(Ljava/lang/Class;)Lcom/fasterxml/jackson/databind/deser/std/StdKeyDeserializer$StringKD; -HSPLcom/fasterxml/jackson/databind/deser/std/StdKeyDeserializer;->(ILjava/lang/Class;)V -HSPLcom/fasterxml/jackson/databind/deser/std/StdKeyDeserializer;->(ILjava/lang/Class;Lcom/fasterxml/jackson/databind/deser/std/FromStringDeserializer;)V -HSPLcom/fasterxml/jackson/databind/deser/std/StdKeyDeserializer;->forType(Ljava/lang/Class;)Lcom/fasterxml/jackson/databind/deser/std/StdKeyDeserializer; HSPLcom/fasterxml/jackson/databind/deser/std/StdKeyDeserializers;->()V -HSPLcom/fasterxml/jackson/databind/deser/std/StdKeyDeserializers;->findKeyDeserializer(Lcom/fasterxml/jackson/databind/JavaType;Lcom/fasterxml/jackson/databind/DeserializationConfig;Lcom/fasterxml/jackson/databind/BeanDescription;)Lcom/fasterxml/jackson/databind/KeyDeserializer; HSPLcom/fasterxml/jackson/databind/deser/std/StdScalarDeserializer;->(Ljava/lang/Class;)V HSPLcom/fasterxml/jackson/databind/deser/std/StdValueInstantiator;->(Lcom/fasterxml/jackson/databind/DeserializationConfig;Lcom/fasterxml/jackson/databind/JavaType;)V HSPLcom/fasterxml/jackson/databind/deser/std/StdValueInstantiator;->(Lcom/fasterxml/jackson/databind/deser/std/StdValueInstantiator;)V @@ -8495,16 +8562,10 @@ HSPLcom/fasterxml/jackson/databind/deser/std/StdValueInstantiator;->createUsingD HSPLcom/fasterxml/jackson/databind/deser/std/StdValueInstantiator;->getArrayDelegateCreator()Lcom/fasterxml/jackson/databind/introspect/AnnotatedWithParams; HSPLcom/fasterxml/jackson/databind/deser/std/StdValueInstantiator;->getDelegateCreator()Lcom/fasterxml/jackson/databind/introspect/AnnotatedWithParams; HSPLcom/fasterxml/jackson/databind/deser/std/StdValueInstantiator;->getFromObjectArguments(Lcom/fasterxml/jackson/databind/DeserializationConfig;)[Lcom/fasterxml/jackson/databind/deser/SettableBeanProperty; -HSPLcom/fasterxml/jackson/databind/deser/std/StringArrayDeserializer;->()V -HSPLcom/fasterxml/jackson/databind/deser/std/StringArrayDeserializer;->()V -HSPLcom/fasterxml/jackson/databind/deser/std/StringArrayDeserializer;->(Lcom/fasterxml/jackson/databind/JsonDeserializer;Lcom/fasterxml/jackson/databind/deser/NullValueProvider;Ljava/lang/Boolean;)V -HSPLcom/fasterxml/jackson/databind/deser/std/StringArrayDeserializer;->createContextual(Lcom/fasterxml/jackson/databind/DeserializationContext;Lcom/fasterxml/jackson/databind/BeanProperty;)Lcom/fasterxml/jackson/databind/JsonDeserializer; HSPLcom/fasterxml/jackson/databind/deser/std/StringCollectionDeserializer;->(Lcom/fasterxml/jackson/databind/JavaType;Lcom/fasterxml/jackson/databind/JsonDeserializer;Lcom/fasterxml/jackson/databind/deser/ValueInstantiator;)V HSPLcom/fasterxml/jackson/databind/deser/std/StringCollectionDeserializer;->(Lcom/fasterxml/jackson/databind/JavaType;Lcom/fasterxml/jackson/databind/deser/ValueInstantiator;Lcom/fasterxml/jackson/databind/JsonDeserializer;Lcom/fasterxml/jackson/databind/JsonDeserializer;Lcom/fasterxml/jackson/databind/deser/NullValueProvider;Ljava/lang/Boolean;)V HSPLcom/fasterxml/jackson/databind/deser/std/StringCollectionDeserializer;->createContextual(Lcom/fasterxml/jackson/databind/DeserializationContext;Lcom/fasterxml/jackson/databind/BeanProperty;)Lcom/fasterxml/jackson/databind/JsonDeserializer; HSPLcom/fasterxml/jackson/databind/deser/std/StringCollectionDeserializer;->deserialize(Lcom/fasterxml/jackson/core/JsonParser;Lcom/fasterxml/jackson/databind/DeserializationContext;)Ljava/lang/Object; -HSPLcom/fasterxml/jackson/databind/deser/std/StringCollectionDeserializer;->deserialize(Lcom/fasterxml/jackson/core/JsonParser;Lcom/fasterxml/jackson/databind/DeserializationContext;)Ljava/util/Collection; -HSPLcom/fasterxml/jackson/databind/deser/std/StringCollectionDeserializer;->deserialize(Lcom/fasterxml/jackson/core/JsonParser;Lcom/fasterxml/jackson/databind/DeserializationContext;Ljava/util/Collection;)Ljava/util/Collection; HSPLcom/fasterxml/jackson/databind/deser/std/StringCollectionDeserializer;->isCachable()Z HSPLcom/fasterxml/jackson/databind/deser/std/StringCollectionDeserializer;->withResolved(Lcom/fasterxml/jackson/databind/JsonDeserializer;Lcom/fasterxml/jackson/databind/JsonDeserializer;Lcom/fasterxml/jackson/databind/deser/NullValueProvider;Ljava/lang/Boolean;)Lcom/fasterxml/jackson/databind/deser/std/StringCollectionDeserializer; HSPLcom/fasterxml/jackson/databind/deser/std/StringDeserializer;->()V @@ -8662,7 +8723,6 @@ HSPLcom/fasterxml/jackson/databind/introspect/AnnotationCollector$EmptyCollector HSPLcom/fasterxml/jackson/databind/introspect/AnnotationCollector$NCollector;->(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/annotation/Annotation;Ljava/lang/Class;Ljava/lang/annotation/Annotation;)V HSPLcom/fasterxml/jackson/databind/introspect/AnnotationCollector$NCollector;->addOrOverride(Ljava/lang/annotation/Annotation;)Lcom/fasterxml/jackson/databind/introspect/AnnotationCollector; HSPLcom/fasterxml/jackson/databind/introspect/AnnotationCollector$NCollector;->asAnnotationMap()Lcom/fasterxml/jackson/databind/introspect/AnnotationMap; -HSPLcom/fasterxml/jackson/databind/introspect/AnnotationCollector$NCollector;->asAnnotations()Lcom/fasterxml/jackson/databind/util/Annotations; HSPLcom/fasterxml/jackson/databind/introspect/AnnotationCollector$NoAnnotations;->()V HSPLcom/fasterxml/jackson/databind/introspect/AnnotationCollector$NoAnnotations;->get(Ljava/lang/Class;)Ljava/lang/annotation/Annotation; HSPLcom/fasterxml/jackson/databind/introspect/AnnotationCollector$OneAnnotation;->(Ljava/lang/Class;Ljava/lang/annotation/Annotation;)V @@ -8671,9 +8731,6 @@ HSPLcom/fasterxml/jackson/databind/introspect/AnnotationCollector$OneCollector;- HSPLcom/fasterxml/jackson/databind/introspect/AnnotationCollector$OneCollector;->addOrOverride(Ljava/lang/annotation/Annotation;)Lcom/fasterxml/jackson/databind/introspect/AnnotationCollector; HSPLcom/fasterxml/jackson/databind/introspect/AnnotationCollector$OneCollector;->asAnnotationMap()Lcom/fasterxml/jackson/databind/introspect/AnnotationMap; HSPLcom/fasterxml/jackson/databind/introspect/AnnotationCollector$OneCollector;->asAnnotations()Lcom/fasterxml/jackson/databind/util/Annotations; -HSPLcom/fasterxml/jackson/databind/introspect/AnnotationCollector$OneCollector;->isPresent(Ljava/lang/annotation/Annotation;)Z -HSPLcom/fasterxml/jackson/databind/introspect/AnnotationCollector$TwoAnnotations;->(Ljava/lang/Class;Ljava/lang/annotation/Annotation;Ljava/lang/Class;Ljava/lang/annotation/Annotation;)V -HSPLcom/fasterxml/jackson/databind/introspect/AnnotationCollector$TwoAnnotations;->get(Ljava/lang/Class;)Ljava/lang/annotation/Annotation; HSPLcom/fasterxml/jackson/databind/introspect/AnnotationCollector;->()V HSPLcom/fasterxml/jackson/databind/introspect/AnnotationCollector;->(Ljava/lang/Object;)V HSPLcom/fasterxml/jackson/databind/introspect/AnnotationCollector;->emptyAnnotations()Lcom/fasterxml/jackson/databind/util/Annotations; @@ -8694,7 +8751,6 @@ HSPLcom/fasterxml/jackson/databind/introspect/AnnotationIntrospectorPair;->findF HSPLcom/fasterxml/jackson/databind/introspect/AnnotationIntrospectorPair;->findFormat(Lcom/fasterxml/jackson/databind/introspect/Annotated;)Lcom/fasterxml/jackson/annotation/JsonFormat$Value; HSPLcom/fasterxml/jackson/databind/introspect/AnnotationIntrospectorPair;->findImplicitPropertyName(Lcom/fasterxml/jackson/databind/introspect/AnnotatedMember;)Ljava/lang/String; HSPLcom/fasterxml/jackson/databind/introspect/AnnotationIntrospectorPair;->findInjectableValue(Lcom/fasterxml/jackson/databind/introspect/AnnotatedMember;)Lcom/fasterxml/jackson/annotation/JacksonInject$Value; -HSPLcom/fasterxml/jackson/databind/introspect/AnnotationIntrospectorPair;->findKeyDeserializer(Lcom/fasterxml/jackson/databind/introspect/Annotated;)Ljava/lang/Object; HSPLcom/fasterxml/jackson/databind/introspect/AnnotationIntrospectorPair;->findKeySerializer(Lcom/fasterxml/jackson/databind/introspect/Annotated;)Ljava/lang/Object; HSPLcom/fasterxml/jackson/databind/introspect/AnnotationIntrospectorPair;->findMergeInfo(Lcom/fasterxml/jackson/databind/introspect/Annotated;)Ljava/lang/Boolean; HSPLcom/fasterxml/jackson/databind/introspect/AnnotationIntrospectorPair;->findNameForDeserialization(Lcom/fasterxml/jackson/databind/introspect/Annotated;)Lcom/fasterxml/jackson/databind/PropertyName; @@ -8839,7 +8895,6 @@ HSPLcom/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector;->fi HSPLcom/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector;->findFormat(Lcom/fasterxml/jackson/databind/introspect/Annotated;)Lcom/fasterxml/jackson/annotation/JsonFormat$Value; HSPLcom/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector;->findImplicitPropertyName(Lcom/fasterxml/jackson/databind/introspect/AnnotatedMember;)Ljava/lang/String; HSPLcom/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector;->findInjectableValue(Lcom/fasterxml/jackson/databind/introspect/AnnotatedMember;)Lcom/fasterxml/jackson/annotation/JacksonInject$Value; -HSPLcom/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector;->findKeyDeserializer(Lcom/fasterxml/jackson/databind/introspect/Annotated;)Ljava/lang/Object; HSPLcom/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector;->findKeySerializer(Lcom/fasterxml/jackson/databind/introspect/Annotated;)Ljava/lang/Object; HSPLcom/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector;->findMergeInfo(Lcom/fasterxml/jackson/databind/introspect/Annotated;)Ljava/lang/Boolean; HSPLcom/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector;->findNameForDeserialization(Lcom/fasterxml/jackson/databind/introspect/Annotated;)Lcom/fasterxml/jackson/databind/PropertyName; @@ -9233,8 +9288,6 @@ HSPLcom/fasterxml/jackson/databind/ser/std/BooleanSerializer;->(Z)V HSPLcom/fasterxml/jackson/databind/ser/std/BooleanSerializer;->createContextual(Lcom/fasterxml/jackson/databind/SerializerProvider;Lcom/fasterxml/jackson/databind/BeanProperty;)Lcom/fasterxml/jackson/databind/JsonSerializer; HSPLcom/fasterxml/jackson/databind/ser/std/BooleanSerializer;->serialize(Ljava/lang/Object;Lcom/fasterxml/jackson/core/JsonGenerator;Lcom/fasterxml/jackson/databind/SerializerProvider;)V HSPLcom/fasterxml/jackson/databind/ser/std/ByteArraySerializer;->()V -HSPLcom/fasterxml/jackson/databind/ser/std/ByteArraySerializer;->serialize(Ljava/lang/Object;Lcom/fasterxml/jackson/core/JsonGenerator;Lcom/fasterxml/jackson/databind/SerializerProvider;)V -HSPLcom/fasterxml/jackson/databind/ser/std/ByteArraySerializer;->serialize([BLcom/fasterxml/jackson/core/JsonGenerator;Lcom/fasterxml/jackson/databind/SerializerProvider;)V HSPLcom/fasterxml/jackson/databind/ser/std/CalendarSerializer;->()V HSPLcom/fasterxml/jackson/databind/ser/std/CalendarSerializer;->()V HSPLcom/fasterxml/jackson/databind/ser/std/CalendarSerializer;->(Ljava/lang/Boolean;Ljava/text/DateFormat;)V @@ -9268,7 +9321,6 @@ HSPLcom/fasterxml/jackson/databind/ser/std/NumberSerializers$FloatSerializer;->< HSPLcom/fasterxml/jackson/databind/ser/std/NumberSerializers$IntLikeSerializer;->()V HSPLcom/fasterxml/jackson/databind/ser/std/NumberSerializers$IntLikeSerializer;->()V HSPLcom/fasterxml/jackson/databind/ser/std/NumberSerializers$IntegerSerializer;->(Ljava/lang/Class;)V -HSPLcom/fasterxml/jackson/databind/ser/std/NumberSerializers$IntegerSerializer;->serialize(Ljava/lang/Object;Lcom/fasterxml/jackson/core/JsonGenerator;Lcom/fasterxml/jackson/databind/SerializerProvider;)V HSPLcom/fasterxml/jackson/databind/ser/std/NumberSerializers$LongSerializer;->(Ljava/lang/Class;)V HSPLcom/fasterxml/jackson/databind/ser/std/NumberSerializers$LongSerializer;->serialize(Ljava/lang/Object;Lcom/fasterxml/jackson/core/JsonGenerator;Lcom/fasterxml/jackson/databind/SerializerProvider;)V HSPLcom/fasterxml/jackson/databind/ser/std/NumberSerializers$ShortSerializer;->()V @@ -9361,7 +9413,6 @@ HSPLcom/fasterxml/jackson/databind/type/LogicalType;->()V HSPLcom/fasterxml/jackson/databind/type/LogicalType;->(Ljava/lang/String;I)V HSPLcom/fasterxml/jackson/databind/type/LogicalType;->values()[Lcom/fasterxml/jackson/databind/type/LogicalType; HSPLcom/fasterxml/jackson/databind/type/MapLikeType;->(Ljava/lang/Class;Lcom/fasterxml/jackson/databind/type/TypeBindings;Lcom/fasterxml/jackson/databind/JavaType;[Lcom/fasterxml/jackson/databind/JavaType;Lcom/fasterxml/jackson/databind/JavaType;Lcom/fasterxml/jackson/databind/JavaType;Ljava/lang/Object;Ljava/lang/Object;Z)V -HSPLcom/fasterxml/jackson/databind/type/MapLikeType;->equals(Ljava/lang/Object;)Z HSPLcom/fasterxml/jackson/databind/type/MapLikeType;->getContentType()Lcom/fasterxml/jackson/databind/JavaType; HSPLcom/fasterxml/jackson/databind/type/MapLikeType;->getKeyType()Lcom/fasterxml/jackson/databind/JavaType; HSPLcom/fasterxml/jackson/databind/type/MapLikeType;->hasHandlers()Z @@ -9371,10 +9422,6 @@ HSPLcom/fasterxml/jackson/databind/type/MapLikeType;->withHandlersFrom(Lcom/fast HSPLcom/fasterxml/jackson/databind/type/MapType;->(Ljava/lang/Class;Lcom/fasterxml/jackson/databind/type/TypeBindings;Lcom/fasterxml/jackson/databind/JavaType;[Lcom/fasterxml/jackson/databind/JavaType;Lcom/fasterxml/jackson/databind/JavaType;Lcom/fasterxml/jackson/databind/JavaType;Ljava/lang/Object;Ljava/lang/Object;Z)V HSPLcom/fasterxml/jackson/databind/type/MapType;->construct(Ljava/lang/Class;Lcom/fasterxml/jackson/databind/type/TypeBindings;Lcom/fasterxml/jackson/databind/JavaType;[Lcom/fasterxml/jackson/databind/JavaType;Lcom/fasterxml/jackson/databind/JavaType;Lcom/fasterxml/jackson/databind/JavaType;)Lcom/fasterxml/jackson/databind/type/MapType; HSPLcom/fasterxml/jackson/databind/type/MapType;->refine(Ljava/lang/Class;Lcom/fasterxml/jackson/databind/type/TypeBindings;Lcom/fasterxml/jackson/databind/JavaType;[Lcom/fasterxml/jackson/databind/JavaType;)Lcom/fasterxml/jackson/databind/JavaType; -HSPLcom/fasterxml/jackson/databind/type/PlaceholderForType;->(I)V -HSPLcom/fasterxml/jackson/databind/type/PlaceholderForType;->actualType()Lcom/fasterxml/jackson/databind/JavaType; -HSPLcom/fasterxml/jackson/databind/type/PlaceholderForType;->actualType(Lcom/fasterxml/jackson/databind/JavaType;)V -HSPLcom/fasterxml/jackson/databind/type/PlaceholderForType;->equals(Ljava/lang/Object;)Z HSPLcom/fasterxml/jackson/databind/type/SimpleType;->(Ljava/lang/Class;)V HSPLcom/fasterxml/jackson/databind/type/SimpleType;->(Ljava/lang/Class;Lcom/fasterxml/jackson/databind/type/TypeBindings;Lcom/fasterxml/jackson/databind/JavaType;[Lcom/fasterxml/jackson/databind/JavaType;)V HSPLcom/fasterxml/jackson/databind/type/SimpleType;->(Ljava/lang/Class;Lcom/fasterxml/jackson/databind/type/TypeBindings;Lcom/fasterxml/jackson/databind/JavaType;[Lcom/fasterxml/jackson/databind/JavaType;Ljava/lang/Object;Ljava/lang/Object;Z)V @@ -9387,7 +9434,6 @@ HSPLcom/fasterxml/jackson/databind/type/SimpleType;->refine(Ljava/lang/Class;Lco HSPLcom/fasterxml/jackson/databind/type/SimpleType;->toString()Ljava/lang/String; HSPLcom/fasterxml/jackson/databind/type/TypeBase;->()V HSPLcom/fasterxml/jackson/databind/type/TypeBase;->(Ljava/lang/Class;Lcom/fasterxml/jackson/databind/type/TypeBindings;Lcom/fasterxml/jackson/databind/JavaType;[Lcom/fasterxml/jackson/databind/JavaType;ILjava/lang/Object;Ljava/lang/Object;Z)V -HSPLcom/fasterxml/jackson/databind/type/TypeBase;->findSuperType(Ljava/lang/Class;)Lcom/fasterxml/jackson/databind/JavaType; HSPLcom/fasterxml/jackson/databind/type/TypeBase;->getBindings()Lcom/fasterxml/jackson/databind/type/TypeBindings; HSPLcom/fasterxml/jackson/databind/type/TypeBase;->getInterfaces()Ljava/util/List; HSPLcom/fasterxml/jackson/databind/type/TypeBase;->getSuperClass()Lcom/fasterxml/jackson/databind/JavaType; @@ -9415,7 +9461,6 @@ HSPLcom/fasterxml/jackson/databind/type/TypeFactory;->()V HSPLcom/fasterxml/jackson/databind/type/TypeFactory;->()V HSPLcom/fasterxml/jackson/databind/type/TypeFactory;->(Lcom/fasterxml/jackson/databind/util/LookupCache;)V HSPLcom/fasterxml/jackson/databind/type/TypeFactory;->_applyModifiers(Ljava/lang/reflect/Type;Lcom/fasterxml/jackson/databind/JavaType;)Lcom/fasterxml/jackson/databind/JavaType; -HSPLcom/fasterxml/jackson/databind/type/TypeFactory;->_bindingsForSubtype(Lcom/fasterxml/jackson/databind/JavaType;ILjava/lang/Class;Z)Lcom/fasterxml/jackson/databind/type/TypeBindings; HSPLcom/fasterxml/jackson/databind/type/TypeFactory;->_collectionType(Ljava/lang/Class;Lcom/fasterxml/jackson/databind/type/TypeBindings;Lcom/fasterxml/jackson/databind/JavaType;[Lcom/fasterxml/jackson/databind/JavaType;)Lcom/fasterxml/jackson/databind/JavaType; HSPLcom/fasterxml/jackson/databind/type/TypeFactory;->_constructSimple(Ljava/lang/Class;Lcom/fasterxml/jackson/databind/type/TypeBindings;Lcom/fasterxml/jackson/databind/JavaType;[Lcom/fasterxml/jackson/databind/JavaType;)Lcom/fasterxml/jackson/databind/JavaType; HSPLcom/fasterxml/jackson/databind/type/TypeFactory;->_findWellKnownSimple(Ljava/lang/Class;)Lcom/fasterxml/jackson/databind/JavaType; @@ -9430,9 +9475,7 @@ HSPLcom/fasterxml/jackson/databind/type/TypeFactory;->_mapType(Ljava/lang/Class; HSPLcom/fasterxml/jackson/databind/type/TypeFactory;->_newSimpleType(Ljava/lang/Class;Lcom/fasterxml/jackson/databind/type/TypeBindings;Lcom/fasterxml/jackson/databind/JavaType;[Lcom/fasterxml/jackson/databind/JavaType;)Lcom/fasterxml/jackson/databind/JavaType; HSPLcom/fasterxml/jackson/databind/type/TypeFactory;->_resolveSuperClass(Lcom/fasterxml/jackson/databind/type/ClassStack;Ljava/lang/Class;Lcom/fasterxml/jackson/databind/type/TypeBindings;)Lcom/fasterxml/jackson/databind/JavaType; HSPLcom/fasterxml/jackson/databind/type/TypeFactory;->_resolveSuperInterfaces(Lcom/fasterxml/jackson/databind/type/ClassStack;Ljava/lang/Class;Lcom/fasterxml/jackson/databind/type/TypeBindings;)[Lcom/fasterxml/jackson/databind/JavaType; -HSPLcom/fasterxml/jackson/databind/type/TypeFactory;->_resolveTypePlaceholders(Lcom/fasterxml/jackson/databind/JavaType;Lcom/fasterxml/jackson/databind/JavaType;)Ljava/lang/String; HSPLcom/fasterxml/jackson/databind/type/TypeFactory;->_unknownType()Lcom/fasterxml/jackson/databind/JavaType; -HSPLcom/fasterxml/jackson/databind/type/TypeFactory;->_verifyAndResolvePlaceholders(Lcom/fasterxml/jackson/databind/JavaType;Lcom/fasterxml/jackson/databind/JavaType;)Z HSPLcom/fasterxml/jackson/databind/type/TypeFactory;->constructCollectionType(Ljava/lang/Class;Lcom/fasterxml/jackson/databind/JavaType;)Lcom/fasterxml/jackson/databind/type/CollectionType; HSPLcom/fasterxml/jackson/databind/type/TypeFactory;->constructCollectionType(Ljava/lang/Class;Ljava/lang/Class;)Lcom/fasterxml/jackson/databind/type/CollectionType; HSPLcom/fasterxml/jackson/databind/type/TypeFactory;->constructSpecializedType(Lcom/fasterxml/jackson/databind/JavaType;Ljava/lang/Class;Z)Lcom/fasterxml/jackson/databind/JavaType; @@ -9896,6 +9939,7 @@ HSPLcom/google/android/gms/common/api/internal/BackgroundDetector;->onActivityCr HSPLcom/google/android/gms/common/api/internal/BackgroundDetector;->onActivityDestroyed(Landroid/app/Activity;)V HSPLcom/google/android/gms/common/api/internal/BackgroundDetector;->onActivityPaused(Landroid/app/Activity;)V HSPLcom/google/android/gms/common/api/internal/BackgroundDetector;->onActivityResumed(Landroid/app/Activity;)V +HSPLcom/google/android/gms/common/api/internal/BackgroundDetector;->onActivitySaveInstanceState(Landroid/app/Activity;Landroid/os/Bundle;)V HSPLcom/google/android/gms/common/api/internal/BackgroundDetector;->onActivityStarted(Landroid/app/Activity;)V HSPLcom/google/android/gms/common/api/internal/BackgroundDetector;->onActivityStopped(Landroid/app/Activity;)V HSPLcom/google/android/gms/common/internal/Preconditions;->checkNotEmpty(Ljava/lang/String;)Ljava/lang/String; @@ -10048,6 +10092,7 @@ HSPLcom/google/android/material/appbar/AppBarLayout;->setLiftedState(ZZ)Z HSPLcom/google/android/material/appbar/AppBarLayout;->setOrientation(I)V HSPLcom/google/android/material/appbar/AppBarLayout;->setStatusBarForeground(Landroid/graphics/drawable/Drawable;)V HSPLcom/google/android/material/appbar/AppBarLayout;->shouldDrawStatusBarForeground()Z +HSPLcom/google/android/material/appbar/AppBarLayout;->verifyDrawable(Landroid/graphics/drawable/Drawable;)Z HSPLcom/google/android/material/appbar/CollapsingToolbarLayout$1;->(Lcom/google/android/material/appbar/CollapsingToolbarLayout;)V HSPLcom/google/android/material/appbar/CollapsingToolbarLayout$2;->(Lcom/google/android/material/appbar/CollapsingToolbarLayout;)V HSPLcom/google/android/material/appbar/CollapsingToolbarLayout$2;->onAnimationUpdate(Landroid/animation/ValueAnimator;)V @@ -10323,6 +10368,7 @@ HSPLcom/google/android/material/elevation/ElevationOverlayProvider;->(ZIII HSPLcom/google/android/material/elevation/ElevationOverlayProvider;->compositeOverlayIfNeeded(IF)I HSPLcom/google/android/material/elevation/ElevationOverlayProvider;->isThemeElevationOverlayEnabled()Z HSPLcom/google/android/material/expandable/ExpandableWidgetHelper;->(Lcom/google/android/material/expandable/ExpandableWidget;)V +HSPLcom/google/android/material/expandable/ExpandableWidgetHelper;->onSaveInstanceState()Landroid/os/Bundle; HSPLcom/google/android/material/floatingactionbutton/BorderDrawable$BorderState;->(Lcom/google/android/material/floatingactionbutton/BorderDrawable;)V HSPLcom/google/android/material/floatingactionbutton/BorderDrawable$BorderState;->(Lcom/google/android/material/floatingactionbutton/BorderDrawable;Lcom/google/android/material/floatingactionbutton/BorderDrawable$1;)V HSPLcom/google/android/material/floatingactionbutton/BorderDrawable;->(Lcom/google/android/material/shape/ShapeAppearanceModel;)V @@ -10353,6 +10399,7 @@ HSPLcom/google/android/material/floatingactionbutton/FloatingActionButton;->getS HSPLcom/google/android/material/floatingactionbutton/FloatingActionButton;->jumpDrawablesToCurrentState()V HSPLcom/google/android/material/floatingactionbutton/FloatingActionButton;->onAttachedToWindow()V HSPLcom/google/android/material/floatingactionbutton/FloatingActionButton;->onMeasure(II)V +HSPLcom/google/android/material/floatingactionbutton/FloatingActionButton;->onSaveInstanceState()Landroid/os/Parcelable; HSPLcom/google/android/material/floatingactionbutton/FloatingActionButton;->setElevation(F)V HSPLcom/google/android/material/floatingactionbutton/FloatingActionButton;->setImageDrawable(Landroid/graphics/drawable/Drawable;)V HSPLcom/google/android/material/floatingactionbutton/FloatingActionButton;->setMaxImageSize(I)V @@ -10414,6 +10461,21 @@ HSPLcom/google/android/material/imageview/ShapeableImageView;->access$000(Lcom/g HSPLcom/google/android/material/imageview/ShapeableImageView;->access$100(Lcom/google/android/material/imageview/ShapeableImageView;)Lcom/google/android/material/shape/MaterialShapeDrawable; HSPLcom/google/android/material/imageview/ShapeableImageView;->access$102(Lcom/google/android/material/imageview/ShapeableImageView;Lcom/google/android/material/shape/MaterialShapeDrawable;)Lcom/google/android/material/shape/MaterialShapeDrawable; HSPLcom/google/android/material/imageview/ShapeableImageView;->access$200(Lcom/google/android/material/imageview/ShapeableImageView;)Landroid/graphics/RectF; +HSPLcom/google/android/material/imageview/ShapeableImageView;->drawStroke(Landroid/graphics/Canvas;)V +HSPLcom/google/android/material/imageview/ShapeableImageView;->getContentPaddingBottom()I +HSPLcom/google/android/material/imageview/ShapeableImageView;->getContentPaddingLeft()I +HSPLcom/google/android/material/imageview/ShapeableImageView;->getContentPaddingRight()I +HSPLcom/google/android/material/imageview/ShapeableImageView;->getContentPaddingTop()I +HSPLcom/google/android/material/imageview/ShapeableImageView;->getPaddingBottom()I +HSPLcom/google/android/material/imageview/ShapeableImageView;->getPaddingLeft()I +HSPLcom/google/android/material/imageview/ShapeableImageView;->getPaddingRight()I +HSPLcom/google/android/material/imageview/ShapeableImageView;->getPaddingTop()I +HSPLcom/google/android/material/imageview/ShapeableImageView;->isContentPaddingRelative()Z +HSPLcom/google/android/material/imageview/ShapeableImageView;->onDraw(Landroid/graphics/Canvas;)V +HSPLcom/google/android/material/imageview/ShapeableImageView;->onMeasure(II)V +HSPLcom/google/android/material/imageview/ShapeableImageView;->onSizeChanged(IIII)V +HSPLcom/google/android/material/imageview/ShapeableImageView;->setPadding(IIII)V +HSPLcom/google/android/material/imageview/ShapeableImageView;->updateShapeMask(II)V HSPLcom/google/android/material/internal/CollapsingTextHelper$1;->(Lcom/google/android/material/internal/CollapsingTextHelper;)V HSPLcom/google/android/material/internal/CollapsingTextHelper$1;->apply(Landroid/graphics/Typeface;)V HSPLcom/google/android/material/internal/CollapsingTextHelper$2;->(Lcom/google/android/material/internal/CollapsingTextHelper;)V @@ -10613,6 +10675,8 @@ HSPLcom/google/android/material/shape/MaterialShapeUtils;->setElevation(Landroid HSPLcom/google/android/material/shape/MaterialShapeUtils;->setParentAbsoluteElevation(Landroid/view/View;)V HSPLcom/google/android/material/shape/MaterialShapeUtils;->setParentAbsoluteElevation(Landroid/view/View;Lcom/google/android/material/shape/MaterialShapeDrawable;)V HSPLcom/google/android/material/shape/RelativeCornerSize;->(F)V +HSPLcom/google/android/material/shape/RelativeCornerSize;->getCornerSize(Landroid/graphics/RectF;)F +HSPLcom/google/android/material/shape/RelativeCornerSize;->getMaxCornerSize(Landroid/graphics/RectF;)F HSPLcom/google/android/material/shape/RoundedCornerTreatment;->()V HSPLcom/google/android/material/shape/RoundedCornerTreatment;->getCornerPath(Lcom/google/android/material/shape/ShapePath;FFF)V HSPLcom/google/android/material/shape/ShapeAppearanceModel$Builder;->()V @@ -10742,6 +10806,9 @@ HSPLcom/google/android/material/shape/ShapePath;->setEndX(F)V HSPLcom/google/android/material/shape/ShapePath;->setEndY(F)V HSPLcom/google/android/material/shape/ShapePath;->setStartX(F)V HSPLcom/google/android/material/shape/ShapePath;->setStartY(F)V +HSPLcom/google/android/material/stateful/ExtendableSavedState$1;->()V +HSPLcom/google/android/material/stateful/ExtendableSavedState;->()V +HSPLcom/google/android/material/stateful/ExtendableSavedState;->(Landroid/os/Parcelable;)V HSPLcom/google/android/material/theme/overlay/MaterialThemeOverlay;->()V HSPLcom/google/android/material/theme/overlay/MaterialThemeOverlay;->obtainAndroidThemeOverlayId(Landroid/content/Context;Landroid/util/AttributeSet;)I HSPLcom/google/android/material/theme/overlay/MaterialThemeOverlay;->obtainMaterialThemeOverlayId(Landroid/content/Context;Landroid/util/AttributeSet;II)I @@ -11267,6 +11334,7 @@ HSPLcom/google/firebase/messaging/FcmLifecycleCallbacks;->onActivityCreated(Land HSPLcom/google/firebase/messaging/FcmLifecycleCallbacks;->onActivityDestroyed(Landroid/app/Activity;)V HSPLcom/google/firebase/messaging/FcmLifecycleCallbacks;->onActivityPaused(Landroid/app/Activity;)V HSPLcom/google/firebase/messaging/FcmLifecycleCallbacks;->onActivityResumed(Landroid/app/Activity;)V +HSPLcom/google/firebase/messaging/FcmLifecycleCallbacks;->onActivitySaveInstanceState(Landroid/app/Activity;Landroid/os/Bundle;)V HSPLcom/google/firebase/messaging/FcmLifecycleCallbacks;->onActivityStarted(Landroid/app/Activity;)V HSPLcom/google/firebase/messaging/FcmLifecycleCallbacks;->onActivityStopped(Landroid/app/Activity;)V HSPLcom/google/firebase/messaging/FirebaseMessaging$$ExternalSyntheticLambda1;->(Lcom/google/firebase/messaging/FirebaseMessaging;)V @@ -11359,12 +11427,15 @@ HSPLcom/google/firebase/tracing/FirebaseTrace;->popTrace()V HSPLcom/google/firebase/tracing/FirebaseTrace;->pushTrace(Ljava/lang/String;)V HSPLcom/google/i18n/phonenumbers/CountryCodeToRegionCodeMap;->getCountryCodeToRegionCodeMap()Ljava/util/Map; HSPLcom/google/i18n/phonenumbers/PhoneNumberUtil$2;->()V +HSPLcom/google/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;->$values()[Lcom/google/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat; HSPLcom/google/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;->()V HSPLcom/google/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;->(Ljava/lang/String;I)V HSPLcom/google/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat;->values()[Lcom/google/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberFormat; +HSPLcom/google/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->$values()[Lcom/google/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType; HSPLcom/google/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->()V HSPLcom/google/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->(Ljava/lang/String;I)V HSPLcom/google/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType;->values()[Lcom/google/i18n/phonenumbers/PhoneNumberUtil$PhoneNumberType; +HSPLcom/google/i18n/phonenumbers/PhoneNumberUtil$ValidationResult;->$values()[Lcom/google/i18n/phonenumbers/PhoneNumberUtil$ValidationResult; HSPLcom/google/i18n/phonenumbers/PhoneNumberUtil$ValidationResult;->()V HSPLcom/google/i18n/phonenumbers/PhoneNumberUtil$ValidationResult;->(Ljava/lang/String;I)V HSPLcom/google/i18n/phonenumbers/PhoneNumberUtil;->()V @@ -11476,6 +11547,7 @@ HSPLcom/google/i18n/phonenumbers/Phonemetadata$PhoneNumberDesc;->getPossibleLeng HSPLcom/google/i18n/phonenumbers/Phonemetadata$PhoneNumberDesc;->readExternal(Ljava/io/ObjectInput;)V HSPLcom/google/i18n/phonenumbers/Phonemetadata$PhoneNumberDesc;->setExampleNumber(Ljava/lang/String;)Lcom/google/i18n/phonenumbers/Phonemetadata$PhoneNumberDesc; HSPLcom/google/i18n/phonenumbers/Phonemetadata$PhoneNumberDesc;->setNationalNumberPattern(Ljava/lang/String;)Lcom/google/i18n/phonenumbers/Phonemetadata$PhoneNumberDesc; +HSPLcom/google/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;->$values()[Lcom/google/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource; HSPLcom/google/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;->()V HSPLcom/google/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;->(Ljava/lang/String;I)V HSPLcom/google/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;->values()[Lcom/google/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource; @@ -11639,13 +11711,11 @@ HSPLcom/squareup/wire/internal/ImmutableList;->getSize()I HSPLcom/squareup/wire/internal/Internal;->checkElementsNotNull(Ljava/util/List;)V HSPLcom/squareup/wire/internal/Internal;->countNonNull(Ljava/lang/Object;Ljava/lang/Object;)I HSPLcom/squareup/wire/internal/Internal;->countNonNull(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)I -HSPLcom/squareup/wire/internal/Internal;->countNonNull(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;)I HSPLcom/squareup/wire/internal/Internal;->immutableCopyOf(Ljava/lang/String;Ljava/util/List;)Ljava/util/List; HSPLcom/squareup/wire/internal/Internal__InternalKt;->checkElementsNotNull(Ljava/util/List;)V HSPLcom/squareup/wire/internal/Internal__InternalKt;->countNonNull(Ljava/lang/Object;Ljava/lang/Object;)I HSPLcom/squareup/wire/internal/Internal__InternalKt;->countNonNull(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)I HSPLcom/squareup/wire/internal/Internal__InternalKt;->countNonNull(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;)I -HSPLcom/squareup/wire/internal/Internal__InternalKt;->immutableCopyOf(Ljava/lang/String;Ljava/util/List;)Ljava/util/List; HSPLio/reactivex/rxjava3/android/plugins/RxAndroidPlugins;->callRequireNonNull(Ljava/util/concurrent/Callable;)Lio/reactivex/rxjava3/core/Scheduler; HSPLio/reactivex/rxjava3/android/plugins/RxAndroidPlugins;->initMainThreadScheduler(Ljava/util/concurrent/Callable;)Lio/reactivex/rxjava3/core/Scheduler; HSPLio/reactivex/rxjava3/android/plugins/RxAndroidPlugins;->onMainThreadScheduler(Lio/reactivex/rxjava3/core/Scheduler;)Lio/reactivex/rxjava3/core/Scheduler; @@ -11680,7 +11750,6 @@ HSPLio/reactivex/rxjava3/core/Flowable;->distinctUntilChanged(Lio/reactivex/rxja HSPLio/reactivex/rxjava3/core/Flowable;->doOnEach(Lio/reactivex/rxjava3/functions/Consumer;Lio/reactivex/rxjava3/functions/Consumer;Lio/reactivex/rxjava3/functions/Action;Lio/reactivex/rxjava3/functions/Action;)Lio/reactivex/rxjava3/core/Flowable; HSPLio/reactivex/rxjava3/core/Flowable;->doOnNext(Lio/reactivex/rxjava3/functions/Consumer;)Lio/reactivex/rxjava3/core/Flowable; HSPLio/reactivex/rxjava3/core/Flowable;->filter(Lio/reactivex/rxjava3/functions/Predicate;)Lio/reactivex/rxjava3/core/Flowable; -HSPLio/reactivex/rxjava3/core/Flowable;->fromFuture(Ljava/util/concurrent/Future;JLjava/util/concurrent/TimeUnit;)Lio/reactivex/rxjava3/core/Flowable; HSPLio/reactivex/rxjava3/core/Flowable;->interval(JJLjava/util/concurrent/TimeUnit;)Lio/reactivex/rxjava3/core/Flowable; HSPLio/reactivex/rxjava3/core/Flowable;->interval(JJLjava/util/concurrent/TimeUnit;Lio/reactivex/rxjava3/core/Scheduler;)Lio/reactivex/rxjava3/core/Flowable; HSPLio/reactivex/rxjava3/core/Flowable;->map(Lio/reactivex/rxjava3/functions/Function;)Lio/reactivex/rxjava3/core/Flowable; @@ -11778,24 +11847,17 @@ HSPLio/reactivex/rxjava3/core/Scheduler;->schedulePeriodicallyDirect(Ljava/lang/ HSPLio/reactivex/rxjava3/core/Single;->()V HSPLio/reactivex/rxjava3/core/Single;->blockingGet()Ljava/lang/Object; HSPLio/reactivex/rxjava3/core/Single;->doOnSuccess(Lio/reactivex/rxjava3/functions/Consumer;)Lio/reactivex/rxjava3/core/Single; -HSPLio/reactivex/rxjava3/core/Single;->error(Lio/reactivex/rxjava3/functions/Supplier;)Lio/reactivex/rxjava3/core/Single; -HSPLio/reactivex/rxjava3/core/Single;->error(Ljava/lang/Throwable;)Lio/reactivex/rxjava3/core/Single; -HSPLio/reactivex/rxjava3/core/Single;->flatMap(Lio/reactivex/rxjava3/functions/Function;)Lio/reactivex/rxjava3/core/Single; HSPLio/reactivex/rxjava3/core/Single;->flatMapObservable(Lio/reactivex/rxjava3/functions/Function;)Lio/reactivex/rxjava3/core/Observable; HSPLio/reactivex/rxjava3/core/Single;->fromCallable(Ljava/util/concurrent/Callable;)Lio/reactivex/rxjava3/core/Single; -HSPLio/reactivex/rxjava3/core/Single;->fromFuture(Ljava/util/concurrent/Future;JLjava/util/concurrent/TimeUnit;)Lio/reactivex/rxjava3/core/Single; HSPLio/reactivex/rxjava3/core/Single;->just(Ljava/lang/Object;)Lio/reactivex/rxjava3/core/Single; HSPLio/reactivex/rxjava3/core/Single;->map(Lio/reactivex/rxjava3/functions/Function;)Lio/reactivex/rxjava3/core/Single; HSPLio/reactivex/rxjava3/core/Single;->observeOn(Lio/reactivex/rxjava3/core/Scheduler;)Lio/reactivex/rxjava3/core/Single; HSPLio/reactivex/rxjava3/core/Single;->onErrorComplete()Lio/reactivex/rxjava3/core/Maybe; HSPLio/reactivex/rxjava3/core/Single;->onErrorComplete(Lio/reactivex/rxjava3/functions/Predicate;)Lio/reactivex/rxjava3/core/Maybe; -HSPLio/reactivex/rxjava3/core/Single;->onErrorResumeNext(Lio/reactivex/rxjava3/functions/Function;)Lio/reactivex/rxjava3/core/Single; -HSPLio/reactivex/rxjava3/core/Single;->onErrorReturn(Lio/reactivex/rxjava3/functions/Function;)Lio/reactivex/rxjava3/core/Single; HSPLio/reactivex/rxjava3/core/Single;->subscribe(Lio/reactivex/rxjava3/core/SingleObserver;)V HSPLio/reactivex/rxjava3/core/Single;->subscribe(Lio/reactivex/rxjava3/functions/Consumer;)Lio/reactivex/rxjava3/disposables/Disposable; HSPLio/reactivex/rxjava3/core/Single;->subscribe(Lio/reactivex/rxjava3/functions/Consumer;Lio/reactivex/rxjava3/functions/Consumer;)Lio/reactivex/rxjava3/disposables/Disposable; HSPLio/reactivex/rxjava3/core/Single;->subscribeOn(Lio/reactivex/rxjava3/core/Scheduler;)Lio/reactivex/rxjava3/core/Single; -HSPLio/reactivex/rxjava3/core/Single;->toSingle(Lio/reactivex/rxjava3/core/Flowable;)Lio/reactivex/rxjava3/core/Single; HSPLio/reactivex/rxjava3/disposables/CompositeDisposable;->()V HSPLio/reactivex/rxjava3/disposables/CompositeDisposable;->add(Lio/reactivex/rxjava3/disposables/Disposable;)Z HSPLio/reactivex/rxjava3/disposables/CompositeDisposable;->delete(Lio/reactivex/rxjava3/disposables/Disposable;)Z @@ -11810,11 +11872,9 @@ HSPLio/reactivex/rxjava3/disposables/ReferenceDisposable;->isDisposed()Z HSPLio/reactivex/rxjava3/disposables/RunnableDisposable;->(Ljava/lang/Runnable;)V HSPLio/reactivex/rxjava3/disposables/RunnableDisposable;->onDisposed(Ljava/lang/Object;)V HSPLio/reactivex/rxjava3/disposables/RunnableDisposable;->onDisposed(Ljava/lang/Runnable;)V -HSPLio/reactivex/rxjava3/exceptions/Exceptions;->throwIfFatal(Ljava/lang/Throwable;)V HSPLio/reactivex/rxjava3/flowables/ConnectableFlowable;->()V HSPLio/reactivex/rxjava3/flowables/ConnectableFlowable;->refCount()Lio/reactivex/rxjava3/core/Flowable; HSPLio/reactivex/rxjava3/internal/disposables/CancellableDisposable;->(Lio/reactivex/rxjava3/functions/Cancellable;)V -HSPLio/reactivex/rxjava3/internal/disposables/CancellableDisposable;->dispose()V HSPLio/reactivex/rxjava3/internal/disposables/DisposableHelper;->()V HSPLio/reactivex/rxjava3/internal/disposables/DisposableHelper;->(Ljava/lang/String;I)V HSPLio/reactivex/rxjava3/internal/disposables/DisposableHelper;->dispose(Ljava/util/concurrent/atomic/AtomicReference;)Z @@ -11826,7 +11886,6 @@ HSPLio/reactivex/rxjava3/internal/disposables/DisposableHelper;->validate(Lio/re HSPLio/reactivex/rxjava3/internal/disposables/EmptyDisposable;->()V HSPLio/reactivex/rxjava3/internal/disposables/EmptyDisposable;->(Ljava/lang/String;I)V HSPLio/reactivex/rxjava3/internal/disposables/EmptyDisposable;->complete(Lio/reactivex/rxjava3/core/MaybeObserver;)V -HSPLio/reactivex/rxjava3/internal/disposables/EmptyDisposable;->error(Ljava/lang/Throwable;Lio/reactivex/rxjava3/core/SingleObserver;)V HSPLio/reactivex/rxjava3/internal/disposables/SequentialDisposable;->()V HSPLio/reactivex/rxjava3/internal/disposables/SequentialDisposable;->(Lio/reactivex/rxjava3/disposables/Disposable;)V HSPLio/reactivex/rxjava3/internal/disposables/SequentialDisposable;->isDisposed()Z @@ -11897,9 +11956,6 @@ HSPLio/reactivex/rxjava3/internal/observers/QueueDrainObserver;->(Lio/reac HSPLio/reactivex/rxjava3/internal/observers/QueueDrainSubscriberPad0;->()V HSPLio/reactivex/rxjava3/internal/observers/QueueDrainSubscriberPad2;->()V HSPLio/reactivex/rxjava3/internal/observers/QueueDrainSubscriberWip;->()V -HSPLio/reactivex/rxjava3/internal/observers/ResumeSingleObserver;->(Ljava/util/concurrent/atomic/AtomicReference;Lio/reactivex/rxjava3/core/SingleObserver;)V -HSPLio/reactivex/rxjava3/internal/observers/ResumeSingleObserver;->onError(Ljava/lang/Throwable;)V -HSPLio/reactivex/rxjava3/internal/observers/ResumeSingleObserver;->onSubscribe(Lio/reactivex/rxjava3/disposables/Disposable;)V HSPLio/reactivex/rxjava3/internal/operators/flowable/AbstractBackpressureThrottlingSubscriber;->(Lorg/reactivestreams/Subscriber;)V HSPLio/reactivex/rxjava3/internal/operators/flowable/AbstractBackpressureThrottlingSubscriber;->checkTerminated(ZZLorg/reactivestreams/Subscriber;Ljava/util/concurrent/atomic/AtomicReference;)Z HSPLio/reactivex/rxjava3/internal/operators/flowable/AbstractBackpressureThrottlingSubscriber;->drain()V @@ -11946,8 +12002,6 @@ HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableFilter$FilterCondit HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableFilter$FilterConditionalSubscriber;->tryOnNext(Ljava/lang/Object;)Z HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableFilter;->(Lio/reactivex/rxjava3/core/Flowable;Lio/reactivex/rxjava3/functions/Predicate;)V HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableFilter;->subscribeActual(Lorg/reactivestreams/Subscriber;)V -HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableFromFuture;->(Ljava/util/concurrent/Future;JLjava/util/concurrent/TimeUnit;)V -HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableFromFuture;->subscribeActual(Lorg/reactivestreams/Subscriber;)V HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableFromObservable$SubscriberObserver;->(Lorg/reactivestreams/Subscriber;)V HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableFromObservable$SubscriberObserver;->onNext(Ljava/lang/Object;)V HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableFromObservable$SubscriberObserver;->onSubscribe(Lio/reactivex/rxjava3/disposables/Disposable;)V @@ -11984,7 +12038,9 @@ HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableObserveOn$ObserveOn HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableObserveOn$ObserveOnConditionalSubscriber;->runAsync()V HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableObserveOn$ObserveOnSubscriber;->(Lorg/reactivestreams/Subscriber;Lio/reactivex/rxjava3/core/Scheduler$Worker;ZI)V HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableObserveOn$ObserveOnSubscriber;->onSubscribe(Lorg/reactivestreams/Subscription;)V +HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableObserveOn$ObserveOnSubscriber;->poll()Ljava/lang/Object; HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableObserveOn$ObserveOnSubscriber;->runAsync()V +HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableObserveOn$ObserveOnSubscriber;->runBackfused()V HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableObserveOn;->(Lio/reactivex/rxjava3/core/Flowable;Lio/reactivex/rxjava3/core/Scheduler;ZI)V HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableObserveOn;->subscribeActual(Lorg/reactivestreams/Subscriber;)V HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableOnBackpressureLatest$BackpressureLatestSubscriber;->(Lorg/reactivestreams/Subscriber;)V @@ -12007,9 +12063,7 @@ HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$BoundedRepla HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$BoundedReplayBuffer;->getHead()Lio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$Node; HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$BoundedReplayBuffer;->leaveTransform(Ljava/lang/Object;)Ljava/lang/Object; HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$BoundedReplayBuffer;->next(Ljava/lang/Object;)V -HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$BoundedReplayBuffer;->removeFirst()V HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$BoundedReplayBuffer;->replay(Lio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$InnerSubscription;)V -HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$BoundedReplayBuffer;->setFirst(Lio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$Node;)V HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$DefaultUnboundedFactory;->()V HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$InnerSubscription;->(Lio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$ReplaySubscriber;Lorg/reactivestreams/Subscriber;)V HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$InnerSubscription;->index()Ljava/lang/Object; @@ -12038,11 +12092,6 @@ HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay;->create(Lio HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay;->create(Lio/reactivex/rxjava3/core/Flowable;Lio/reactivex/rxjava3/functions/Supplier;)Lio/reactivex/rxjava3/flowables/ConnectableFlowable; HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay;->subscribeActual(Lorg/reactivestreams/Subscriber;)V HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableScalarXMap;->tryScalarXMapSubscribe(Lorg/reactivestreams/Publisher;Lorg/reactivestreams/Subscriber;Lio/reactivex/rxjava3/functions/Function;)Z -HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableSingleSingle$SingleElementSubscriber;->(Lio/reactivex/rxjava3/core/SingleObserver;Ljava/lang/Object;)V -HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableSingleSingle$SingleElementSubscriber;->onError(Ljava/lang/Throwable;)V -HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableSingleSingle$SingleElementSubscriber;->onSubscribe(Lorg/reactivestreams/Subscription;)V -HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableSingleSingle;->(Lio/reactivex/rxjava3/core/Flowable;Ljava/lang/Object;)V -HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableSingleSingle;->subscribeActual(Lio/reactivex/rxjava3/core/SingleObserver;)V HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableSubscribeOn$SubscribeOnSubscriber$Request;->(Lorg/reactivestreams/Subscription;J)V HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableSubscribeOn$SubscribeOnSubscriber$Request;->run()V HSPLio/reactivex/rxjava3/internal/operators/flowable/FlowableSubscribeOn$SubscribeOnSubscriber;->(Lorg/reactivestreams/Subscriber;Lio/reactivex/rxjava3/core/Scheduler$Worker;Lorg/reactivestreams/Publisher;Z)V @@ -12172,7 +12221,6 @@ HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableConcatMap$Conca HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableConcatMap;->(Lio/reactivex/rxjava3/core/ObservableSource;Lio/reactivex/rxjava3/functions/Function;ILio/reactivex/rxjava3/internal/util/ErrorMode;)V HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableConcatMap;->subscribeActual(Lio/reactivex/rxjava3/core/Observer;)V HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableCreate$CreateEmitter;->(Lio/reactivex/rxjava3/core/Observer;)V -HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableCreate$CreateEmitter;->dispose()V HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableCreate$CreateEmitter;->isDisposed()Z HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableCreate$CreateEmitter;->onNext(Ljava/lang/Object;)V HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableCreate$CreateEmitter;->setCancellable(Lio/reactivex/rxjava3/functions/Cancellable;)V @@ -12232,6 +12280,7 @@ HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableJust;->(L HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableJust;->get()Ljava/lang/Object; HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableMap$MapObserver;->(Lio/reactivex/rxjava3/core/Observer;Lio/reactivex/rxjava3/functions/Function;)V HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableMap$MapObserver;->onNext(Ljava/lang/Object;)V +HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableMap$MapObserver;->requestFusion(I)I HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableMap;->(Lio/reactivex/rxjava3/core/ObservableSource;Lio/reactivex/rxjava3/functions/Function;)V HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableMap;->subscribeActual(Lio/reactivex/rxjava3/core/Observer;)V HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableObserveOn$ObserveOnObserver;->(Lio/reactivex/rxjava3/core/Observer;Lio/reactivex/rxjava3/core/Scheduler$Worker;ZI)V @@ -12239,6 +12288,7 @@ HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableObserveOn$Obser HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableObserveOn$ObserveOnObserver;->dispose()V HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableObserveOn$ObserveOnObserver;->drainFused()V HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableObserveOn$ObserveOnObserver;->drainNormal()V +HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableObserveOn$ObserveOnObserver;->isEmpty()Z HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableObserveOn$ObserveOnObserver;->onComplete()V HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableObserveOn$ObserveOnObserver;->onNext(Ljava/lang/Object;)V HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableObserveOn$ObserveOnObserver;->onSubscribe(Lio/reactivex/rxjava3/disposables/Disposable;)V @@ -12265,9 +12315,7 @@ HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableReplay$BoundedR HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableReplay$BoundedReplayBuffer;->getHead()Lio/reactivex/rxjava3/internal/operators/observable/ObservableReplay$Node; HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableReplay$BoundedReplayBuffer;->leaveTransform(Ljava/lang/Object;)Ljava/lang/Object; HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableReplay$BoundedReplayBuffer;->next(Ljava/lang/Object;)V -HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableReplay$BoundedReplayBuffer;->removeFirst()V HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableReplay$BoundedReplayBuffer;->replay(Lio/reactivex/rxjava3/internal/operators/observable/ObservableReplay$InnerDisposable;)V -HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableReplay$BoundedReplayBuffer;->setFirst(Lio/reactivex/rxjava3/internal/operators/observable/ObservableReplay$Node;)V HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableReplay$InnerDisposable;->(Lio/reactivex/rxjava3/internal/operators/observable/ObservableReplay$ReplayObserver;Lio/reactivex/rxjava3/core/Observer;)V HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableReplay$InnerDisposable;->dispose()V HSPLio/reactivex/rxjava3/internal/operators/observable/ObservableReplay$InnerDisposable;->index()Ljava/lang/Object; @@ -12347,23 +12395,11 @@ HSPLio/reactivex/rxjava3/internal/operators/single/SingleDoOnSuccess$DoOnSuccess HSPLio/reactivex/rxjava3/internal/operators/single/SingleDoOnSuccess$DoOnSuccess;->onSuccess(Ljava/lang/Object;)V HSPLio/reactivex/rxjava3/internal/operators/single/SingleDoOnSuccess;->(Lio/reactivex/rxjava3/core/SingleSource;Lio/reactivex/rxjava3/functions/Consumer;)V HSPLio/reactivex/rxjava3/internal/operators/single/SingleDoOnSuccess;->subscribeActual(Lio/reactivex/rxjava3/core/SingleObserver;)V -HSPLio/reactivex/rxjava3/internal/operators/single/SingleError;->(Lio/reactivex/rxjava3/functions/Supplier;)V -HSPLio/reactivex/rxjava3/internal/operators/single/SingleError;->subscribeActual(Lio/reactivex/rxjava3/core/SingleObserver;)V -HSPLio/reactivex/rxjava3/internal/operators/single/SingleFlatMap$SingleFlatMapCallback$FlatMapSingleObserver;->(Ljava/util/concurrent/atomic/AtomicReference;Lio/reactivex/rxjava3/core/SingleObserver;)V -HSPLio/reactivex/rxjava3/internal/operators/single/SingleFlatMap$SingleFlatMapCallback$FlatMapSingleObserver;->onSubscribe(Lio/reactivex/rxjava3/disposables/Disposable;)V -HSPLio/reactivex/rxjava3/internal/operators/single/SingleFlatMap$SingleFlatMapCallback$FlatMapSingleObserver;->onSuccess(Ljava/lang/Object;)V -HSPLio/reactivex/rxjava3/internal/operators/single/SingleFlatMap$SingleFlatMapCallback;->(Lio/reactivex/rxjava3/core/SingleObserver;Lio/reactivex/rxjava3/functions/Function;)V -HSPLio/reactivex/rxjava3/internal/operators/single/SingleFlatMap$SingleFlatMapCallback;->isDisposed()Z -HSPLio/reactivex/rxjava3/internal/operators/single/SingleFlatMap$SingleFlatMapCallback;->onSubscribe(Lio/reactivex/rxjava3/disposables/Disposable;)V -HSPLio/reactivex/rxjava3/internal/operators/single/SingleFlatMap$SingleFlatMapCallback;->onSuccess(Ljava/lang/Object;)V -HSPLio/reactivex/rxjava3/internal/operators/single/SingleFlatMap;->(Lio/reactivex/rxjava3/core/SingleSource;Lio/reactivex/rxjava3/functions/Function;)V -HSPLio/reactivex/rxjava3/internal/operators/single/SingleFlatMap;->subscribeActual(Lio/reactivex/rxjava3/core/SingleObserver;)V HSPLio/reactivex/rxjava3/internal/operators/single/SingleFromCallable;->(Ljava/util/concurrent/Callable;)V HSPLio/reactivex/rxjava3/internal/operators/single/SingleFromCallable;->subscribeActual(Lio/reactivex/rxjava3/core/SingleObserver;)V HSPLio/reactivex/rxjava3/internal/operators/single/SingleJust;->(Ljava/lang/Object;)V HSPLio/reactivex/rxjava3/internal/operators/single/SingleJust;->subscribeActual(Lio/reactivex/rxjava3/core/SingleObserver;)V HSPLio/reactivex/rxjava3/internal/operators/single/SingleMap$MapSingleObserver;->(Lio/reactivex/rxjava3/core/SingleObserver;Lio/reactivex/rxjava3/functions/Function;)V -HSPLio/reactivex/rxjava3/internal/operators/single/SingleMap$MapSingleObserver;->onError(Ljava/lang/Throwable;)V HSPLio/reactivex/rxjava3/internal/operators/single/SingleMap$MapSingleObserver;->onSubscribe(Lio/reactivex/rxjava3/disposables/Disposable;)V HSPLio/reactivex/rxjava3/internal/operators/single/SingleMap$MapSingleObserver;->onSuccess(Ljava/lang/Object;)V HSPLio/reactivex/rxjava3/internal/operators/single/SingleMap;->(Lio/reactivex/rxjava3/core/SingleSource;Lio/reactivex/rxjava3/functions/Function;)V @@ -12376,17 +12412,6 @@ HSPLio/reactivex/rxjava3/internal/operators/single/SingleObserveOn;->(Lio/ HSPLio/reactivex/rxjava3/internal/operators/single/SingleObserveOn;->subscribeActual(Lio/reactivex/rxjava3/core/SingleObserver;)V HSPLio/reactivex/rxjava3/internal/operators/single/SingleOnErrorComplete;->(Lio/reactivex/rxjava3/core/Single;Lio/reactivex/rxjava3/functions/Predicate;)V HSPLio/reactivex/rxjava3/internal/operators/single/SingleOnErrorComplete;->subscribeActual(Lio/reactivex/rxjava3/core/MaybeObserver;)V -HSPLio/reactivex/rxjava3/internal/operators/single/SingleOnErrorReturn$OnErrorReturn;->(Lio/reactivex/rxjava3/internal/operators/single/SingleOnErrorReturn;Lio/reactivex/rxjava3/core/SingleObserver;)V -HSPLio/reactivex/rxjava3/internal/operators/single/SingleOnErrorReturn$OnErrorReturn;->onError(Ljava/lang/Throwable;)V -HSPLio/reactivex/rxjava3/internal/operators/single/SingleOnErrorReturn$OnErrorReturn;->onSubscribe(Lio/reactivex/rxjava3/disposables/Disposable;)V -HSPLio/reactivex/rxjava3/internal/operators/single/SingleOnErrorReturn$OnErrorReturn;->onSuccess(Ljava/lang/Object;)V -HSPLio/reactivex/rxjava3/internal/operators/single/SingleOnErrorReturn;->(Lio/reactivex/rxjava3/core/SingleSource;Lio/reactivex/rxjava3/functions/Function;Ljava/lang/Object;)V -HSPLio/reactivex/rxjava3/internal/operators/single/SingleOnErrorReturn;->subscribeActual(Lio/reactivex/rxjava3/core/SingleObserver;)V -HSPLio/reactivex/rxjava3/internal/operators/single/SingleResumeNext$ResumeMainSingleObserver;->(Lio/reactivex/rxjava3/core/SingleObserver;Lio/reactivex/rxjava3/functions/Function;)V -HSPLio/reactivex/rxjava3/internal/operators/single/SingleResumeNext$ResumeMainSingleObserver;->onError(Ljava/lang/Throwable;)V -HSPLio/reactivex/rxjava3/internal/operators/single/SingleResumeNext$ResumeMainSingleObserver;->onSubscribe(Lio/reactivex/rxjava3/disposables/Disposable;)V -HSPLio/reactivex/rxjava3/internal/operators/single/SingleResumeNext;->(Lio/reactivex/rxjava3/core/SingleSource;Lio/reactivex/rxjava3/functions/Function;)V -HSPLio/reactivex/rxjava3/internal/operators/single/SingleResumeNext;->subscribeActual(Lio/reactivex/rxjava3/core/SingleObserver;)V HSPLio/reactivex/rxjava3/internal/operators/single/SingleSubscribeOn$SubscribeOnObserver;->(Lio/reactivex/rxjava3/core/SingleObserver;Lio/reactivex/rxjava3/core/SingleSource;)V HSPLio/reactivex/rxjava3/internal/operators/single/SingleSubscribeOn$SubscribeOnObserver;->onSubscribe(Lio/reactivex/rxjava3/disposables/Disposable;)V HSPLio/reactivex/rxjava3/internal/operators/single/SingleSubscribeOn$SubscribeOnObserver;->onSuccess(Ljava/lang/Object;)V @@ -12402,9 +12427,7 @@ HSPLio/reactivex/rxjava3/internal/queue/MpscLinkedQueue$LinkedQueueNode;->soNext HSPLio/reactivex/rxjava3/internal/queue/MpscLinkedQueue$LinkedQueueNode;->spValue(Ljava/lang/Object;)V HSPLio/reactivex/rxjava3/internal/queue/MpscLinkedQueue;->()V HSPLio/reactivex/rxjava3/internal/queue/MpscLinkedQueue;->clear()V -HSPLio/reactivex/rxjava3/internal/queue/MpscLinkedQueue;->isEmpty()Z HSPLio/reactivex/rxjava3/internal/queue/MpscLinkedQueue;->lpConsumerNode()Lio/reactivex/rxjava3/internal/queue/MpscLinkedQueue$LinkedQueueNode; -HSPLio/reactivex/rxjava3/internal/queue/MpscLinkedQueue;->lvConsumerNode()Lio/reactivex/rxjava3/internal/queue/MpscLinkedQueue$LinkedQueueNode; HSPLio/reactivex/rxjava3/internal/queue/MpscLinkedQueue;->lvProducerNode()Lio/reactivex/rxjava3/internal/queue/MpscLinkedQueue$LinkedQueueNode; HSPLio/reactivex/rxjava3/internal/queue/MpscLinkedQueue;->offer(Ljava/lang/Object;)Z HSPLio/reactivex/rxjava3/internal/queue/MpscLinkedQueue;->poll()Ljava/lang/Object; @@ -12441,7 +12464,6 @@ HSPLio/reactivex/rxjava3/internal/queue/SpscLinkedArrayQueue;->soProducerIndex(J HSPLio/reactivex/rxjava3/internal/queue/SpscLinkedArrayQueue;->writeToQueue(Ljava/util/concurrent/atomic/AtomicReferenceArray;Ljava/lang/Object;JI)Z HSPLio/reactivex/rxjava3/internal/schedulers/AbstractDirectTask;->()V HSPLio/reactivex/rxjava3/internal/schedulers/AbstractDirectTask;->(Ljava/lang/Runnable;Z)V -HSPLio/reactivex/rxjava3/internal/schedulers/AbstractDirectTask;->cancelFuture(Ljava/util/concurrent/Future;)V HSPLio/reactivex/rxjava3/internal/schedulers/AbstractDirectTask;->dispose()V HSPLio/reactivex/rxjava3/internal/schedulers/AbstractDirectTask;->setFuture(Ljava/util/concurrent/Future;)V HSPLio/reactivex/rxjava3/internal/schedulers/DisposeOnCancel;->(Lio/reactivex/rxjava3/disposables/Disposable;)V @@ -12515,9 +12537,6 @@ HSPLio/reactivex/rxjava3/internal/subscribers/LambdaSubscriber;->onNext(Ljava/la HSPLio/reactivex/rxjava3/internal/subscribers/LambdaSubscriber;->onSubscribe(Lorg/reactivestreams/Subscription;)V HSPLio/reactivex/rxjava3/internal/subscribers/LambdaSubscriber;->request(J)V HSPLio/reactivex/rxjava3/internal/subscriptions/BasicIntQueueSubscription;->()V -HSPLio/reactivex/rxjava3/internal/subscriptions/DeferredScalarSubscription;->(Lorg/reactivestreams/Subscriber;)V -HSPLio/reactivex/rxjava3/internal/subscriptions/DeferredScalarSubscription;->isCancelled()Z -HSPLio/reactivex/rxjava3/internal/subscriptions/DeferredScalarSubscription;->request(J)V HSPLio/reactivex/rxjava3/internal/subscriptions/SubscriptionHelper;->()V HSPLio/reactivex/rxjava3/internal/subscriptions/SubscriptionHelper;->(Ljava/lang/String;I)V HSPLio/reactivex/rxjava3/internal/subscriptions/SubscriptionHelper;->cancel(Ljava/util/concurrent/atomic/AtomicReference;)Z @@ -12724,7 +12743,6 @@ HSPLj$/time/LocalDate;->getLong(Lj$/time/temporal/TemporalField;)J HSPLj$/time/LocalDate;->o(III)Lj$/time/LocalDate; HSPLj$/time/LocalDate;->q(Lj$/time/temporal/TemporalField;)I HSPLj$/time/LocalDate;->toEpochDay()J -HSPLj$/time/LocalDate;->w()Z HSPLj$/time/LocalDate;->x(III)Lj$/time/LocalDate; HSPLj$/time/LocalDate;->z(J)Lj$/time/LocalDate; HSPLj$/time/LocalDateTime;->()V @@ -12768,7 +12786,6 @@ HSPLj$/time/chrono/a;->()V HSPLj$/time/chrono/a;->()V HSPLj$/time/chrono/f;->()V HSPLj$/time/chrono/f;->()V -HSPLj$/time/chrono/f;->f(J)Z HSPLj$/time/f;->()V HSPLj$/time/format/B;->(Lj$/time/temporal/TemporalAccessor;Lj$/time/format/DateTimeFormatter;)V HSPLj$/time/format/B;->b()Lj$/time/format/E; @@ -12908,6 +12925,7 @@ HSPLj$/util/Comparator$-CC;->a()Ljava/util/Comparator; HSPLj$/util/DateRetargetClass;->toInstant(Ljava/util/Date;)Lj$/time/Instant; HSPLj$/util/DesugarArrays;->stream([Ljava/lang/Object;)Lj$/util/stream/Stream; HSPLj$/util/DesugarCollections;->()V +HSPLj$/util/DesugarCollections;->a()Ljava/lang/reflect/Constructor; HSPLj$/util/DesugarCollections;->b()Ljava/lang/reflect/Constructor; HSPLj$/util/DesugarCollections;->synchronizedMap(Ljava/util/Map;)Ljava/util/Map; HSPLj$/util/DesugarTimeZone;->getTimeZone(Ljava/lang/String;)Ljava/util/TimeZone; @@ -12915,10 +12933,11 @@ HSPLj$/util/List$-CC;->$default$spliterator(Ljava/util/List;)Lj$/util/Spliterato HSPLj$/util/List$-EL;->sort(Ljava/util/List;Ljava/util/Comparator;)V HSPLj$/util/Map$-CC;->$default$getOrDefault(Ljava/util/Map;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; HSPLj$/util/Map$-CC;->$default$putIfAbsent(Ljava/util/Map;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; -HSPLj$/util/Map$-EL;->a(Ljava/util/Map;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; +HSPLj$/util/Map$-EL;->b(Ljava/util/Map;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; HSPLj$/util/Map$-EL;->getOrDefault(Ljava/util/Map;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; HSPLj$/util/Optional;->()V HSPLj$/util/Optional;->()V +HSPLj$/util/Optional;->(Ljava/lang/Object;)V HSPLj$/util/Optional;->empty()Lj$/util/Optional; HSPLj$/util/Optional;->get()Ljava/lang/Object; HSPLj$/util/Optional;->ifPresent(Lj$/util/function/Consumer;)V @@ -12951,19 +12970,17 @@ HSPLj$/util/c;->()V HSPLj$/util/concurrent/ConcurrentHashMap;->()V HSPLj$/util/concurrent/ConcurrentHashMap;->()V HSPLj$/util/concurrent/ConcurrentHashMap;->(IFI)V +HSPLj$/util/concurrent/ConcurrentHashMap;->addCount(JI)V HSPLj$/util/concurrent/ConcurrentHashMap;->casTabAt([Lj$/util/concurrent/l;ILj$/util/concurrent/l;Lj$/util/concurrent/l;)Z HSPLj$/util/concurrent/ConcurrentHashMap;->clear()V HSPLj$/util/concurrent/ConcurrentHashMap;->comparableClassFor(Ljava/lang/Object;)Ljava/lang/Class; -HSPLj$/util/concurrent/ConcurrentHashMap;->containsKey(Ljava/lang/Object;)Z HSPLj$/util/concurrent/ConcurrentHashMap;->entrySet()Ljava/util/Set; -HSPLj$/util/concurrent/ConcurrentHashMap;->get(Ljava/lang/Object;)Ljava/lang/Object; HSPLj$/util/concurrent/ConcurrentHashMap;->initTable()[Lj$/util/concurrent/l; HSPLj$/util/concurrent/ConcurrentHashMap;->isEmpty()Z HSPLj$/util/concurrent/ConcurrentHashMap;->keySet()Ljava/util/Set; HSPLj$/util/concurrent/ConcurrentHashMap;->mappingCount()J HSPLj$/util/concurrent/ConcurrentHashMap;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; HSPLj$/util/concurrent/ConcurrentHashMap;->putIfAbsent(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; -HSPLj$/util/concurrent/ConcurrentHashMap;->putVal(Ljava/lang/Object;Ljava/lang/Object;Z)Ljava/lang/Object; HSPLj$/util/concurrent/ConcurrentHashMap;->remove(Ljava/lang/Object;)Ljava/lang/Object; HSPLj$/util/concurrent/ConcurrentHashMap;->replaceNode(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; HSPLj$/util/concurrent/ConcurrentHashMap;->resizeStamp(I)I @@ -13007,144 +13024,147 @@ HSPLj$/util/concurrent/v;->a(Lsun/misc/Unsafe;Ljava/lang/Object;J)I HSPLj$/util/concurrent/v;->b()Ljava/lang/reflect/Field; HSPLj$/util/concurrent/v;->c()Lsun/misc/Unsafe; HSPLj$/util/d;->(Ljava/util/Map;)V +HSPLj$/util/d;->a(Ljava/util/Set;Ljava/lang/Object;)Ljava/util/Set; HSPLj$/util/d;->get(Ljava/lang/Object;)Ljava/lang/Object; +HSPLj$/util/d;->keySet()Ljava/util/Set; HSPLj$/util/d;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; -HSPLj$/util/d;->remove(Ljava/lang/Object;)Ljava/lang/Object; HSPLj$/util/d;->values()Ljava/util/Collection; HSPLj$/util/function/b;->(Ljava/util/Comparator;I)V HSPLj$/util/m;->()V HSPLj$/util/m;->j(Lj$/util/Spliterator;)J HSPLj$/util/m;->y(Ljava/lang/Object;Ljava/lang/Object;)Z -HSPLj$/util/stream/A2;->(Lj$/util/stream/c;)V -HSPLj$/util/stream/A2;->B1(ILj$/util/stream/f2;)Lj$/util/stream/f2; -HSPLj$/util/stream/B2;->(Lj$/util/stream/f2;Ljava/util/Comparator;)V -HSPLj$/util/stream/B2;->m()V -HSPLj$/util/stream/B2;->n(J)V +HSPLj$/util/stream/A1;->(Lj$/util/stream/V2;Ljava/lang/Object;I)V +HSPLj$/util/stream/A1;->l1()Lj$/util/stream/P1; +HSPLj$/util/stream/B2;->(Lj$/util/stream/c;)V +HSPLj$/util/stream/B2;->B1(ILj$/util/stream/g2;)Lj$/util/stream/g2; +HSPLj$/util/stream/C2;->(Lj$/util/stream/g2;Ljava/util/Comparator;)V +HSPLj$/util/stream/C2;->m()V +HSPLj$/util/stream/C2;->n(J)V HSPLj$/util/stream/Collector$Characteristics;->()V HSPLj$/util/stream/Collector$Characteristics;->(Ljava/lang/String;I)V HSPLj$/util/stream/Collector$Characteristics;->values()[Lj$/util/stream/Collector$Characteristics; HSPLj$/util/stream/Collectors;->()V HSPLj$/util/stream/Collectors;->toList()Lj$/util/stream/Collector; HSPLj$/util/stream/Collectors;->toSet()Lj$/util/stream/Collector; -HSPLj$/util/stream/D1;->(Lj$/util/function/BinaryOperator;)V -HSPLj$/util/stream/D1;->accept(Ljava/lang/Object;)V -HSPLj$/util/stream/D1;->get()Ljava/lang/Object; -HSPLj$/util/stream/D1;->m()V -HSPLj$/util/stream/D1;->n(J)V -HSPLj$/util/stream/D1;->q()Z -HSPLj$/util/stream/E1;->(Lj$/util/stream/U2;Lj$/util/function/BinaryOperator;Lj$/util/function/BiConsumer;Lj$/util/function/Supplier;Lj$/util/stream/Collector;)V -HSPLj$/util/stream/E1;->l1()Lj$/util/stream/O1; -HSPLj$/util/stream/E1;->p()I -HSPLj$/util/stream/F1;->(Lj$/util/function/Supplier;Lj$/util/function/BiConsumer;Lj$/util/function/BinaryOperator;)V -HSPLj$/util/stream/F1;->accept(Ljava/lang/Object;)V -HSPLj$/util/stream/F1;->m()V -HSPLj$/util/stream/F1;->n(J)V -HSPLj$/util/stream/G;->(ZLj$/util/stream/U2;Ljava/lang/Object;Lj$/util/stream/K0;Lj$/util/stream/b;)V -HSPLj$/util/stream/G;->f0(Lj$/util/stream/v0;Lj$/util/Spliterator;)Ljava/lang/Object; -HSPLj$/util/stream/G;->p()I -HSPLj$/util/stream/K0;->(I)V -HSPLj$/util/stream/K0;->accept(Ljava/lang/Object;Ljava/lang/Object;)V -HSPLj$/util/stream/K0;->get()Ljava/lang/Object; -HSPLj$/util/stream/K;->()V -HSPLj$/util/stream/K;->get()Ljava/lang/Object; -HSPLj$/util/stream/L;->()V -HSPLj$/util/stream/L;->m()V -HSPLj$/util/stream/L;->n(J)V -HSPLj$/util/stream/P1;->()V -HSPLj$/util/stream/P1;->get()Ljava/lang/Object; -HSPLj$/util/stream/Q;->(Lj$/util/function/Consumer;Z)V -HSPLj$/util/stream/Q;->accept(Ljava/lang/Object;)V -HSPLj$/util/stream/R1;->(Lj$/util/stream/c;Lj$/util/stream/f2;I)V -HSPLj$/util/stream/R1;->accept(Ljava/lang/Object;)V -HSPLj$/util/stream/R1;->n(J)V -HSPLj$/util/stream/R2;->(Ljava/util/Map;)V -HSPLj$/util/stream/R2;->a(Lj$/util/stream/S2;)V -HSPLj$/util/stream/S2;->()V -HSPLj$/util/stream/S2;->(Ljava/lang/String;I)V -HSPLj$/util/stream/S2;->values()[Lj$/util/stream/S2; -HSPLj$/util/stream/S;->(Z)V -HSPLj$/util/stream/S;->f0(Lj$/util/stream/v0;Lj$/util/Spliterator;)Ljava/lang/Object; -HSPLj$/util/stream/S;->m()V -HSPLj$/util/stream/S;->n(J)V -HSPLj$/util/stream/S;->p()I -HSPLj$/util/stream/T1;->(Lj$/util/stream/c;ILj$/util/function/Function;I)V -HSPLj$/util/stream/T1;->B1(ILj$/util/stream/f2;)Lj$/util/stream/f2; +HSPLj$/util/stream/E1;->(Lj$/util/function/BinaryOperator;)V +HSPLj$/util/stream/E1;->accept(Ljava/lang/Object;)V +HSPLj$/util/stream/E1;->get()Ljava/lang/Object; +HSPLj$/util/stream/E1;->m()V +HSPLj$/util/stream/E1;->n(J)V +HSPLj$/util/stream/E1;->q()Z +HSPLj$/util/stream/F1;->(Lj$/util/stream/V2;Lj$/util/function/BinaryOperator;Lj$/util/function/BiConsumer;Lj$/util/function/Supplier;Lj$/util/stream/Collector;)V +HSPLj$/util/stream/F1;->l1()Lj$/util/stream/P1; +HSPLj$/util/stream/F1;->p()I +HSPLj$/util/stream/G1;->(Lj$/util/function/Supplier;Lj$/util/function/BiConsumer;Lj$/util/function/BinaryOperator;)V +HSPLj$/util/stream/G1;->accept(Ljava/lang/Object;)V +HSPLj$/util/stream/G1;->m()V +HSPLj$/util/stream/G1;->n(J)V +HSPLj$/util/stream/H;->(I)V +HSPLj$/util/stream/I;->(ZLj$/util/stream/V2;Ljava/lang/Object;Lj$/util/function/Predicate;Lj$/util/stream/b;)V +HSPLj$/util/stream/I;->f0(Lj$/util/stream/w0;Lj$/util/Spliterator;)Ljava/lang/Object; +HSPLj$/util/stream/I;->p()I +HSPLj$/util/stream/L0;->(I)V +HSPLj$/util/stream/L0;->accept(Ljava/lang/Object;Ljava/lang/Object;)V +HSPLj$/util/stream/L0;->get()Ljava/lang/Object; +HSPLj$/util/stream/M;->()V +HSPLj$/util/stream/M;->get()Ljava/lang/Object; +HSPLj$/util/stream/N;->()V +HSPLj$/util/stream/N;->m()V +HSPLj$/util/stream/N;->n(J)V +HSPLj$/util/stream/Q1;->()V +HSPLj$/util/stream/Q1;->get()Ljava/lang/Object; +HSPLj$/util/stream/S1;->(Lj$/util/stream/c;Lj$/util/stream/g2;I)V +HSPLj$/util/stream/S1;->accept(Ljava/lang/Object;)V +HSPLj$/util/stream/S1;->n(J)V +HSPLj$/util/stream/S2;->(Ljava/util/Map;)V +HSPLj$/util/stream/S2;->a(Lj$/util/stream/T2;)V HSPLj$/util/stream/T2;->()V -HSPLj$/util/stream/T2;->(Ljava/lang/String;IILj$/util/stream/R2;)V -HSPLj$/util/stream/T2;->b(II)I -HSPLj$/util/stream/T2;->d(Lj$/util/stream/S2;)I -HSPLj$/util/stream/T2;->f(Lj$/util/Spliterator;)I -HSPLj$/util/stream/T2;->g(I)Z -HSPLj$/util/stream/T2;->j(Lj$/util/stream/S2;)Lj$/util/stream/R2; +HSPLj$/util/stream/T2;->(Ljava/lang/String;I)V HSPLj$/util/stream/T2;->values()[Lj$/util/stream/T2; -HSPLj$/util/stream/U0;->()V -HSPLj$/util/stream/U1;->(Lj$/util/Spliterator;IZ)V +HSPLj$/util/stream/T;->(Lj$/util/function/Consumer;Z)V +HSPLj$/util/stream/T;->accept(Ljava/lang/Object;)V +HSPLj$/util/stream/U1;->(Lj$/util/stream/c;ILj$/util/function/Function;I)V +HSPLj$/util/stream/U1;->B1(ILj$/util/stream/g2;)Lj$/util/stream/g2; HSPLj$/util/stream/U2;->()V -HSPLj$/util/stream/U2;->(Ljava/lang/String;I)V +HSPLj$/util/stream/U2;->(Ljava/lang/String;IILj$/util/stream/S2;)V +HSPLj$/util/stream/U2;->b(II)I +HSPLj$/util/stream/U2;->d(Lj$/util/stream/T2;)I +HSPLj$/util/stream/U2;->f(Lj$/util/Spliterator;)I +HSPLj$/util/stream/U2;->g(I)Z +HSPLj$/util/stream/U2;->j(Lj$/util/stream/T2;)Lj$/util/stream/S2; +HSPLj$/util/stream/U2;->values()[Lj$/util/stream/U2; +HSPLj$/util/stream/U;->(Z)V +HSPLj$/util/stream/U;->f0(Lj$/util/stream/w0;Lj$/util/Spliterator;)Ljava/lang/Object; +HSPLj$/util/stream/U;->m()V +HSPLj$/util/stream/U;->n(J)V +HSPLj$/util/stream/U;->p()I HSPLj$/util/stream/V0;->()V -HSPLj$/util/stream/V1;->()V -HSPLj$/util/stream/V1;->(Lj$/util/stream/c;I)V -HSPLj$/util/stream/V1;->A1()Z +HSPLj$/util/stream/V1;->(Lj$/util/Spliterator;IZ)V +HSPLj$/util/stream/V2;->()V +HSPLj$/util/stream/V2;->(Ljava/lang/String;I)V HSPLj$/util/stream/W0;->()V HSPLj$/util/stream/W1;->()V HSPLj$/util/stream/W1;->(Lj$/util/stream/c;I)V HSPLj$/util/stream/W1;->A1()Z HSPLj$/util/stream/X0;->()V -HSPLj$/util/stream/X1;->(Lj$/util/Spliterator;IZ)V +HSPLj$/util/stream/X1;->()V HSPLj$/util/stream/X1;->(Lj$/util/stream/c;I)V -HSPLj$/util/stream/X1;->collect(Lj$/util/stream/Collector;)Ljava/lang/Object; -HSPLj$/util/stream/X1;->filter(Lj$/util/function/Predicate;)Lj$/util/stream/Stream; -HSPLj$/util/stream/X1;->findFirst()Lj$/util/Optional; -HSPLj$/util/stream/X1;->forEach(Lj$/util/function/Consumer;)V -HSPLj$/util/stream/X1;->limit(J)Lj$/util/stream/Stream; -HSPLj$/util/stream/X1;->map(Lj$/util/function/Function;)Lj$/util/stream/Stream; -HSPLj$/util/stream/X1;->max(Ljava/util/Comparator;)Lj$/util/Optional; -HSPLj$/util/stream/X1;->n(Lj$/util/function/BinaryOperator;)Lj$/util/Optional; -HSPLj$/util/stream/X1;->sorted()Lj$/util/stream/Stream; -HSPLj$/util/stream/X1;->t1(Lj$/util/Spliterator;Lj$/util/stream/f2;)V +HSPLj$/util/stream/X1;->A1()Z HSPLj$/util/stream/Y0;->()V -HSPLj$/util/stream/b2;->(Lj$/util/stream/f2;)V -HSPLj$/util/stream/b2;->m()V -HSPLj$/util/stream/b2;->q()Z +HSPLj$/util/stream/Y1;->(Lj$/util/Spliterator;IZ)V +HSPLj$/util/stream/Y1;->(Lj$/util/stream/c;I)V +HSPLj$/util/stream/Y1;->collect(Lj$/util/stream/Collector;)Ljava/lang/Object; +HSPLj$/util/stream/Y1;->filter(Lj$/util/function/Predicate;)Lj$/util/stream/Stream; +HSPLj$/util/stream/Y1;->findFirst()Lj$/util/Optional; +HSPLj$/util/stream/Y1;->forEach(Lj$/util/function/Consumer;)V +HSPLj$/util/stream/Y1;->limit(J)Lj$/util/stream/Stream; +HSPLj$/util/stream/Y1;->map(Lj$/util/function/Function;)Lj$/util/stream/Stream; +HSPLj$/util/stream/Y1;->max(Ljava/util/Comparator;)Lj$/util/Optional; +HSPLj$/util/stream/Y1;->n(Lj$/util/function/BinaryOperator;)Lj$/util/Optional; +HSPLj$/util/stream/Y1;->sorted()Lj$/util/stream/Stream; +HSPLj$/util/stream/Y1;->t1(Lj$/util/Spliterator;Lj$/util/stream/g2;)V +HSPLj$/util/stream/Z0;->()V HSPLj$/util/stream/b;->(I)V HSPLj$/util/stream/b;->get()Ljava/lang/Object; +HSPLj$/util/stream/c2;->(Lj$/util/stream/g2;)V +HSPLj$/util/stream/c2;->m()V +HSPLj$/util/stream/c2;->q()Z HSPLj$/util/stream/c;->()V HSPLj$/util/stream/c;->(Lj$/util/Spliterator;IZ)V HSPLj$/util/stream/c;->(Lj$/util/stream/c;I)V HSPLj$/util/stream/c;->C1(I)Lj$/util/Spliterator; -HSPLj$/util/stream/c;->M0(Lj$/util/Spliterator;Lj$/util/stream/f2;)V -HSPLj$/util/stream/c;->N0(Lj$/util/Spliterator;Lj$/util/stream/f2;)V +HSPLj$/util/stream/c;->M0(Lj$/util/Spliterator;Lj$/util/stream/g2;)V +HSPLj$/util/stream/c;->N0(Lj$/util/Spliterator;Lj$/util/stream/g2;)V HSPLj$/util/stream/c;->isParallel()Z -HSPLj$/util/stream/c;->n1(Lj$/util/Spliterator;Lj$/util/stream/f2;)Lj$/util/stream/f2; -HSPLj$/util/stream/c;->o1(Lj$/util/stream/f2;)Lj$/util/stream/f2; -HSPLj$/util/stream/c;->q1(Lj$/util/stream/E3;)Ljava/lang/Object; -HSPLj$/util/stream/g2;->(Lj$/util/stream/h2;Lj$/util/stream/f2;)V -HSPLj$/util/stream/g2;->accept(Ljava/lang/Object;)V -HSPLj$/util/stream/g2;->n(J)V -HSPLj$/util/stream/g2;->q()Z -HSPLj$/util/stream/h2;->(Lj$/util/stream/c;IJJ)V -HSPLj$/util/stream/h2;->B1(ILj$/util/stream/f2;)Lj$/util/stream/f2; +HSPLj$/util/stream/c;->n1(Lj$/util/Spliterator;Lj$/util/stream/g2;)Lj$/util/stream/g2; +HSPLj$/util/stream/c;->o1(Lj$/util/stream/g2;)Lj$/util/stream/g2; +HSPLj$/util/stream/c;->q1(Lj$/util/stream/F3;)Ljava/lang/Object; +HSPLj$/util/stream/h2;->(Lj$/util/stream/i2;Lj$/util/stream/g2;)V +HSPLj$/util/stream/h2;->accept(Ljava/lang/Object;)V +HSPLj$/util/stream/h2;->n(J)V +HSPLj$/util/stream/h2;->q()Z +HSPLj$/util/stream/i2;->(Lj$/util/stream/c;IJJ)V +HSPLj$/util/stream/i2;->B1(ILj$/util/stream/g2;)Lj$/util/stream/g2; HSPLj$/util/stream/l;->(I)V -HSPLj$/util/stream/m;->(Lj$/util/function/Supplier;Lj$/util/stream/K0;Lj$/util/stream/l;Ljava/util/Set;)V -HSPLj$/util/stream/m;->accumulator()Lj$/util/function/BiConsumer; -HSPLj$/util/stream/m;->characteristics()Ljava/util/Set; -HSPLj$/util/stream/m;->combiner()Lj$/util/function/BinaryOperator; -HSPLj$/util/stream/m;->supplier()Lj$/util/function/Supplier; -HSPLj$/util/stream/t2;->(Lj$/util/stream/f2;Ljava/util/Comparator;)V -HSPLj$/util/stream/t2;->q()Z -HSPLj$/util/stream/v0;->()V -HSPLj$/util/stream/v0;->()V -HSPLj$/util/stream/v0;->(Lj$/util/stream/U2;)V -HSPLj$/util/stream/v0;->C0(JJJ)J -HSPLj$/util/stream/v0;->S0(J)I -HSPLj$/util/stream/v0;->f0(Lj$/util/stream/v0;Lj$/util/Spliterator;)Ljava/lang/Object; -HSPLj$/util/stream/v0;->k1(Lj$/util/stream/c;JJ)Lj$/util/stream/Stream; -HSPLj$/util/stream/v0;->m1(Lj$/util/Spliterator;Z)Lj$/util/stream/Stream; -HSPLj$/util/stream/v0;->p()I -HSPLj$/util/stream/v;->(Lj$/util/stream/c;ILjava/lang/Object;I)V -HSPLj$/util/stream/v;->B1(ILj$/util/stream/f2;)Lj$/util/stream/f2; -HSPLj$/util/stream/z1;->(Lj$/util/stream/U2;Ljava/lang/Object;I)V -HSPLj$/util/stream/z1;->l1()Lj$/util/stream/O1; +HSPLj$/util/stream/n;->(Lj$/util/function/Supplier;Lj$/util/function/BiConsumer;Lj$/util/function/BinaryOperator;Lj$/util/function/Function;Ljava/util/Set;)V +HSPLj$/util/stream/n;->(Lj$/util/function/Supplier;Lj$/util/function/BiConsumer;Lj$/util/function/BinaryOperator;Ljava/util/Set;)V +HSPLj$/util/stream/n;->accumulator()Lj$/util/function/BiConsumer; +HSPLj$/util/stream/n;->characteristics()Ljava/util/Set; +HSPLj$/util/stream/n;->combiner()Lj$/util/function/BinaryOperator; +HSPLj$/util/stream/n;->supplier()Lj$/util/function/Supplier; +HSPLj$/util/stream/u2;->(Lj$/util/stream/g2;Ljava/util/Comparator;)V +HSPLj$/util/stream/u2;->q()Z +HSPLj$/util/stream/w0;->()V +HSPLj$/util/stream/w0;->()V +HSPLj$/util/stream/w0;->(Lj$/util/stream/V2;)V +HSPLj$/util/stream/w0;->C0(JJJ)J +HSPLj$/util/stream/w0;->S0(J)I +HSPLj$/util/stream/w0;->f0(Lj$/util/stream/w0;Lj$/util/Spliterator;)Ljava/lang/Object; +HSPLj$/util/stream/w0;->k1(Lj$/util/stream/c;JJ)Lj$/util/stream/Stream; +HSPLj$/util/stream/w0;->m1(Lj$/util/Spliterator;Z)Lj$/util/stream/Stream; +HSPLj$/util/stream/w0;->p()I +HSPLj$/util/stream/w;->(Lj$/util/stream/c;ILjava/lang/Object;I)V +HSPLj$/util/stream/w;->B1(ILj$/util/stream/g2;)Lj$/util/stream/g2; HSPLkotlin/InitializedLazyImpl;->(Ljava/lang/Object;)V HSPLkotlin/KotlinVersion$Companion;->()V HSPLkotlin/KotlinVersion$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -13178,13 +13198,10 @@ HSPLkotlin/Result$Companion;->()V HSPLkotlin/Result$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLkotlin/Result$Failure;->(Ljava/lang/Throwable;)V HSPLkotlin/Result;->()V -HSPLkotlin/Result;->(Ljava/lang/Object;)V -HSPLkotlin/Result;->box-impl(Ljava/lang/Object;)Lkotlin/Result; HSPLkotlin/Result;->constructor-impl(Ljava/lang/Object;)Ljava/lang/Object; HSPLkotlin/Result;->exceptionOrNull-impl(Ljava/lang/Object;)Ljava/lang/Throwable; HSPLkotlin/Result;->isFailure-impl(Ljava/lang/Object;)Z HSPLkotlin/Result;->isSuccess-impl(Ljava/lang/Object;)Z -HSPLkotlin/Result;->unbox-impl()Ljava/lang/Object; HSPLkotlin/ResultKt;->createFailure(Ljava/lang/Throwable;)Ljava/lang/Object; HSPLkotlin/ResultKt;->throwOnFailure(Ljava/lang/Object;)V HSPLkotlin/SafePublicationLazyImpl$Companion;->()V @@ -13212,13 +13229,15 @@ HSPLkotlin/UnsafeLazyImpl;->(Lkotlin/jvm/functions/Function0;)V HSPLkotlin/UnsafeLazyImpl;->getValue()Ljava/lang/Object; HSPLkotlin/collections/AbstractCollection$toString$1;->(Lkotlin/collections/AbstractCollection;)V HSPLkotlin/collections/AbstractCollection;->()V +HSPLkotlin/collections/AbstractCollection;->contains(Ljava/lang/Object;)Z HSPLkotlin/collections/AbstractCollection;->isEmpty()Z +HSPLkotlin/collections/AbstractCollection;->size()I HSPLkotlin/collections/AbstractCollection;->toString()Ljava/lang/String; HSPLkotlin/collections/AbstractList$Companion;->()V HSPLkotlin/collections/AbstractList$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLkotlin/collections/AbstractList$Companion;->checkElementIndex$kotlin_stdlib(II)V HSPLkotlin/collections/AbstractList$IteratorImpl;->(Lkotlin/collections/AbstractList;)V -HSPLkotlin/collections/AbstractList$IteratorImpl;->next()Ljava/lang/Object; +HSPLkotlin/collections/AbstractList$IteratorImpl;->hasNext()Z HSPLkotlin/collections/AbstractList;->()V HSPLkotlin/collections/AbstractList;->()V HSPLkotlin/collections/AbstractList;->iterator()Ljava/util/Iterator; @@ -13346,7 +13365,6 @@ HSPLkotlin/collections/CollectionsKt;->optimizeReadOnlyList(Ljava/util/List;)Lja HSPLkotlin/collections/CollectionsKt;->plus(Ljava/util/Collection;Ljava/lang/Iterable;)Ljava/util/List; HSPLkotlin/collections/CollectionsKt;->plus(Ljava/util/Collection;Ljava/lang/Object;)Ljava/util/List; HSPLkotlin/collections/CollectionsKt;->plus(Ljava/util/Collection;[Ljava/lang/Object;)Ljava/util/List; -HSPLkotlin/collections/CollectionsKt;->retainAll(Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;)Z HSPLkotlin/collections/CollectionsKt;->reverse(Ljava/util/List;)V HSPLkotlin/collections/CollectionsKt;->reversed(Ljava/lang/Iterable;)Ljava/util/List; HSPLkotlin/collections/CollectionsKt;->sort(Ljava/util/List;)V @@ -13374,7 +13392,6 @@ HSPLkotlin/collections/CollectionsKt__CollectionsKt;->listOfNotNull([Ljava/lang/ HSPLkotlin/collections/CollectionsKt__CollectionsKt;->mutableListOf([Ljava/lang/Object;)Ljava/util/List; HSPLkotlin/collections/CollectionsKt__CollectionsKt;->optimizeReadOnlyList(Ljava/util/List;)Ljava/util/List; HSPLkotlin/collections/CollectionsKt__CollectionsKt;->rangeCheck$CollectionsKt__CollectionsKt(III)V -HSPLkotlin/collections/CollectionsKt__IterablesKt;->collectionSizeOrDefault(Ljava/lang/Iterable;I)I HSPLkotlin/collections/CollectionsKt__IterablesKt;->collectionSizeOrNull(Ljava/lang/Iterable;)Ljava/lang/Integer; HSPLkotlin/collections/CollectionsKt__IterablesKt;->flatten(Ljava/lang/Iterable;)Ljava/util/List; HSPLkotlin/collections/CollectionsKt__MutableCollectionsJVMKt;->sort(Ljava/util/List;)V @@ -13382,8 +13399,6 @@ HSPLkotlin/collections/CollectionsKt__MutableCollectionsJVMKt;->sortWith(Ljava/u HSPLkotlin/collections/CollectionsKt__MutableCollectionsKt;->addAll(Ljava/util/Collection;Ljava/lang/Iterable;)Z HSPLkotlin/collections/CollectionsKt__MutableCollectionsKt;->addAll(Ljava/util/Collection;[Ljava/lang/Object;)Z HSPLkotlin/collections/CollectionsKt__MutableCollectionsKt;->convertToListIfNotCollection(Ljava/lang/Iterable;)Ljava/util/Collection; -HSPLkotlin/collections/CollectionsKt__MutableCollectionsKt;->filterInPlace$CollectionsKt__MutableCollectionsKt(Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;Z)Z -HSPLkotlin/collections/CollectionsKt__MutableCollectionsKt;->retainAll(Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;)Z HSPLkotlin/collections/CollectionsKt__MutableCollectionsKt;->retainAll(Ljava/util/Collection;Ljava/lang/Iterable;)Z HSPLkotlin/collections/CollectionsKt___CollectionsJvmKt;->filterIsInstance(Ljava/lang/Iterable;Ljava/lang/Class;)Ljava/util/List; HSPLkotlin/collections/CollectionsKt___CollectionsJvmKt;->filterIsInstanceTo(Ljava/lang/Iterable;Ljava/util/Collection;Ljava/lang/Class;)Ljava/util/Collection; @@ -13484,7 +13499,6 @@ HSPLkotlin/collections/MapsKt;->createMapBuilder()Ljava/util/Map; HSPLkotlin/collections/MapsKt;->emptyMap()Ljava/util/Map; HSPLkotlin/collections/MapsKt;->linkedMapOf([Lkotlin/Pair;)Ljava/util/LinkedHashMap; HSPLkotlin/collections/MapsKt;->mapCapacity(I)I -HSPLkotlin/collections/MapsKt;->mapOf(Lkotlin/Pair;)Ljava/util/Map; HSPLkotlin/collections/MapsKt;->mapOf([Lkotlin/Pair;)Ljava/util/Map; HSPLkotlin/collections/MapsKt;->plus(Ljava/util/Map;Ljava/util/Map;)Ljava/util/Map; HSPLkotlin/collections/MapsKt;->putAll(Ljava/util/Map;[Lkotlin/Pair;)V @@ -13494,7 +13508,6 @@ HSPLkotlin/collections/MapsKt;->toMutableMap(Ljava/util/Map;)Ljava/util/Map; HSPLkotlin/collections/MapsKt__MapsJVMKt;->build(Ljava/util/Map;)Ljava/util/Map; HSPLkotlin/collections/MapsKt__MapsJVMKt;->createMapBuilder()Ljava/util/Map; HSPLkotlin/collections/MapsKt__MapsJVMKt;->mapCapacity(I)I -HSPLkotlin/collections/MapsKt__MapsJVMKt;->mapOf(Lkotlin/Pair;)Ljava/util/Map; HSPLkotlin/collections/MapsKt__MapsJVMKt;->toSingletonMap(Ljava/util/Map;)Ljava/util/Map; HSPLkotlin/collections/MapsKt__MapsKt;->emptyMap()Ljava/util/Map; HSPLkotlin/collections/MapsKt__MapsKt;->linkedMapOf([Lkotlin/Pair;)Ljava/util/LinkedHashMap; @@ -13967,6 +13980,7 @@ HSPLkotlin/reflect/jvm/internal/KTypeImpl;->isMarkedNullable()Z HSPLkotlin/reflect/jvm/internal/ModuleByClassLoaderKt;->()V HSPLkotlin/reflect/jvm/internal/ModuleByClassLoaderKt;->getOrCreateModule(Ljava/lang/Class;)Lkotlin/reflect/jvm/internal/impl/descriptors/runtime/components/RuntimeModuleData; HSPLkotlin/reflect/jvm/internal/ReflectProperties$LazySoftVal;->(Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)V +HSPLkotlin/reflect/jvm/internal/ReflectProperties$LazySoftVal;->invoke()Ljava/lang/Object; HSPLkotlin/reflect/jvm/internal/ReflectProperties$LazyVal;->(Lkotlin/jvm/functions/Function0;)V HSPLkotlin/reflect/jvm/internal/ReflectProperties$LazyVal;->invoke()Ljava/lang/Object; HSPLkotlin/reflect/jvm/internal/ReflectProperties$Val$1;->()V @@ -13993,7 +14007,6 @@ HSPLkotlin/reflect/jvm/internal/UtilKt;->()V HSPLkotlin/reflect/jvm/internal/UtilKt;->asKCallableImpl(Ljava/lang/Object;)Lkotlin/reflect/jvm/internal/KCallableImpl; HSPLkotlin/reflect/jvm/internal/UtilKt;->asKFunctionImpl(Ljava/lang/Object;)Lkotlin/reflect/jvm/internal/KFunctionImpl; HSPLkotlin/reflect/jvm/internal/UtilKt;->computeAnnotations(Lkotlin/reflect/jvm/internal/impl/descriptors/annotations/Annotated;)Ljava/util/List; -HSPLkotlin/reflect/jvm/internal/UtilKt;->createArrayType(Ljava/lang/Class;)Ljava/lang/Class; HSPLkotlin/reflect/jvm/internal/UtilKt;->getInstanceReceiverParameter(Lkotlin/reflect/jvm/internal/impl/descriptors/CallableDescriptor;)Lkotlin/reflect/jvm/internal/impl/descriptors/ReceiverParameterDescriptor; HSPLkotlin/reflect/jvm/internal/UtilKt;->toJavaClass(Lkotlin/reflect/jvm/internal/impl/descriptors/ClassDescriptor;)Ljava/lang/Class; HSPLkotlin/reflect/jvm/internal/UtilKt;->unwrapRepeatableAnnotations(Ljava/util/List;)Ljava/util/List; @@ -14601,7 +14614,6 @@ HSPLkotlin/reflect/jvm/internal/impl/descriptors/runtime/components/RuntimeSourc HSPLkotlin/reflect/jvm/internal/impl/descriptors/runtime/components/SignatureSerializer;->()V HSPLkotlin/reflect/jvm/internal/impl/descriptors/runtime/components/SignatureSerializer;->()V HSPLkotlin/reflect/jvm/internal/impl/descriptors/runtime/components/SignatureSerializer;->constructorDesc(Ljava/lang/reflect/Constructor;)Ljava/lang/String; -HSPLkotlin/reflect/jvm/internal/impl/descriptors/runtime/components/SignatureSerializer;->fieldDesc(Ljava/lang/reflect/Field;)Ljava/lang/String; HSPLkotlin/reflect/jvm/internal/impl/descriptors/runtime/components/SignatureSerializer;->methodDesc(Ljava/lang/reflect/Method;)Ljava/lang/String; HSPLkotlin/reflect/jvm/internal/impl/descriptors/runtime/structure/Java16SealedRecordLoader$Cache;->(Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;)V HSPLkotlin/reflect/jvm/internal/impl/descriptors/runtime/structure/Java16SealedRecordLoader$Cache;->isSealed()Ljava/lang/reflect/Method; @@ -14906,13 +14918,10 @@ HSPLkotlin/reflect/jvm/internal/impl/load/java/typeEnhancement/SignatureEnhancem HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader$AnnotationsContainerWithConstants;->(Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;)V HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader$AnnotationsContainerWithConstants;->getMemberAnnotations()Ljava/util/Map; HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader$loadAnnotationsAndInitializers$1$AnnotationVisitorForMethod;->(Lkotlin/reflect/jvm/internal/impl/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader$loadAnnotationsAndInitializers$1;Lkotlin/reflect/jvm/internal/impl/load/kotlin/MemberSignature;)V -HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader$loadAnnotationsAndInitializers$1$AnnotationVisitorForMethod;->visitParameterAnnotation(ILkotlin/reflect/jvm/internal/impl/name/ClassId;Lkotlin/reflect/jvm/internal/impl/descriptors/SourceElement;)Lkotlin/reflect/jvm/internal/impl/load/kotlin/KotlinJvmBinaryClass$AnnotationArgumentVisitor; HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader$loadAnnotationsAndInitializers$1$MemberAnnotationVisitor;->(Lkotlin/reflect/jvm/internal/impl/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader$loadAnnotationsAndInitializers$1;Lkotlin/reflect/jvm/internal/impl/load/kotlin/MemberSignature;)V -HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader$loadAnnotationsAndInitializers$1$MemberAnnotationVisitor;->getSignature()Lkotlin/reflect/jvm/internal/impl/load/kotlin/MemberSignature; HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader$loadAnnotationsAndInitializers$1$MemberAnnotationVisitor;->visitAnnotation(Lkotlin/reflect/jvm/internal/impl/name/ClassId;Lkotlin/reflect/jvm/internal/impl/descriptors/SourceElement;)Lkotlin/reflect/jvm/internal/impl/load/kotlin/KotlinJvmBinaryClass$AnnotationArgumentVisitor; HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader$loadAnnotationsAndInitializers$1$MemberAnnotationVisitor;->visitEnd()V HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader$loadAnnotationsAndInitializers$1;->(Lkotlin/reflect/jvm/internal/impl/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader;Ljava/util/HashMap;Lkotlin/reflect/jvm/internal/impl/load/kotlin/KotlinJvmBinaryClass;Ljava/util/HashMap;Ljava/util/HashMap;)V -HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader$loadAnnotationsAndInitializers$1;->visitField(Lkotlin/reflect/jvm/internal/impl/name/Name;Ljava/lang/String;Ljava/lang/Object;)Lkotlin/reflect/jvm/internal/impl/load/kotlin/KotlinJvmBinaryClass$AnnotationVisitor; HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader$loadAnnotationsAndInitializers$1;->visitMethod(Lkotlin/reflect/jvm/internal/impl/name/Name;Ljava/lang/String;)Lkotlin/reflect/jvm/internal/impl/load/kotlin/KotlinJvmBinaryClass$MethodAnnotationVisitor; HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader$storage$1;->(Lkotlin/reflect/jvm/internal/impl/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader;)V HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader$storage$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; @@ -14938,14 +14947,9 @@ HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/AbstractBinaryClassAnnotationLo HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/AbstractBinaryClassAnnotationLoader;->loadTypeAnnotations(Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Type;Lkotlin/reflect/jvm/internal/impl/metadata/deserialization/NameResolver;)Ljava/util/List; HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/AbstractBinaryClassAnnotationLoader;->toBinaryClass(Lkotlin/reflect/jvm/internal/impl/serialization/deserialization/ProtoContainer$Class;)Lkotlin/reflect/jvm/internal/impl/load/kotlin/KotlinJvmBinaryClass; HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/BinaryClassAnnotationAndConstantLoaderImpl$AbstractAnnotationArgumentVisitor;->(Lkotlin/reflect/jvm/internal/impl/load/kotlin/BinaryClassAnnotationAndConstantLoaderImpl;)V -HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/BinaryClassAnnotationAndConstantLoaderImpl$AbstractAnnotationArgumentVisitor;->visit(Lkotlin/reflect/jvm/internal/impl/name/Name;Ljava/lang/Object;)V -HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/BinaryClassAnnotationAndConstantLoaderImpl$AbstractAnnotationArgumentVisitor;->visitEnum(Lkotlin/reflect/jvm/internal/impl/name/Name;Lkotlin/reflect/jvm/internal/impl/name/ClassId;Lkotlin/reflect/jvm/internal/impl/name/Name;)V HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/BinaryClassAnnotationAndConstantLoaderImpl$loadAnnotation$1;->(Lkotlin/reflect/jvm/internal/impl/load/kotlin/BinaryClassAnnotationAndConstantLoaderImpl;Lkotlin/reflect/jvm/internal/impl/descriptors/ClassDescriptor;Lkotlin/reflect/jvm/internal/impl/name/ClassId;Ljava/util/List;Lkotlin/reflect/jvm/internal/impl/descriptors/SourceElement;)V -HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/BinaryClassAnnotationAndConstantLoaderImpl$loadAnnotation$1;->visitConstantValue(Lkotlin/reflect/jvm/internal/impl/name/Name;Lkotlin/reflect/jvm/internal/impl/resolve/constants/ConstantValue;)V HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/BinaryClassAnnotationAndConstantLoaderImpl$loadAnnotation$1;->visitEnd()V HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/BinaryClassAnnotationAndConstantLoaderImpl;->(Lkotlin/reflect/jvm/internal/impl/descriptors/ModuleDescriptor;Lkotlin/reflect/jvm/internal/impl/descriptors/NotFoundClasses;Lkotlin/reflect/jvm/internal/impl/storage/StorageManager;Lkotlin/reflect/jvm/internal/impl/load/kotlin/KotlinClassFinder;)V -HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/BinaryClassAnnotationAndConstantLoaderImpl;->access$createConstant(Lkotlin/reflect/jvm/internal/impl/load/kotlin/BinaryClassAnnotationAndConstantLoaderImpl;Lkotlin/reflect/jvm/internal/impl/name/Name;Ljava/lang/Object;)Lkotlin/reflect/jvm/internal/impl/resolve/constants/ConstantValue; -HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/BinaryClassAnnotationAndConstantLoaderImpl;->createConstant(Lkotlin/reflect/jvm/internal/impl/name/Name;Ljava/lang/Object;)Lkotlin/reflect/jvm/internal/impl/resolve/constants/ConstantValue; HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/BinaryClassAnnotationAndConstantLoaderImpl;->loadAnnotation(Lkotlin/reflect/jvm/internal/impl/name/ClassId;Lkotlin/reflect/jvm/internal/impl/descriptors/SourceElement;Ljava/util/List;)Lkotlin/reflect/jvm/internal/impl/load/kotlin/KotlinJvmBinaryClass$AnnotationArgumentVisitor; HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/BinaryClassAnnotationAndConstantLoaderImpl;->resolveClass(Lkotlin/reflect/jvm/internal/impl/name/ClassId;)Lkotlin/reflect/jvm/internal/impl/descriptors/ClassDescriptor; HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/DeserializationComponentsForJava$Companion$ModuleData;->(Lkotlin/reflect/jvm/internal/impl/load/kotlin/DeserializationComponentsForJava;Lkotlin/reflect/jvm/internal/impl/load/kotlin/DeserializedDescriptorResolver;)V @@ -14991,15 +14995,12 @@ HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/KotlinJvmBinarySourceElement;-> HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/KotlinJvmBinarySourceElement;->getBinaryClass()Lkotlin/reflect/jvm/internal/impl/load/kotlin/KotlinJvmBinaryClass; HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/MemberSignature$Companion;->()V HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/MemberSignature$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V -HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/MemberSignature$Companion;->fromFieldNameAndDesc(Ljava/lang/String;Ljava/lang/String;)Lkotlin/reflect/jvm/internal/impl/load/kotlin/MemberSignature; HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/MemberSignature$Companion;->fromJvmMemberSignature(Lkotlin/reflect/jvm/internal/impl/metadata/jvm/deserialization/JvmMemberSignature;)Lkotlin/reflect/jvm/internal/impl/load/kotlin/MemberSignature; HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/MemberSignature$Companion;->fromMethodNameAndDesc(Ljava/lang/String;Ljava/lang/String;)Lkotlin/reflect/jvm/internal/impl/load/kotlin/MemberSignature; -HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/MemberSignature$Companion;->fromMethodSignatureAndParameterIndex(Lkotlin/reflect/jvm/internal/impl/load/kotlin/MemberSignature;I)Lkotlin/reflect/jvm/internal/impl/load/kotlin/MemberSignature; HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/MemberSignature;->()V HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/MemberSignature;->(Ljava/lang/String;)V HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/MemberSignature;->(Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/MemberSignature;->equals(Ljava/lang/Object;)Z -HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/MemberSignature;->getSignature()Ljava/lang/String; HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/MemberSignature;->hashCode()I HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/PackagePartProvider$Empty;->()V HSPLkotlin/reflect/jvm/internal/impl/load/kotlin/PackagePartProvider$Empty;->()V @@ -15147,12 +15148,10 @@ HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Annotation$Builder;->buil HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Annotation$Builder;->buildPartial()Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Annotation; HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Annotation$Builder;->create()Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Annotation$Builder; HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Annotation$Builder;->maybeForceBuilderInitialization()V -HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Annotation$Builder;->mergeFrom(Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Annotation;)Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Annotation$Builder; HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Annotation$Builder;->mergeFrom(Lkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;Lkotlin/reflect/jvm/internal/impl/protobuf/ExtensionRegistryLite;)Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Annotation$Builder; HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Annotation$Builder;->mergeFrom(Lkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;Lkotlin/reflect/jvm/internal/impl/protobuf/ExtensionRegistryLite;)Lkotlin/reflect/jvm/internal/impl/protobuf/MessageLite$Builder; HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Annotation$Builder;->setId(I)Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Annotation$Builder; HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Annotation;->()V -HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Annotation;->(Lkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;Lkotlin/reflect/jvm/internal/impl/protobuf/ExtensionRegistryLite;)V HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Annotation;->(Lkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;Lkotlin/reflect/jvm/internal/impl/protobuf/ExtensionRegistryLite;Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$1;)V HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Annotation;->(Lkotlin/reflect/jvm/internal/impl/protobuf/GeneratedMessageLite$Builder;)V HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Annotation;->(Lkotlin/reflect/jvm/internal/impl/protobuf/GeneratedMessageLite$Builder;Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$1;)V @@ -15275,7 +15274,6 @@ HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->getOldFlags()I HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->getReceiverType()Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Type; HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->getReturnType()Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Type; HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->getReturnTypeId()I -HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->getSerializedSize()I HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->getTypeParameter(I)Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$TypeParameter; HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->getTypeParameterCount()I HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->getTypeParameterList()Ljava/util/List; @@ -15292,8 +15290,6 @@ HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->hasReturnType( HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->hasReturnTypeId()Z HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->hasTypeTable()Z HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->initFields()V -HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->isInitialized()Z -HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;->writeTo(Lkotlin/reflect/jvm/internal/impl/protobuf/CodedOutputStream;)V HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$MemberKind$1;->()V HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$MemberKind;->()V HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$MemberKind;->(Ljava/lang/String;III)V @@ -15348,7 +15344,6 @@ HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Property$1;->()V HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Property$1;->parsePartialFrom(Lkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;Lkotlin/reflect/jvm/internal/impl/protobuf/ExtensionRegistryLite;)Ljava/lang/Object; HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Property$1;->parsePartialFrom(Lkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;Lkotlin/reflect/jvm/internal/impl/protobuf/ExtensionRegistryLite;)Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Property; HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Property;->()V -HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Property;->(Lkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;Lkotlin/reflect/jvm/internal/impl/protobuf/ExtensionRegistryLite;)V HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Property;->(Lkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;Lkotlin/reflect/jvm/internal/impl/protobuf/ExtensionRegistryLite;Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$1;)V HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Property;->(Z)V HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Property;->getContextReceiverTypeCount()I @@ -15493,6 +15488,7 @@ HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$TypeTable$1;->()V HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$TypeTable$1;->parsePartialFrom(Lkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;Lkotlin/reflect/jvm/internal/impl/protobuf/ExtensionRegistryLite;)Ljava/lang/Object; HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$TypeTable$1;->parsePartialFrom(Lkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;Lkotlin/reflect/jvm/internal/impl/protobuf/ExtensionRegistryLite;)Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$TypeTable; HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$TypeTable;->()V +HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$TypeTable;->(Lkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;Lkotlin/reflect/jvm/internal/impl/protobuf/ExtensionRegistryLite;)V HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$TypeTable;->(Lkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;Lkotlin/reflect/jvm/internal/impl/protobuf/ExtensionRegistryLite;Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$1;)V HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$TypeTable;->(Z)V HSPLkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$TypeTable;->getDefaultInstance()Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$TypeTable; @@ -15931,7 +15927,6 @@ HSPLkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;->readInt32()I HSPLkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;->readRawByte()B HSPLkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;->readRawLittleEndian32()I HSPLkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;->readRawLittleEndian64()J -HSPLkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;->readRawVarint32()I HSPLkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;->readRawVarint32(ILjava/io/InputStream;)I HSPLkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;->readRawVarint64()J HSPLkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;->readRawVarint64SlowPath()J @@ -15952,7 +15947,6 @@ HSPLkotlin/reflect/jvm/internal/impl/protobuf/CodedOutputStream;->computeRawVari HSPLkotlin/reflect/jvm/internal/impl/protobuf/CodedOutputStream;->computeTagSize(I)I HSPLkotlin/reflect/jvm/internal/impl/protobuf/CodedOutputStream;->flush()V HSPLkotlin/reflect/jvm/internal/impl/protobuf/CodedOutputStream;->newInstance(Ljava/io/OutputStream;I)Lkotlin/reflect/jvm/internal/impl/protobuf/CodedOutputStream; -HSPLkotlin/reflect/jvm/internal/impl/protobuf/CodedOutputStream;->refreshBuffer()V HSPLkotlin/reflect/jvm/internal/impl/protobuf/CodedOutputStream;->writeBool(IZ)V HSPLkotlin/reflect/jvm/internal/impl/protobuf/CodedOutputStream;->writeBoolNoTag(Z)V HSPLkotlin/reflect/jvm/internal/impl/protobuf/CodedOutputStream;->writeEnum(II)V @@ -15990,7 +15984,6 @@ HSPLkotlin/reflect/jvm/internal/impl/protobuf/FieldSet;->getWireFormatForFieldTy HSPLkotlin/reflect/jvm/internal/impl/protobuf/FieldSet;->hasField(Lkotlin/reflect/jvm/internal/impl/protobuf/FieldSet$FieldDescriptorLite;)Z HSPLkotlin/reflect/jvm/internal/impl/protobuf/FieldSet;->isInitialized(Ljava/util/Map$Entry;)Z HSPLkotlin/reflect/jvm/internal/impl/protobuf/FieldSet;->iterator()Ljava/util/Iterator; -HSPLkotlin/reflect/jvm/internal/impl/protobuf/FieldSet;->makeImmutable()V HSPLkotlin/reflect/jvm/internal/impl/protobuf/FieldSet;->newFieldSet()Lkotlin/reflect/jvm/internal/impl/protobuf/FieldSet; HSPLkotlin/reflect/jvm/internal/impl/protobuf/FieldSet;->readPrimitiveField(Lkotlin/reflect/jvm/internal/impl/protobuf/CodedInputStream;Lkotlin/reflect/jvm/internal/impl/protobuf/WireFormat$FieldType;Z)Ljava/lang/Object; HSPLkotlin/reflect/jvm/internal/impl/protobuf/FieldSet;->setField(Lkotlin/reflect/jvm/internal/impl/protobuf/FieldSet$FieldDescriptorLite;Ljava/lang/Object;)V @@ -16328,15 +16321,6 @@ HSPLkotlin/reflect/jvm/internal/impl/resolve/ResolutionAnchorProviderKt;->getRes HSPLkotlin/reflect/jvm/internal/impl/resolve/calls/inference/CapturedTypeConstructorKt;->createCapturedIfNeeded(Lkotlin/reflect/jvm/internal/impl/types/TypeProjection;Lkotlin/reflect/jvm/internal/impl/descriptors/TypeParameterDescriptor;)Lkotlin/reflect/jvm/internal/impl/types/TypeProjection; HSPLkotlin/reflect/jvm/internal/impl/resolve/calls/inference/CapturedTypeConstructorKt;->wrapWithCapturingSubstitution$default(Lkotlin/reflect/jvm/internal/impl/types/TypeSubstitution;ZILjava/lang/Object;)Lkotlin/reflect/jvm/internal/impl/types/TypeSubstitution; HSPLkotlin/reflect/jvm/internal/impl/resolve/calls/inference/CapturedTypeConstructorKt;->wrapWithCapturingSubstitution(Lkotlin/reflect/jvm/internal/impl/types/TypeSubstitution;Z)Lkotlin/reflect/jvm/internal/impl/types/TypeSubstitution; -HSPLkotlin/reflect/jvm/internal/impl/resolve/constants/BooleanValue;->(Z)V -HSPLkotlin/reflect/jvm/internal/impl/resolve/constants/ConstantValue;->(Ljava/lang/Object;)V -HSPLkotlin/reflect/jvm/internal/impl/resolve/constants/ConstantValueFactory;->()V -HSPLkotlin/reflect/jvm/internal/impl/resolve/constants/ConstantValueFactory;->()V -HSPLkotlin/reflect/jvm/internal/impl/resolve/constants/ConstantValueFactory;->createConstantValue(Ljava/lang/Object;)Lkotlin/reflect/jvm/internal/impl/resolve/constants/ConstantValue; -HSPLkotlin/reflect/jvm/internal/impl/resolve/constants/EnumValue;->(Lkotlin/reflect/jvm/internal/impl/name/ClassId;Lkotlin/reflect/jvm/internal/impl/name/Name;)V -HSPLkotlin/reflect/jvm/internal/impl/resolve/constants/IntValue;->(I)V -HSPLkotlin/reflect/jvm/internal/impl/resolve/constants/IntegerValueConstant;->(Ljava/lang/Object;)V -HSPLkotlin/reflect/jvm/internal/impl/resolve/constants/StringValue;->(Ljava/lang/String;)V HSPLkotlin/reflect/jvm/internal/impl/resolve/descriptorUtil/DescriptorUtilsKt$$Lambda$0;->()V HSPLkotlin/reflect/jvm/internal/impl/resolve/descriptorUtil/DescriptorUtilsKt$$Lambda$0;->()V HSPLkotlin/reflect/jvm/internal/impl/resolve/descriptorUtil/DescriptorUtilsKt$$Lambda$0;->getNeighbors(Ljava/lang/Object;)Ljava/lang/Iterable; @@ -16568,7 +16552,6 @@ HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/MemberDeseria HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/MemberDeserializer;->getReceiverParameterAnnotations(Lkotlin/reflect/jvm/internal/impl/protobuf/MessageLite;Lkotlin/reflect/jvm/internal/impl/serialization/deserialization/AnnotatedCallableKind;)Lkotlin/reflect/jvm/internal/impl/descriptors/annotations/Annotations; HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/MemberDeserializer;->initializeWithCoroutinesExperimentalityStatus(Lkotlin/reflect/jvm/internal/impl/serialization/deserialization/descriptors/DeserializedSimpleFunctionDescriptor;Lkotlin/reflect/jvm/internal/impl/descriptors/ReceiverParameterDescriptor;Lkotlin/reflect/jvm/internal/impl/descriptors/ReceiverParameterDescriptor;Ljava/util/List;Ljava/util/List;Ljava/util/List;Lkotlin/reflect/jvm/internal/impl/types/KotlinType;Lkotlin/reflect/jvm/internal/impl/descriptors/Modality;Lkotlin/reflect/jvm/internal/impl/descriptors/DescriptorVisibility;Ljava/util/Map;)V HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/MemberDeserializer;->loadConstructor(Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Constructor;Z)Lkotlin/reflect/jvm/internal/impl/descriptors/ClassConstructorDescriptor; -HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/MemberDeserializer;->loadFunction(Lkotlin/reflect/jvm/internal/impl/metadata/ProtoBuf$Function;)Lkotlin/reflect/jvm/internal/impl/descriptors/SimpleFunctionDescriptor; HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/MemberDeserializer;->loadOldFlags(I)I HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/MemberDeserializer;->valueParameters(Ljava/util/List;Lkotlin/reflect/jvm/internal/impl/protobuf/MessageLite;Lkotlin/reflect/jvm/internal/impl/serialization/deserialization/AnnotatedCallableKind;)Ljava/util/List; HSPLkotlin/reflect/jvm/internal/impl/serialization/deserialization/NameResolverUtilKt;->getClassId(Lkotlin/reflect/jvm/internal/impl/metadata/deserialization/NameResolver;I)Lkotlin/reflect/jvm/internal/impl/name/ClassId; @@ -17284,7 +17267,6 @@ HSPLkotlin/text/StringsKt;->contains$default(Ljava/lang/CharSequence;CZILjava/la HSPLkotlin/text/StringsKt;->contains$default(Ljava/lang/CharSequence;Ljava/lang/CharSequence;ZILjava/lang/Object;)Z HSPLkotlin/text/StringsKt;->contains(Ljava/lang/CharSequence;Ljava/lang/CharSequence;Z)Z HSPLkotlin/text/StringsKt;->drop(Ljava/lang/String;I)Ljava/lang/String; -HSPLkotlin/text/StringsKt;->endsWith$default(Ljava/lang/String;Ljava/lang/String;ZILjava/lang/Object;)Z HSPLkotlin/text/StringsKt;->getIndices(Ljava/lang/CharSequence;)Lkotlin/ranges/IntRange; HSPLkotlin/text/StringsKt;->getLastIndex(Ljava/lang/CharSequence;)I HSPLkotlin/text/StringsKt;->indexOf$default(Ljava/lang/CharSequence;CIZILjava/lang/Object;)I @@ -17299,6 +17281,7 @@ HSPLkotlin/text/StringsKt;->startsWith$default(Ljava/lang/CharSequence;Ljava/lan HSPLkotlin/text/StringsKt;->startsWith$default(Ljava/lang/String;Ljava/lang/String;ZILjava/lang/Object;)Z HSPLkotlin/text/StringsKt;->substringAfter(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; HSPLkotlin/text/StringsKt;->substringBeforeLast(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; +HSPLkotlin/text/StringsKt;->take(Ljava/lang/String;I)Ljava/lang/String; HSPLkotlin/text/StringsKt;->toLongOrNull(Ljava/lang/String;)Ljava/lang/Long; HSPLkotlin/text/StringsKt;->trim(Ljava/lang/CharSequence;)Ljava/lang/CharSequence; HSPLkotlin/text/StringsKt;->trimIndent(Ljava/lang/String;)Ljava/lang/String; @@ -17313,8 +17296,6 @@ HSPLkotlin/text/StringsKt__IndentKt;->replaceIndent(Ljava/lang/String;Ljava/lang HSPLkotlin/text/StringsKt__IndentKt;->trimIndent(Ljava/lang/String;)Ljava/lang/String; HSPLkotlin/text/StringsKt__StringNumberConversionsKt;->toLongOrNull(Ljava/lang/String;)Ljava/lang/Long; HSPLkotlin/text/StringsKt__StringNumberConversionsKt;->toLongOrNull(Ljava/lang/String;I)Ljava/lang/Long; -HSPLkotlin/text/StringsKt__StringsJVMKt;->endsWith$default(Ljava/lang/String;Ljava/lang/String;ZILjava/lang/Object;)Z -HSPLkotlin/text/StringsKt__StringsJVMKt;->endsWith(Ljava/lang/String;Ljava/lang/String;Z)Z HSPLkotlin/text/StringsKt__StringsJVMKt;->isBlank(Ljava/lang/CharSequence;)Z HSPLkotlin/text/StringsKt__StringsJVMKt;->regionMatches(Ljava/lang/String;ILjava/lang/String;IIZ)Z HSPLkotlin/text/StringsKt__StringsJVMKt;->replace$default(Ljava/lang/String;CCZILjava/lang/Object;)Ljava/lang/String; @@ -17334,10 +17315,12 @@ HSPLkotlin/text/StringsKt__StringsKt;->contains$default(Ljava/lang/CharSequence; HSPLkotlin/text/StringsKt__StringsKt;->contains$default(Ljava/lang/CharSequence;Ljava/lang/CharSequence;ZILjava/lang/Object;)Z HSPLkotlin/text/StringsKt__StringsKt;->contains(Ljava/lang/CharSequence;CZ)Z HSPLkotlin/text/StringsKt__StringsKt;->contains(Ljava/lang/CharSequence;Ljava/lang/CharSequence;Z)Z +HSPLkotlin/text/StringsKt__StringsKt;->findAnyOf$StringsKt__StringsKt(Ljava/lang/CharSequence;Ljava/util/Collection;IZZ)Lkotlin/Pair; HSPLkotlin/text/StringsKt__StringsKt;->getIndices(Ljava/lang/CharSequence;)Lkotlin/ranges/IntRange; HSPLkotlin/text/StringsKt__StringsKt;->getLastIndex(Ljava/lang/CharSequence;)I HSPLkotlin/text/StringsKt__StringsKt;->indexOf$default(Ljava/lang/CharSequence;CIZILjava/lang/Object;)I HSPLkotlin/text/StringsKt__StringsKt;->indexOf$default(Ljava/lang/CharSequence;Ljava/lang/String;IZILjava/lang/Object;)I +HSPLkotlin/text/StringsKt__StringsKt;->indexOf(Ljava/lang/CharSequence;CIZ)I HSPLkotlin/text/StringsKt__StringsKt;->indexOf(Ljava/lang/CharSequence;Ljava/lang/String;IZ)I HSPLkotlin/text/StringsKt__StringsKt;->lastIndexOf$default(Ljava/lang/CharSequence;Ljava/lang/String;IZILjava/lang/Object;)I HSPLkotlin/text/StringsKt__StringsKt;->lastIndexOf(Ljava/lang/CharSequence;Ljava/lang/String;IZ)I @@ -17359,6 +17342,7 @@ HSPLkotlin/text/StringsKt__StringsKt;->substringAfter(Ljava/lang/String;Ljava/la HSPLkotlin/text/StringsKt__StringsKt;->substringBeforeLast(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; HSPLkotlin/text/StringsKt__StringsKt;->trim(Ljava/lang/CharSequence;)Ljava/lang/CharSequence; HSPLkotlin/text/StringsKt___StringsKt;->drop(Ljava/lang/String;I)Ljava/lang/String; +HSPLkotlin/text/StringsKt___StringsKt;->take(Ljava/lang/String;I)Ljava/lang/String; HSPLkotlin/time/Duration$Companion;->()V HSPLkotlin/time/Duration$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLkotlin/time/Duration;->()V @@ -17474,46 +17458,6 @@ HSPLkotlinx/coroutines/flow/internal/AbstractSharedFlow;->getSlots()[Lkotlinx/co HSPLkotlinx/coroutines/flow/internal/AbstractSharedFlowKt;->()V HSPLkotlinx/coroutines/flow/internal/NullSurrogateKt;->()V HSPLkotlinx/coroutines/internal/Symbol;->(Ljava/lang/String;)V -HSPLme/leolin/shortcutbadger/ShortcutBadgeException;->(Ljava/lang/String;)V -HSPLme/leolin/shortcutbadger/ShortcutBadgeException;->(Ljava/lang/String;Ljava/lang/Exception;)V -HSPLme/leolin/shortcutbadger/ShortcutBadger;->()V -HSPLme/leolin/shortcutbadger/ShortcutBadger;->applyCount(Landroid/content/Context;I)Z -HSPLme/leolin/shortcutbadger/ShortcutBadger;->applyCountOrThrow(Landroid/content/Context;I)V -HSPLme/leolin/shortcutbadger/ShortcutBadger;->initBadger(Landroid/content/Context;)Z -HSPLme/leolin/shortcutbadger/ShortcutBadger;->removeCount(Landroid/content/Context;)Z -HSPLme/leolin/shortcutbadger/impl/AdwHomeBadger;->()V -HSPLme/leolin/shortcutbadger/impl/AdwHomeBadger;->getSupportLaunchers()Ljava/util/List; -HSPLme/leolin/shortcutbadger/impl/ApexHomeBadger;->()V -HSPLme/leolin/shortcutbadger/impl/ApexHomeBadger;->getSupportLaunchers()Ljava/util/List; -HSPLme/leolin/shortcutbadger/impl/AsusHomeBadger;->()V -HSPLme/leolin/shortcutbadger/impl/AsusHomeBadger;->getSupportLaunchers()Ljava/util/List; -HSPLme/leolin/shortcutbadger/impl/DefaultBadger;->()V -HSPLme/leolin/shortcutbadger/impl/DefaultBadger;->executeBadge(Landroid/content/Context;Landroid/content/ComponentName;I)V -HSPLme/leolin/shortcutbadger/impl/DefaultBadger;->getSupportLaunchers()Ljava/util/List; -HSPLme/leolin/shortcutbadger/impl/EverythingMeHomeBadger;->()V -HSPLme/leolin/shortcutbadger/impl/EverythingMeHomeBadger;->getSupportLaunchers()Ljava/util/List; -HSPLme/leolin/shortcutbadger/impl/HuaweiHomeBadger;->()V -HSPLme/leolin/shortcutbadger/impl/HuaweiHomeBadger;->getSupportLaunchers()Ljava/util/List; -HSPLme/leolin/shortcutbadger/impl/NewHtcHomeBadger;->()V -HSPLme/leolin/shortcutbadger/impl/NewHtcHomeBadger;->getSupportLaunchers()Ljava/util/List; -HSPLme/leolin/shortcutbadger/impl/NovaHomeBadger;->()V -HSPLme/leolin/shortcutbadger/impl/NovaHomeBadger;->getSupportLaunchers()Ljava/util/List; -HSPLme/leolin/shortcutbadger/impl/OPPOHomeBader;->()V -HSPLme/leolin/shortcutbadger/impl/OPPOHomeBader;->getSupportLaunchers()Ljava/util/List; -HSPLme/leolin/shortcutbadger/impl/SamsungHomeBadger;->()V -HSPLme/leolin/shortcutbadger/impl/SamsungHomeBadger;->()V -HSPLme/leolin/shortcutbadger/impl/SamsungHomeBadger;->getSupportLaunchers()Ljava/util/List; -HSPLme/leolin/shortcutbadger/impl/SonyHomeBadger;->()V -HSPLme/leolin/shortcutbadger/impl/SonyHomeBadger;->getSupportLaunchers()Ljava/util/List; -HSPLme/leolin/shortcutbadger/impl/VivoHomeBadger;->()V -HSPLme/leolin/shortcutbadger/impl/VivoHomeBadger;->getSupportLaunchers()Ljava/util/List; -HSPLme/leolin/shortcutbadger/impl/ZTEHomeBadger;->()V -HSPLme/leolin/shortcutbadger/impl/ZTEHomeBadger;->getSupportLaunchers()Ljava/util/List; -HSPLme/leolin/shortcutbadger/impl/ZukHomeBadger;->()V -HSPLme/leolin/shortcutbadger/impl/ZukHomeBadger;->getSupportLaunchers()Ljava/util/List; -HSPLme/leolin/shortcutbadger/util/BroadcastHelper;->resolveBroadcast(Landroid/content/Context;Landroid/content/Intent;)Ljava/util/List; -HSPLme/leolin/shortcutbadger/util/BroadcastHelper;->sendDefaultIntentExplicitly(Landroid/content/Context;Landroid/content/Intent;)V -HSPLme/leolin/shortcutbadger/util/BroadcastHelper;->sendIntentExplicitly(Landroid/content/Context;Landroid/content/Intent;)V HSPLnet/zetetic/database/DatabaseUtils;->()V HSPLnet/zetetic/database/DatabaseUtils;->cursorPickFillWindowStartPosition(II)I HSPLnet/zetetic/database/DatabaseUtils;->getSqlStatementType(Ljava/lang/String;)I @@ -17552,10 +17496,11 @@ HSPLnet/zetetic/database/sqlcipher/SQLiteConnection;->access$500()[B HSPLnet/zetetic/database/sqlcipher/SQLiteConnection;->acquirePreparedStatement(Ljava/lang/String;)Lnet/zetetic/database/sqlcipher/SQLiteConnection$PreparedStatement; HSPLnet/zetetic/database/sqlcipher/SQLiteConnection;->applyBlockGuardPolicy(Lnet/zetetic/database/sqlcipher/SQLiteConnection$PreparedStatement;)V HSPLnet/zetetic/database/sqlcipher/SQLiteConnection;->attachCancellationSignal(Landroid/os/CancellationSignal;)V +HSPLnet/zetetic/database/sqlcipher/SQLiteConnection;->bindArguments(Lnet/zetetic/database/sqlcipher/SQLiteConnection$PreparedStatement;[Ljava/lang/Object;)V HSPLnet/zetetic/database/sqlcipher/SQLiteConnection;->canonicalizeSyncMode(Ljava/lang/String;)Ljava/lang/String; HSPLnet/zetetic/database/sqlcipher/SQLiteConnection;->detachCancellationSignal(Landroid/os/CancellationSignal;)V HSPLnet/zetetic/database/sqlcipher/SQLiteConnection;->execute(Ljava/lang/String;[Ljava/lang/Object;Landroid/os/CancellationSignal;)V -HSPLnet/zetetic/database/sqlcipher/SQLiteConnection;->executeForCursorWindow(Ljava/lang/String;[Ljava/lang/Object;Landroid/database/CursorWindow;IIZLandroid/os/CancellationSignal;)I +HSPLnet/zetetic/database/sqlcipher/SQLiteConnection;->executeForChangedRowCount(Ljava/lang/String;[Ljava/lang/Object;Landroid/os/CancellationSignal;)I HSPLnet/zetetic/database/sqlcipher/SQLiteConnection;->executeForLastInsertedRowId(Ljava/lang/String;[Ljava/lang/Object;Landroid/os/CancellationSignal;)J HSPLnet/zetetic/database/sqlcipher/SQLiteConnection;->executeForLong(Ljava/lang/String;[Ljava/lang/Object;Landroid/os/CancellationSignal;)J HSPLnet/zetetic/database/sqlcipher/SQLiteConnection;->executeForString(Ljava/lang/String;[Ljava/lang/Object;Landroid/os/CancellationSignal;)Ljava/lang/String; @@ -17582,24 +17527,18 @@ HSPLnet/zetetic/database/sqlcipher/SQLiteConnection;->setWalModeFromConfiguratio HSPLnet/zetetic/database/sqlcipher/SQLiteConnection;->throwIfStatementForbidden(Lnet/zetetic/database/sqlcipher/SQLiteConnection$PreparedStatement;)V HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool$AcquiredConnectionStatus;->()V HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool$AcquiredConnectionStatus;->(Ljava/lang/String;I)V -HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool$ConnectionWaiter;->()V -HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool$ConnectionWaiter;->(Lnet/zetetic/database/sqlcipher/SQLiteConnectionPool$1;)V HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->()V HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->(Lnet/zetetic/database/sqlcipher/SQLiteDatabaseConfiguration;)V HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->acquireConnection(Ljava/lang/String;ILandroid/os/CancellationSignal;)Lnet/zetetic/database/sqlcipher/SQLiteConnection; HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->closeExcessConnectionsAndLogExceptionsLocked()V HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->finishAcquireConnectionLocked(Lnet/zetetic/database/sqlcipher/SQLiteConnection;I)V -HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->getPriority(I)I HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->markAcquiredConnectionsLocked(Lnet/zetetic/database/sqlcipher/SQLiteConnectionPool$AcquiredConnectionStatus;)V -HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->obtainConnectionWaiterLocked(Ljava/lang/Thread;JIZLjava/lang/String;I)Lnet/zetetic/database/sqlcipher/SQLiteConnectionPool$ConnectionWaiter; HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->open()V HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->open(Lnet/zetetic/database/sqlcipher/SQLiteDatabaseConfiguration;)Lnet/zetetic/database/sqlcipher/SQLiteConnectionPool; HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->openConnectionLocked(Lnet/zetetic/database/sqlcipher/SQLiteDatabaseConfiguration;Z)Lnet/zetetic/database/sqlcipher/SQLiteConnection; HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->reconfigure(Lnet/zetetic/database/sqlcipher/SQLiteDatabaseConfiguration;)V HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->reconfigureAllConnectionsLocked()V HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->recycleConnectionLocked(Lnet/zetetic/database/sqlcipher/SQLiteConnection;Lnet/zetetic/database/sqlcipher/SQLiteConnectionPool$AcquiredConnectionStatus;)Z -HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->recycleConnectionWaiterLocked(Lnet/zetetic/database/sqlcipher/SQLiteConnectionPool$ConnectionWaiter;)V -HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->releaseConnection(Lnet/zetetic/database/sqlcipher/SQLiteConnection;)V HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->setMaxConnectionPoolSizeLocked()V HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->throwIfClosedLocked()V HSPLnet/zetetic/database/sqlcipher/SQLiteConnectionPool;->tryAcquireNonPrimaryConnectionLocked(Ljava/lang/String;I)Lnet/zetetic/database/sqlcipher/SQLiteConnection; @@ -17626,6 +17565,7 @@ HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase$1;->initialValue()Lnet/zetetic HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->()V HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->(Ljava/lang/String;[BILnet/zetetic/database/sqlcipher/SQLiteDatabase$CursorFactory;Lnet/zetetic/database/DatabaseErrorHandler;Lnet/zetetic/database/sqlcipher/SQLiteDatabaseHook;)V HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->beginTransaction()V +HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->beginTransaction(Lnet/zetetic/database/sqlcipher/SQLiteTransactionListener;Z)V HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->beginTransactionWithListener(Lnet/zetetic/database/sqlcipher/SQLiteTransactionListener;)V HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->compileStatement(Ljava/lang/String;)Lnet/zetetic/database/sqlcipher/SQLiteStatement; HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->createSession()Lnet/zetetic/database/sqlcipher/SQLiteSession; @@ -17636,6 +17576,7 @@ HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->execSQL(Ljava/lang/String;)V HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->execSQL(Ljava/lang/String;[Ljava/lang/Object;)V HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->executeSql(Ljava/lang/String;[Ljava/lang/Object;)I HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->findEditTable(Ljava/lang/String;)Ljava/lang/String; +HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->getPath()Ljava/lang/String; HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->getThreadDefaultConnectionFlags(Z)I HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->getThreadSession()Lnet/zetetic/database/sqlcipher/SQLiteSession; HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->getVersion()I @@ -17668,7 +17609,6 @@ HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->setVersion(I)V HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->throwIfNotOpenLocked()V HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->update(Ljava/lang/String;ILandroid/content/ContentValues;Ljava/lang/String;[Ljava/lang/Object;)I HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->update(Ljava/lang/String;Landroid/content/ContentValues;Ljava/lang/String;[Ljava/lang/String;)I -HSPLnet/zetetic/database/sqlcipher/SQLiteDatabase;->updateWithOnConflict(Ljava/lang/String;Landroid/content/ContentValues;Ljava/lang/String;[Ljava/lang/String;I)I HSPLnet/zetetic/database/sqlcipher/SQLiteDatabaseConfiguration;->()V HSPLnet/zetetic/database/sqlcipher/SQLiteDatabaseConfiguration;->(Ljava/lang/String;I[BLnet/zetetic/database/sqlcipher/SQLiteDatabaseHook;)V HSPLnet/zetetic/database/sqlcipher/SQLiteDatabaseConfiguration;->(Lnet/zetetic/database/sqlcipher/SQLiteDatabaseConfiguration;)V @@ -17699,12 +17639,10 @@ HSPLnet/zetetic/database/sqlcipher/SQLiteProgram;->bind(ILjava/lang/Object;)V HSPLnet/zetetic/database/sqlcipher/SQLiteProgram;->bindAllArgs([Ljava/lang/Object;)V HSPLnet/zetetic/database/sqlcipher/SQLiteProgram;->bindAllArgsAsStrings([Ljava/lang/String;)V HSPLnet/zetetic/database/sqlcipher/SQLiteProgram;->bindString(ILjava/lang/String;)V -HSPLnet/zetetic/database/sqlcipher/SQLiteProgram;->clearBindings()V HSPLnet/zetetic/database/sqlcipher/SQLiteProgram;->getBindArgs()[Ljava/lang/Object; HSPLnet/zetetic/database/sqlcipher/SQLiteProgram;->getColumnNames()[Ljava/lang/String; HSPLnet/zetetic/database/sqlcipher/SQLiteProgram;->getConnectionFlags()I HSPLnet/zetetic/database/sqlcipher/SQLiteProgram;->getDatabase()Lnet/zetetic/database/sqlcipher/SQLiteDatabase; -HSPLnet/zetetic/database/sqlcipher/SQLiteProgram;->getSession()Lnet/zetetic/database/sqlcipher/SQLiteSession; HSPLnet/zetetic/database/sqlcipher/SQLiteProgram;->getSql()Ljava/lang/String; HSPLnet/zetetic/database/sqlcipher/SQLiteProgram;->onAllReferencesReleased()V HSPLnet/zetetic/database/sqlcipher/SQLiteQuery;->(Lnet/zetetic/database/sqlcipher/SQLiteDatabase;Ljava/lang/String;Landroid/os/CancellationSignal;)V @@ -17736,8 +17674,10 @@ HSPLnet/zetetic/database/sqlcipher/SQLiteSession;->setTransactionSuccessful()V HSPLnet/zetetic/database/sqlcipher/SQLiteSession;->throwIfNoTransaction()V HSPLnet/zetetic/database/sqlcipher/SQLiteSession;->throwIfTransactionMarkedSuccessful()V HSPLnet/zetetic/database/sqlcipher/SQLiteStatement;->(Lnet/zetetic/database/sqlcipher/SQLiteDatabase;Ljava/lang/String;[Ljava/lang/Object;)V +HSPLnet/zetetic/database/sqlcipher/SQLiteStatement;->executeInsert()J HSPLnet/zetetic/database/sqlcipher/SQLiteStatement;->executeUpdateDelete()I HSPLnet/zetetic/database/sqlcipher/SQLiteStatement;->simpleQueryForLong()J +HSPLnet/zetetic/database/sqlcipher/SQLiteStatementInfo;->()V HSPLokhttp3/Address;->(Ljava/lang/String;ILokhttp3/Dns;Ljavax/net/SocketFactory;Ljavax/net/ssl/SSLSocketFactory;Ljavax/net/ssl/HostnameVerifier;Lokhttp3/CertificatePinner;Lokhttp3/Authenticator;Ljava/net/Proxy;Ljava/util/List;Ljava/util/List;Ljava/net/ProxySelector;)V HSPLokhttp3/Address;->certificatePinner()Lokhttp3/CertificatePinner; HSPLokhttp3/Address;->connectionSpecs()Ljava/util/List; @@ -17913,11 +17853,9 @@ HSPLokhttp3/HttpUrl;->toString()Ljava/lang/String; HSPLokhttp3/HttpUrl;->uri()Ljava/net/URI; HSPLokhttp3/MediaType;->()V HSPLokhttp3/MediaType;->(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V -HSPLokhttp3/MediaType;->charset()Ljava/nio/charset/Charset; HSPLokhttp3/MediaType;->charset(Ljava/nio/charset/Charset;)Ljava/nio/charset/Charset; HSPLokhttp3/MediaType;->get(Ljava/lang/String;)Lokhttp3/MediaType; HSPLokhttp3/MediaType;->parse(Ljava/lang/String;)Lokhttp3/MediaType; -HSPLokhttp3/MediaType;->toString()Ljava/lang/String; HSPLokhttp3/OkHttpClient$1;->()V HSPLokhttp3/OkHttpClient$1;->addLenient(Lokhttp3/Headers$Builder;Ljava/lang/String;)V HSPLokhttp3/OkHttpClient$1;->apply(Lokhttp3/ConnectionSpec;Ljavax/net/ssl/SSLSocket;Z)V @@ -17942,7 +17880,6 @@ HSPLokhttp3/OkHttpClient$Builder;->eventListener(Lokhttp3/EventListener;)Lokhttp HSPLokhttp3/OkHttpClient$Builder;->protocols(Ljava/util/List;)Lokhttp3/OkHttpClient$Builder; HSPLokhttp3/OkHttpClient$Builder;->proxySelector(Ljava/net/ProxySelector;)Lokhttp3/OkHttpClient$Builder; HSPLokhttp3/OkHttpClient$Builder;->readTimeout(JLjava/util/concurrent/TimeUnit;)Lokhttp3/OkHttpClient$Builder; -HSPLokhttp3/OkHttpClient$Builder;->retryOnConnectionFailure(Z)Lokhttp3/OkHttpClient$Builder; HSPLokhttp3/OkHttpClient$Builder;->sslSocketFactory(Ljavax/net/ssl/SSLSocketFactory;Ljavax/net/ssl/X509TrustManager;)Lokhttp3/OkHttpClient$Builder; HSPLokhttp3/OkHttpClient;->()V HSPLokhttp3/OkHttpClient;->(Lokhttp3/OkHttpClient$Builder;)V @@ -18011,7 +17948,6 @@ HSPLokhttp3/Request;->newBuilder()Lokhttp3/Request$Builder; HSPLokhttp3/Request;->url()Lokhttp3/HttpUrl; HSPLokhttp3/RequestBody$2;->(Lokhttp3/MediaType;I[BI)V HSPLokhttp3/RequestBody;->()V -HSPLokhttp3/RequestBody;->create(Lokhttp3/MediaType;Ljava/lang/String;)Lokhttp3/RequestBody; HSPLokhttp3/RequestBody;->create(Lokhttp3/MediaType;[B)Lokhttp3/RequestBody; HSPLokhttp3/RequestBody;->create(Lokhttp3/MediaType;[BII)Lokhttp3/RequestBody; HSPLokhttp3/Response$Builder;->()V @@ -18040,7 +17976,6 @@ HSPLokhttp3/Response;->isSuccessful()Z HSPLokhttp3/Response;->message()Ljava/lang/String; HSPLokhttp3/Response;->newBuilder()Lokhttp3/Response$Builder; HSPLokhttp3/Response;->request()Lokhttp3/Request; -HSPLokhttp3/Response;->toString()Ljava/lang/String; HSPLokhttp3/ResponseBody$1;->(Lokhttp3/MediaType;JLokio/BufferedSource;)V HSPLokhttp3/ResponseBody$1;->source()Lokio/BufferedSource; HSPLokhttp3/ResponseBody;->()V @@ -18287,7 +18222,6 @@ HSPLokio/AsyncTimeout;->()V HSPLokio/AsyncTimeout;->()V HSPLokio/AsyncTimeout;->access$getHead$cp()Lokio/AsyncTimeout; HSPLokio/AsyncTimeout;->access$getIDLE_TIMEOUT_MILLIS$cp()J -HSPLokio/AsyncTimeout;->access$getIDLE_TIMEOUT_NANOS$cp()J HSPLokio/AsyncTimeout;->access$getInQueue$p(Lokio/AsyncTimeout;)Z HSPLokio/AsyncTimeout;->access$getNext$p(Lokio/AsyncTimeout;)Lokio/AsyncTimeout; HSPLokio/AsyncTimeout;->access$remainingNanos(Lokio/AsyncTimeout;J)J @@ -18310,6 +18244,7 @@ HSPLokio/Buffer;->close()V HSPLokio/Buffer;->completeSegmentByteCount()J HSPLokio/Buffer;->copyTo(Lokio/Buffer;JJ)Lokio/Buffer; HSPLokio/Buffer;->exhausted()Z +HSPLokio/Buffer;->getByte(J)B HSPLokio/Buffer;->indexOf(BJJ)J HSPLokio/Buffer;->indexOfElement(Lokio/ByteString;J)J HSPLokio/Buffer;->read(Lokio/Buffer;J)J @@ -18322,14 +18257,11 @@ HSPLokio/Buffer;->readHexadecimalUnsignedLong()J HSPLokio/Buffer;->readInt()I HSPLokio/Buffer;->readIntLe()I HSPLokio/Buffer;->readShort()S -HSPLokio/Buffer;->readString(JLjava/nio/charset/Charset;)Ljava/lang/String; HSPLokio/Buffer;->readString(Ljava/nio/charset/Charset;)Ljava/lang/String; HSPLokio/Buffer;->readUtf8(J)Ljava/lang/String; HSPLokio/Buffer;->setSize$okio(J)V HSPLokio/Buffer;->size()J -HSPLokio/Buffer;->skip(J)V HSPLokio/Buffer;->writableSegment$okio(I)Lokio/Segment; -HSPLokio/Buffer;->write(Lokio/Buffer;J)V HSPLokio/Buffer;->write([B)Lokio/Buffer; HSPLokio/Buffer;->write([BII)Lokio/Buffer; HSPLokio/Buffer;->writeAll(Lokio/Source;)J @@ -18393,6 +18325,7 @@ HSPLokio/InflaterSource;->refill()Z HSPLokio/InflaterSource;->releaseBytesAfterInflate()V HSPLokio/InputStreamSource;->(Ljava/io/InputStream;Lokio/Timeout;)V HSPLokio/InputStreamSource;->close()V +HSPLokio/InputStreamSource;->read(Lokio/Buffer;J)J HSPLokio/Okio;->blackhole()Lokio/Sink; HSPLokio/Okio;->buffer(Lokio/Sink;)Lokio/BufferedSink; HSPLokio/Okio;->buffer(Lokio/Source;)Lokio/BufferedSource; @@ -18455,7 +18388,6 @@ HSPLokio/RealBufferedSource;->readShort()S HSPLokio/RealBufferedSource;->readString(Ljava/nio/charset/Charset;)Ljava/lang/String; HSPLokio/RealBufferedSource;->readUtf8LineStrict()Ljava/lang/String; HSPLokio/RealBufferedSource;->readUtf8LineStrict(J)Ljava/lang/String; -HSPLokio/RealBufferedSource;->request(J)Z HSPLokio/RealBufferedSource;->require(J)V HSPLokio/RealBufferedSource;->select(Lokio/Options;)I HSPLokio/RealBufferedSource;->skip(J)V @@ -18549,7 +18481,6 @@ HSPLorg/conscrypt/ActiveSession;->onPeerCertificateAvailable(Ljava/lang/String;I HSPLorg/conscrypt/ActiveSession;->onPeerCertificatesReceived(Ljava/lang/String;I[Ljava/security/cert/X509Certificate;)V HSPLorg/conscrypt/AddressUtils;->isLiteralIpAddress(Ljava/lang/String;)Z HSPLorg/conscrypt/AddressUtils;->isValidSniHostname(Ljava/lang/String;)Z -HSPLorg/conscrypt/ArrayUtils;->checkOffsetAndCount(III)V HSPLorg/conscrypt/BufferUtils;->checkNotNull([Ljava/nio/ByteBuffer;)V HSPLorg/conscrypt/BufferUtils;->getBufferLargerThan([Ljava/nio/ByteBuffer;I)Ljava/nio/ByteBuffer; HSPLorg/conscrypt/BufferUtils;->remaining([Ljava/nio/ByteBuffer;)J @@ -18652,6 +18583,7 @@ HSPLorg/conscrypt/ConscryptEngineSocket$SSLInputStream;->(Lorg/conscrypt/C HSPLorg/conscrypt/ConscryptEngineSocket$SSLInputStream;->access$100(Lorg/conscrypt/ConscryptEngineSocket$SSLInputStream;[BII)I HSPLorg/conscrypt/ConscryptEngineSocket$SSLInputStream;->init()V HSPLorg/conscrypt/ConscryptEngineSocket$SSLInputStream;->isHandshaking(Ljavax/net/ssl/SSLEngineResult$HandshakeStatus;)Z +HSPLorg/conscrypt/ConscryptEngineSocket$SSLInputStream;->processDataFromSocket([BII)I HSPLorg/conscrypt/ConscryptEngineSocket$SSLInputStream;->read([BII)I HSPLorg/conscrypt/ConscryptEngineSocket$SSLInputStream;->readFromSocket()I HSPLorg/conscrypt/ConscryptEngineSocket$SSLInputStream;->readUntilDataAvailable([BII)I @@ -18704,9 +18636,6 @@ HSPLorg/conscrypt/ExternalSession;->getPacketBufferSize()I HSPLorg/conscrypt/ExternalSession;->getPeerCertificates()[Ljava/security/cert/X509Certificate; HSPLorg/conscrypt/ExternalSession;->getPeerHost()Ljava/lang/String; HSPLorg/conscrypt/ExternalSession;->getProtocol()Ljava/lang/String; -HSPLorg/conscrypt/GCMParameters;->(I[B)V -HSPLorg/conscrypt/GCMParameters;->getIV()[B -HSPLorg/conscrypt/GCMParameters;->getTLen()I HSPLorg/conscrypt/HandshakeListener;->()V HSPLorg/conscrypt/Java7ExtendedSSLSession;->()V HSPLorg/conscrypt/Java7ExtendedSSLSession;->(Lorg/conscrypt/ExternalSession;)V @@ -18752,7 +18681,6 @@ HSPLorg/conscrypt/NativeRef$EVP_PKEY;->doFree(J)V HSPLorg/conscrypt/NativeRef$HMAC_CTX;->(J)V HSPLorg/conscrypt/NativeRef$HMAC_CTX;->doFree(J)V HSPLorg/conscrypt/NativeRef$SSL_SESSION;->(J)V -HSPLorg/conscrypt/NativeRef$SSL_SESSION;->doFree(J)V HSPLorg/conscrypt/NativeRef;->(J)V HSPLorg/conscrypt/NativeRef;->finalize()V HSPLorg/conscrypt/NativeSsl$BioWrapper;->(Lorg/conscrypt/NativeSsl;)V @@ -18804,22 +18732,6 @@ HSPLorg/conscrypt/NativeSslSession;->getOcspResponse(Lorg/conscrypt/ConscryptSes HSPLorg/conscrypt/NativeSslSession;->newInstance(Lorg/conscrypt/NativeRef$SSL_SESSION;Lorg/conscrypt/ConscryptSession;)Lorg/conscrypt/NativeSslSession; HSPLorg/conscrypt/OidData;->()V HSPLorg/conscrypt/OidData;->oidToAlgorithmName(Ljava/lang/String;)Ljava/lang/String; -HSPLorg/conscrypt/OpenSSLAeadCipher;->()V -HSPLorg/conscrypt/OpenSSLAeadCipher;->(Lorg/conscrypt/OpenSSLCipher$Mode;)V -HSPLorg/conscrypt/OpenSSLAeadCipher;->allowsNonceReuse()Z -HSPLorg/conscrypt/OpenSSLAeadCipher;->checkInitialization()V -HSPLorg/conscrypt/OpenSSLAeadCipher;->checkSupportedTagLength(I)V -HSPLorg/conscrypt/OpenSSLAeadCipher;->doFinalInternal([BII)I -HSPLorg/conscrypt/OpenSSLAeadCipher;->engineInitInternal([BLjava/security/spec/AlgorithmParameterSpec;Ljava/security/SecureRandom;)V -HSPLorg/conscrypt/OpenSSLAeadCipher;->expand(I)V -HSPLorg/conscrypt/OpenSSLAeadCipher;->reset()V -HSPLorg/conscrypt/OpenSSLAeadCipher;->updateInternal([BII[BII)I -HSPLorg/conscrypt/OpenSSLAeadCipherAES$GCM;->()V -HSPLorg/conscrypt/OpenSSLAeadCipherAES$GCM;->getEVP_AEAD(I)J -HSPLorg/conscrypt/OpenSSLAeadCipherAES;->(Lorg/conscrypt/OpenSSLCipher$Mode;)V -HSPLorg/conscrypt/OpenSSLAeadCipherAES;->checkSupportedKeySize(I)V -HSPLorg/conscrypt/OpenSSLAeadCipherAES;->getCipherBlockSize()I -HSPLorg/conscrypt/OpenSSLAeadCipherAES;->getOutputSizeForFinal(I)I HSPLorg/conscrypt/OpenSSLBIOInputStream;->(Ljava/io/InputStream;Z)V HSPLorg/conscrypt/OpenSSLBIOInputStream;->getBioContext()J HSPLorg/conscrypt/OpenSSLBIOInputStream;->read([B)I @@ -18834,6 +18746,7 @@ HSPLorg/conscrypt/OpenSSLCipher;->checkAndSetEncodedKey(ILjava/security/Key;)[B HSPLorg/conscrypt/OpenSSLCipher;->engineDoFinal([BII)[B HSPLorg/conscrypt/OpenSSLCipher;->engineDoFinal([BII[BI)I HSPLorg/conscrypt/OpenSSLCipher;->engineGetIV()[B +HSPLorg/conscrypt/OpenSSLCipher;->engineGetOutputSize(I)I HSPLorg/conscrypt/OpenSSLCipher;->engineInit(ILjava/security/Key;Ljava/security/SecureRandom;)V HSPLorg/conscrypt/OpenSSLCipher;->engineInit(ILjava/security/Key;Ljava/security/spec/AlgorithmParameterSpec;Ljava/security/SecureRandom;)V HSPLorg/conscrypt/OpenSSLCipher;->engineUpdate([BII)[B @@ -18970,7 +18883,6 @@ HSPLorg/conscrypt/Platform;->blockGuardOnNetwork()V HSPLorg/conscrypt/Platform;->checkServerTrusted(Ljavax/net/ssl/X509TrustManager;[Ljava/security/cert/X509Certificate;Ljava/lang/String;Lorg/conscrypt/ConscryptEngine;)V HSPLorg/conscrypt/Platform;->checkTrusted(Ljava/lang/String;Ljavax/net/ssl/X509TrustManager;[Ljava/security/cert/X509Certificate;Ljava/lang/String;Ljava/lang/Class;Ljava/lang/Object;)Z HSPLorg/conscrypt/Platform;->createEngineSocket(Ljava/net/Socket;Ljava/lang/String;IZLorg/conscrypt/SSLParametersImpl;)Lorg/conscrypt/ConscryptEngineSocket; -HSPLorg/conscrypt/Platform;->fromGCMParameterSpec(Ljava/security/spec/AlgorithmParameterSpec;)Lorg/conscrypt/GCMParameters; HSPLorg/conscrypt/Platform;->getDefaultProviderName()Ljava/lang/String; HSPLorg/conscrypt/Platform;->getSSLParameters(Ljavax/net/ssl/SSLParameters;Lorg/conscrypt/SSLParametersImpl;Lorg/conscrypt/ConscryptEngine;)V HSPLorg/conscrypt/Platform;->getSSLParametersFromImpl(Ljavax/net/ssl/SSLParameters;Lorg/conscrypt/SSLParametersImpl;)V @@ -19152,6 +19064,8 @@ HSPLorg/signal/core/util/CharacterIterable;->(Ljava/lang/String;)V HSPLorg/signal/core/util/CharacterIterable;->iterator()Ljava/util/Iterator; HSPLorg/signal/core/util/CollectionsExtensionsKt;->flatten(Ljava/util/List;)Ljava/util/Map; HSPLorg/signal/core/util/Conversions;->longTo4ByteArray([BIJ)I +HSPLorg/signal/core/util/CryptoUtil;->hmacSha256([B[B)[B +HSPLorg/signal/core/util/CursorExtensionsKt;->isNull(Landroid/database/Cursor;Ljava/lang/String;)Z HSPLorg/signal/core/util/CursorExtensionsKt;->optionalBlob(Landroid/database/Cursor;Ljava/lang/String;)Lj$/util/Optional; HSPLorg/signal/core/util/CursorExtensionsKt;->optionalBoolean(Landroid/database/Cursor;Ljava/lang/String;)Lj$/util/Optional; HSPLorg/signal/core/util/CursorExtensionsKt;->optionalInt(Landroid/database/Cursor;Ljava/lang/String;)Lj$/util/Optional; @@ -19165,7 +19079,6 @@ HSPLorg/signal/core/util/CursorExtensionsKt;->requireBlob(Landroid/database/Curs HSPLorg/signal/core/util/CursorExtensionsKt;->requireBoolean(Landroid/database/Cursor;Ljava/lang/String;)Z HSPLorg/signal/core/util/CursorExtensionsKt;->requireInt(Landroid/database/Cursor;Ljava/lang/String;)I HSPLorg/signal/core/util/CursorExtensionsKt;->requireLong(Landroid/database/Cursor;Ljava/lang/String;)J -HSPLorg/signal/core/util/CursorExtensionsKt;->requireNonNullString(Landroid/database/Cursor;Ljava/lang/String;)Ljava/lang/String; HSPLorg/signal/core/util/CursorExtensionsKt;->requireString(Landroid/database/Cursor;Ljava/lang/String;)Ljava/lang/String; HSPLorg/signal/core/util/CursorExtensionsKt;->toInt(Z)I HSPLorg/signal/core/util/CursorUtil;->getBlob(Landroid/database/Cursor;Ljava/lang/String;)Lj$/util/Optional; @@ -19173,11 +19086,11 @@ HSPLorg/signal/core/util/CursorUtil;->getBoolean(Landroid/database/Cursor;Ljava/ HSPLorg/signal/core/util/CursorUtil;->getInt(Landroid/database/Cursor;Ljava/lang/String;)Lj$/util/Optional; HSPLorg/signal/core/util/CursorUtil;->getLong(Landroid/database/Cursor;Ljava/lang/String;)Lj$/util/Optional; HSPLorg/signal/core/util/CursorUtil;->getString(Landroid/database/Cursor;Ljava/lang/String;)Lj$/util/Optional; +HSPLorg/signal/core/util/CursorUtil;->isNull(Landroid/database/Cursor;Ljava/lang/String;)Z HSPLorg/signal/core/util/CursorUtil;->requireBlob(Landroid/database/Cursor;Ljava/lang/String;)[B HSPLorg/signal/core/util/CursorUtil;->requireBoolean(Landroid/database/Cursor;Ljava/lang/String;)Z HSPLorg/signal/core/util/CursorUtil;->requireInt(Landroid/database/Cursor;Ljava/lang/String;)I HSPLorg/signal/core/util/CursorUtil;->requireLong(Landroid/database/Cursor;Ljava/lang/String;)J -HSPLorg/signal/core/util/CursorUtil;->requireString(Landroid/database/Cursor;Ljava/lang/String;)Ljava/lang/String; HSPLorg/signal/core/util/DeleteBuilderPart1;->(Landroidx/sqlite/db/SupportSQLiteDatabase;Ljava/lang/String;)V HSPLorg/signal/core/util/DeleteBuilderPart1;->run()I HSPLorg/signal/core/util/DeleteBuilderPart1;->where(Ljava/lang/String;[Ljava/lang/Object;)Lorg/signal/core/util/DeleteBuilderPart2; @@ -19225,7 +19138,6 @@ HSPLorg/signal/core/util/OptionalExtensionsKt;->orNull(Lj$/util/Optional;)Ljava/ HSPLorg/signal/core/util/OptionalExtensionsKt;->toOptional(Ljava/lang/Object;)Lj$/util/Optional; HSPLorg/signal/core/util/PendingIntentFlags;->()V HSPLorg/signal/core/util/PendingIntentFlags;->()V -HSPLorg/signal/core/util/PendingIntentFlags;->cancelCurrent()I HSPLorg/signal/core/util/PendingIntentFlags;->immutable()I HSPLorg/signal/core/util/PendingIntentFlags;->mutable()I HSPLorg/signal/core/util/PendingIntentFlags;->updateCurrent()I @@ -19276,6 +19188,7 @@ HSPLorg/signal/core/util/SqlUtil;->()V HSPLorg/signal/core/util/SqlUtil;->access$buildSingleCustomCollectionQuery(Lorg/signal/core/util/SqlUtil;Ljava/lang/String;Ljava/util/List;)Lorg/signal/core/util/SqlUtil$Query; HSPLorg/signal/core/util/SqlUtil;->appendArg([Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String; HSPLorg/signal/core/util/SqlUtil;->buildArgs(J)[Ljava/lang/String; +HSPLorg/signal/core/util/SqlUtil;->buildArgs([Ljava/lang/Object;)[Ljava/lang/String; HSPLorg/signal/core/util/SqlUtil;->buildCollectionQuery$default(Ljava/lang/String;Ljava/util/Collection;Ljava/lang/String;ILorg/signal/core/util/SqlUtil$CollectionOperator;ILjava/lang/Object;)Ljava/util/List; HSPLorg/signal/core/util/SqlUtil;->buildCollectionQuery(Ljava/lang/String;Ljava/util/Collection;Ljava/lang/String;ILorg/signal/core/util/SqlUtil$CollectionOperator;)Ljava/util/List; HSPLorg/signal/core/util/SqlUtil;->buildCustomCollectionQuery$lambda$11(Lkotlin/jvm/functions/Function1;Ljava/lang/Object;)Lorg/signal/core/util/SqlUtil$Query; @@ -19283,7 +19196,6 @@ HSPLorg/signal/core/util/SqlUtil;->buildCustomCollectionQuery(Ljava/lang/String; HSPLorg/signal/core/util/SqlUtil;->buildCustomCollectionQuery(Ljava/lang/String;Ljava/util/List;I)Ljava/util/List; HSPLorg/signal/core/util/SqlUtil;->buildQuery(Ljava/lang/String;[Ljava/lang/Object;)Lorg/signal/core/util/SqlUtil$Query; HSPLorg/signal/core/util/SqlUtil;->buildSingleCollectionQuery$default(Ljava/lang/String;Ljava/util/Collection;Ljava/lang/String;Lorg/signal/core/util/SqlUtil$CollectionOperator;ILjava/lang/Object;)Lorg/signal/core/util/SqlUtil$Query; -HSPLorg/signal/core/util/SqlUtil;->buildSingleCollectionQuery(Ljava/lang/String;Ljava/util/Collection;)Lorg/signal/core/util/SqlUtil$Query; HSPLorg/signal/core/util/SqlUtil;->buildSingleCollectionQuery(Ljava/lang/String;Ljava/util/Collection;Ljava/lang/String;Lorg/signal/core/util/SqlUtil$CollectionOperator;)Lorg/signal/core/util/SqlUtil$Query; HSPLorg/signal/core/util/SqlUtil;->buildSingleCustomCollectionQuery(Ljava/lang/String;Ljava/util/List;)Lorg/signal/core/util/SqlUtil$Query; HSPLorg/signal/core/util/SqlUtil;->buildTrueUpdateQuery(Ljava/lang/String;[Ljava/lang/String;Landroid/content/ContentValues;)Lorg/signal/core/util/SqlUtil$Query; @@ -19301,9 +19213,7 @@ HSPLorg/signal/core/util/Stopwatch;->access$getDecimalPlaces$p(Lorg/signal/core/ HSPLorg/signal/core/util/Stopwatch;->split(Ljava/lang/String;)V HSPLorg/signal/core/util/Stopwatch;->stop(Ljava/lang/String;)V HSPLorg/signal/core/util/Stopwatch;->stopAndGetLogString()Ljava/lang/String; -HSPLorg/signal/core/util/StreamUtil;->()V -HSPLorg/signal/core/util/StreamUtil;->close(Ljava/io/Closeable;)V -HSPLorg/signal/core/util/StreamUtil;->copy(Ljava/io/InputStream;Ljava/io/OutputStream;)J +HSPLorg/signal/core/util/StringExtensionsKt;->nullIfBlank(Ljava/lang/String;)Ljava/lang/String; HSPLorg/signal/core/util/StringExtensionsKt;->toSingleLine(Ljava/lang/String;)Ljava/lang/String; HSPLorg/signal/core/util/StringUtil;->()V HSPLorg/signal/core/util/StringUtil;->forceLtr(Ljava/lang/CharSequence;)Ljava/lang/String; @@ -19380,7 +19290,6 @@ HSPLorg/signal/core/util/concurrent/RxExtensions$subscribeWithSubject$3;-> HSPLorg/signal/core/util/concurrent/RxExtensions;->safeBlockingGet(Lio/reactivex/rxjava3/core/Single;)Ljava/lang/Object; HSPLorg/signal/core/util/concurrent/RxExtensions;->subscribeWithSubject(Lio/reactivex/rxjava3/core/Observable;Lio/reactivex/rxjava3/subjects/Subject;Lio/reactivex/rxjava3/disposables/CompositeDisposable;)Lio/reactivex/rxjava3/subjects/Subject; HSPLorg/signal/core/util/concurrent/SignalExecutors$$ExternalSyntheticLambda0;->()V -HSPLorg/signal/core/util/concurrent/SignalExecutors$$ExternalSyntheticLambda0;->rejectedExecution(Ljava/lang/Runnable;Ljava/util/concurrent/ThreadPoolExecutor;)V HSPLorg/signal/core/util/concurrent/SignalExecutors$$ExternalSyntheticLambda1;->(Ljava/lang/String;I)V HSPLorg/signal/core/util/concurrent/SignalExecutors$$ExternalSyntheticLambda1;->newThread(Ljava/lang/Runnable;)Ljava/lang/Thread; HSPLorg/signal/core/util/concurrent/SignalExecutors$1;->(Ljava/lang/Runnable;Ljava/lang/String;I)V @@ -19393,11 +19302,9 @@ HSPLorg/signal/core/util/concurrent/SignalExecutors$NumberedThreadFactory$1;->ru HSPLorg/signal/core/util/concurrent/SignalExecutors$NumberedThreadFactory;->-$$Nest$fgetpriority(Lorg/signal/core/util/concurrent/SignalExecutors$NumberedThreadFactory;)I HSPLorg/signal/core/util/concurrent/SignalExecutors$NumberedThreadFactory;->(Ljava/lang/String;I)V HSPLorg/signal/core/util/concurrent/SignalExecutors$NumberedThreadFactory;->newThread(Ljava/lang/Runnable;)Ljava/lang/Thread; -HSPLorg/signal/core/util/concurrent/SignalExecutors;->$r8$lambda$0Q0afsv1raKIrq3aP-SuMcT2Ad0(Ljava/lang/Runnable;Ljava/util/concurrent/ThreadPoolExecutor;)V HSPLorg/signal/core/util/concurrent/SignalExecutors;->$r8$lambda$QcfzSx3VRxairlCydtFL9hAJp4M(Ljava/lang/String;ILjava/lang/Runnable;)Ljava/lang/Thread; HSPLorg/signal/core/util/concurrent/SignalExecutors;->()V HSPLorg/signal/core/util/concurrent/SignalExecutors;->getAndStartHandlerThread(Ljava/lang/String;I)Landroid/os/HandlerThread; -HSPLorg/signal/core/util/concurrent/SignalExecutors;->lambda$newCachedBoundedExecutor$1(Ljava/lang/Runnable;Ljava/util/concurrent/ThreadPoolExecutor;)V HSPLorg/signal/core/util/concurrent/SignalExecutors;->lambda$newCachedSingleThreadExecutor$0(Ljava/lang/String;ILjava/lang/Runnable;)Ljava/lang/Thread; HSPLorg/signal/core/util/concurrent/SignalExecutors;->newCachedBoundedExecutor(Ljava/lang/String;IIII)Ljava/util/concurrent/ExecutorService; HSPLorg/signal/core/util/concurrent/SignalExecutors;->newCachedSingleThreadExecutor(Ljava/lang/String;I)Ljava/util/concurrent/ExecutorService; @@ -19432,8 +19339,6 @@ HSPLorg/signal/core/util/logging/CompoundLogger;->flush()V HSPLorg/signal/core/util/logging/CompoundLogger;->i(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;Z)V HSPLorg/signal/core/util/logging/CompoundLogger;->w(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;Z)V HSPLorg/signal/core/util/logging/Log$Logger;->()V -HSPLorg/signal/core/util/logging/Log$Logger;->i(Ljava/lang/String;Ljava/lang/String;)V -HSPLorg/signal/core/util/logging/Log$Logger;->i(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)V HSPLorg/signal/core/util/logging/Log;->()V HSPLorg/signal/core/util/logging/Log;->()V HSPLorg/signal/core/util/logging/Log;->blockUntilAllWritesFinished()V @@ -19445,7 +19350,6 @@ HSPLorg/signal/core/util/logging/Log;->i(Ljava/lang/String;Ljava/lang/String;Lja HSPLorg/signal/core/util/logging/Log;->i(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;Z)V HSPLorg/signal/core/util/logging/Log;->i(Ljava/lang/String;Ljava/lang/String;Z)V HSPLorg/signal/core/util/logging/Log;->initialize(Lorg/signal/core/util/logging/Log$InternalCheck;[Lorg/signal/core/util/logging/Log$Logger;)V -HSPLorg/signal/core/util/logging/Log;->internal()Lorg/signal/core/util/logging/Log$Logger; HSPLorg/signal/core/util/logging/Log;->tag(Ljava/lang/Class;)Ljava/lang/String; HSPLorg/signal/core/util/logging/Log;->w(Ljava/lang/String;Ljava/lang/String;)V HSPLorg/signal/core/util/logging/Log;->w(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)V @@ -19453,6 +19357,8 @@ HSPLorg/signal/core/util/logging/Log;->w(Ljava/lang/String;Ljava/lang/String;Lja HSPLorg/signal/core/util/logging/Log;->w(Ljava/lang/String;Ljava/lang/Throwable;)V HSPLorg/signal/core/util/logging/NoopLogger;->()V HSPLorg/signal/core/util/logging/NoopLogger;->i(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;Z)V +HSPLorg/signal/core/util/logging/Scrubber$identifierHmacKeyProvider$1;->()V +HSPLorg/signal/core/util/logging/Scrubber$identifierHmacKeyProvider$1;->()V HSPLorg/signal/core/util/logging/Scrubber$scrubCallLinkKeys$1;->()V HSPLorg/signal/core/util/logging/Scrubber$scrubCallLinkKeys$1;->()V HSPLorg/signal/core/util/logging/Scrubber$scrubDomains$1;->()V @@ -19461,6 +19367,10 @@ HSPLorg/signal/core/util/logging/Scrubber$scrubDomains$1;->invoke(Ljava/lang/Obj HSPLorg/signal/core/util/logging/Scrubber$scrubDomains$1;->invoke(Ljava/util/regex/Matcher;Ljava/lang/StringBuilder;)V HSPLorg/signal/core/util/logging/Scrubber$scrubE164$1;->()V HSPLorg/signal/core/util/logging/Scrubber$scrubE164$1;->()V +HSPLorg/signal/core/util/logging/Scrubber$scrubE164$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/signal/core/util/logging/Scrubber$scrubE164$1;->invoke(Ljava/util/regex/Matcher;Ljava/lang/StringBuilder;)V +HSPLorg/signal/core/util/logging/Scrubber$scrubE164Zero$1;->()V +HSPLorg/signal/core/util/logging/Scrubber$scrubE164Zero$1;->()V HSPLorg/signal/core/util/logging/Scrubber$scrubEmail$1;->()V HSPLorg/signal/core/util/logging/Scrubber$scrubEmail$1;->()V HSPLorg/signal/core/util/logging/Scrubber$scrubGroupsV1$1;->()V @@ -19471,6 +19381,10 @@ HSPLorg/signal/core/util/logging/Scrubber$scrubIpv4$1;->()V HSPLorg/signal/core/util/logging/Scrubber$scrubIpv4$1;->()V HSPLorg/signal/core/util/logging/Scrubber$scrubIpv6$1;->()V HSPLorg/signal/core/util/logging/Scrubber$scrubIpv6$1;->()V +HSPLorg/signal/core/util/logging/Scrubber$scrubPnis$1;->()V +HSPLorg/signal/core/util/logging/Scrubber$scrubPnis$1;->()V +HSPLorg/signal/core/util/logging/Scrubber$scrubPnis$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/signal/core/util/logging/Scrubber$scrubPnis$1;->invoke(Ljava/util/regex/Matcher;Ljava/lang/StringBuilder;)V HSPLorg/signal/core/util/logging/Scrubber$scrubUuids$1;->()V HSPLorg/signal/core/util/logging/Scrubber$scrubUuids$1;->()V HSPLorg/signal/core/util/logging/Scrubber$scrubUuids$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; @@ -19478,19 +19392,24 @@ HSPLorg/signal/core/util/logging/Scrubber$scrubUuids$1;->invoke(Ljava/util/regex HSPLorg/signal/core/util/logging/Scrubber;->()V HSPLorg/signal/core/util/logging/Scrubber;->()V HSPLorg/signal/core/util/logging/Scrubber;->access$getTOP_100_TLDS$p()Ljava/util/Set; +HSPLorg/signal/core/util/logging/Scrubber;->access$hash(Lorg/signal/core/util/logging/Scrubber;Ljava/lang/String;)Ljava/lang/String; +HSPLorg/signal/core/util/logging/Scrubber;->hash(Ljava/lang/String;)Ljava/lang/String; HSPLorg/signal/core/util/logging/Scrubber;->scrub(Ljava/lang/CharSequence;)Ljava/lang/CharSequence; HSPLorg/signal/core/util/logging/Scrubber;->scrub(Ljava/lang/CharSequence;Ljava/util/regex/Pattern;Lkotlin/jvm/functions/Function2;)Ljava/lang/CharSequence; HSPLorg/signal/core/util/logging/Scrubber;->scrubCallLinkKeys(Ljava/lang/CharSequence;)Ljava/lang/CharSequence; HSPLorg/signal/core/util/logging/Scrubber;->scrubDomains(Ljava/lang/CharSequence;)Ljava/lang/CharSequence; HSPLorg/signal/core/util/logging/Scrubber;->scrubE164(Ljava/lang/CharSequence;)Ljava/lang/CharSequence; +HSPLorg/signal/core/util/logging/Scrubber;->scrubE164Zero(Ljava/lang/CharSequence;)Ljava/lang/CharSequence; HSPLorg/signal/core/util/logging/Scrubber;->scrubEmail(Ljava/lang/CharSequence;)Ljava/lang/CharSequence; HSPLorg/signal/core/util/logging/Scrubber;->scrubGroupsV1(Ljava/lang/CharSequence;)Ljava/lang/CharSequence; HSPLorg/signal/core/util/logging/Scrubber;->scrubGroupsV2(Ljava/lang/CharSequence;)Ljava/lang/CharSequence; HSPLorg/signal/core/util/logging/Scrubber;->scrubIpv4(Ljava/lang/CharSequence;)Ljava/lang/CharSequence; HSPLorg/signal/core/util/logging/Scrubber;->scrubIpv6(Ljava/lang/CharSequence;)Ljava/lang/CharSequence; +HSPLorg/signal/core/util/logging/Scrubber;->scrubPnis(Ljava/lang/CharSequence;)Ljava/lang/CharSequence; HSPLorg/signal/core/util/logging/Scrubber;->scrubUuids(Ljava/lang/CharSequence;)Ljava/lang/CharSequence; +HSPLorg/signal/core/util/logging/Scrubber;->setIdentifierHmacKeyProvider(Lkotlin/jvm/functions/Function0;)V +HSPLorg/signal/core/util/stream/TruncatingInputStream$$ExternalSyntheticBackport0;->m(J)I HSPLorg/signal/core/util/tracing/DebugAnnotation$Builder;->()V -HSPLorg/signal/core/util/tracing/DebugAnnotation$Builder;->build()Lorg/signal/core/util/tracing/DebugAnnotation; HSPLorg/signal/core/util/tracing/DebugAnnotation$Builder;->name(Ljava/lang/String;)Lorg/signal/core/util/tracing/DebugAnnotation$Builder; HSPLorg/signal/core/util/tracing/DebugAnnotation$Builder;->string_value(Ljava/lang/String;)Lorg/signal/core/util/tracing/DebugAnnotation$Builder; HSPLorg/signal/core/util/tracing/DebugAnnotation$Companion$ADAPTER$1;->(Lcom/squareup/wire/FieldEncoding;Lkotlin/reflect/KClass;Lcom/squareup/wire/Syntax;)V @@ -19500,7 +19419,6 @@ HSPLorg/signal/core/util/tracing/DebugAnnotation;->()V HSPLorg/signal/core/util/tracing/DebugAnnotation;->(Ljava/lang/Long;Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/Double;Ljava/lang/String;Ljava/lang/Long;Lorg/signal/core/util/tracing/DebugAnnotation$NestedValue;Lokio/ByteString;)V HSPLorg/signal/core/util/tracing/DebugAnnotation;->equals(Ljava/lang/Object;)Z HSPLorg/signal/core/util/tracing/TracePacket$Builder;->()V -HSPLorg/signal/core/util/tracing/TracePacket$Builder;->build()Lorg/signal/core/util/tracing/TracePacket; HSPLorg/signal/core/util/tracing/TracePacket$Builder;->synchronization_marker(Lokio/ByteString;)Lorg/signal/core/util/tracing/TracePacket$Builder; HSPLorg/signal/core/util/tracing/TracePacket$Builder;->timestamp(Ljava/lang/Long;)Lorg/signal/core/util/tracing/TracePacket$Builder; HSPLorg/signal/core/util/tracing/TracePacket$Builder;->track_descriptor(Lorg/signal/core/util/tracing/TrackDescriptor;)Lorg/signal/core/util/tracing/TracePacket$Builder; @@ -19516,15 +19434,15 @@ HSPLorg/signal/core/util/tracing/Tracer$$ExternalSyntheticLambda0;->getTimeNanos HSPLorg/signal/core/util/tracing/Tracer;->()V HSPLorg/signal/core/util/tracing/Tracer;->()V HSPLorg/signal/core/util/tracing/Tracer;->addPacket(Lorg/signal/core/util/tracing/TracePacket;)V +HSPLorg/signal/core/util/tracing/Tracer;->debugAnnotation(Ljava/lang/String;Ljava/lang/String;)Lorg/signal/core/util/tracing/DebugAnnotation; +HSPLorg/signal/core/util/tracing/Tracer;->end(Ljava/lang/String;)V HSPLorg/signal/core/util/tracing/Tracer;->end(Ljava/lang/String;J)V -HSPLorg/signal/core/util/tracing/Tracer;->forMethodEnd(Ljava/lang/String;JJ)Lorg/signal/core/util/tracing/TracePacket; HSPLorg/signal/core/util/tracing/Tracer;->forSynchronization(J)Lorg/signal/core/util/tracing/TracePacket; HSPLorg/signal/core/util/tracing/Tracer;->forTrack(JLjava/lang/String;)Lorg/signal/core/util/tracing/TracePacket; HSPLorg/signal/core/util/tracing/Tracer;->forTrackId(J)Lorg/signal/core/util/tracing/TracePacket; HSPLorg/signal/core/util/tracing/Tracer;->getInstance()Lorg/signal/core/util/tracing/Tracer; HSPLorg/signal/core/util/tracing/Tracer;->start(Ljava/lang/String;)V HSPLorg/signal/core/util/tracing/Tracer;->start(Ljava/lang/String;JLjava/lang/String;Ljava/lang/String;)V -HSPLorg/signal/core/util/tracing/Tracer;->start(Ljava/lang/String;JLjava/util/Map;)V HSPLorg/signal/core/util/tracing/Tracer;->start(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V HSPLorg/signal/core/util/tracing/Tracer;->start(Ljava/lang/String;Ljava/util/Map;)V HSPLorg/signal/core/util/tracing/Tracer;->toByteArray(Ljava/util/UUID;)[B @@ -19538,7 +19456,6 @@ HSPLorg/signal/core/util/tracing/TrackDescriptor$Companion;->(Lkotlin/jvm/ HSPLorg/signal/core/util/tracing/TrackDescriptor;->()V HSPLorg/signal/core/util/tracing/TrackDescriptor;->(Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/String;Lorg/signal/core/util/tracing/ThreadDescriptor;Lorg/signal/core/util/tracing/CounterDescriptor;Lokio/ByteString;)V HSPLorg/signal/core/util/tracing/TrackEvent$Builder;->()V -HSPLorg/signal/core/util/tracing/TrackEvent$Builder;->build()Lorg/signal/core/util/tracing/TrackEvent; HSPLorg/signal/core/util/tracing/TrackEvent$Builder;->debug_annotations(Ljava/util/List;)Lorg/signal/core/util/tracing/TrackEvent$Builder; HSPLorg/signal/core/util/tracing/TrackEvent$Builder;->name(Ljava/lang/String;)Lorg/signal/core/util/tracing/TrackEvent$Builder; HSPLorg/signal/core/util/tracing/TrackEvent$Builder;->track_uuid(Ljava/lang/Long;)Lorg/signal/core/util/tracing/TrackEvent$Builder; @@ -19589,7 +19506,6 @@ HSPLorg/signal/libsignal/protocol/ServiceId;->getRawUUID()Ljava/util/UUID; HSPLorg/signal/libsignal/protocol/ServiceId;->hashCode()I HSPLorg/signal/libsignal/protocol/ServiceId;->parseFromFixedWidthBinary([B)Lorg/signal/libsignal/protocol/ServiceId; HSPLorg/signal/libsignal/protocol/ServiceId;->parseFromString(Ljava/lang/String;)Lorg/signal/libsignal/protocol/ServiceId; -HSPLorg/signal/libsignal/protocol/ServiceId;->toServiceIdFixedWidthBinary()[B HSPLorg/signal/libsignal/protocol/ServiceId;->toServiceIdString()Ljava/lang/String; HSPLorg/signal/libsignal/protocol/ServiceId;->uuidFromBytes(Ljava/nio/ByteBuffer;)Ljava/util/UUID; HSPLorg/signal/libsignal/protocol/SignalProtocolAddress;->(Ljava/lang/String;I)V @@ -19649,7 +19565,6 @@ HSPLorg/signal/libsignal/protocol/state/SignedPreKeyRecord;->getKeyPair()Lorg/si HSPLorg/signal/libsignal/protocol/state/SignedPreKeyRecord;->getSignature()[B HSPLorg/signal/libsignal/protocol/state/SignedPreKeyRecord;->getTimestamp()J HSPLorg/signal/libsignal/protocol/state/SignedPreKeyRecord;->unsafeNativeHandleWithoutGuard()J -HSPLorg/signal/libsignal/protocol/util/ByteUtil;->trim([BI)[B HSPLorg/signal/libsignal/protocol/util/KeyHelper;->generateRegistrationId(Z)I HSPLorg/signal/libsignal/protocol/util/Medium;->()V HSPLorg/signal/libsignal/protocol/util/Pair;->(Ljava/lang/Object;Ljava/lang/Object;)V @@ -19661,25 +19576,17 @@ HSPLorg/signal/libsignal/zkgroup/internal/ByteArray;->([B)V HSPLorg/signal/libsignal/zkgroup/internal/ByteArray;->serialize()[B HSPLorg/signal/libsignal/zkgroup/profiles/ClientZkProfileOperations;->(Lorg/signal/libsignal/zkgroup/ServerPublicParams;)V HSPLorg/signal/libsignal/zkgroup/profiles/ProfileKey;->([B)V -HSPLorg/signal/libsignal/zkgroup/profiles/ProfileKey;->getProfileKeyVersion(Lorg/signal/libsignal/protocol/ServiceId$Aci;)Lorg/signal/libsignal/zkgroup/profiles/ProfileKeyVersion; -HSPLorg/signal/libsignal/zkgroup/profiles/ProfileKeyVersion;->([B)V -HSPLorg/signal/libsignal/zkgroup/profiles/ProfileKeyVersion;->serialize()Ljava/lang/String; HSPLorg/signal/libsignal/zkgroup/receipts/ClientZkReceiptOperations;->(Lorg/signal/libsignal/zkgroup/ServerPublicParams;)V HSPLorg/signal/paging/BufferedPagingController$$ExternalSyntheticLambda1;->(Lorg/signal/paging/BufferedPagingController;I)V HSPLorg/signal/paging/BufferedPagingController$$ExternalSyntheticLambda1;->run()V -HSPLorg/signal/paging/BufferedPagingController$$ExternalSyntheticLambda2;->(Lorg/signal/paging/BufferedPagingController;Ljava/lang/Object;)V -HSPLorg/signal/paging/BufferedPagingController$$ExternalSyntheticLambda2;->run()V HSPLorg/signal/paging/BufferedPagingController$$ExternalSyntheticLambda3;->(Lorg/signal/paging/BufferedPagingController;)V HSPLorg/signal/paging/BufferedPagingController$$ExternalSyntheticLambda3;->run()V -HSPLorg/signal/paging/BufferedPagingController;->$r8$lambda$GxlLAxjfERBgyqmyvxteAPWaQkA(Lorg/signal/paging/BufferedPagingController;Ljava/lang/Object;)V HSPLorg/signal/paging/BufferedPagingController;->$r8$lambda$LZo0__w5_DH31X-e0IJhSqYlhmM(Lorg/signal/paging/BufferedPagingController;I)V HSPLorg/signal/paging/BufferedPagingController;->$r8$lambda$rEsvvMe_LeaFyq_h5sQ9KWDvIFs(Lorg/signal/paging/BufferedPagingController;)V HSPLorg/signal/paging/BufferedPagingController;->(Lorg/signal/paging/PagedDataSource;Lorg/signal/paging/PagingConfig;Lorg/signal/paging/DataStream;)V HSPLorg/signal/paging/BufferedPagingController;->lambda$onDataInvalidated$1()V -HSPLorg/signal/paging/BufferedPagingController;->lambda$onDataItemChanged$2(Ljava/lang/Object;)V HSPLorg/signal/paging/BufferedPagingController;->lambda$onDataNeededAroundIndex$0(I)V HSPLorg/signal/paging/BufferedPagingController;->onDataInvalidated()V -HSPLorg/signal/paging/BufferedPagingController;->onDataItemChanged(Ljava/lang/Object;)V HSPLorg/signal/paging/BufferedPagingController;->onDataNeededAroundIndex(I)V HSPLorg/signal/paging/CompressedList;->(I)V HSPLorg/signal/paging/CompressedList;->(Ljava/util/List;)V @@ -19690,27 +19597,22 @@ HSPLorg/signal/paging/DataStatus;->()V HSPLorg/signal/paging/DataStatus;->(ILjava/util/BitSet;)V HSPLorg/signal/paging/DataStatus;->getEarliestUnmarkedIndexInRange(II)I HSPLorg/signal/paging/DataStatus;->getLatestUnmarkedIndexInRange(II)I -HSPLorg/signal/paging/DataStatus;->mark(I)V HSPLorg/signal/paging/DataStatus;->markRange(II)V HSPLorg/signal/paging/DataStatus;->obtain(I)Lorg/signal/paging/DataStatus; HSPLorg/signal/paging/DataStatus;->recycle()V HSPLorg/signal/paging/DataStatus;->size()I -HSPLorg/signal/paging/FixedSizePagingController$$ExternalSyntheticLambda0;->(Lorg/signal/paging/FixedSizePagingController;Ljava/lang/Object;)V -HSPLorg/signal/paging/FixedSizePagingController$$ExternalSyntheticLambda0;->run()V HSPLorg/signal/paging/FixedSizePagingController$$ExternalSyntheticLambda2;->(Lorg/signal/paging/FixedSizePagingController;IIII)V HSPLorg/signal/paging/FixedSizePagingController$$ExternalSyntheticLambda2;->run()V HSPLorg/signal/paging/FixedSizePagingController$$ExternalSyntheticLambda3;->(Lorg/signal/paging/FixedSizePagingController;)V HSPLorg/signal/paging/FixedSizePagingController$$ExternalSyntheticLambda3;->isCanceled()Z -HSPLorg/signal/paging/FixedSizePagingController;->$r8$lambda$2jZFFAhs3dG0IThMmzJQSvWvcd0(Lorg/signal/paging/FixedSizePagingController;Ljava/lang/Object;)V HSPLorg/signal/paging/FixedSizePagingController;->$r8$lambda$S1N9oMReIFywjAgkTfXskOOFzyk(Lorg/signal/paging/FixedSizePagingController;)Z HSPLorg/signal/paging/FixedSizePagingController;->$r8$lambda$pQWvrV6w7QQq3SnkCgnHNDTtP_I(Lorg/signal/paging/FixedSizePagingController;IIII)V HSPLorg/signal/paging/FixedSizePagingController;->()V HSPLorg/signal/paging/FixedSizePagingController;->(Lorg/signal/paging/PagedDataSource;Lorg/signal/paging/PagingConfig;Lorg/signal/paging/DataStream;I)V -HSPLorg/signal/paging/FixedSizePagingController;->lambda$onDataItemChanged$2(Ljava/lang/Object;)V +HSPLorg/signal/paging/FixedSizePagingController;->buildDataNeededLog(ILjava/lang/String;)Ljava/lang/String; HSPLorg/signal/paging/FixedSizePagingController;->lambda$onDataNeededAroundIndex$0()Z HSPLorg/signal/paging/FixedSizePagingController;->lambda$onDataNeededAroundIndex$1(IIII)V HSPLorg/signal/paging/FixedSizePagingController;->onDataInvalidated()V -HSPLorg/signal/paging/FixedSizePagingController;->onDataItemChanged(Ljava/lang/Object;)V HSPLorg/signal/paging/FixedSizePagingController;->onDataNeededAroundIndex(I)V HSPLorg/signal/paging/LivePagedData;->(Landroidx/lifecycle/LiveData;Lorg/signal/paging/PagingController;)V HSPLorg/signal/paging/LivePagedData;->getData()Landroidx/lifecycle/LiveData; @@ -19739,7 +19641,6 @@ HSPLorg/signal/paging/PagingConfig;->pageSize()I HSPLorg/signal/paging/PagingConfig;->startIndex()I HSPLorg/signal/paging/ProxyPagingController;->()V HSPLorg/signal/paging/ProxyPagingController;->onDataInvalidated()V -HSPLorg/signal/paging/ProxyPagingController;->onDataItemChanged(Ljava/lang/Object;)V HSPLorg/signal/paging/ProxyPagingController;->onDataNeededAroundIndex(I)V HSPLorg/signal/paging/ProxyPagingController;->set(Lorg/signal/paging/PagingController;)V HSPLorg/signal/ringrtc/BuildInfo;->()V @@ -19756,14 +19657,11 @@ HSPLorg/signal/ringrtc/Log;->i(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Th HSPLorg/signal/ringrtc/Log;->initialize(Lorg/signal/ringrtc/Log$Logger;)V HSPLorg/signal/ringrtc/WebRtcLogger;->()V HSPLorg/signal/ringrtc/WebRtcLogger;->()V -HSPLorg/thoughtcrime/securesms/AppCapabilities;->()V -HSPLorg/thoughtcrime/securesms/AppCapabilities;->()V -HSPLorg/thoughtcrime/securesms/AppCapabilities;->getCapabilities(Z)Lorg/whispersystems/signalservice/api/account/AccountAttributes$Capabilities; -HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V -HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda0;->run()V -HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda10;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V +HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda0;->()V +HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda0;->invoke()Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda10;->()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda10;->run()V -HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda11;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V +HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda11;->()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda11;->run()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda12;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda12;->run()V @@ -19775,13 +19673,13 @@ HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda15;->< HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda15;->run()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda16;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda16;->run()V -HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda17;->()V +HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda17;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda17;->run()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda18;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda18;->run()V -HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda19;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V +HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda19;->()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda19;->run()V -HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda1;->()V +HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda1;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda1;->run()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda20;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda20;->run()V @@ -19789,25 +19687,25 @@ HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda21;->< HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda21;->run()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda22;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda22;->run()V -HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda23;->()V +HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda23;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda23;->run()V -HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda24;->()V +HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda24;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda24;->run()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda25;->()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda25;->run()V -HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda26;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V +HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda26;->()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda26;->run()V -HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda27;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V +HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda27;->()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda27;->run()V -HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda28;->()V +HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda28;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda28;->run()V -HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda29;->()V +HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda29;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda29;->run()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda2;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda2;->run()V -HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda30;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V +HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda30;->()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda30;->run()V -HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda31;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V +HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda31;->()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda31;->run()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda32;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda32;->run()V @@ -19815,35 +19713,35 @@ HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda33;->< HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda33;->run()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda34;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda34;->run()V -HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda35;->()V +HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda35;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda35;->run()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda36;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda36;->run()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda37;->()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda37;->run()V -HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda38;->()V +HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda38;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda38;->run()V -HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda39;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V +HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda39;->()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda39;->run()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda3;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda3;->run()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda40;->()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda40;->run()V -HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda41;->()V +HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda41;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda41;->run()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda42;->()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda42;->run()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda43;->()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda43;->run()V -HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda44;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V +HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda44;->()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda44;->run()V -HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda45;->()V +HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda45;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda45;->run()V -HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda46;->(Lorg/thoughtcrime/securesms/jobs/StoryOnboardingDownloadJob$Companion;)V +HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda46;->()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda46;->run()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda47;->()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda47;->run()V -HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda48;->()V +HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda48;->(Lorg/thoughtcrime/securesms/jobs/StoryOnboardingDownloadJob$Companion;)V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda48;->run()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda49;->()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda49;->run()V @@ -19851,86 +19749,93 @@ HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda4;->run()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda50;->()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda50;->run()V -HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda51;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V +HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda51;->()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda51;->run()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda52;->()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda52;->run()V -HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda53;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V +HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda53;->()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda53;->run()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda54;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda54;->run()V -HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda55;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V +HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda55;->()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda55;->run()V -HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda56;->()V -HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda56;->isInternal()Z -HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda57;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V +HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda56;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V +HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda56;->run()V +HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda57;->()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda57;->run()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda58;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda58;->run()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda59;->()V -HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda5;->()V +HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda5;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda5;->run()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda60;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V -HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda61;->()V -HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda61;->apply(Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda60;->run()V +HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda61;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V +HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda61;->run()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda62;->()V -HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda62;->apply(Ljava/lang/Object;)Ljava/lang/Object; -HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda63;->()V -HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda6;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V +HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda63;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V +HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda64;->()V +HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda64;->apply(Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda65;->()V +HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda65;->apply(Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda66;->()V +HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda6;->()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda6;->run()V -HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda7;->()V +HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda7;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda7;->run()V -HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda8;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V +HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda8;->()V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda8;->run()V -HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda9;->()V +HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda9;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda9;->run()V HSPLorg/thoughtcrime/securesms/ApplicationContext$1;->(Lorg/thoughtcrime/securesms/ApplicationContext;)V -HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$-C9Peb3FgJUzS6NSCmy4GbYf_TI(Lio/reactivex/rxjava3/functions/Supplier;)Lio/reactivex/rxjava3/core/Scheduler; HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$-Us13jCXTd2iOM0Y6BraEZ1Ael8()V HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$-wHBiLBDa8OJf1k67fcIsduxfzw(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$1vBGBwM2yUq7VyFaTDG10uWDuPc(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$3QS0qrAN7nliLfSi63lQeukrNXw(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$66l9coKQ_660ADutlicH4GeEP6I(Lorg/thoughtcrime/securesms/ApplicationContext;)V -HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$6C1lVL7dG_-wAqmFY6Az_Qc88wU()V HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$6_YWJVxHZo1viw16tbrU7MDbDJg(Lorg/thoughtcrime/securesms/ApplicationContext;)V +HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$6huj-CNiUCq08incBeS5_VdKXcY(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$6jOUg8y8V8AjFQVDC73SAPGSCaQ(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$74GuULvxFktNDe6rV-VHuIqPVJg(Lorg/thoughtcrime/securesms/ApplicationContext;)V +HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$7zPKdwtEmk03-CX6PmLruPNQaJU()V +HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$8LAgI8pE7DigK3aYaz2Nk9lcfrc(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$AGPtfUzlxykaeQb2_And4XR5_cI()V HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$BM9zXNQo3VjL7z8V2jLaBOSk3xM(Lorg/thoughtcrime/securesms/ApplicationContext;)V +HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$C38kbsmhoy45H7BvcAkMdNLosx8(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$CadNTUODZG2Ef63Ub7A1il6CBD0(Lorg/thoughtcrime/securesms/ApplicationContext;)V -HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$DG-_eyh8CcGxwAk-palhZ1OT2O0(Lorg/thoughtcrime/securesms/ApplicationContext;)V -HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$Fx-d1Nr9YoqUcyvu6Ylhfae3_S4(Lio/reactivex/rxjava3/functions/Supplier;)Lio/reactivex/rxjava3/core/Scheduler; -HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$IhFCWemzVRbrwoHW9Ns1V3CCNsI(Lorg/thoughtcrime/securesms/ApplicationContext;)V +HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$EhXD2bA_njJxvcXV3OctyCch4wQ()V +HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$Euao8MtJ5sFx1Uzh_MqEQ-p_wco()V +HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$GzI-Jr48663DXUZ6ABZXYlOQuSQ(Lorg/thoughtcrime/securesms/ApplicationContext;)V +HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$HAbYs3d3-yBwCTZTjClId9asWMo(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$JFFOXPLm4n83iu1CrQCwt8Rt1vs(Lorg/thoughtcrime/securesms/ApplicationContext;)V -HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$MEJyjPRiOVBusJTwA48ER4J3M9A(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$QTEjfsRccjaeTw0od8y5UGofLnw()V -HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$RIVst1BPIo-PfHpYnrmNZ6n5IC4()V +HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$QoPhFlm0XNA9Pq0eqhx6SpS3Yf8(Lio/reactivex/rxjava3/functions/Supplier;)Lio/reactivex/rxjava3/core/Scheduler; HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$RhzI01M6WPCcwVqZ9bQRQL3rEFk(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$VPmvaBBdqqvvVsnAcF1XPhfNAwg(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$Wc-lTVXx_Cj3iM16JtR6Ju_gG5s(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$WhOpCGkNoE806tpRKQr4cnlCHgs()V HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$XEBpgM6g0m715OxGVQde5BV4obs()V HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$YPJbusS4_A6ryTaa4uhXIPJboWA(Lorg/thoughtcrime/securesms/ApplicationContext;)V +HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$Ziq45AtHm7EBURq_Andgbc-2PQA(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$avxGAGkfv3Ev4XZkSUGrF0ov8Xw(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$bErCCzC-UcRA-wciaIM0aFj_fYc(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$cQnTp954Na4k9YavnTCgv-5QUVQ()V -HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$d1IiCBeJCM6wI7G1fz_gXkhXmmY()V HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$dXZkn_yX1EofCAblV1XQ5RabFZk(Lorg/thoughtcrime/securesms/ApplicationContext;)V -HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$gAiTx-utj3eJij4oz9BiVGkOVS4(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$gsuXSdfhwRFZebO53F99f8IHmAg(Lorg/thoughtcrime/securesms/ApplicationContext;)V +HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$idYYlPtuECvmRBdMPMIWbD5a3kU()V HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$j9SYVZNI3aEbrOx6jxBWYL046uc()V +HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$kC2GAeM9RoAdu5aL5vjepb8NRt4()V HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$ltM0LNCO0IpxItflkQs1-OW5yWo(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$nCL1qFe_6GUmC6LRVpbNvqPnxzs(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$nYPlff79P2l-muLhW8EMuKgR50w(Lorg/thoughtcrime/securesms/ApplicationContext;)V +HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$oBwElCRwdRH0K-dD4FNR2zBIlik()V HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$ot12xp6xNVtSSQ4GK9tV7IySHoM(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$ruqqX1Z0LLWOgajy7Wtjs5N3iks(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$uRzpmm1efLVUhJMEHNtyfP7BiA8(Lorg/thoughtcrime/securesms/ApplicationContext;)V HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$vq13xoFA2KFXFtE5Jlx3i42fU-8(Lorg/thoughtcrime/securesms/ApplicationContext;)V -HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$wAw2Kp5XlAPghxmIdIo41QzCx9g(Lorg/thoughtcrime/securesms/ApplicationContext;)V -HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$wIlBIGBTTiiajQBAE1x8xR74OLI()V -HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$xQ9ZfcE66PEynu9uNL9k6bEaLXs(Lorg/thoughtcrime/securesms/ApplicationContext;)V -HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$xsfc4Wa_kVMkmyR3F3728Xm6Sz0()V +HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$y2wfEuKgd8zQpNdKJyo7v3d1ZLs(Lio/reactivex/rxjava3/functions/Supplier;)Lio/reactivex/rxjava3/core/Scheduler; HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$zLJvo83whBob1hm-1YvxiH4EKqg(Lorg/thoughtcrime/securesms/ApplicationContext;)V +HSPLorg/thoughtcrime/securesms/ApplicationContext;->$r8$lambda$zNA2sxapNn8tnXiST4flFZyy97A()[B HSPLorg/thoughtcrime/securesms/ApplicationContext;->()V HSPLorg/thoughtcrime/securesms/ApplicationContext;->()V HSPLorg/thoughtcrime/securesms/ApplicationContext;->attachBaseContext(Landroid/content/Context;)V @@ -19958,9 +19863,9 @@ HSPLorg/thoughtcrime/securesms/ApplicationContext;->initializeRx()V HSPLorg/thoughtcrime/securesms/ApplicationContext;->initializeScheduledMessageManager()V HSPLorg/thoughtcrime/securesms/ApplicationContext;->initializeSecurityProvider()V HSPLorg/thoughtcrime/securesms/ApplicationContext;->initializeTrimThreadsByDateManager()V -HSPLorg/thoughtcrime/securesms/ApplicationContext;->lambda$initializeLogging$24()V -HSPLorg/thoughtcrime/securesms/ApplicationContext;->lambda$initializeRx$25(Lio/reactivex/rxjava3/functions/Supplier;)Lio/reactivex/rxjava3/core/Scheduler; -HSPLorg/thoughtcrime/securesms/ApplicationContext;->lambda$initializeRx$26(Lio/reactivex/rxjava3/functions/Supplier;)Lio/reactivex/rxjava3/core/Scheduler; +HSPLorg/thoughtcrime/securesms/ApplicationContext;->lambda$initializeLogging$26()V +HSPLorg/thoughtcrime/securesms/ApplicationContext;->lambda$initializeRx$27(Lio/reactivex/rxjava3/functions/Supplier;)Lio/reactivex/rxjava3/core/Scheduler; +HSPLorg/thoughtcrime/securesms/ApplicationContext;->lambda$initializeRx$28(Lio/reactivex/rxjava3/functions/Supplier;)Lio/reactivex/rxjava3/core/Scheduler; HSPLorg/thoughtcrime/securesms/ApplicationContext;->lambda$onCreate$0()V HSPLorg/thoughtcrime/securesms/ApplicationContext;->lambda$onCreate$1()V HSPLorg/thoughtcrime/securesms/ApplicationContext;->lambda$onCreate$10()V @@ -19976,14 +19881,16 @@ HSPLorg/thoughtcrime/securesms/ApplicationContext;->lambda$onCreate$19()V HSPLorg/thoughtcrime/securesms/ApplicationContext;->lambda$onCreate$2()V HSPLorg/thoughtcrime/securesms/ApplicationContext;->lambda$onCreate$20()V HSPLorg/thoughtcrime/securesms/ApplicationContext;->lambda$onCreate$21()V -HSPLorg/thoughtcrime/securesms/ApplicationContext;->lambda$onCreate$3()V +HSPLorg/thoughtcrime/securesms/ApplicationContext;->lambda$onCreate$22()V +HSPLorg/thoughtcrime/securesms/ApplicationContext;->lambda$onCreate$23()V +HSPLorg/thoughtcrime/securesms/ApplicationContext;->lambda$onCreate$3()[B HSPLorg/thoughtcrime/securesms/ApplicationContext;->lambda$onCreate$4()V HSPLorg/thoughtcrime/securesms/ApplicationContext;->lambda$onCreate$5()V HSPLorg/thoughtcrime/securesms/ApplicationContext;->lambda$onCreate$6()V HSPLorg/thoughtcrime/securesms/ApplicationContext;->lambda$onCreate$7()V HSPLorg/thoughtcrime/securesms/ApplicationContext;->lambda$onCreate$8()V HSPLorg/thoughtcrime/securesms/ApplicationContext;->lambda$onCreate$9()V -HSPLorg/thoughtcrime/securesms/ApplicationContext;->lambda$onForeground$22()V +HSPLorg/thoughtcrime/securesms/ApplicationContext;->lambda$onForeground$24()V HSPLorg/thoughtcrime/securesms/ApplicationContext;->onCreate()V HSPLorg/thoughtcrime/securesms/ApplicationContext;->onForeground()V HSPLorg/thoughtcrime/securesms/ApplicationContext;->startAnrDetector()V @@ -20005,6 +19912,7 @@ HSPLorg/thoughtcrime/securesms/LoggingFragment;->(I)V HSPLorg/thoughtcrime/securesms/LoggingFragment;->logEvent(Ljava/lang/String;)V HSPLorg/thoughtcrime/securesms/LoggingFragment;->onCreate(Landroid/os/Bundle;)V HSPLorg/thoughtcrime/securesms/LoggingFragment;->onStart()V +HSPLorg/thoughtcrime/securesms/LoggingFragment;->onStop()V HSPLorg/thoughtcrime/securesms/MainActivity$$ExternalSyntheticLambda2;->(Lorg/thoughtcrime/securesms/MainActivity;)V HSPLorg/thoughtcrime/securesms/MainActivity$$ExternalSyntheticLambda2;->accept(Ljava/lang/Object;)V HSPLorg/thoughtcrime/securesms/MainActivity$1;->(Lorg/thoughtcrime/securesms/MainActivity;Landroid/view/View;)V @@ -20017,6 +19925,8 @@ HSPLorg/thoughtcrime/securesms/MainActivity;->getIntent()Landroid/content/Intent HSPLorg/thoughtcrime/securesms/MainActivity;->getNavigator()Lorg/thoughtcrime/securesms/MainNavigator; HSPLorg/thoughtcrime/securesms/MainActivity;->getVoiceNoteMediaController()Lorg/thoughtcrime/securesms/components/voice/VoiceNoteMediaController; HSPLorg/thoughtcrime/securesms/MainActivity;->handleCallLinkInIntent(Landroid/content/Intent;)V +HSPLorg/thoughtcrime/securesms/MainActivity;->handleDeeplinkIntent(Landroid/content/Intent;)V +HSPLorg/thoughtcrime/securesms/MainActivity;->handleDonateReturnIntent(Landroid/content/Intent;)V HSPLorg/thoughtcrime/securesms/MainActivity;->handleGroupLinkInIntent(Landroid/content/Intent;)V HSPLorg/thoughtcrime/securesms/MainActivity;->handleProxyInIntent(Landroid/content/Intent;)V HSPLorg/thoughtcrime/securesms/MainActivity;->handleSignalMeIntent(Landroid/content/Intent;)V @@ -20024,6 +19934,7 @@ HSPLorg/thoughtcrime/securesms/MainActivity;->onCreate(Landroid/os/Bundle;Z)V HSPLorg/thoughtcrime/securesms/MainActivity;->onFirstRender()V HSPLorg/thoughtcrime/securesms/MainActivity;->onPreCreate()V HSPLorg/thoughtcrime/securesms/MainActivity;->onResume()V +HSPLorg/thoughtcrime/securesms/MainActivity;->onStop()V HSPLorg/thoughtcrime/securesms/MainActivity;->presentVitalsState(Lorg/thoughtcrime/securesms/notifications/VitalsViewModel$State;)V HSPLorg/thoughtcrime/securesms/MainActivity;->updateTabVisibility()V HSPLorg/thoughtcrime/securesms/MainFragment;->()V @@ -20056,56 +19967,36 @@ HSPLorg/thoughtcrime/securesms/PassphraseRequiredActivity;->userMustSetProfileNa HSPLorg/thoughtcrime/securesms/R$styleable;->()V HSPLorg/thoughtcrime/securesms/animation/AnimationCompleteListener;->()V HSPLorg/thoughtcrime/securesms/animation/AnimationCompleteListener;->onAnimationStart(Landroid/animation/Animator;)V +HSPLorg/thoughtcrime/securesms/attachments/Attachment$Companion;->()V +HSPLorg/thoughtcrime/securesms/attachments/Attachment$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/attachments/Attachment;->()V -HSPLorg/thoughtcrime/securesms/attachments/Attachment;->(Ljava/lang/String;IJLjava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;[B[BLjava/lang/String;ZZZIIIZJLjava/lang/String;Lorg/thoughtcrime/securesms/stickers/StickerLocator;Lorg/thoughtcrime/securesms/blurhash/BlurHash;Lorg/thoughtcrime/securesms/audio/AudioHash;Lorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties;)V -HSPLorg/thoughtcrime/securesms/attachments/Attachment;->getAudioHash()Lorg/thoughtcrime/securesms/audio/AudioHash; -HSPLorg/thoughtcrime/securesms/attachments/Attachment;->getBlurHash()Lorg/thoughtcrime/securesms/blurhash/BlurHash; -HSPLorg/thoughtcrime/securesms/attachments/Attachment;->getCaption()Ljava/lang/String; -HSPLorg/thoughtcrime/securesms/attachments/Attachment;->getCdnNumber()I -HSPLorg/thoughtcrime/securesms/attachments/Attachment;->getContentType()Ljava/lang/String; -HSPLorg/thoughtcrime/securesms/attachments/Attachment;->getDigest()[B -HSPLorg/thoughtcrime/securesms/attachments/Attachment;->getFastPreflightId()Ljava/lang/String; -HSPLorg/thoughtcrime/securesms/attachments/Attachment;->getFileName()Ljava/lang/String; -HSPLorg/thoughtcrime/securesms/attachments/Attachment;->getHeight()I -HSPLorg/thoughtcrime/securesms/attachments/Attachment;->getIncrementalDigest()[B -HSPLorg/thoughtcrime/securesms/attachments/Attachment;->getIncrementalMacChunkSize()I -HSPLorg/thoughtcrime/securesms/attachments/Attachment;->getKey()Ljava/lang/String; -HSPLorg/thoughtcrime/securesms/attachments/Attachment;->getLocation()Ljava/lang/String; -HSPLorg/thoughtcrime/securesms/attachments/Attachment;->getRelay()Ljava/lang/String; -HSPLorg/thoughtcrime/securesms/attachments/Attachment;->getSize()J -HSPLorg/thoughtcrime/securesms/attachments/Attachment;->getTransferState()I -HSPLorg/thoughtcrime/securesms/attachments/Attachment;->getTransformProperties()Lorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties; -HSPLorg/thoughtcrime/securesms/attachments/Attachment;->getUploadTimestamp()J -HSPLorg/thoughtcrime/securesms/attachments/Attachment;->getWidth()I -HSPLorg/thoughtcrime/securesms/attachments/Attachment;->isBorderless()Z +HSPLorg/thoughtcrime/securesms/attachments/Attachment;->(Ljava/lang/String;IJLjava/lang/String;ILjava/lang/String;Ljava/lang/String;[B[BLjava/lang/String;ZZZIIIZJLjava/lang/String;Lorg/thoughtcrime/securesms/stickers/StickerLocator;Lorg/thoughtcrime/securesms/blurhash/BlurHash;Lorg/thoughtcrime/securesms/audio/AudioHash;Lorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties;)V +HSPLorg/thoughtcrime/securesms/attachments/Attachment;->isInProgress()Z HSPLorg/thoughtcrime/securesms/attachments/Attachment;->isPermanentlyFailed()Z -HSPLorg/thoughtcrime/securesms/attachments/Attachment;->isQuote()Z HSPLorg/thoughtcrime/securesms/attachments/Attachment;->isSticker()Z -HSPLorg/thoughtcrime/securesms/attachments/Attachment;->isVideoGif()Z -HSPLorg/thoughtcrime/securesms/attachments/Attachment;->isVoiceNote()Z HSPLorg/thoughtcrime/securesms/attachments/AttachmentCreator;->()V HSPLorg/thoughtcrime/securesms/attachments/AttachmentCreator;->()V -HSPLorg/thoughtcrime/securesms/attachments/AttachmentId$1;->()V +HSPLorg/thoughtcrime/securesms/attachments/AttachmentId$Creator;->()V HSPLorg/thoughtcrime/securesms/attachments/AttachmentId;->()V -HSPLorg/thoughtcrime/securesms/attachments/AttachmentId;->(JJ)V -HSPLorg/thoughtcrime/securesms/attachments/AttachmentId;->getRowId()J -HSPLorg/thoughtcrime/securesms/attachments/AttachmentId;->getUniqueId()J +HSPLorg/thoughtcrime/securesms/attachments/AttachmentId;->(J)V HSPLorg/thoughtcrime/securesms/attachments/AttachmentId;->hashCode()I HSPLorg/thoughtcrime/securesms/attachments/AttachmentId;->toString()Ljava/lang/String; -HSPLorg/thoughtcrime/securesms/attachments/AttachmentId;->toStrings()[Ljava/lang/String; +HSPLorg/thoughtcrime/securesms/attachments/DatabaseAttachment$DisplayOrderComparator;->()V HSPLorg/thoughtcrime/securesms/attachments/DatabaseAttachment$DisplayOrderComparator;->()V HSPLorg/thoughtcrime/securesms/attachments/DatabaseAttachment$DisplayOrderComparator;->compare(Ljava/lang/Object;Ljava/lang/Object;)I HSPLorg/thoughtcrime/securesms/attachments/DatabaseAttachment$DisplayOrderComparator;->compare(Lorg/thoughtcrime/securesms/attachments/DatabaseAttachment;Lorg/thoughtcrime/securesms/attachments/DatabaseAttachment;)I -HSPLorg/thoughtcrime/securesms/attachments/DatabaseAttachment;->(Lorg/thoughtcrime/securesms/attachments/AttachmentId;JZZLjava/lang/String;IJLjava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;[B[BILjava/lang/String;ZZZIIZLjava/lang/String;Lorg/thoughtcrime/securesms/stickers/StickerLocator;Lorg/thoughtcrime/securesms/blurhash/BlurHash;Lorg/thoughtcrime/securesms/audio/AudioHash;Lorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties;IJ)V -HSPLorg/thoughtcrime/securesms/attachments/DatabaseAttachment;->getAttachmentId()Lorg/thoughtcrime/securesms/attachments/AttachmentId; +HSPLorg/thoughtcrime/securesms/attachments/DatabaseAttachment;->()V HSPLorg/thoughtcrime/securesms/attachments/DatabaseAttachment;->getDisplayOrder()I -HSPLorg/thoughtcrime/securesms/attachments/DatabaseAttachment;->getMmsId()J HSPLorg/thoughtcrime/securesms/attachments/DatabaseAttachment;->getUri()Landroid/net/Uri; HSPLorg/thoughtcrime/securesms/attachments/DatabaseAttachment;->hashCode()I -HSPLorg/thoughtcrime/securesms/attachments/PointerAttachment;->(Ljava/lang/String;IJLjava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;[B[BILjava/lang/String;ZZZIIJLjava/lang/String;Lorg/thoughtcrime/securesms/stickers/StickerLocator;Lorg/thoughtcrime/securesms/blurhash/BlurHash;)V -HSPLorg/thoughtcrime/securesms/attachments/PointerAttachment;->forPointer(Lj$/util/Optional;)Lj$/util/Optional; -HSPLorg/thoughtcrime/securesms/attachments/PointerAttachment;->forPointer(Lj$/util/Optional;Lorg/thoughtcrime/securesms/stickers/StickerLocator;Ljava/lang/String;)Lj$/util/Optional; -HSPLorg/thoughtcrime/securesms/attachments/PointerAttachment;->forPointers(Lj$/util/Optional;)Ljava/util/List; +HSPLorg/thoughtcrime/securesms/attachments/PointerAttachment$Companion;->()V +HSPLorg/thoughtcrime/securesms/attachments/PointerAttachment$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/attachments/PointerAttachment$Companion;->forPointer$default(Lorg/thoughtcrime/securesms/attachments/PointerAttachment$Companion;Lj$/util/Optional;Lorg/thoughtcrime/securesms/stickers/StickerLocator;Ljava/lang/String;ILjava/lang/Object;)Lj$/util/Optional; +HSPLorg/thoughtcrime/securesms/attachments/PointerAttachment$Companion;->forPointer(Lj$/util/Optional;Lorg/thoughtcrime/securesms/stickers/StickerLocator;Ljava/lang/String;)Lj$/util/Optional; +HSPLorg/thoughtcrime/securesms/attachments/PointerAttachment$Companion;->forPointers(Lj$/util/Optional;)Ljava/util/List; +HSPLorg/thoughtcrime/securesms/attachments/PointerAttachment;->()V +HSPLorg/thoughtcrime/securesms/attachments/PointerAttachment;->(Ljava/lang/String;IJLjava/lang/String;ILjava/lang/String;Ljava/lang/String;[B[BILjava/lang/String;ZZZIIJLjava/lang/String;Lorg/thoughtcrime/securesms/stickers/StickerLocator;Lorg/thoughtcrime/securesms/blurhash/BlurHash;)V +HSPLorg/thoughtcrime/securesms/attachments/PointerAttachment;->(Ljava/lang/String;IJLjava/lang/String;ILjava/lang/String;Ljava/lang/String;[B[BILjava/lang/String;ZZZIIJLjava/lang/String;Lorg/thoughtcrime/securesms/stickers/StickerLocator;Lorg/thoughtcrime/securesms/blurhash/BlurHash;Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/attachments/PointerAttachment;->getUri()Landroid/net/Uri; HSPLorg/thoughtcrime/securesms/audio/AudioRecorder$$ExternalSyntheticLambda1;->(Lorg/thoughtcrime/securesms/audio/AudioRecorder;)V HSPLorg/thoughtcrime/securesms/audio/AudioRecorder;->()V @@ -20124,10 +20015,6 @@ HSPLorg/thoughtcrime/securesms/avatar/Avatar$DatabaseId$DoNotPersist;->()V HSPLorg/thoughtcrime/securesms/avatar/Avatar$DatabaseId;->()V HSPLorg/thoughtcrime/securesms/avatar/Avatar$DatabaseId;->()V HSPLorg/thoughtcrime/securesms/avatar/Avatar$DatabaseId;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V -HSPLorg/thoughtcrime/securesms/avatar/Avatar$Resource;->()V -HSPLorg/thoughtcrime/securesms/avatar/Avatar$Resource;->(ILorg/thoughtcrime/securesms/avatar/Avatars$ColorPair;)V -HSPLorg/thoughtcrime/securesms/avatar/Avatar$Resource;->getColor()Lorg/thoughtcrime/securesms/avatar/Avatars$ColorPair; -HSPLorg/thoughtcrime/securesms/avatar/Avatar$Resource;->getResourceId()I HSPLorg/thoughtcrime/securesms/avatar/Avatar$Text;->()V HSPLorg/thoughtcrime/securesms/avatar/Avatar$Text;->(Ljava/lang/String;Lorg/thoughtcrime/securesms/avatar/Avatars$ColorPair;Lorg/thoughtcrime/securesms/avatar/Avatar$DatabaseId;)V HSPLorg/thoughtcrime/securesms/avatar/Avatar$Text;->getColor()Lorg/thoughtcrime/securesms/avatar/Avatars$ColorPair; @@ -20138,26 +20025,13 @@ HSPLorg/thoughtcrime/securesms/avatar/Avatar;->(Lorg/thoughtcrime/securesm HSPLorg/thoughtcrime/securesms/avatar/AvatarPickerStorage;->()V HSPLorg/thoughtcrime/securesms/avatar/AvatarPickerStorage;->()V HSPLorg/thoughtcrime/securesms/avatar/AvatarPickerStorage;->cleanOrphans(Landroid/content/Context;)V -HSPLorg/thoughtcrime/securesms/avatar/AvatarRenderer$$ExternalSyntheticLambda0;->(Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Landroid/content/Context;Lkotlin/jvm/functions/Function1;)V -HSPLorg/thoughtcrime/securesms/avatar/AvatarRenderer$$ExternalSyntheticLambda0;->run()V -HSPLorg/thoughtcrime/securesms/avatar/AvatarRenderer$renderResource$1;->(Landroid/content/Context;Lorg/thoughtcrime/securesms/avatar/Avatar$Resource;)V -HSPLorg/thoughtcrime/securesms/avatar/AvatarRenderer$renderResource$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; -HSPLorg/thoughtcrime/securesms/avatar/AvatarRenderer$renderResource$1;->invoke-IoAF18A(Landroid/graphics/Canvas;)Ljava/lang/Object; -HSPLorg/thoughtcrime/securesms/avatar/AvatarRenderer;->$r8$lambda$LMRb5EH7u5JsF-ZatOYl7HXPvGU(Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Landroid/content/Context;Lkotlin/jvm/functions/Function1;)V HSPLorg/thoughtcrime/securesms/avatar/AvatarRenderer;->()V HSPLorg/thoughtcrime/securesms/avatar/AvatarRenderer;->()V -HSPLorg/thoughtcrime/securesms/avatar/AvatarRenderer;->createMedia(Landroid/net/Uri;J)Lorg/thoughtcrime/securesms/mediasend/Media; HSPLorg/thoughtcrime/securesms/avatar/AvatarRenderer;->createTextDrawable(Landroid/content/Context;Lorg/thoughtcrime/securesms/avatar/Avatar$Text;ZIZ)Landroid/graphics/drawable/Drawable; -HSPLorg/thoughtcrime/securesms/avatar/AvatarRenderer;->getDIMENSIONS()I HSPLorg/thoughtcrime/securesms/avatar/AvatarRenderer;->getTypeface(Landroid/content/Context;)Landroid/graphics/Typeface; -HSPLorg/thoughtcrime/securesms/avatar/AvatarRenderer;->renderAvatar(Landroid/content/Context;Lorg/thoughtcrime/securesms/avatar/Avatar;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V -HSPLorg/thoughtcrime/securesms/avatar/AvatarRenderer;->renderInBackground$lambda$1(Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Landroid/content/Context;Lkotlin/jvm/functions/Function1;)V -HSPLorg/thoughtcrime/securesms/avatar/AvatarRenderer;->renderInBackground(Landroid/content/Context;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V -HSPLorg/thoughtcrime/securesms/avatar/AvatarRenderer;->renderResource(Landroid/content/Context;Lorg/thoughtcrime/securesms/avatar/Avatar$Resource;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V HSPLorg/thoughtcrime/securesms/avatar/Avatars$ColorPair;->()V HSPLorg/thoughtcrime/securesms/avatar/Avatars$ColorPair;->(IILjava/lang/String;)V HSPLorg/thoughtcrime/securesms/avatar/Avatars$ColorPair;->(Lorg/thoughtcrime/securesms/conversation/colors/AvatarColor;Lorg/thoughtcrime/securesms/avatar/Avatars$ForegroundColor;)V -HSPLorg/thoughtcrime/securesms/avatar/Avatars$ColorPair;->getBackgroundColor()I HSPLorg/thoughtcrime/securesms/avatar/Avatars$ColorPair;->getCode()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/avatar/Avatars$ColorPair;->getForegroundColor()I HSPLorg/thoughtcrime/securesms/avatar/Avatars$DefaultAvatar;->()V @@ -20219,19 +20093,20 @@ HSPLorg/thoughtcrime/securesms/blurhash/BlurHashResourceDecoder;->()V HSPLorg/thoughtcrime/securesms/components/AlbumThumbnailView$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/components/AlbumThumbnailView;)V HSPLorg/thoughtcrime/securesms/components/AlbumThumbnailView$$ExternalSyntheticLambda1;->(Lorg/thoughtcrime/securesms/components/AlbumThumbnailView;)V HSPLorg/thoughtcrime/securesms/components/AlbumThumbnailView$$ExternalSyntheticLambda2;->(Lorg/thoughtcrime/securesms/components/AlbumThumbnailView;Ljava/util/List;)V +HSPLorg/thoughtcrime/securesms/components/AlbumThumbnailView$$ExternalSyntheticLambda3;->(Lorg/thoughtcrime/securesms/components/AlbumThumbnailView;Ljava/util/List;)V HSPLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->(Landroid/content/Context;Landroid/util/AttributeSet;)V HSPLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->applyCorners()V HSPLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->applyCornersForSizeClass2()V HSPLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->getCells()[Lorg/thoughtcrime/securesms/components/ThumbnailView; HSPLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->inflateLayout(I)V -HSPLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->setCancelDownloadClickListener(Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;)V +HSPLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->setCancelTransferClickListener(Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;)V HSPLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->setCellBackgroundColor(I)V -HSPLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->setDownloadClickListener(Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;)V HSPLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->setPlayVideoClickListener(Lorg/thoughtcrime/securesms/mms/SlideClickListener;)V HSPLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->setRadii(IIII)V HSPLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->setRelativeRadii(Lorg/thoughtcrime/securesms/components/ThumbnailView;IIII)V HSPLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->setSlide(Lorg/thoughtcrime/securesms/mms/GlideRequests;Lorg/thoughtcrime/securesms/mms/Slide;IZ)V HSPLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->setSlides(Lorg/thoughtcrime/securesms/mms/GlideRequests;Ljava/util/List;Z)V +HSPLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->setStartTransferClickListener(Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;)V HSPLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->setThumbnailClickListener(Lorg/thoughtcrime/securesms/mms/SlideClickListener;)V HSPLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->showSlides(Lorg/thoughtcrime/securesms/mms/GlideRequests;Ljava/util/List;)V HSPLorg/thoughtcrime/securesms/components/AlbumThumbnailView;->sizeClass(I)I @@ -20241,6 +20116,7 @@ HSPLorg/thoughtcrime/securesms/components/AlertView;->setNone()V HSPLorg/thoughtcrime/securesms/components/AnimatingToggle;->(Landroid/content/Context;Landroid/util/AttributeSet;)V HSPLorg/thoughtcrime/securesms/components/AnimatingToggle;->(Landroid/content/Context;Landroid/util/AttributeSet;I)V HSPLorg/thoughtcrime/securesms/components/AnimatingToggle;->addView(Landroid/view/View;ILandroid/view/ViewGroup$LayoutParams;)V +HSPLorg/thoughtcrime/securesms/components/AnimatingToggle;->display(Landroid/view/View;)V HSPLorg/thoughtcrime/securesms/components/AudioView$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/components/AudioView;)V HSPLorg/thoughtcrime/securesms/components/AudioView$$ExternalSyntheticLambda0;->onChanged(Ljava/lang/Object;)V HSPLorg/thoughtcrime/securesms/components/AudioView$$ExternalSyntheticLambda1;->(Lorg/thoughtcrime/securesms/components/AudioView;)V @@ -20359,17 +20235,17 @@ HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->(Lan HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->dispatchDraw(Landroid/graphics/Canvas;)V HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->getFooter()Lorg/thoughtcrime/securesms/util/views/Stub; HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setBorderless(Z)V -HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setCancelDownloadClickListener(Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;)V +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setCancelTransferClickListener(Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;)V HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setClickable(Z)V HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setConversationColor(I)V HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setCorners(IIII)V -HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setDownloadClickListener(Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;)V HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setFocusable(Z)V HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setImageResource(Lorg/thoughtcrime/securesms/mms/GlideRequests;Ljava/util/List;ZZ)V HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setMaximumThumbnailHeight(I)V HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setMinimumThumbnailWidth(I)V HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setOnLongClickListener(Landroid/view/View$OnLongClickListener;)V HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setPlayVideoClickListener(Lorg/thoughtcrime/securesms/mms/SlideClickListener;)V +HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setStartTransferClickListener(Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;)V HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setThumbnailBounds([I)V HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->setThumbnailClickListener(Lorg/thoughtcrime/securesms/mms/SlideClickListener;)V HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnail;->showShade(Z)V @@ -20399,7 +20275,10 @@ HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState;->getAl HSPLorg/thoughtcrime/securesms/components/ConversationItemThumbnailState;->getThumbnailViewState()Lorg/thoughtcrime/securesms/components/ConversationItemThumbnailState$ThumbnailViewState; HSPLorg/thoughtcrime/securesms/components/ConversationScrollToView;->(Landroid/content/Context;Landroid/util/AttributeSet;)V HSPLorg/thoughtcrime/securesms/components/ConversationScrollToView;->(Landroid/content/Context;Landroid/util/AttributeSet;I)V +HSPLorg/thoughtcrime/securesms/components/ConversationScrollToView;->formatUnreadCount(I)Ljava/lang/CharSequence; HSPLorg/thoughtcrime/securesms/components/ConversationScrollToView;->setOnClickListener(Landroid/view/View$OnClickListener;)V +HSPLorg/thoughtcrime/securesms/components/ConversationScrollToView;->setShown(Z)V +HSPLorg/thoughtcrime/securesms/components/ConversationScrollToView;->setUnreadCount(I)V HSPLorg/thoughtcrime/securesms/components/ConversationScrollToView;->setUnreadCountBackgroundTint(I)V HSPLorg/thoughtcrime/securesms/components/ConversationScrollToView;->setWallpaperEnabled(Z)V HSPLorg/thoughtcrime/securesms/components/ConversationSearchBottomBar;->(Landroid/content/Context;Landroid/util/AttributeSet;)V @@ -20417,10 +20296,12 @@ HSPLorg/thoughtcrime/securesms/components/DeliveryStatusView$State;->(Ljav HSPLorg/thoughtcrime/securesms/components/DeliveryStatusView;->(Landroid/content/Context;Landroid/util/AttributeSet;)V HSPLorg/thoughtcrime/securesms/components/DeliveryStatusView;->(Landroid/content/Context;Landroid/util/AttributeSet;I)V HSPLorg/thoughtcrime/securesms/components/DeliveryStatusView;->clearAnimation()V +HSPLorg/thoughtcrime/securesms/components/DeliveryStatusView;->isPending()Z HSPLorg/thoughtcrime/securesms/components/DeliveryStatusView;->setNone()V HSPLorg/thoughtcrime/securesms/components/DeliveryStatusView;->setTint(I)V HSPLorg/thoughtcrime/securesms/components/DeliveryStatusView;->updateContentDescription()V HSPLorg/thoughtcrime/securesms/components/ExpirationTimerView;->(Landroid/content/Context;Landroid/util/AttributeSet;)V +HSPLorg/thoughtcrime/securesms/components/ExpirationTimerView;->stopAnimation()V HSPLorg/thoughtcrime/securesms/components/FromTextView;->()V HSPLorg/thoughtcrime/securesms/components/FromTextView;->(Landroid/content/Context;Landroid/util/AttributeSet;)V HSPLorg/thoughtcrime/securesms/components/FromTextView;->setText(Lorg/thoughtcrime/securesms/recipients/Recipient;Ljava/lang/CharSequence;ZLjava/lang/String;)V @@ -20428,12 +20309,14 @@ HSPLorg/thoughtcrime/securesms/components/FromTextView;->setText(Lorg/thoughtcri HSPLorg/thoughtcrime/securesms/components/FromTextView;->setText(Lorg/thoughtcrime/securesms/recipients/Recipient;Z)V HSPLorg/thoughtcrime/securesms/components/FromTextView;->setText(Lorg/thoughtcrime/securesms/recipients/Recipient;ZLjava/lang/String;)V HSPLorg/thoughtcrime/securesms/components/HidingLinearLayout;->(Landroid/content/Context;Landroid/util/AttributeSet;)V +HSPLorg/thoughtcrime/securesms/components/HidingLinearLayout;->hide()V +HSPLorg/thoughtcrime/securesms/components/HidingLinearLayout;->show()V HSPLorg/thoughtcrime/securesms/components/InputAwareConstraintLayout;->()V HSPLorg/thoughtcrime/securesms/components/InputAwareConstraintLayout;->(Landroid/content/Context;Landroid/util/AttributeSet;)V HSPLorg/thoughtcrime/securesms/components/InputAwareConstraintLayout;->(Landroid/content/Context;Landroid/util/AttributeSet;I)V HSPLorg/thoughtcrime/securesms/components/InputAwareConstraintLayout;->(Landroid/content/Context;Landroid/util/AttributeSet;IILkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/components/InputAwareConstraintLayout;->addInputListener(Lorg/thoughtcrime/securesms/components/InputAwareConstraintLayout$Listener;)V HSPLorg/thoughtcrime/securesms/components/InputAwareConstraintLayout;->setFragmentManager(Landroidx/fragment/app/FragmentManager;)V -HSPLorg/thoughtcrime/securesms/components/InputAwareConstraintLayout;->setListener(Lorg/thoughtcrime/securesms/components/InputAwareConstraintLayout$Listener;)V HSPLorg/thoughtcrime/securesms/components/InputPanel$$ExternalSyntheticLambda2;->(Lorg/thoughtcrime/securesms/components/InputPanel$Listener;)V HSPLorg/thoughtcrime/securesms/components/InputPanel$$ExternalSyntheticLambda3;->(Lorg/thoughtcrime/securesms/components/InputPanel$Listener;)V HSPLorg/thoughtcrime/securesms/components/InputPanel$$ExternalSyntheticLambda4;->(Lorg/thoughtcrime/securesms/components/InputPanel;)V @@ -20497,6 +20380,9 @@ HSPLorg/thoughtcrime/securesms/components/LinkPreviewViewThumbnailState;->()V HSPLorg/thoughtcrime/securesms/components/LinkPreviewViewThumbnailState;->(IIIILorg/thoughtcrime/securesms/mms/SlidesClickedListener;)V HSPLorg/thoughtcrime/securesms/components/LinkPreviewViewThumbnailState;->(IIIILorg/thoughtcrime/securesms/mms/SlidesClickedListener;ILkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/components/LinkPreviewViewThumbnailState;->applyState(Lorg/thoughtcrime/securesms/util/views/Stub;)V +HSPLorg/thoughtcrime/securesms/components/LinkPreviewViewThumbnailState;->copy(IIIILorg/thoughtcrime/securesms/mms/SlidesClickedListener;)Lorg/thoughtcrime/securesms/components/LinkPreviewViewThumbnailState; +HSPLorg/thoughtcrime/securesms/components/LinkPreviewViewThumbnailState;->getDownloadListener()Lorg/thoughtcrime/securesms/mms/SlidesClickedListener; HSPLorg/thoughtcrime/securesms/components/Material3SearchToolbar$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/components/Material3SearchToolbar;)V HSPLorg/thoughtcrime/securesms/components/Material3SearchToolbar$$ExternalSyntheticLambda1;->(Lorg/thoughtcrime/securesms/components/Material3SearchToolbar;)V HSPLorg/thoughtcrime/securesms/components/Material3SearchToolbar$special$$inlined$addTextChangedListener$default$1;->(Landroid/view/View;Lorg/thoughtcrime/securesms/components/Material3SearchToolbar;)V @@ -20508,6 +20394,7 @@ HSPLorg/thoughtcrime/securesms/components/MicrophoneRecorderView$State;->$values HSPLorg/thoughtcrime/securesms/components/MicrophoneRecorderView$State;->()V HSPLorg/thoughtcrime/securesms/components/MicrophoneRecorderView$State;->(Ljava/lang/String;I)V HSPLorg/thoughtcrime/securesms/components/MicrophoneRecorderView;->(Landroid/content/Context;Landroid/util/AttributeSet;)V +HSPLorg/thoughtcrime/securesms/components/MicrophoneRecorderView;->isRecordingLocked()Z HSPLorg/thoughtcrime/securesms/components/MicrophoneRecorderView;->onFinishInflate()V HSPLorg/thoughtcrime/securesms/components/MicrophoneRecorderView;->setHandler(Lorg/thoughtcrime/securesms/audio/AudioRecordingHandler;)V HSPLorg/thoughtcrime/securesms/components/Outliner;->()V @@ -20567,6 +20454,11 @@ HSPLorg/thoughtcrime/securesms/components/ScrollToPositionDelegate;->(Land HSPLorg/thoughtcrime/securesms/components/ScrollToPositionDelegate;->(Landroidx/recyclerview/widget/RecyclerView;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/components/ScrollToPositionDelegate;->(Landroidx/recyclerview/widget/RecyclerView;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lio/reactivex/rxjava3/disposables/CompositeDisposable;)V HSPLorg/thoughtcrime/securesms/components/ScrollToPositionDelegate;->notifyListCommitted()V +HSPLorg/thoughtcrime/securesms/components/SearchView;->(Landroid/content/Context;)V +HSPLorg/thoughtcrime/securesms/components/SearchView;->(Landroid/content/Context;Landroid/util/AttributeSet;)V +HSPLorg/thoughtcrime/securesms/components/SearchView;->(Landroid/content/Context;Landroid/util/AttributeSet;I)V +HSPLorg/thoughtcrime/securesms/components/SearchView;->appendEmojiFilter(Landroid/widget/TextView;)[Landroid/text/InputFilter; +HSPLorg/thoughtcrime/securesms/components/SearchView;->initEmojiFilter()V HSPLorg/thoughtcrime/securesms/components/SendButton;->()V HSPLorg/thoughtcrime/securesms/components/SendButton;->(Landroid/content/Context;Landroid/util/AttributeSet;)V HSPLorg/thoughtcrime/securesms/components/SendButton;->setPopupContainer(Landroid/view/ViewGroup;)V @@ -20590,14 +20482,14 @@ HSPLorg/thoughtcrime/securesms/components/ThumbnailView;->hasSameContents(Lorg/t HSPLorg/thoughtcrime/securesms/components/ThumbnailView;->onMeasure(II)V HSPLorg/thoughtcrime/securesms/components/ThumbnailView;->onSizeChanged(IIII)V HSPLorg/thoughtcrime/securesms/components/ThumbnailView;->setBounds(IIII)V -HSPLorg/thoughtcrime/securesms/components/ThumbnailView;->setCancelDownloadClickListener(Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;)V +HSPLorg/thoughtcrime/securesms/components/ThumbnailView;->setCancelTransferClickListener(Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;)V HSPLorg/thoughtcrime/securesms/components/ThumbnailView;->setClickable(Z)V -HSPLorg/thoughtcrime/securesms/components/ThumbnailView;->setDownloadClickListener(Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;)V HSPLorg/thoughtcrime/securesms/components/ThumbnailView;->setFocusable(Z)V HSPLorg/thoughtcrime/securesms/components/ThumbnailView;->setImageResource(Lorg/thoughtcrime/securesms/mms/GlideRequests;Lorg/thoughtcrime/securesms/mms/Slide;ZZ)Lorg/thoughtcrime/securesms/util/concurrent/ListenableFuture; HSPLorg/thoughtcrime/securesms/components/ThumbnailView;->setImageResource(Lorg/thoughtcrime/securesms/mms/GlideRequests;Lorg/thoughtcrime/securesms/mms/Slide;ZZII)Lorg/thoughtcrime/securesms/util/concurrent/ListenableFuture; HSPLorg/thoughtcrime/securesms/components/ThumbnailView;->setPlayVideoClickListener(Lorg/thoughtcrime/securesms/mms/SlideClickListener;)V HSPLorg/thoughtcrime/securesms/components/ThumbnailView;->setRadii(IIII)V +HSPLorg/thoughtcrime/securesms/components/ThumbnailView;->setStartTransferClickListener(Lorg/thoughtcrime/securesms/mms/SlidesClickedListener;)V HSPLorg/thoughtcrime/securesms/components/ThumbnailView;->setThumbnailClickListener(Lorg/thoughtcrime/securesms/mms/SlideClickListener;)V HSPLorg/thoughtcrime/securesms/components/ThumbnailView;->showSecondaryText(Z)V HSPLorg/thoughtcrime/securesms/components/TypingIndicatorView;->(Landroid/content/Context;Landroid/util/AttributeSet;)V @@ -20618,6 +20510,7 @@ HSPLorg/thoughtcrime/securesms/components/ViewBinderDelegate;->onCreate(Landroid HSPLorg/thoughtcrime/securesms/components/ViewBinderDelegate;->onPause(Landroidx/lifecycle/LifecycleOwner;)V HSPLorg/thoughtcrime/securesms/components/ViewBinderDelegate;->onResume(Landroidx/lifecycle/LifecycleOwner;)V HSPLorg/thoughtcrime/securesms/components/ViewBinderDelegate;->onStart(Landroidx/lifecycle/LifecycleOwner;)V +HSPLorg/thoughtcrime/securesms/components/ViewBinderDelegate;->onStop(Landroidx/lifecycle/LifecycleOwner;)V HSPLorg/thoughtcrime/securesms/components/WaveFormSeekBarView;->(Landroid/content/Context;Landroid/util/AttributeSet;)V HSPLorg/thoughtcrime/securesms/components/WaveFormSeekBarView;->getProgressDrawable()Landroid/graphics/drawable/Drawable; HSPLorg/thoughtcrime/securesms/components/WaveFormSeekBarView;->init()V @@ -20677,10 +20570,12 @@ HSPLorg/thoughtcrime/securesms/components/emoji/EmojiTextView;->applyWidthMeasur HSPLorg/thoughtcrime/securesms/components/emoji/EmojiTextView;->drawSpecialRenderers(Landroid/graphics/Canvas;Lorg/thoughtcrime/securesms/components/mention/MentionRendererDelegate;Lorg/thoughtcrime/securesms/components/spoiler/SpoilerRendererDelegate;)V HSPLorg/thoughtcrime/securesms/components/emoji/EmojiTextView;->ellipsizeAnyTextForMaxLength()V HSPLorg/thoughtcrime/securesms/components/emoji/EmojiTextView;->ellipsizeEmojiTextForMaxLines()V +HSPLorg/thoughtcrime/securesms/components/emoji/EmojiTextView;->getLastLineWidth()I HSPLorg/thoughtcrime/securesms/components/emoji/EmojiTextView;->getLongestLineWidth(Ljava/lang/CharSequence;)F HSPLorg/thoughtcrime/securesms/components/emoji/EmojiTextView;->hasMetricAffectingSpan(Ljava/lang/CharSequence;)Z HSPLorg/thoughtcrime/securesms/components/emoji/EmojiTextView;->invalidateDrawable(Landroid/graphics/drawable/Drawable;)V HSPLorg/thoughtcrime/securesms/components/emoji/EmojiTextView;->isEllipsizedAtEnd()Z +HSPLorg/thoughtcrime/securesms/components/emoji/EmojiTextView;->isJumbomoji()Z HSPLorg/thoughtcrime/securesms/components/emoji/EmojiTextView;->isSingleLine()Z HSPLorg/thoughtcrime/securesms/components/emoji/EmojiTextView;->lambda$ellipsizeEmojiTextForMaxLines$2()V HSPLorg/thoughtcrime/securesms/components/emoji/EmojiTextView;->lambda$ellipsizeEmojiTextForMaxLines$3(Ljava/lang/Runnable;Landroid/view/View;)Lkotlin/Unit; @@ -20690,7 +20585,6 @@ HSPLorg/thoughtcrime/securesms/components/emoji/EmojiTextView;->onMeasure(II)V HSPLorg/thoughtcrime/securesms/components/emoji/EmojiTextView;->onSizeChanged(IIII)V HSPLorg/thoughtcrime/securesms/components/emoji/EmojiTextView;->setMentionBackgroundTint(I)V HSPLorg/thoughtcrime/securesms/components/emoji/EmojiTextView;->setOverflowText(Ljava/lang/CharSequence;)V -HSPLorg/thoughtcrime/securesms/components/emoji/EmojiTextView;->setText(Ljava/lang/CharSequence;Landroid/widget/TextView$BufferType;)V HSPLorg/thoughtcrime/securesms/components/emoji/EmojiTextView;->setTextColor(I)V HSPLorg/thoughtcrime/securesms/components/emoji/EmojiTextView;->setTextSize(IF)V HSPLorg/thoughtcrime/securesms/components/emoji/EmojiTextView;->unchanged(Ljava/lang/CharSequence;Ljava/lang/CharSequence;Landroid/widget/TextView$BufferType;)Z @@ -20836,6 +20730,7 @@ HSPLorg/thoughtcrime/securesms/components/settings/app/subscription/completed/Te HSPLorg/thoughtcrime/securesms/components/settings/app/subscription/completed/TerminalDonationDelegate;->onPause(Landroidx/lifecycle/LifecycleOwner;)V HSPLorg/thoughtcrime/securesms/components/settings/app/subscription/completed/TerminalDonationDelegate;->onResume(Landroidx/lifecycle/LifecycleOwner;)V HSPLorg/thoughtcrime/securesms/components/settings/app/subscription/completed/TerminalDonationDelegate;->onStart(Landroidx/lifecycle/LifecycleOwner;)V +HSPLorg/thoughtcrime/securesms/components/settings/app/subscription/completed/TerminalDonationDelegate;->onStop(Landroidx/lifecycle/LifecycleOwner;)V HSPLorg/thoughtcrime/securesms/components/settings/app/subscription/completed/TerminalDonationRepository;->()V HSPLorg/thoughtcrime/securesms/components/settings/app/subscription/completed/TerminalDonationRepository;->(Lorg/whispersystems/signalservice/api/services/DonationsService;)V HSPLorg/thoughtcrime/securesms/components/settings/app/subscription/completed/TerminalDonationRepository;->(Lorg/whispersystems/signalservice/api/services/DonationsService;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -20865,6 +20760,7 @@ HSPLorg/thoughtcrime/securesms/components/spoiler/SpoilerRendererDelegate$1$onVi HSPLorg/thoughtcrime/securesms/components/spoiler/SpoilerRendererDelegate$1$onViewAttachedToWindow$1;->onPause(Landroidx/lifecycle/LifecycleOwner;)V HSPLorg/thoughtcrime/securesms/components/spoiler/SpoilerRendererDelegate$1$onViewAttachedToWindow$1;->onResume(Landroidx/lifecycle/LifecycleOwner;)V HSPLorg/thoughtcrime/securesms/components/spoiler/SpoilerRendererDelegate$1$onViewAttachedToWindow$1;->onStart(Landroidx/lifecycle/LifecycleOwner;)V +HSPLorg/thoughtcrime/securesms/components/spoiler/SpoilerRendererDelegate$1$onViewAttachedToWindow$1;->onStop(Landroidx/lifecycle/LifecycleOwner;)V HSPLorg/thoughtcrime/securesms/components/spoiler/SpoilerRendererDelegate$1;->(Lorg/thoughtcrime/securesms/components/spoiler/SpoilerRendererDelegate;)V HSPLorg/thoughtcrime/securesms/components/spoiler/SpoilerRendererDelegate$1;->onViewAttachedToWindow(Landroid/view/View;)V HSPLorg/thoughtcrime/securesms/components/spoiler/SpoilerRendererDelegate;->()V @@ -20885,22 +20781,17 @@ HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$C HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Mode;->$values()[Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Mode; HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Mode;->()V HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Mode;->(Ljava/lang/String;I)V -HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Mode;->values()[Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Mode; HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Progress$Companion;->()V HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Progress$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Progress;->()V HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Progress;->(JJ)V HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Progress;->toString()Ljava/lang/String; -HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$WhenMappings;->()V HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setCancelClickListener$1;->(Landroid/view/View$OnClickListener;)V HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setCancelClickListener$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setCancelClickListener$1;->invoke(Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;)Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState; HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setClickable$1;->(Z)V HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setClickable$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setClickable$1;->invoke(Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;)Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState; -HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setDownloadClickListener$1;->(Landroid/view/View$OnClickListener;)V -HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setDownloadClickListener$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; -HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setDownloadClickListener$1;->invoke(Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;)Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState; HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setFocusable$1;->(Z)V HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setFocusable$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setFocusable$1;->invoke(Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;)Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState; @@ -20910,6 +20801,9 @@ HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$s HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setSlides$2;->(Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;Ljava/util/List;)V HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setSlides$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setSlides$2;->invoke(Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;)Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState; +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setTransferClickListener$1;->(Landroid/view/View$OnClickListener;)V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setTransferClickListener$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setTransferClickListener$1;->invoke(Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;)Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState; HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setVisible$1;->(Z)V HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setVisible$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setVisible$1;->invoke(Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;)Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState; @@ -20920,19 +20814,17 @@ HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;- HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->access$isUpdateToExistingSet(Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;Ljava/util/List;)Z HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->access$slidesAsListOfTimestamps(Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;Ljava/util/List;)Ljava/lang/String; HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->access$verboseLog(Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;Ljava/lang/String;)V -HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->applyState(Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;)V HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->containsPlayableSlides(Ljava/util/List;)Z HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->deriveMode(Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;)Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Mode; -HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->displayChildrenAsGone()V HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->getTransferState(Ljava/util/List;)I HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->isUpdateToExistingSet(Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState;Ljava/util/List;)Z HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->onAttachedToWindow()V HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->setCancelClickListener(Landroid/view/View$OnClickListener;)V HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->setClickable(Z)V -HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->setDownloadClickListener(Landroid/view/View$OnClickListener;)V HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->setFocusable(Z)V HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->setShowSecondaryText(Z)V HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->setSlides(Ljava/util/List;)V +HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->setTransferClickListener(Landroid/view/View$OnClickListener;)V HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->setVisible(Z)V HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->slidesAsListOfTimestamps(Ljava/util/List;)Ljava/lang/String; HSPLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->updateState(Lkotlin/jvm/functions/Function1;)V @@ -21215,10 +21107,6 @@ HSPLorg/thoughtcrime/securesms/contacts/paged/ContactSearchViewModel;->getSelect HSPLorg/thoughtcrime/securesms/contacts/paged/ContactSearchViewModel;->setConfiguration(Lorg/thoughtcrime/securesms/contacts/paged/ContactSearchConfiguration;)V HSPLorg/thoughtcrime/securesms/contacts/paged/ContactSearchViewModel;->setConversationFilterRequest$lambda$1(Lorg/thoughtcrime/securesms/conversationlist/chatfilter/ConversationFilterRequest;Lorg/thoughtcrime/securesms/contacts/paged/ContactSearchState;)Lorg/thoughtcrime/securesms/contacts/paged/ContactSearchState; HSPLorg/thoughtcrime/securesms/contacts/paged/ContactSearchViewModel;->setConversationFilterRequest(Lorg/thoughtcrime/securesms/conversationlist/chatfilter/ConversationFilterRequest;)V -HSPLorg/thoughtcrime/securesms/contacts/sync/ContactDiscovery;->()V -HSPLorg/thoughtcrime/securesms/contacts/sync/ContactDiscovery;->()V -HSPLorg/thoughtcrime/securesms/contacts/sync/ContactDiscovery;->hasContactsPermissions(Landroid/content/Context;)Z -HSPLorg/thoughtcrime/securesms/contacts/sync/ContactDiscovery;->refreshAll(Landroid/content/Context;Z)V HSPLorg/thoughtcrime/securesms/conversation/BodyBubbleLayoutTransition;->()V HSPLorg/thoughtcrime/securesms/conversation/BodyBubbleLayoutTransition;->()V HSPLorg/thoughtcrime/securesms/conversation/ClipProjectionDrawable;->()V @@ -21246,11 +21134,13 @@ HSPLorg/thoughtcrime/securesms/conversation/ConversationHeaderView$FallbackPhoto HSPLorg/thoughtcrime/securesms/conversation/ConversationHeaderView$FallbackPhotoProvider;->(Lorg/thoughtcrime/securesms/conversation/ConversationHeaderView$FallbackPhotoProvider-IA;)V HSPLorg/thoughtcrime/securesms/conversation/ConversationHeaderView;->(Landroid/content/Context;Landroid/util/AttributeSet;)V HSPLorg/thoughtcrime/securesms/conversation/ConversationHeaderView;->(Landroid/content/Context;Landroid/util/AttributeSet;I)V +HSPLorg/thoughtcrime/securesms/conversation/ConversationHeaderView;->hideDecoratorsIfContentIsNotPresent()V HSPLorg/thoughtcrime/securesms/conversation/ConversationHeaderView;->hideDescription()V +HSPLorg/thoughtcrime/securesms/conversation/ConversationHeaderView;->prependIcon(Ljava/lang/CharSequence;I)Ljava/lang/CharSequence; HSPLorg/thoughtcrime/securesms/conversation/ConversationHeaderView;->setAbout(Lorg/thoughtcrime/securesms/recipients/Recipient;)V HSPLorg/thoughtcrime/securesms/conversation/ConversationHeaderView;->setAvatar(Lorg/thoughtcrime/securesms/mms/GlideRequests;Lorg/thoughtcrime/securesms/recipients/Recipient;)V HSPLorg/thoughtcrime/securesms/conversation/ConversationHeaderView;->setLinkifyDescription(Z)V -HSPLorg/thoughtcrime/securesms/conversation/ConversationHeaderView;->setSubtitle(Ljava/lang/CharSequence;)V +HSPLorg/thoughtcrime/securesms/conversation/ConversationHeaderView;->setSubtitle(Ljava/lang/CharSequence;I)V HSPLorg/thoughtcrime/securesms/conversation/ConversationHeaderView;->setTitle(Lorg/thoughtcrime/securesms/recipients/Recipient;)Ljava/lang/String; HSPLorg/thoughtcrime/securesms/conversation/ConversationHeaderView;->showBackgroundBubble(Z)V HSPLorg/thoughtcrime/securesms/conversation/ConversationIntents$Args;->(Lorg/thoughtcrime/securesms/recipients/RecipientId;JLjava/lang/String;Landroid/net/Uri;Ljava/lang/String;Ljava/util/ArrayList;Lorg/thoughtcrime/securesms/stickers/StickerLocator;ZIIZZLorg/thoughtcrime/securesms/badges/models/Badge;JLorg/thoughtcrime/securesms/conversation/ConversationIntents$ConversationScreenType;)V @@ -21298,8 +21188,6 @@ HSPLorg/thoughtcrime/securesms/conversation/ConversationIntents;->isBubbleIntent HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$$ExternalSyntheticLambda6;->()V HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$$ExternalSyntheticLambda9;->(Lorg/thoughtcrime/securesms/conversation/ConversationItem;Lorg/thoughtcrime/securesms/recipients/RecipientId;)V HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$1;->(Lorg/thoughtcrime/securesms/conversation/ConversationItem;)V -HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$AttachmentCancelClickListener;->(Lorg/thoughtcrime/securesms/conversation/ConversationItem;)V -HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$AttachmentCancelClickListener;->(Lorg/thoughtcrime/securesms/conversation/ConversationItem;Lorg/thoughtcrime/securesms/conversation/ConversationItem$AttachmentCancelClickListener-IA;)V HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$AttachmentDownloadClickListener;->(Lorg/thoughtcrime/securesms/conversation/ConversationItem;)V HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$AttachmentDownloadClickListener;->(Lorg/thoughtcrime/securesms/conversation/ConversationItem;Lorg/thoughtcrime/securesms/conversation/ConversationItem$AttachmentDownloadClickListener-IA;)V HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$ClickListener;->(Lorg/thoughtcrime/securesms/conversation/ConversationItem;Landroid/view/View$OnClickListener;)V @@ -21309,10 +21197,8 @@ HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$LinkPreviewClickLis HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$LinkPreviewClickListener;->(Lorg/thoughtcrime/securesms/conversation/ConversationItem;Lorg/thoughtcrime/securesms/conversation/ConversationItem$LinkPreviewClickListener-IA;)V HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$PassthroughClickListener;->(Lorg/thoughtcrime/securesms/conversation/ConversationItem;)V HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$PassthroughClickListener;->(Lorg/thoughtcrime/securesms/conversation/ConversationItem;Lorg/thoughtcrime/securesms/conversation/ConversationItem$PassthroughClickListener-IA;)V -HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$PlayVideoClickListener;->-$$Nest$mcleanup(Lorg/thoughtcrime/securesms/conversation/ConversationItem$PlayVideoClickListener;)V HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$PlayVideoClickListener;->(Lorg/thoughtcrime/securesms/conversation/ConversationItem;)V HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$PlayVideoClickListener;->(Lorg/thoughtcrime/securesms/conversation/ConversationItem;Lorg/thoughtcrime/securesms/conversation/ConversationItem$PlayVideoClickListener-IA;)V -HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$PlayVideoClickListener;->cleanup()V HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$QuotedIndicatorClickListener;->(Lorg/thoughtcrime/securesms/conversation/ConversationItem;)V HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$QuotedIndicatorClickListener;->(Lorg/thoughtcrime/securesms/conversation/ConversationItem;Lorg/thoughtcrime/securesms/conversation/ConversationItem$QuotedIndicatorClickListener-IA;)V HSPLorg/thoughtcrime/securesms/conversation/ConversationItem$ScheduledIndicatorClickListener;->(Lorg/thoughtcrime/securesms/conversation/ConversationItem;)V @@ -21369,13 +21255,14 @@ HSPLorg/thoughtcrime/securesms/conversation/ConversationItem;->isStartOfMessageC HSPLorg/thoughtcrime/securesms/conversation/ConversationItem;->isStoryReaction(Lorg/thoughtcrime/securesms/database/model/MessageRecord;)Z HSPLorg/thoughtcrime/securesms/conversation/ConversationItem;->isViewOnceMessage(Lorg/thoughtcrime/securesms/database/model/MessageRecord;)Z HSPLorg/thoughtcrime/securesms/conversation/ConversationItem;->isWithinClusteringTime(Lorg/thoughtcrime/securesms/database/model/MessageRecord;Lorg/thoughtcrime/securesms/database/model/MessageRecord;)Z -HSPLorg/thoughtcrime/securesms/conversation/ConversationItem;->linkifyMessageBody(Landroid/text/Spannable;Z)V HSPLorg/thoughtcrime/securesms/conversation/ConversationItem;->onFinishInflate()V HSPLorg/thoughtcrime/securesms/conversation/ConversationItem;->onMeasure(II)V +HSPLorg/thoughtcrime/securesms/conversation/ConversationItem;->onRecipientChanged(Lorg/thoughtcrime/securesms/recipients/Recipient;)V HSPLorg/thoughtcrime/securesms/conversation/ConversationItem;->readDimen(I)I HSPLorg/thoughtcrime/securesms/conversation/ConversationItem;->readDimen(Landroid/content/Context;I)I HSPLorg/thoughtcrime/securesms/conversation/ConversationItem;->setAuthor(Lorg/thoughtcrime/securesms/database/model/MessageRecord;Lj$/util/Optional;Lj$/util/Optional;ZZ)V HSPLorg/thoughtcrime/securesms/conversation/ConversationItem;->setBodyText(Lorg/thoughtcrime/securesms/database/model/MessageRecord;Ljava/lang/String;Z)V +HSPLorg/thoughtcrime/securesms/conversation/ConversationItem;->setBubbleState(Lorg/thoughtcrime/securesms/database/model/MessageRecord;Lorg/thoughtcrime/securesms/recipients/Recipient;ZLorg/thoughtcrime/securesms/conversation/colors/Colorizer;)V HSPLorg/thoughtcrime/securesms/conversation/ConversationItem;->setContactPhoto(Lorg/thoughtcrime/securesms/recipients/Recipient;)V HSPLorg/thoughtcrime/securesms/conversation/ConversationItem;->setEventListener(Lorg/thoughtcrime/securesms/BindableConversationItem$EventListener;)V HSPLorg/thoughtcrime/securesms/conversation/ConversationItem;->setFooter(Lorg/thoughtcrime/securesms/database/model/MessageRecord;Lj$/util/Optional;Ljava/util/Locale;ZZ)V @@ -21446,9 +21333,12 @@ HSPLorg/thoughtcrime/securesms/conversation/ConversationMessage;->hasBeenQuoted( HSPLorg/thoughtcrime/securesms/conversation/ConversationMessage;->hasStyleLinks()Z HSPLorg/thoughtcrime/securesms/conversation/ConversationMessage;->hashCode()I HSPLorg/thoughtcrime/securesms/conversation/ConversationMessage;->isTextOnly(Landroid/content/Context;)Z +HSPLorg/thoughtcrime/securesms/conversation/ConversationOptionsMenu$Provider$onCreateMenu$1;->(Landroid/view/Menu;Z)V HSPLorg/thoughtcrime/securesms/conversation/ConversationOptionsMenu$Provider;->()V HSPLorg/thoughtcrime/securesms/conversation/ConversationOptionsMenu$Provider;->(Lorg/thoughtcrime/securesms/conversation/ConversationOptionsMenu$Callback;Lorg/signal/core/util/concurrent/LifecycleDisposable;Z)V HSPLorg/thoughtcrime/securesms/conversation/ConversationOptionsMenu$Provider;->(Lorg/thoughtcrime/securesms/conversation/ConversationOptionsMenu$Callback;Lorg/signal/core/util/concurrent/LifecycleDisposable;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/conversation/ConversationOptionsMenu$Provider;->applyTitleSpan(Landroid/view/MenuItem;Ljava/lang/Object;)V +HSPLorg/thoughtcrime/securesms/conversation/ConversationOptionsMenu$Provider;->hideMenuItem(Landroid/view/Menu;I)V HSPLorg/thoughtcrime/securesms/conversation/ConversationOptionsMenu$Provider;->onCreateMenu(Landroid/view/Menu;Landroid/view/MenuInflater;)V HSPLorg/thoughtcrime/securesms/conversation/ConversationOptionsMenu$Provider;->setAfterFirstRenderMode(Z)V HSPLorg/thoughtcrime/securesms/conversation/ConversationOptionsMenu$Snapshot;->()V @@ -21464,9 +21354,6 @@ HSPLorg/thoughtcrime/securesms/conversation/ConversationOptionsMenu$Snapshot;->c HSPLorg/thoughtcrime/securesms/conversation/ConversationOptionsMenu$Snapshot;->component7()Z HSPLorg/thoughtcrime/securesms/conversation/ConversationOptionsMenu$Snapshot;->component8()I HSPLorg/thoughtcrime/securesms/conversation/ConversationOptionsMenu$Snapshot;->component9()J -HSPLorg/thoughtcrime/securesms/conversation/ConversationOptionsMenu;->()V -HSPLorg/thoughtcrime/securesms/conversation/ConversationOptionsMenu;->()V -HSPLorg/thoughtcrime/securesms/conversation/ConversationOptionsMenu;->access$getTAG$p()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/conversation/ConversationRepository;->()V HSPLorg/thoughtcrime/securesms/conversation/ConversationRepository;->()V HSPLorg/thoughtcrime/securesms/conversation/ConversationRepository;->getConversationData(JLorg/thoughtcrime/securesms/recipients/Recipient;I)Lorg/thoughtcrime/securesms/conversation/ConversationData; @@ -21489,6 +21376,7 @@ HSPLorg/thoughtcrime/securesms/conversation/ConversationTitleView;->setOnStoryRi HSPLorg/thoughtcrime/securesms/conversation/ConversationTitleView;->setRecipientTitle(Lorg/thoughtcrime/securesms/recipients/Recipient;)V HSPLorg/thoughtcrime/securesms/conversation/ConversationTitleView;->setStoryRingFromState(Lorg/thoughtcrime/securesms/database/model/StoryViewState;)V HSPLorg/thoughtcrime/securesms/conversation/ConversationTitleView;->setTitle(Lorg/thoughtcrime/securesms/mms/GlideRequests;Lorg/thoughtcrime/securesms/recipients/Recipient;)V +HSPLorg/thoughtcrime/securesms/conversation/ConversationTitleView;->setVerified(Z)V HSPLorg/thoughtcrime/securesms/conversation/ConversationTitleView;->updateSubtitleVisibility()V HSPLorg/thoughtcrime/securesms/conversation/ConversationTitleView;->updateVerifiedSubtitleVisibility()V HSPLorg/thoughtcrime/securesms/conversation/ConversationUpdateItem$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/conversation/ConversationUpdateItem;)V @@ -21518,16 +21406,13 @@ HSPLorg/thoughtcrime/securesms/conversation/ConversationUpdateTick;->onStart(Lan HSPLorg/thoughtcrime/securesms/conversation/MarkReadHelper$$ExternalSyntheticLambda0;->()V HSPLorg/thoughtcrime/securesms/conversation/MarkReadHelper$$ExternalSyntheticLambda1;->()V HSPLorg/thoughtcrime/securesms/conversation/MarkReadHelper$$ExternalSyntheticLambda2;->(Lorg/thoughtcrime/securesms/conversation/MarkReadHelper;J)V -HSPLorg/thoughtcrime/securesms/conversation/MarkReadHelper$$ExternalSyntheticLambda2;->run()V HSPLorg/thoughtcrime/securesms/conversation/MarkReadHelper$$ExternalSyntheticLambda3;->(Lorg/thoughtcrime/securesms/conversation/MarkReadHelper;J)V HSPLorg/thoughtcrime/securesms/conversation/MarkReadHelper$$ExternalSyntheticLambda3;->run()V HSPLorg/thoughtcrime/securesms/conversation/MarkReadHelper;->$r8$lambda$gcFI10LhFCaBEmJzQp8t_xBcU8U(Lorg/thoughtcrime/securesms/conversation/MarkReadHelper;J)V -HSPLorg/thoughtcrime/securesms/conversation/MarkReadHelper;->$r8$lambda$h27hRrs_Rwv2sGlsmjqcW0dGIZI(Lorg/thoughtcrime/securesms/conversation/MarkReadHelper;J)V HSPLorg/thoughtcrime/securesms/conversation/MarkReadHelper;->()V HSPLorg/thoughtcrime/securesms/conversation/MarkReadHelper;->(Lorg/thoughtcrime/securesms/notifications/v2/ConversationId;Landroid/content/Context;Landroidx/lifecycle/LifecycleOwner;)V HSPLorg/thoughtcrime/securesms/conversation/MarkReadHelper;->getLatestTimestamp(Lorg/thoughtcrime/securesms/conversation/ConversationAdapterBridge;Landroidx/recyclerview/widget/LinearLayoutManager;)Lj$/util/Optional; HSPLorg/thoughtcrime/securesms/conversation/MarkReadHelper;->ignoreViewReveals()V -HSPLorg/thoughtcrime/securesms/conversation/MarkReadHelper;->lambda$onViewsRevealed$0(J)V HSPLorg/thoughtcrime/securesms/conversation/MarkReadHelper;->lambda$onViewsRevealed$1(J)V HSPLorg/thoughtcrime/securesms/conversation/MarkReadHelper;->onViewsRevealed(J)V HSPLorg/thoughtcrime/securesms/conversation/MarkReadHelper;->stopIgnoringViewReveals(Ljava/lang/Long;)V @@ -21559,6 +21444,10 @@ HSPLorg/thoughtcrime/securesms/conversation/MessageStyler$Result;->getHasStyleLi HSPLorg/thoughtcrime/securesms/conversation/MessageStyler$Result;->none()Lorg/thoughtcrime/securesms/conversation/MessageStyler$Result; HSPLorg/thoughtcrime/securesms/conversation/MessageStyler;->()V HSPLorg/thoughtcrime/securesms/conversation/MessageStyler;->()V +HSPLorg/thoughtcrime/securesms/conversation/MessageStyler;->boldStyle()Landroid/text/style/CharacterStyle; +HSPLorg/thoughtcrime/securesms/conversation/MessageStyler;->italicStyle()Landroid/text/style/CharacterStyle; +HSPLorg/thoughtcrime/securesms/conversation/MessageStyler;->monoStyle()Landroid/text/style/CharacterStyle; +HSPLorg/thoughtcrime/securesms/conversation/MessageStyler;->strikethroughStyle()Landroid/text/style/CharacterStyle; HSPLorg/thoughtcrime/securesms/conversation/MessageStyler;->style$default(Ljava/lang/Object;Lorg/thoughtcrime/securesms/database/model/databaseprotos/BodyRangeList;Landroid/text/Spannable;ZILjava/lang/Object;)Lorg/thoughtcrime/securesms/conversation/MessageStyler$Result; HSPLorg/thoughtcrime/securesms/conversation/MessageStyler;->style(Ljava/lang/Object;Lorg/thoughtcrime/securesms/database/model/databaseprotos/BodyRangeList;Landroid/text/Spannable;)Lorg/thoughtcrime/securesms/conversation/MessageStyler$Result; HSPLorg/thoughtcrime/securesms/conversation/MessageStyler;->style(Ljava/lang/Object;Lorg/thoughtcrime/securesms/database/model/databaseprotos/BodyRangeList;Landroid/text/Spannable;Z)Lorg/thoughtcrime/securesms/conversation/MessageStyler$Result; @@ -21576,6 +21465,7 @@ HSPLorg/thoughtcrime/securesms/conversation/VoiceNoteDraftView;->()V HSPLorg/thoughtcrime/securesms/conversation/VoiceNoteDraftView;->(Landroid/content/Context;Landroid/util/AttributeSet;)V HSPLorg/thoughtcrime/securesms/conversation/VoiceNoteDraftView;->(Landroid/content/Context;Landroid/util/AttributeSet;I)V HSPLorg/thoughtcrime/securesms/conversation/VoiceNoteDraftView;->(Landroid/content/Context;Landroid/util/AttributeSet;IILkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/conversation/VoiceNoteDraftView;->clearDraft()V HSPLorg/thoughtcrime/securesms/conversation/VoiceNoteDraftView;->getPlaybackStateObserver()Landroidx/lifecycle/Observer; HSPLorg/thoughtcrime/securesms/conversation/VoiceNoteDraftView;->setListener(Lorg/thoughtcrime/securesms/conversation/VoiceNoteDraftView$Listener;)V HSPLorg/thoughtcrime/securesms/conversation/VoiceRecorderWakeLock;->()V @@ -21583,12 +21473,15 @@ HSPLorg/thoughtcrime/securesms/conversation/VoiceRecorderWakeLock;->(Landr HSPLorg/thoughtcrime/securesms/conversation/VoiceRecorderWakeLock;->onCreate(Landroidx/lifecycle/LifecycleOwner;)V HSPLorg/thoughtcrime/securesms/conversation/VoiceRecorderWakeLock;->onResume(Landroidx/lifecycle/LifecycleOwner;)V HSPLorg/thoughtcrime/securesms/conversation/VoiceRecorderWakeLock;->onStart(Landroidx/lifecycle/LifecycleOwner;)V +HSPLorg/thoughtcrime/securesms/conversation/clicklisteners/AttachmentCancelClickListener$Companion;->()V +HSPLorg/thoughtcrime/securesms/conversation/clicklisteners/AttachmentCancelClickListener$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/conversation/clicklisteners/AttachmentCancelClickListener;->()V +HSPLorg/thoughtcrime/securesms/conversation/clicklisteners/AttachmentCancelClickListener;->()V HSPLorg/thoughtcrime/securesms/conversation/colors/AvatarColor;->$values()[Lorg/thoughtcrime/securesms/conversation/colors/AvatarColor; HSPLorg/thoughtcrime/securesms/conversation/colors/AvatarColor;->()V HSPLorg/thoughtcrime/securesms/conversation/colors/AvatarColor;->(Ljava/lang/String;ILjava/lang/String;I)V HSPLorg/thoughtcrime/securesms/conversation/colors/AvatarColor;->colorInt()I HSPLorg/thoughtcrime/securesms/conversation/colors/AvatarColor;->deserialize(Ljava/lang/String;)Lorg/thoughtcrime/securesms/conversation/colors/AvatarColor; -HSPLorg/thoughtcrime/securesms/conversation/colors/AvatarColor;->random()Lorg/thoughtcrime/securesms/conversation/colors/AvatarColor; HSPLorg/thoughtcrime/securesms/conversation/colors/AvatarColor;->serialize()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/conversation/colors/AvatarColor;->values()[Lorg/thoughtcrime/securesms/conversation/colors/AvatarColor; HSPLorg/thoughtcrime/securesms/conversation/colors/AvatarColorHash$$ExternalSyntheticBackport0;->m(II)I @@ -21620,6 +21513,8 @@ HSPLorg/thoughtcrime/securesms/conversation/colors/ChatColors$Id$BuiltIn;->()V HSPLorg/thoughtcrime/securesms/conversation/colors/ChatColors$Id$Companion;->()V HSPLorg/thoughtcrime/securesms/conversation/colors/ChatColors$Id$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/conversation/colors/ChatColors$Id$NotSet;->()V +HSPLorg/thoughtcrime/securesms/conversation/colors/ChatColors$Id$NotSet;->()V HSPLorg/thoughtcrime/securesms/conversation/colors/ChatColors$Id;->()V HSPLorg/thoughtcrime/securesms/conversation/colors/ChatColors$Id;->(J)V HSPLorg/thoughtcrime/securesms/conversation/colors/ChatColors$Id;->(JLkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -21662,8 +21557,6 @@ HSPLorg/thoughtcrime/securesms/conversation/colors/RecyclerViewColorizer;->acces HSPLorg/thoughtcrime/securesms/conversation/colors/RecyclerViewColorizer;->access$setUseLayer$p(Lorg/thoughtcrime/securesms/conversation/colors/RecyclerViewColorizer;Z)V HSPLorg/thoughtcrime/securesms/conversation/colors/RecyclerViewColorizer;->getLayoutManager()Landroidx/recyclerview/widget/LinearLayoutManager; HSPLorg/thoughtcrime/securesms/conversation/colors/RecyclerViewColorizer;->setChatColors(Lorg/thoughtcrime/securesms/conversation/colors/ChatColors;)V -HSPLorg/thoughtcrime/securesms/conversation/drafts/DraftRepository$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/database/DraftTable$Drafts;Lorg/thoughtcrime/securesms/conversation/drafts/DraftRepository;J)V -HSPLorg/thoughtcrime/securesms/conversation/drafts/DraftRepository$$ExternalSyntheticLambda0;->run()V HSPLorg/thoughtcrime/securesms/conversation/drafts/DraftRepository$Companion;->()V HSPLorg/thoughtcrime/securesms/conversation/drafts/DraftRepository$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/conversation/drafts/DraftRepository$DatabaseDraft;->()V @@ -21673,7 +21566,6 @@ HSPLorg/thoughtcrime/securesms/conversation/drafts/DraftRepository$DatabaseDraft HSPLorg/thoughtcrime/securesms/conversation/drafts/DraftRepository$getShareOrDraftData$1;->(Lorg/thoughtcrime/securesms/conversation/drafts/DraftRepository;J)V HSPLorg/thoughtcrime/securesms/conversation/drafts/DraftRepository$getShareOrDraftData$1;->invoke()Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/conversation/drafts/DraftRepository$getShareOrDraftData$1;->invoke()Lkotlin/Pair; -HSPLorg/thoughtcrime/securesms/conversation/drafts/DraftRepository;->$r8$lambda$SLYPkhFM2MVtyCpgHajSG6aOkdY(Lorg/thoughtcrime/securesms/database/DraftTable$Drafts;Lorg/thoughtcrime/securesms/conversation/drafts/DraftRepository;J)V HSPLorg/thoughtcrime/securesms/conversation/drafts/DraftRepository;->()V HSPLorg/thoughtcrime/securesms/conversation/drafts/DraftRepository;->(Landroid/content/Context;Lorg/thoughtcrime/securesms/database/ThreadTable;Lorg/thoughtcrime/securesms/database/DraftTable;Ljava/util/concurrent/Executor;Lorg/thoughtcrime/securesms/conversation/ConversationIntents$Args;)V HSPLorg/thoughtcrime/securesms/conversation/drafts/DraftRepository;->(Landroid/content/Context;Lorg/thoughtcrime/securesms/database/ThreadTable;Lorg/thoughtcrime/securesms/database/DraftTable;Ljava/util/concurrent/Executor;Lorg/thoughtcrime/securesms/conversation/ConversationIntents$Args;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -21681,19 +21573,11 @@ HSPLorg/thoughtcrime/securesms/conversation/drafts/DraftRepository;->access$getS HSPLorg/thoughtcrime/securesms/conversation/drafts/DraftRepository;->getShareOrDraftData(J)Lio/reactivex/rxjava3/core/Maybe; HSPLorg/thoughtcrime/securesms/conversation/drafts/DraftRepository;->getShareOrDraftDataInternal(J)Lkotlin/Pair; HSPLorg/thoughtcrime/securesms/conversation/drafts/DraftRepository;->loadDraftsInternal(J)Lorg/thoughtcrime/securesms/conversation/drafts/DraftRepository$DatabaseDraft; -HSPLorg/thoughtcrime/securesms/conversation/drafts/DraftRepository;->saveDrafts$lambda$9(Lorg/thoughtcrime/securesms/database/DraftTable$Drafts;Lorg/thoughtcrime/securesms/conversation/drafts/DraftRepository;J)V -HSPLorg/thoughtcrime/securesms/conversation/drafts/DraftRepository;->saveDrafts(JLorg/thoughtcrime/securesms/database/DraftTable$Drafts;)V HSPLorg/thoughtcrime/securesms/conversation/drafts/DraftState;->()V HSPLorg/thoughtcrime/securesms/conversation/drafts/DraftState;->(JLorg/thoughtcrime/securesms/database/DraftTable$Draft;Lorg/thoughtcrime/securesms/database/DraftTable$Draft;Lorg/thoughtcrime/securesms/database/DraftTable$Draft;Lorg/thoughtcrime/securesms/database/DraftTable$Draft;Lorg/thoughtcrime/securesms/database/DraftTable$Draft;Lorg/thoughtcrime/securesms/database/DraftTable$Draft;)V HSPLorg/thoughtcrime/securesms/conversation/drafts/DraftState;->(JLorg/thoughtcrime/securesms/database/DraftTable$Draft;Lorg/thoughtcrime/securesms/database/DraftTable$Draft;Lorg/thoughtcrime/securesms/database/DraftTable$Draft;Lorg/thoughtcrime/securesms/database/DraftTable$Draft;Lorg/thoughtcrime/securesms/database/DraftTable$Draft;Lorg/thoughtcrime/securesms/database/DraftTable$Draft;ILkotlin/jvm/internal/DefaultConstructorMarker;)V -HSPLorg/thoughtcrime/securesms/conversation/drafts/DraftState;->copy(JLorg/thoughtcrime/securesms/database/DraftTable$Draft;Lorg/thoughtcrime/securesms/database/DraftTable$Draft;Lorg/thoughtcrime/securesms/database/DraftTable$Draft;Lorg/thoughtcrime/securesms/database/DraftTable$Draft;Lorg/thoughtcrime/securesms/database/DraftTable$Draft;Lorg/thoughtcrime/securesms/database/DraftTable$Draft;)Lorg/thoughtcrime/securesms/conversation/drafts/DraftState; -HSPLorg/thoughtcrime/securesms/conversation/drafts/DraftState;->copyAndSetDrafts$default(Lorg/thoughtcrime/securesms/conversation/drafts/DraftState;JLorg/thoughtcrime/securesms/database/DraftTable$Drafts;ILjava/lang/Object;)Lorg/thoughtcrime/securesms/conversation/drafts/DraftState; -HSPLorg/thoughtcrime/securesms/conversation/drafts/DraftState;->copyAndSetDrafts(JLorg/thoughtcrime/securesms/database/DraftTable$Drafts;)Lorg/thoughtcrime/securesms/conversation/drafts/DraftState; -HSPLorg/thoughtcrime/securesms/conversation/drafts/DraftState;->getThreadId()J -HSPLorg/thoughtcrime/securesms/conversation/drafts/DraftState;->toDrafts()Lorg/thoughtcrime/securesms/database/DraftTable$Drafts; +HSPLorg/thoughtcrime/securesms/conversation/drafts/DraftState;->getVoiceNoteDraft()Lorg/thoughtcrime/securesms/database/DraftTable$Draft; HSPLorg/thoughtcrime/securesms/conversation/drafts/DraftViewModel$loadShareOrDraftData$1$1;->(Lorg/thoughtcrime/securesms/conversation/drafts/DraftViewModel;Lorg/thoughtcrime/securesms/database/DraftTable$Drafts;)V -HSPLorg/thoughtcrime/securesms/conversation/drafts/DraftViewModel$loadShareOrDraftData$1$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; -HSPLorg/thoughtcrime/securesms/conversation/drafts/DraftViewModel$loadShareOrDraftData$1$1;->invoke(Lorg/thoughtcrime/securesms/conversation/drafts/DraftState;)Lorg/thoughtcrime/securesms/conversation/drafts/DraftState; HSPLorg/thoughtcrime/securesms/conversation/drafts/DraftViewModel$loadShareOrDraftData$1;->(Lorg/thoughtcrime/securesms/conversation/drafts/DraftViewModel;)V HSPLorg/thoughtcrime/securesms/conversation/drafts/DraftViewModel$loadShareOrDraftData$1;->accept(Ljava/lang/Object;)V HSPLorg/thoughtcrime/securesms/conversation/drafts/DraftViewModel$loadShareOrDraftData$1;->accept(Lkotlin/Pair;)V @@ -21704,10 +21588,9 @@ HSPLorg/thoughtcrime/securesms/conversation/drafts/DraftViewModel$loadShareOrDra HSPLorg/thoughtcrime/securesms/conversation/drafts/DraftViewModel;->()V HSPLorg/thoughtcrime/securesms/conversation/drafts/DraftViewModel;->(JLorg/thoughtcrime/securesms/conversation/drafts/DraftRepository;)V HSPLorg/thoughtcrime/securesms/conversation/drafts/DraftViewModel;->access$getStore$p(Lorg/thoughtcrime/securesms/conversation/drafts/DraftViewModel;)Lorg/thoughtcrime/securesms/util/rx/RxStore; -HSPLorg/thoughtcrime/securesms/conversation/drafts/DraftViewModel;->access$saveDrafts(Lorg/thoughtcrime/securesms/conversation/drafts/DraftViewModel;Lorg/thoughtcrime/securesms/conversation/drafts/DraftState;)Lorg/thoughtcrime/securesms/conversation/drafts/DraftState; HSPLorg/thoughtcrime/securesms/conversation/drafts/DraftViewModel;->getState()Lio/reactivex/rxjava3/core/Flowable; +HSPLorg/thoughtcrime/securesms/conversation/drafts/DraftViewModel;->getVoiceNoteDraft()Lorg/thoughtcrime/securesms/database/DraftTable$Draft; HSPLorg/thoughtcrime/securesms/conversation/drafts/DraftViewModel;->loadShareOrDraftData(J)Lio/reactivex/rxjava3/core/Maybe; -HSPLorg/thoughtcrime/securesms/conversation/drafts/DraftViewModel;->saveDrafts(Lorg/thoughtcrime/securesms/conversation/drafts/DraftState;)Lorg/thoughtcrime/securesms/conversation/drafts/DraftState; HSPLorg/thoughtcrime/securesms/conversation/mutiselect/ConversationItemAnimator$$ExternalSyntheticLambda1;->(Landroidx/recyclerview/widget/RecyclerView;)V HSPLorg/thoughtcrime/securesms/conversation/mutiselect/ConversationItemAnimator$$ExternalSyntheticLambda1;->run()V HSPLorg/thoughtcrime/securesms/conversation/mutiselect/ConversationItemAnimator$Companion;->()V @@ -21775,8 +21658,6 @@ HSPLorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectRecyclerView$C HSPLorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectRecyclerView$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectRecyclerView;->()V HSPLorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectRecyclerView;->(Landroid/content/Context;Landroid/util/AttributeSet;)V -HSPLorg/thoughtcrime/securesms/conversation/ui/error/SafetyNumberChangeDialog$$ExternalSyntheticLambda5;->()V -HSPLorg/thoughtcrime/securesms/conversation/ui/error/SafetyNumberChangeDialog$$ExternalSyntheticLambda5;->apply(Ljava/lang/Object;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/conversation/ui/inlinequery/InlineQueryResultsControllerV2$$ExternalSyntheticLambda1;->(Lorg/thoughtcrime/securesms/conversation/ui/inlinequery/InlineQueryResultsControllerV2;)V HSPLorg/thoughtcrime/securesms/conversation/ui/inlinequery/InlineQueryResultsControllerV2$1;->(Lorg/thoughtcrime/securesms/conversation/ui/inlinequery/InlineQueryResultsControllerV2;)V HSPLorg/thoughtcrime/securesms/conversation/ui/inlinequery/InlineQueryResultsControllerV2$2;->(Lorg/thoughtcrime/securesms/conversation/ui/inlinequery/InlineQueryResultsControllerV2;)V @@ -21871,9 +21752,9 @@ HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$$ExternalSy HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$$ExternalSyntheticLambda4;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$$ExternalSyntheticLambda5;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$$ExternalSyntheticLambda5;->createViewHolder(Landroid/view/ViewGroup;)Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingViewHolder; -HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$$ExternalSyntheticLambda8;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;)V -HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$$ExternalSyntheticLambda9;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;)V -HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$$ExternalSyntheticLambda9;->createViewHolder(Landroid/view/ViewGroup;)Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingViewHolder; +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$$ExternalSyntheticLambda6;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$$ExternalSyntheticLambda7;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$$ExternalSyntheticLambda7;->createViewHolder(Landroid/view/ViewGroup;)Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingViewHolder; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$Companion;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ConversationViewHolder$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ConversationViewHolder;)V @@ -21890,27 +21771,24 @@ HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$Conversatio HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$IncomingMediaViewHolder;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;Landroid/view/View;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$IncomingMediaViewHolder;->bind(Ljava/lang/Object;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$IncomingMediaViewHolder;->bind(Lorg/thoughtcrime/securesms/conversation/v2/data/IncomingMedia;)V -HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$IncomingTextOnlyViewHolder;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;Landroid/view/View;)V -HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$IncomingTextOnlyViewHolder;->bind(Ljava/lang/Object;)V -HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$IncomingTextOnlyViewHolder;->bind(Lorg/thoughtcrime/securesms/conversation/v2/data/IncomingTextOnly;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$OnScrollStateChangedListener;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ThreadHeaderViewHolder$$ExternalSyntheticLambda0;->(Lkotlin/jvm/functions/Function1;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ThreadHeaderViewHolder$$ExternalSyntheticLambda0;->apply(Ljava/lang/Object;)Ljava/lang/Object; -HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ThreadHeaderViewHolder$bind$subtitle$1;->()V -HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ThreadHeaderViewHolder$bind$subtitle$1;->()V -HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ThreadHeaderViewHolder$bind$subtitle$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; -HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ThreadHeaderViewHolder$bind$subtitle$1;->invoke(Ljava/lang/String;)Ljava/lang/String; -HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ThreadHeaderViewHolder;->$r8$lambda$IF2igntoyr54qg7R4qDyHaeULCk(Lkotlin/jvm/functions/Function1;Ljava/lang/Object;)Ljava/lang/String; +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ThreadHeaderViewHolder$bind$subtitle$2;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ThreadHeaderViewHolder$bind$subtitle$2;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ThreadHeaderViewHolder$bind$subtitle$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ThreadHeaderViewHolder$bind$subtitle$2;->invoke(Ljava/lang/String;)Ljava/lang/String; +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ThreadHeaderViewHolder;->$r8$lambda$RwrnJe2SKX6YQ0B5PDojQRbXNOc(Lkotlin/jvm/functions/Function1;Ljava/lang/Object;)Ljava/lang/String; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ThreadHeaderViewHolder;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;Landroid/view/View;)V -HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ThreadHeaderViewHolder;->bind$lambda$0(Lkotlin/jvm/functions/Function1;Ljava/lang/Object;)Ljava/lang/String; +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ThreadHeaderViewHolder;->bind$lambda$1(Lkotlin/jvm/functions/Function1;Ljava/lang/Object;)Ljava/lang/String; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ThreadHeaderViewHolder;->bind(Ljava/lang/Object;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ThreadHeaderViewHolder;->bind(Lorg/thoughtcrime/securesms/conversation/v2/data/ThreadHeader;)V -HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;->$r8$lambda$5Xd-4X5sWkP72M6nDvFpF2ZqeJY(Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;Landroid/view/ViewGroup;)Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingViewHolder; +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;->$r8$lambda$0GW66dll143qhTHiVUdlBHolclI(Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;Landroid/view/ViewGroup;)Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingViewHolder; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;->$r8$lambda$u2AJxgyeBquqI1nF9ok3s6g0b5Q(Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;Landroid/view/ViewGroup;)Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingViewHolder; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;->(Landroidx/lifecycle/LifecycleOwner;Lorg/thoughtcrime/securesms/mms/GlideRequests;Lorg/thoughtcrime/securesms/conversation/ConversationAdapter$ItemClickListener;ZLorg/thoughtcrime/securesms/conversation/colors/Colorizer;Lkotlin/jvm/functions/Function1;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;->_init_$lambda$4(Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;Landroid/view/ViewGroup;)Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingViewHolder; -HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;->_init_$lambda$8(Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;Landroid/view/ViewGroup;)Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingViewHolder; +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;->_init_$lambda$6(Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;Landroid/view/ViewGroup;)Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingViewHolder; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;->access$getColorizer$p(Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;)Lorg/thoughtcrime/securesms/conversation/colors/Colorizer; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;->access$getCondensedMode$p(Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;)Lorg/thoughtcrime/securesms/conversation/ConversationItemDisplayMode; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;->access$getHasWallpaper$p(Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;)Z @@ -21919,11 +21797,16 @@ HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;->access$ge HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;->access$get_selected$p(Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;)Ljava/util/HashSet; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;->consumePulseRequest()Lorg/thoughtcrime/securesms/conversation/ConversationAdapterBridge$PulseRequest; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;->getClickListener()Lorg/thoughtcrime/securesms/conversation/ConversationAdapter$ItemClickListener; +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;->getColorizer()Lorg/thoughtcrime/securesms/conversation/colors/Colorizer; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;->getConversationMessage(I)Lorg/thoughtcrime/securesms/conversation/ConversationMessage; +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;->getDisplayMode()Lorg/thoughtcrime/securesms/conversation/ConversationItemDisplayMode; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;->getGlideRequests()Lorg/thoughtcrime/securesms/mms/GlideRequests; +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;->getNextMessage(I)Lorg/thoughtcrime/securesms/database/model/MessageRecord; +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;->getPreviousMessage(I)Lorg/thoughtcrime/securesms/database/model/MessageRecord; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;->getSearchQuery()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;->getSelectedItems()Ljava/util/Set; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;->hasNoConversationMessages()Z +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;->hasWallpaper()Z HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;->isMessageRequestAccepted()Z HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;->isParentInScroll()Z HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;->onAttachedToRecyclerView(Landroidx/recyclerview/widget/RecyclerView;)V @@ -21933,8 +21816,14 @@ HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;->setMessag HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;->setSearchQuery(Ljava/lang/String;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;->updateSearchQuery(Ljava/lang/String;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView$reminderStub$2;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView$reminderStub$2;->invoke()Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView$reminderStub$2;->invoke()Lorg/thoughtcrime/securesms/util/views/Stub; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView$reviewBannerStub$2;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView$reviewBannerStub$2;->invoke()Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView$reviewBannerStub$2;->invoke()Lorg/thoughtcrime/securesms/util/views/Stub; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView$unverifiedBannerStub$2;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView$unverifiedBannerStub$2;->invoke()Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView$unverifiedBannerStub$2;->invoke()Lorg/thoughtcrime/securesms/util/views/Stub; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView$voiceNotePlayerStub$2;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView$voiceNotePlayerStub$2;->invoke()Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView$voiceNotePlayerStub$2;->invoke()Lorg/thoughtcrime/securesms/util/views/Stub; @@ -21942,7 +21831,13 @@ HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView;-> HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView;->(Landroid/content/Context;Landroid/util/AttributeSet;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView;->(Landroid/content/Context;Landroid/util/AttributeSet;I)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView;->(Landroid/content/Context;Landroid/util/AttributeSet;IILkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView;->clearReminder()V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView;->clearRequestReview()V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView;->clearUnverifiedBanner()V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView;->clearVoiceNotePlayer()V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView;->getReminderStub()Lorg/thoughtcrime/securesms/util/views/Stub; +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView;->getReviewBannerStub()Lorg/thoughtcrime/securesms/util/views/Stub; +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView;->getUnverifiedBannerStub()Lorg/thoughtcrime/securesms/util/views/Stub; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView;->getVoiceNotePlayerStub()Lorg/thoughtcrime/securesms/util/views/Stub; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView;->hide(Lorg/thoughtcrime/securesms/util/views/Stub;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView;->setListener(Lorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView$Listener;)V @@ -21967,8 +21862,12 @@ HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$Companion;-> HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ComposeTextEventsListener;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ConversationBannerListener;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ConversationItemClickListener;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ConversationOptionsMenuCallback$onOptionsMenuCreated$1;->(Landroidx/appcompat/widget/SearchView;Landroidx/appcompat/widget/SearchView$OnQueryTextListener;Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;Landroid/view/Menu;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ConversationOptionsMenuCallback$onOptionsMenuCreated$queryListener$1;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ConversationOptionsMenuCallback;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ConversationOptionsMenuCallback;->clearExpiring()V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ConversationOptionsMenuCallback;->getSnapshot()Lorg/thoughtcrime/securesms/conversation/ConversationOptionsMenu$Snapshot; +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ConversationOptionsMenuCallback;->onOptionsMenuCreated(Landroid/view/Menu;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$DataObserver;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$DisabledInputListener;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$InputPanelListener;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)V @@ -22016,20 +21915,34 @@ HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$conversation HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$conversationRecipientRepository$2;->invoke()Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$conversationRecipientRepository$2;->invoke()Lorg/thoughtcrime/securesms/conversation/v2/ConversationRecipientRepository; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$10;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$10;->invoke(Lj$/util/Optional;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$10;->invoke(Ljava/lang/Object;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$11;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$11;->invoke(Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$11;->invoke(Lorg/thoughtcrime/securesms/conversation/v2/IdentityRecordsState;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$12;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$12;->invoke(Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$12;->invoke(Lorg/thoughtcrime/securesms/conversation/v2/RequestReviewState;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$14;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$15;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$15;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$16;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$16;->accept(Ljava/lang/Object;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$16;->accept(Lorg/thoughtcrime/securesms/conversation/drafts/DraftState;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$17;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$17;->invoke(I)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$17;->invoke(Ljava/lang/Object;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$18;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$18;->invoke(Lj$/util/Optional;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$18;->invoke(Ljava/lang/Object;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$1;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$1;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$2;->(Ljava/lang/Object;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$2;->invoke(Lorg/thoughtcrime/securesms/recipients/Recipient;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$3;->(Ljava/lang/Object;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$3;->invoke(Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$3;->invoke(Lorg/thoughtcrime/securesms/conversation/v2/ConversationScrollButtonState;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$4;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$attachListener$1;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$conversationUpdateTick$1;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)V @@ -22056,6 +21969,8 @@ HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$initializeCo HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$initializeInlineSearch$1$1;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$initializeInlineSearch$2;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$initializeLinkPreviews$1;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$initializeLinkPreviews$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$initializeLinkPreviews$1;->invoke(Lorg/thoughtcrime/securesms/linkpreview/LinkPreviewState;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$initializeSearch$1;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$initializeSearch$2;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$initializeSearch$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object; @@ -22114,6 +22029,8 @@ HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$onViewCreate HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$onViewCreated$conversationToolbarOnScrollHelper$1;->(Ljava/lang/Object;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$onViewCreated$conversationToolbarOnScrollHelper$1;->get()Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$presentGroupCallJoinButton$2;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$presentGroupCallJoinButton$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$presentGroupCallJoinButton$2;->invoke(Lorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallState;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$presentStoryRing$1;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$presentStoryRing$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$presentStoryRing$1;->invoke(Lorg/thoughtcrime/securesms/database/model/StoryViewState;)V @@ -22127,6 +22044,8 @@ HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$registerForR HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$sam$androidx_lifecycle_Observer$0;->(Lkotlin/jvm/functions/Function1;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$sam$androidx_lifecycle_Observer$0;->onChanged(Ljava/lang/Object;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$scheduledMessagesStub$2;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$scheduledMessagesStub$2;->invoke()Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$scheduledMessagesStub$2;->invoke()Lorg/thoughtcrime/securesms/util/views/Stub; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$searchViewModel$2;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$searchViewModel$2;->invoke()Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$searchViewModel$2;->invoke()Lorg/thoughtcrime/securesms/conversation/ConversationSearchViewModel; @@ -22198,13 +22117,23 @@ HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->access$get HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->access$getMarkReadHelper$p(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)Lorg/thoughtcrime/securesms/conversation/MarkReadHelper; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->access$getMessageRequestRepository(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)Lorg/thoughtcrime/securesms/messagerequests/MessageRequestRepository; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->access$getScrollToPositionDelegate$p(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)Lorg/thoughtcrime/securesms/components/ScrollToPositionDelegate; +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->access$getSearchMenuItem$p(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)Landroid/view/MenuItem; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->access$getThreadHeaderMarginDecoration$p(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ThreadHeaderMarginDecoration; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->access$getViewModel(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)Lorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel; +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->access$handleScheduledMessagesCountChange(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;I)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->access$invalidateOptionsMenu(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->access$isScrolledToBottom(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)Z +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->access$isSearchRequested$p(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)Z HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->access$moveToStartPosition(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;Lorg/thoughtcrime/securesms/conversation/ConversationData;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->access$onRecipientChanged(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;Lorg/thoughtcrime/securesms/recipients/Recipient;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->access$presentIdentityRecordsState(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;Lorg/thoughtcrime/securesms/conversation/v2/IdentityRecordsState;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->access$presentInputReadyState(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;Lorg/thoughtcrime/securesms/conversation/v2/InputReadyState;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->access$presentRequestReviewState(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;Lorg/thoughtcrime/securesms/conversation/v2/RequestReviewState;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->access$presentScrollButtons(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;Lorg/thoughtcrime/securesms/conversation/v2/ConversationScrollButtonState;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->access$setAnimationsAllowed$p(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;Z)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->access$setSearchMenuItem$p(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;Landroid/view/MenuItem;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->access$updateMessageRequestAcceptedState(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;Z)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->access$updateToggleButtonState(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->doAfterFirstRender()V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->getArgs()Lorg/thoughtcrime/securesms/conversation/ConversationIntents$Args; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->getBinding()Lorg/thoughtcrime/securesms/databinding/V2ConversationFragmentBinding; @@ -22220,6 +22149,7 @@ HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->getKeyboar HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->getLinkPreviewViewModel()Lorg/thoughtcrime/securesms/linkpreview/LinkPreviewViewModelV2; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->getMessageRequestRepository()Lorg/thoughtcrime/securesms/messagerequests/MessageRequestRepository; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->getMotionEventRelay()Lorg/thoughtcrime/securesms/conversation/v2/MotionEventRelay; +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->getScheduledMessagesStub()Lorg/thoughtcrime/securesms/util/views/Stub; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->getSearchNav()Lorg/thoughtcrime/securesms/components/ConversationSearchBottomBar; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->getSearchViewModel()Lorg/thoughtcrime/securesms/conversation/ConversationSearchViewModel; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->getSendButton()Lorg/thoughtcrime/securesms/components/SendButton; @@ -22228,6 +22158,7 @@ HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->getShareDa HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->getStickerViewModel()Lorg/thoughtcrime/securesms/conversation/v2/StickerSuggestionsViewModel; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->getViewModel()Lorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->getVoiceNoteMediaController()Lorg/thoughtcrime/securesms/components/voice/VoiceNoteMediaController; +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->handleScheduledMessagesCountChange(I)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->initializeConversationThreadUi()V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->initializeGiphyMp4()Lorg/thoughtcrime/securesms/giph/mp4/GiphyMp4ProjectionRecycler; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->initializeInlineSearch()V @@ -22240,6 +22171,7 @@ HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->isScrolled HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->moveToStartPosition(Lorg/thoughtcrime/securesms/conversation/ConversationData;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->observeConversationThread()V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->onCreate(Landroid/os/Bundle;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->onRecipientChanged(Lorg/thoughtcrime/securesms/recipients/Recipient;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->onResume()V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->onViewCreated(Landroid/view/View;Landroid/os/Bundle;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->onViewStateRestored(Landroid/os/Bundle;)V @@ -22247,13 +22179,17 @@ HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->presentAct HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->presentChatColors(Lorg/thoughtcrime/securesms/conversation/colors/ChatColors;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->presentConversationTitle(Lorg/thoughtcrime/securesms/recipients/Recipient;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->presentGroupCallJoinButton()V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->presentIdentityRecordsState(Lorg/thoughtcrime/securesms/conversation/v2/IdentityRecordsState;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->presentInputReadyState(Lorg/thoughtcrime/securesms/conversation/v2/InputReadyState;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->presentNavigationIconForNormal()V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->presentRequestReviewState(Lorg/thoughtcrime/securesms/conversation/v2/RequestReviewState;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->presentScrollButtons(Lorg/thoughtcrime/securesms/conversation/v2/ConversationScrollButtonState;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->presentStoryRing()V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->presentTypingIndicator()V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->presentWallpaper(Lorg/thoughtcrime/securesms/wallpaper/ChatWallpaper;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->registerForResults()V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->updateMessageRequestAcceptedState(Z)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->updateToggleButtonState()V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationItemDecorations$DateHeaderViewHolder;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationItemDecorations;Landroid/view/View;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationItemDecorations$DateHeaderViewHolder;->bind(Lorg/thoughtcrime/securesms/conversation/v2/data/ConversationMessageElement;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationItemDecorations$DateHeaderViewHolder;->getHeight()I @@ -22350,6 +22286,10 @@ HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationScrollButtonState;->< HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationScrollButtonState;->(ZZZIZILkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationScrollButtonState;->copy$default(Lorg/thoughtcrime/securesms/conversation/v2/ConversationScrollButtonState;ZZZIZILjava/lang/Object;)Lorg/thoughtcrime/securesms/conversation/v2/ConversationScrollButtonState; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationScrollButtonState;->copy(ZZZIZ)Lorg/thoughtcrime/securesms/conversation/v2/ConversationScrollButtonState; +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationScrollButtonState;->getHasMentions()Z +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationScrollButtonState;->getShowScrollButtons()Z +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationScrollButtonState;->getUnreadCount()I +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationScrollButtonState;->toString()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationThreadState;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationThreadState;->(Lorg/signal/paging/ObservablePagedData;Lorg/thoughtcrime/securesms/conversation/ConversationData;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationThreadState;->getItems()Lorg/signal/paging/ObservablePagedData; @@ -22428,8 +22368,6 @@ HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$5;->( HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$5;->invoke(Ljava/lang/Object;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$5;->invoke(Lorg/thoughtcrime/securesms/recipients/Recipient;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$6;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel;)V -HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$6;->invoke(Ljava/lang/Object;)Ljava/lang/Object; -HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$6;->invoke(Lorg/thoughtcrime/securesms/recipients/Recipient;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$7;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$7;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$7;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; @@ -22441,6 +22379,8 @@ HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$9;->( HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$9;->accept(Ljava/lang/Object;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$9;->accept(Lorg/thoughtcrime/securesms/conversation/v2/InputReadyState;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$canShowAsBubble$1;->(Landroid/content/Context;Lorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$canShowAsBubble$1;->apply(Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$canShowAsBubble$1;->apply(Lorg/thoughtcrime/securesms/recipients/Recipient;)Ljava/lang/Boolean; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$getRequestReviewState$1;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel;)V HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$getRequestReviewState$1;->apply(Ljava/lang/Object;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$getRequestReviewState$1;->apply(Lorg/thoughtcrime/securesms/conversation/v2/InputReadyState;)Lio/reactivex/rxjava3/core/SingleSource; @@ -22495,9 +22435,11 @@ HSPLorg/thoughtcrime/securesms/conversation/v2/DisabledInputView;->setWallpaperE HSPLorg/thoughtcrime/securesms/conversation/v2/IdentityRecordsState;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/IdentityRecordsState;->(Lorg/thoughtcrime/securesms/recipients/Recipient;Lorg/thoughtcrime/securesms/database/model/GroupRecord;ZLorg/thoughtcrime/securesms/database/identity/IdentityRecordList;Z)V HSPLorg/thoughtcrime/securesms/conversation/v2/IdentityRecordsState;->(Lorg/thoughtcrime/securesms/recipients/Recipient;Lorg/thoughtcrime/securesms/database/model/GroupRecord;ZLorg/thoughtcrime/securesms/database/identity/IdentityRecordList;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/IdentityRecordsState;->isGroup()Z +HSPLorg/thoughtcrime/securesms/conversation/v2/IdentityRecordsState;->isUnverified()Z +HSPLorg/thoughtcrime/securesms/conversation/v2/IdentityRecordsState;->isVerified()Z HSPLorg/thoughtcrime/securesms/conversation/v2/InputReadyState;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/InputReadyState;->(Lorg/thoughtcrime/securesms/recipients/Recipient;Lorg/thoughtcrime/securesms/messagerequests/MessageRequestState;Lorg/thoughtcrime/securesms/database/model/GroupRecord;ZZ)V -HSPLorg/thoughtcrime/securesms/conversation/v2/InputReadyState;->equals(Ljava/lang/Object;)Z HSPLorg/thoughtcrime/securesms/conversation/v2/InputReadyState;->getConversationRecipient()Lorg/thoughtcrime/securesms/recipients/Recipient; HSPLorg/thoughtcrime/securesms/conversation/v2/InputReadyState;->getGroupRecord()Lorg/thoughtcrime/securesms/database/model/GroupRecord; HSPLorg/thoughtcrime/securesms/conversation/v2/InputReadyState;->getMessageRequestState()Lorg/thoughtcrime/securesms/messagerequests/MessageRequestState; @@ -22513,6 +22455,7 @@ HSPLorg/thoughtcrime/securesms/conversation/v2/MotionEventRelay;->setDrain(Lorg/ HSPLorg/thoughtcrime/securesms/conversation/v2/RequestReviewState;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/RequestReviewState;->(Lorg/thoughtcrime/securesms/conversation/v2/RequestReviewState$IndividualReviewState;Lorg/thoughtcrime/securesms/conversation/v2/RequestReviewState$GroupReviewState;)V HSPLorg/thoughtcrime/securesms/conversation/v2/RequestReviewState;->(Lorg/thoughtcrime/securesms/conversation/v2/RequestReviewState$IndividualReviewState;Lorg/thoughtcrime/securesms/conversation/v2/RequestReviewState$GroupReviewState;ILkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/RequestReviewState;->shouldShowReviewBanner()Z HSPLorg/thoughtcrime/securesms/conversation/v2/ShareDataTimestampViewModel;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/ShareDataTimestampViewModel;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/ShareDataTimestampViewModel;->getTimestamp()J @@ -22529,7 +22472,7 @@ HSPLorg/thoughtcrime/securesms/conversation/v2/VoiceMessageRecordingDelegate$Com HSPLorg/thoughtcrime/securesms/conversation/v2/VoiceMessageRecordingDelegate;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/VoiceMessageRecordingDelegate;->(Landroidx/fragment/app/Fragment;Lorg/thoughtcrime/securesms/audio/AudioRecorder;Lorg/thoughtcrime/securesms/conversation/v2/VoiceMessageRecordingDelegate$SessionCallback;)V HSPLorg/thoughtcrime/securesms/conversation/v2/computed/FormattedDate;->()V -HSPLorg/thoughtcrime/securesms/conversation/v2/computed/FormattedDate;->(ZLjava/lang/String;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/computed/FormattedDate;->(ZZLjava/lang/String;)V HSPLorg/thoughtcrime/securesms/conversation/v2/computed/FormattedDate;->getValue()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/conversation/v2/data/ConversationDataSource$Companion;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/data/ConversationDataSource$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -22545,28 +22488,14 @@ HSPLorg/thoughtcrime/securesms/conversation/v2/data/ConversationDataSource;->get HSPLorg/thoughtcrime/securesms/conversation/v2/data/ConversationDataSource;->getSizeInternal()I HSPLorg/thoughtcrime/securesms/conversation/v2/data/ConversationDataSource;->getThreadRecipient()Lorg/thoughtcrime/securesms/recipients/Recipient; HSPLorg/thoughtcrime/securesms/conversation/v2/data/ConversationDataSource;->load(IIILorg/signal/paging/PagedDataSource$CancellationSignal;)Ljava/util/List; -HSPLorg/thoughtcrime/securesms/conversation/v2/data/ConversationDataSource;->load(Ljava/lang/Object;)Ljava/lang/Object; -HSPLorg/thoughtcrime/securesms/conversation/v2/data/ConversationDataSource;->load(Lorg/thoughtcrime/securesms/conversation/v2/data/ConversationElementKey;)Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingModel; HSPLorg/thoughtcrime/securesms/conversation/v2/data/ConversationDataSource;->loadThreadHeader()Lorg/thoughtcrime/securesms/conversation/v2/data/ThreadHeader; HSPLorg/thoughtcrime/securesms/conversation/v2/data/ConversationDataSource;->size()I HSPLorg/thoughtcrime/securesms/conversation/v2/data/ConversationDataSource;->toMappingModel(Lorg/thoughtcrime/securesms/conversation/ConversationMessage;)Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingModel; -HSPLorg/thoughtcrime/securesms/conversation/v2/data/ConversationElementKey$Companion;->()V -HSPLorg/thoughtcrime/securesms/conversation/v2/data/ConversationElementKey$Companion;->()V -HSPLorg/thoughtcrime/securesms/conversation/v2/data/ConversationElementKey$Companion;->getThreadHeader()Lorg/thoughtcrime/securesms/conversation/v2/data/ConversationElementKey; -HSPLorg/thoughtcrime/securesms/conversation/v2/data/ConversationElementKey;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/data/IncomingMedia;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/data/IncomingMedia;->(Lorg/thoughtcrime/securesms/conversation/ConversationMessage;)V -HSPLorg/thoughtcrime/securesms/conversation/v2/data/IncomingMedia;->areContentsTheSame(Ljava/lang/Object;)Z -HSPLorg/thoughtcrime/securesms/conversation/v2/data/IncomingMedia;->areContentsTheSame(Lorg/thoughtcrime/securesms/conversation/v2/data/IncomingMedia;)Z -HSPLorg/thoughtcrime/securesms/conversation/v2/data/IncomingMedia;->areItemsTheSame(Ljava/lang/Object;)Z -HSPLorg/thoughtcrime/securesms/conversation/v2/data/IncomingMedia;->areItemsTheSame(Lorg/thoughtcrime/securesms/conversation/v2/data/IncomingMedia;)Z HSPLorg/thoughtcrime/securesms/conversation/v2/data/IncomingMedia;->getConversationMessage()Lorg/thoughtcrime/securesms/conversation/ConversationMessage; HSPLorg/thoughtcrime/securesms/conversation/v2/data/IncomingTextOnly;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/data/IncomingTextOnly;->(Lorg/thoughtcrime/securesms/conversation/ConversationMessage;)V -HSPLorg/thoughtcrime/securesms/conversation/v2/data/IncomingTextOnly;->areContentsTheSame(Ljava/lang/Object;)Z -HSPLorg/thoughtcrime/securesms/conversation/v2/data/IncomingTextOnly;->areContentsTheSame(Lorg/thoughtcrime/securesms/conversation/v2/data/IncomingTextOnly;)Z -HSPLorg/thoughtcrime/securesms/conversation/v2/data/IncomingTextOnly;->areItemsTheSame(Ljava/lang/Object;)Z -HSPLorg/thoughtcrime/securesms/conversation/v2/data/IncomingTextOnly;->areItemsTheSame(Lorg/thoughtcrime/securesms/conversation/v2/data/IncomingTextOnly;)Z HSPLorg/thoughtcrime/securesms/conversation/v2/data/IncomingTextOnly;->getConversationMessage()Lorg/thoughtcrime/securesms/conversation/ConversationMessage; HSPLorg/thoughtcrime/securesms/conversation/v2/data/MessageBackedKey;->(J)V HSPLorg/thoughtcrime/securesms/conversation/v2/data/MessageBackedKey;->hashCode()I @@ -22624,10 +22553,6 @@ HSPLorg/thoughtcrime/securesms/conversation/v2/data/MessageDataFetcher;->updateM HSPLorg/thoughtcrime/securesms/conversation/v2/data/MessageDataFetcher;->updateWithData(Lorg/thoughtcrime/securesms/database/model/MessageRecord;Lorg/thoughtcrime/securesms/conversation/v2/data/MessageDataFetcher$ExtraMessageData;)Lorg/thoughtcrime/securesms/database/model/MessageRecord; HSPLorg/thoughtcrime/securesms/conversation/v2/data/ThreadHeader;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/data/ThreadHeader;->(Lorg/thoughtcrime/securesms/messagerequests/MessageRequestRecipientInfo;)V -HSPLorg/thoughtcrime/securesms/conversation/v2/data/ThreadHeader;->areContentsTheSame(Ljava/lang/Object;)Z -HSPLorg/thoughtcrime/securesms/conversation/v2/data/ThreadHeader;->areContentsTheSame(Lorg/thoughtcrime/securesms/conversation/v2/data/ThreadHeader;)Z -HSPLorg/thoughtcrime/securesms/conversation/v2/data/ThreadHeader;->areItemsTheSame(Ljava/lang/Object;)Z -HSPLorg/thoughtcrime/securesms/conversation/v2/data/ThreadHeader;->areItemsTheSame(Lorg/thoughtcrime/securesms/conversation/v2/data/ThreadHeader;)Z HSPLorg/thoughtcrime/securesms/conversation/v2/data/ThreadHeader;->getRecipientInfo()Lorg/thoughtcrime/securesms/messagerequests/MessageRequestRecipientInfo; HSPLorg/thoughtcrime/securesms/conversation/v2/data/ThreadHeaderKey;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/data/ThreadHeaderKey;->()V @@ -22636,6 +22561,7 @@ HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallState HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallState;->(Lorg/thoughtcrime/securesms/recipients/RecipientId;ZZZILkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallState;->copy(Lorg/thoughtcrime/securesms/recipients/RecipientId;ZZZ)Lorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallState; HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallState;->getActiveV2Group()Z +HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallState;->getHasCapacity()Z HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallState;->getOngoingCall()Z HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallState;->getRecipientId()Lorg/thoughtcrime/securesms/recipients/RecipientId; HSPLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel$1$1;->(Lorg/thoughtcrime/securesms/recipients/Recipient;)V @@ -22713,14 +22639,141 @@ HSPLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable$Companio HSPLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable$Companion;->attach(Landroidx/recyclerview/widget/RecyclerView;)V HSPLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable$Companion;->setGlobalChatColors(Landroidx/recyclerview/widget/RecyclerView;Lorg/thoughtcrime/securesms/conversation/colors/ChatColors;)V HSPLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable;->access$getGlobalChatColors$cp()Lorg/thoughtcrime/securesms/conversation/colors/ChatColors; HSPLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable;->access$getGlobalMask$cp()Landroid/graphics/drawable/Drawable; HSPLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable;->access$setGlobalChatColors$cp(Lorg/thoughtcrime/securesms/conversation/colors/ChatColors;)V HSPLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable;->access$setGlobalMask$cp(Landroid/graphics/drawable/Drawable;)V HSPLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable;->access$setLatestBounds$cp(Landroid/graphics/Rect;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable;->draw(Landroid/graphics/Canvas;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable;->getChatColors()Lorg/thoughtcrime/securesms/conversation/colors/ChatColors; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable;->getMask()Landroid/graphics/drawable/Drawable; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable;->getOpacity()I +HSPLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable;->getOutline(Landroid/graphics/Outline;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable;->isSolidColor()Z +HSPLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable;->setCorners(Lorg/thoughtcrime/securesms/util/Projection$Corners;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable;->setCorners([F)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable;->setLocalChatColors(Lorg/thoughtcrime/securesms/conversation/colors/ChatColors;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout;->(Landroid/content/Context;Landroid/util/AttributeSet;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout;->addOnMeasureListener(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout$OnMeasureListener;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout;->onMeasure(II)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout;->removeOnMeasureListener(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout$OnMeasureListener;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout;->setOnDispatchTouchEventListener(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout$OnDispatchTouchEventListener;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape$Companion;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape$MessageShape;->$values()[Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape$MessageShape; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape$MessageShape;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape$MessageShape;->(Ljava/lang/String;IFF)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape$MessageShape;->getBottomPadding()F +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape$MessageShape;->getTopPadding()F +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape$MessageShape;->isEndingShape()Z +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape$MessageShape;->isStartingShape()Z +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationContext;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape;->access$getCollapsedSpacing$cp()F +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape;->access$getDefaultSpacing$cp()F +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape;->getCornersLTR()Lorg/thoughtcrime/securesms/util/Projection$Corners; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape;->isEndOfMessageCluster(Lorg/thoughtcrime/securesms/database/model/MessageRecord;Lorg/thoughtcrime/securesms/database/model/MessageRecord;)Z +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape;->isSingularMessage(Lorg/thoughtcrime/securesms/database/model/MessageRecord;Lorg/thoughtcrime/securesms/database/model/MessageRecord;Lorg/thoughtcrime/securesms/database/model/MessageRecord;Z)Z +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape;->isStartOfMessageCluster(Lorg/thoughtcrime/securesms/database/model/MessageRecord;Lorg/thoughtcrime/securesms/database/model/MessageRecord;Z)Z +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape;->isWithinClusteringTime(Lorg/thoughtcrime/securesms/database/model/MessageRecord;Lorg/thoughtcrime/securesms/database/model/MessageRecord;)Z +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape;->setBodyBubbleCorners(FFFF)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape;->setMessageShape(Lorg/thoughtcrime/securesms/database/model/MessageRecord;ZI)Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape$MessageShape; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout;Lorg/thoughtcrime/securesms/components/emoji/EmojiTextView;Lorg/thoughtcrime/securesms/components/AvatarImageView;Lorg/thoughtcrime/securesms/badges/BadgeImageView;Landroid/view/ViewGroup;Lorg/thoughtcrime/securesms/components/emoji/EmojiTextView;Lcom/google/android/material/imageview/ShapeableImageView;Lorg/thoughtcrime/securesms/reactions/ReactionsConversationView;Lorg/thoughtcrime/securesms/components/DeliveryStatusView;Landroid/widget/TextView;Lorg/thoughtcrime/securesms/components/ExpirationTimerView;Landroid/view/View;Landroid/widget/Space;Lorg/thoughtcrime/securesms/components/AlertView;Z)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->getAlert()Lorg/thoughtcrime/securesms/components/AlertView; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->getBody()Lorg/thoughtcrime/securesms/components/emoji/EmojiTextView; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->getBodyWrapper()Landroid/view/ViewGroup; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->getDeliveryStatus()Lorg/thoughtcrime/securesms/components/DeliveryStatusView; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->getFooterBackground()Landroid/view/View; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->getFooterDate()Landroid/widget/TextView; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->getFooterExpiry()Lorg/thoughtcrime/securesms/components/ExpirationTimerView; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->getFooterSpace()Landroid/widget/Space; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->getReactions()Lorg/thoughtcrime/securesms/reactions/ReactionsConversationView; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->getReply()Lcom/google/android/material/imageview/ShapeableImageView; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->getRoot()Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->getSenderBadge()Lorg/thoughtcrime/securesms/badges/BadgeImageView; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->getSenderName()Lorg/thoughtcrime/securesms/components/emoji/EmojiTextView; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->getSenderPhoto()Lorg/thoughtcrime/securesms/components/AvatarImageView; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;->isIncoming()Z +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridgeKt;->bridge(Lorg/thoughtcrime/securesms/databinding/V2ConversationItemTextOnlyIncomingBinding;)Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda0;->onLayoutChange(Landroid/view/View;IIIIIIII)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda1;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda2;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda3;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda4;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda6;->(Lorg/thoughtcrime/securesms/conversation/ConversationAdapter$ItemClickListener;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$Companion;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$PassthroughClickListener;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$ReactionMeasureListener;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$replyDelegate$1;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->$r8$lambda$ocilDMoff9b132TfYhzB6ol1qqk(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;Landroid/view/View;IIIIIIII)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationContext;Lorg/thoughtcrime/securesms/conversation/v2/items/V2FooterPositionDelegate;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationContext;Lorg/thoughtcrime/securesms/conversation/v2/items/V2FooterPositionDelegate;ILkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->_init_$lambda$0(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;Landroid/view/View;IIIIIIII)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->bind(Ljava/lang/Object;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->bind(Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingModel;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->canPlayContent()Z +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->getColorizerProjections(Landroid/view/ViewGroup;)Lorg/thoughtcrime/securesms/util/ProjectionList; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->getConversationMessage()Lorg/thoughtcrime/securesms/conversation/ConversationMessage; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->getShape()Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape$MessageShape; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->invalidateBodyBubbleDrawable(Landroid/view/ViewGroup;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->invalidateChatColorsDrawable(Landroid/view/ViewGroup;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->invalidateFooterDrawable(Landroid/view/ViewGroup;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->isContentCondensed()Z +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->isForcedFooter()Z +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->linkifyMessageBody(Landroid/text/Spannable;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->presentAlert()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->presentBody()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->presentDate()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->presentDeliveryStatus()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->presentFooterBackground()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->presentFooterEndPadding()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->presentFooterExpiry()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->presentReactions()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->presentSender()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->presentSenderNameBackground()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->presentSenderNameColor()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->setConversationMessage(Lorg/thoughtcrime/securesms/conversation/ConversationMessage;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->setShape(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape$MessageShape;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder;->showProjectionArea()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTheme$getBodyTextColor$1;->(Ljava/lang/Object;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTheme$getBodyTextColor$2;->(Ljava/lang/Object;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTheme$getBodyTextColor$2;->invoke(Landroid/content/Context;Z)Ljava/lang/Integer; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTheme$getBodyTextColor$2;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTheme;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTheme;->(Landroid/content/Context;Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationContext;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTheme;->getBodyBubbleColor(Lorg/thoughtcrime/securesms/conversation/ConversationMessage;)I +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTheme;->getBodyTextColor(Lorg/thoughtcrime/securesms/conversation/ConversationMessage;)I +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTheme;->getColor(Lorg/thoughtcrime/securesms/conversation/ConversationMessage;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)I +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTheme;->getFooterBubbleColor(Lorg/thoughtcrime/securesms/conversation/ConversationMessage;)I +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTheme;->getReplyIconBackgroundColor()I HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemUtils;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemUtils;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemUtils;->linkifyUrlLinks(Landroid/text/Spannable;ZLorg/thoughtcrime/securesms/util/UrlClickHandler;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemViewHolder;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemViewHolder;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout;Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationContext;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemViewHolder;->getShapeDelegate()Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemViewHolder;->getThemeDelegate()Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTheme; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2FooterPositionDelegate$DisplayState;->$values()[Lorg/thoughtcrime/securesms/conversation/v2/items/V2FooterPositionDelegate$DisplayState; +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2FooterPositionDelegate$DisplayState;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2FooterPositionDelegate$DisplayState;->(Ljava/lang/String;I)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2FooterPositionDelegate;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2FooterPositionDelegate;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout;Ljava/util/List;Landroid/view/View;Lorg/thoughtcrime/securesms/components/emoji/EmojiTextView;Landroid/widget/Space;Lorg/thoughtcrime/securesms/util/views/Stub;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2FooterPositionDelegate;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2FooterPositionDelegate;->displayTuckedIntoBody()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2FooterPositionDelegate;->getFooterWidth()I +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2FooterPositionDelegate;->onPostMeasure()Z +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2FooterPositionDelegate;->onPreMeasure()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2OnDispatchTouchEventListener$$ExternalSyntheticLambda1;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2OnDispatchTouchEventListener;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2OnDispatchTouchEventListener$Companion;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2OnDispatchTouchEventListener$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2OnDispatchTouchEventListener;->()V +HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2OnDispatchTouchEventListener;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationContext;Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge;)V HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2Payload;->$values()[Lorg/thoughtcrime/securesms/conversation/v2/items/V2Payload; HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2Payload;->()V HSPLorg/thoughtcrime/securesms/conversation/v2/items/V2Payload;->(Ljava/lang/String;I)V @@ -22744,11 +22797,13 @@ HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListAdapter$Conversa HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListAdapter$Payload;->$values()[Lorg/thoughtcrime/securesms/conversationlist/ConversationListAdapter$Payload; HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListAdapter$Payload;->()V HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListAdapter$Payload;->(Ljava/lang/String;I)V +HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListAdapter$Payload;->values()[Lorg/thoughtcrime/securesms/conversationlist/ConversationListAdapter$Payload; HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListAdapter;->$r8$lambda$2QDH4q4xSHpazU1KhZ4uhNUhhdI(Lorg/thoughtcrime/securesms/conversationlist/ConversationListAdapter;Lorg/thoughtcrime/securesms/conversationlist/ConversationListAdapter$ConversationViewHolder;Landroid/view/View;)V HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListAdapter;->(Landroidx/lifecycle/LifecycleOwner;Lorg/thoughtcrime/securesms/mms/GlideRequests;Lorg/thoughtcrime/securesms/conversationlist/ConversationListAdapter$OnConversationClickListener;Lorg/thoughtcrime/securesms/conversationlist/ClearFilterViewHolder$OnClearFilterClickListener;)V HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListAdapter;->getItem(I)Lorg/thoughtcrime/securesms/conversationlist/model/Conversation; HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListAdapter;->getItemViewType(I)I HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListAdapter;->lambda$onCreateViewHolder$1(Lorg/thoughtcrime/securesms/conversationlist/ConversationListAdapter$ConversationViewHolder;Landroid/view/View;)V +HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListAdapter;->notifyTimestampPayloadUpdate()V HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListAdapter;->onBindViewHolder(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;I)V HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListAdapter;->onBindViewHolder(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;ILjava/util/List;)V HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListAdapter;->onCreateViewHolder(Landroid/view/ViewGroup;I)Landroidx/recyclerview/widget/RecyclerView$ViewHolder; @@ -22903,6 +22958,7 @@ HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListFragment;->onPos HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListFragment;->onPrepareOptionsMenu(Landroid/view/Menu;)V HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListFragment;->onResume()V HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListFragment;->onStart()V +HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListFragment;->onStop()V HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListFragment;->onViewCreated(Landroid/view/View;Landroid/os/Bundle;)V HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListFragment;->requireCallback()Lorg/thoughtcrime/securesms/conversationlist/ConversationListFragment$Callback; HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListFragment;->setAdapter(Landroidx/recyclerview/widget/RecyclerView$Adapter;)V @@ -22910,23 +22966,27 @@ HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListFragment;->updat HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListFragment;->updateMultiSelectState()V HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListFragment;->updateReminders()V HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListFragment;->updateSearchToolbarHint(Lorg/thoughtcrime/securesms/conversationlist/chatfilter/ConversationFilterRequest;)V -HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListItem$$ExternalSyntheticLambda3;->(Lorg/thoughtcrime/securesms/conversationlist/ConversationListItem;)V +HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListItem$$ExternalSyntheticLambda12;->(Lorg/thoughtcrime/securesms/database/model/ThreadRecord;Landroid/content/Context;)V +HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListItem$$ExternalSyntheticLambda12;->invoke(Ljava/lang/Object;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListItem$$ExternalSyntheticLambda4;->(Lorg/thoughtcrime/securesms/conversationlist/ConversationListItem;)V -HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListItem$$ExternalSyntheticLambda4;->onChanged(Ljava/lang/Object;)V -HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListItem$$ExternalSyntheticLambda5;->(Lorg/thoughtcrime/securesms/conversationlist/ConversationListItem;)V -HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListItem$$ExternalSyntheticLambda5;->onChanged(Ljava/lang/Object;)V -HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListItem$$ExternalSyntheticLambda9;->(Lorg/thoughtcrime/securesms/database/model/ThreadRecord;Landroid/content/Context;)V -HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListItem$$ExternalSyntheticLambda9;->invoke(Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListItem$$ExternalSyntheticLambda5;->(Lorg/thoughtcrime/securesms/conversationlist/ConversationListItem;Ljava/util/Locale;Lorg/thoughtcrime/securesms/database/model/ThreadRecord;)V +HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListItem$$ExternalSyntheticLambda5;->run()V +HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListItem$$ExternalSyntheticLambda6;->(Lorg/thoughtcrime/securesms/conversationlist/ConversationListItem;)V +HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListItem$$ExternalSyntheticLambda6;->onChanged(Ljava/lang/Object;)V +HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListItem$$ExternalSyntheticLambda7;->(Lorg/thoughtcrime/securesms/conversationlist/ConversationListItem;)V +HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListItem$$ExternalSyntheticLambda7;->onChanged(Ljava/lang/Object;)V HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListItem;->$r8$lambda$LAkoq7MeJO-fCezsfjX-mK5pWPo(Lorg/thoughtcrime/securesms/conversationlist/ConversationListItem;Landroid/text/SpannableString;)V HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListItem;->$r8$lambda$dlLpa5X9k6koT8N7PU2hVGd424s(Lorg/thoughtcrime/securesms/conversationlist/ConversationListItem;Lorg/thoughtcrime/securesms/recipients/Recipient;)V -HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListItem;->$r8$lambda$uO5laplVi7cVt9Dj9uPwXCNI7Tk(Lorg/thoughtcrime/securesms/database/model/ThreadRecord;Landroid/content/Context;Ljava/lang/CharSequence;)Landroid/text/SpannableString; +HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListItem;->$r8$lambda$k1MmUJ3xOtwOkC4HHJE6kKVtClw(Lorg/thoughtcrime/securesms/conversationlist/ConversationListItem;Ljava/util/Locale;Lorg/thoughtcrime/securesms/database/model/ThreadRecord;)V +HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListItem;->$r8$lambda$m6UBhFD1iIvqrclbuOW7C8gY-JM(Lorg/thoughtcrime/securesms/database/model/ThreadRecord;Landroid/content/Context;Ljava/lang/CharSequence;)Landroid/text/SpannableString; HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListItem;->()V HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListItem;->(Landroid/content/Context;Landroid/util/AttributeSet;)V HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListItem;->bind(Landroidx/lifecycle/LifecycleOwner;Lorg/thoughtcrime/securesms/database/model/ThreadRecord;Lorg/thoughtcrime/securesms/mms/GlideRequests;Ljava/util/Locale;Ljava/util/Set;Lorg/thoughtcrime/securesms/conversationlist/model/ConversationSet;)V HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListItem;->bindThread(Landroidx/lifecycle/LifecycleOwner;Lorg/thoughtcrime/securesms/database/model/ThreadRecord;Lorg/thoughtcrime/securesms/mms/GlideRequests;Ljava/util/Locale;Ljava/util/Set;Lorg/thoughtcrime/securesms/conversationlist/model/ConversationSet;Ljava/lang/String;)V HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListItem;->createFinalBodyWithMediaIcon(Landroid/content/Context;Ljava/lang/CharSequence;Lorg/thoughtcrime/securesms/database/model/ThreadRecord;Lorg/thoughtcrime/securesms/mms/GlideRequests;ILorg/thoughtcrime/securesms/glide/GlideLiveDataTarget;)Landroidx/lifecycle/LiveData; HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListItem;->getThreadDisplayBody(Landroid/content/Context;Lorg/thoughtcrime/securesms/database/model/ThreadRecord;Lorg/thoughtcrime/securesms/mms/GlideRequests;ILorg/thoughtcrime/securesms/glide/GlideLiveDataTarget;)Landroidx/lifecycle/LiveData; -HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListItem;->lambda$getThreadDisplayBody$6(Lorg/thoughtcrime/securesms/database/model/ThreadRecord;Landroid/content/Context;Ljava/lang/CharSequence;)Landroid/text/SpannableString; +HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListItem;->lambda$bindThread$1(Ljava/util/Locale;Lorg/thoughtcrime/securesms/database/model/ThreadRecord;)V +HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListItem;->lambda$getThreadDisplayBody$9(Lorg/thoughtcrime/securesms/database/model/ThreadRecord;Landroid/content/Context;Ljava/lang/CharSequence;)Landroid/text/SpannableString; HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListItem;->observeDisplayBody(Landroidx/lifecycle/LifecycleOwner;Landroidx/lifecycle/LiveData;)V HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListItem;->observeRecipient(Landroidx/lifecycle/LifecycleOwner;Lorg/thoughtcrime/securesms/recipients/LiveRecipient;)V HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListItem;->onDisplayBodyChanged(Landroid/text/SpannableString;)V @@ -22951,6 +23011,8 @@ HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListSearchAdapter$$E HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListSearchAdapter$$ExternalSyntheticLambda5;->(Lorg/thoughtcrime/securesms/conversationlist/ConversationListSearchAdapter$ConversationListSearchClickCallbacks;Landroidx/lifecycle/LifecycleOwner;Lorg/thoughtcrime/securesms/mms/GlideRequests;)V HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListSearchAdapter$ChatFilterRepository;->()V HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListSearchAdapter$ChatFilterRepository;->()V +HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListSearchAdapter$Companion;->()V +HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListSearchAdapter$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListSearchAdapter;->()V HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListSearchAdapter;->(Landroid/content/Context;Ljava/util/Set;Lorg/thoughtcrime/securesms/contacts/paged/ContactSearchAdapter$DisplayOptions;Lorg/thoughtcrime/securesms/conversationlist/ConversationListSearchAdapter$ConversationListSearchClickCallbacks;Lorg/thoughtcrime/securesms/contacts/paged/ContactSearchAdapter$LongClickCallbacks;Lorg/thoughtcrime/securesms/contacts/paged/ContactSearchAdapter$StoryContextMenuCallbacks;Lorg/thoughtcrime/securesms/contacts/paged/ContactSearchAdapter$CallButtonClickCallbacks;Landroidx/lifecycle/LifecycleOwner;Lorg/thoughtcrime/securesms/mms/GlideRequests;)V HSPLorg/thoughtcrime/securesms/conversationlist/ConversationListViewModel$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/conversationlist/ConversationListViewModel;)V @@ -23066,6 +23128,7 @@ HSPLorg/thoughtcrime/securesms/conversationlist/chatfilter/ConversationListFilte HSPLorg/thoughtcrime/securesms/conversationlist/chatfilter/ConversationListFilterPullView;->()V HSPLorg/thoughtcrime/securesms/conversationlist/chatfilter/ConversationListFilterPullView;->(Landroid/content/Context;Landroid/util/AttributeSet;)V HSPLorg/thoughtcrime/securesms/conversationlist/chatfilter/ConversationListFilterPullView;->access$getBinding$p(Lorg/thoughtcrime/securesms/conversationlist/chatfilter/ConversationListFilterPullView;)Lorg/thoughtcrime/securesms/databinding/ConversationListFilterPullViewBinding; +HSPLorg/thoughtcrime/securesms/conversationlist/chatfilter/ConversationListFilterPullView;->onSaveInstanceState()Landroid/os/Parcelable; HSPLorg/thoughtcrime/securesms/conversationlist/chatfilter/ConversationListFilterPullView;->onUserDrag(F)V HSPLorg/thoughtcrime/securesms/conversationlist/chatfilter/ConversationListFilterPullView;->onUserDragFinished()V HSPLorg/thoughtcrime/securesms/conversationlist/chatfilter/ConversationListFilterPullView;->setOnCloseClicked(Lorg/thoughtcrime/securesms/conversationlist/chatfilter/ConversationListFilterPullView$OnCloseClicked;)V @@ -23122,7 +23185,6 @@ HSPLorg/thoughtcrime/securesms/conversationlist/model/Conversation$Type;->(Ljava/lang/String;I)V HSPLorg/thoughtcrime/securesms/conversationlist/model/Conversation$Type;->values()[Lorg/thoughtcrime/securesms/conversationlist/model/Conversation$Type; HSPLorg/thoughtcrime/securesms/conversationlist/model/Conversation;->(Lorg/thoughtcrime/securesms/database/model/ThreadRecord;)V -HSPLorg/thoughtcrime/securesms/conversationlist/model/Conversation;->equals(Ljava/lang/Object;)Z HSPLorg/thoughtcrime/securesms/conversationlist/model/Conversation;->getThreadRecord()Lorg/thoughtcrime/securesms/database/model/ThreadRecord; HSPLorg/thoughtcrime/securesms/conversationlist/model/Conversation;->getType()Lorg/thoughtcrime/securesms/conversationlist/model/Conversation$Type; HSPLorg/thoughtcrime/securesms/conversationlist/model/ConversationFilter;->$values()[Lorg/thoughtcrime/securesms/conversationlist/model/ConversationFilter; @@ -23217,9 +23279,6 @@ HSPLorg/thoughtcrime/securesms/crypto/PreKeyUtil;->storeLastResortKyberPreKey(Lo HSPLorg/thoughtcrime/securesms/crypto/PreKeyUtil;->storeSignedPreKey(Lorg/signal/libsignal/protocol/state/SignalProtocolStore;Lorg/thoughtcrime/securesms/crypto/storage/PreKeyMetadataStore;Lorg/signal/libsignal/protocol/state/SignedPreKeyRecord;)V HSPLorg/thoughtcrime/securesms/crypto/ProfileKeyUtil;->()V HSPLorg/thoughtcrime/securesms/crypto/ProfileKeyUtil;->createNew()Lorg/signal/libsignal/zkgroup/profiles/ProfileKey; -HSPLorg/thoughtcrime/securesms/crypto/ProfileKeyUtil;->getSelfProfileKey()Lorg/signal/libsignal/zkgroup/profiles/ProfileKey; -HSPLorg/thoughtcrime/securesms/crypto/ProfileKeyUtil;->profileKeyOptional([B)Lj$/util/Optional; -HSPLorg/thoughtcrime/securesms/crypto/ProfileKeyUtil;->profileKeyOrNull([B)Lorg/signal/libsignal/zkgroup/profiles/ProfileKey; HSPLorg/thoughtcrime/securesms/crypto/ReentrantSessionLock$$ExternalSyntheticLambda0;->(Ljava/util/concurrent/locks/ReentrantLock;)V HSPLorg/thoughtcrime/securesms/crypto/ReentrantSessionLock$$ExternalSyntheticLambda0;->close()V HSPLorg/thoughtcrime/securesms/crypto/ReentrantSessionLock;->$values()[Lorg/thoughtcrime/securesms/crypto/ReentrantSessionLock; @@ -23278,23 +23337,24 @@ HSPLorg/thoughtcrime/securesms/crypto/storage/TextSecurePreKeyStore;->storeSigne HSPLorg/thoughtcrime/securesms/crypto/storage/TextSecureSessionStore;->()V HSPLorg/thoughtcrime/securesms/crypto/storage/TextSecureSessionStore;->(Lorg/whispersystems/signalservice/api/push/ServiceId;)V HSPLorg/thoughtcrime/securesms/crypto/storage/TextSecureSessionStore;->archiveAllSessions()V -HSPLorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties$1;->()V +HSPLorg/thoughtcrime/securesms/database/AttachmentTable$Companion;->()V +HSPLorg/thoughtcrime/securesms/database/AttachmentTable$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties$Companion;->()V +HSPLorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties$Companion;->empty()Lorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties; +HSPLorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties$Companion;->parse(Ljava/lang/String;)Lorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties; +HSPLorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties$Creator;->()V HSPLorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties;->()V -HSPLorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties;->(ZZJJI)V -HSPLorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties;->empty()Lorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties; -HSPLorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties;->getSentMediaQuality()I -HSPLorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties;->getVideoTrimEndTimeUs()J -HSPLorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties;->getVideoTrimStartTimeUs()J -HSPLorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties;->isVideoEdited()Z -HSPLorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties;->isVideoTrim()Z -HSPLorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties;->parse(Ljava/lang/String;)Lorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties; -HSPLorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties;->serialize()Ljava/lang/String; +HSPLorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties;->(ZZJJIZ)V +HSPLorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties;->access$getDEFAULT_MEDIA_QUALITY$cp()I +HSPLorg/thoughtcrime/securesms/database/AttachmentTable$insertAttachment$attachmentId$1;->(Lorg/thoughtcrime/securesms/attachments/Attachment;Lorg/thoughtcrime/securesms/database/AttachmentTable;JZLkotlin/jvm/internal/Ref$BooleanRef;)V +HSPLorg/thoughtcrime/securesms/database/AttachmentTable$insertAttachment$attachmentId$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/database/AttachmentTable$insertAttachment$attachmentId$1;->invoke(Lorg/thoughtcrime/securesms/database/SQLiteDatabase;)Lorg/thoughtcrime/securesms/attachments/AttachmentId; HSPLorg/thoughtcrime/securesms/database/AttachmentTable;->()V HSPLorg/thoughtcrime/securesms/database/AttachmentTable;->(Landroid/content/Context;Lorg/thoughtcrime/securesms/database/SignalDatabase;Lorg/thoughtcrime/securesms/crypto/AttachmentSecret;)V -HSPLorg/thoughtcrime/securesms/database/AttachmentTable;->containsStickerPackId(Ljava/lang/String;)Z +HSPLorg/thoughtcrime/securesms/database/AttachmentTable;->access$getVisualHashStringOrNull(Lorg/thoughtcrime/securesms/database/AttachmentTable;Lorg/thoughtcrime/securesms/attachments/Attachment;)Ljava/lang/String; HSPLorg/thoughtcrime/securesms/database/AttachmentTable;->deleteAbandonedPreuploadedAttachments()I HSPLorg/thoughtcrime/securesms/database/AttachmentTable;->getAttachment(Landroid/database/Cursor;)Lorg/thoughtcrime/securesms/attachments/DatabaseAttachment; -HSPLorg/thoughtcrime/securesms/database/AttachmentTable;->getAttachment(Lorg/thoughtcrime/securesms/attachments/AttachmentId;)Lorg/thoughtcrime/securesms/attachments/DatabaseAttachment; HSPLorg/thoughtcrime/securesms/database/AttachmentTable;->getAttachments(Landroid/database/Cursor;)Ljava/util/List; HSPLorg/thoughtcrime/securesms/database/AttachmentTable;->getAttachmentsForMessage(J)Ljava/util/List; HSPLorg/thoughtcrime/securesms/database/AttachmentTable;->getAttachmentsForMessages(Ljava/util/Collection;)Ljava/util/Map; @@ -23302,6 +23362,9 @@ HSPLorg/thoughtcrime/securesms/database/AttachmentTable;->getVisualHashStringOrN HSPLorg/thoughtcrime/securesms/database/AttachmentTable;->hasStickerAttachments()Z HSPLorg/thoughtcrime/securesms/database/AttachmentTable;->insertAttachment(JLorg/thoughtcrime/securesms/attachments/Attachment;Z)Lorg/thoughtcrime/securesms/attachments/AttachmentId; HSPLorg/thoughtcrime/securesms/database/AttachmentTable;->insertAttachmentsForMessage(JLjava/util/List;Ljava/util/List;)Ljava/util/Map; +HSPLorg/thoughtcrime/securesms/database/AttachmentTable;->readAttachment(Landroid/database/Cursor;)Lorg/thoughtcrime/securesms/attachments/DatabaseAttachment; +HSPLorg/thoughtcrime/securesms/database/AttachmentTable;->readAttachments(Landroid/database/Cursor;)Ljava/util/List; +HSPLorg/thoughtcrime/securesms/database/AttachmentTable;->readStickerLocator(Landroid/database/Cursor;)Lorg/thoughtcrime/securesms/stickers/StickerLocator; HSPLorg/thoughtcrime/securesms/database/AttachmentTable;->setTransferProgressPermanentFailure(Lorg/thoughtcrime/securesms/attachments/AttachmentId;J)V HSPLorg/thoughtcrime/securesms/database/AttachmentTable;->setTransferState(JLorg/thoughtcrime/securesms/attachments/AttachmentId;I)V HSPLorg/thoughtcrime/securesms/database/BodyAdjustment;->()V @@ -23342,11 +23405,8 @@ HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambd HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda11;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda11;->run()V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda13;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver;JLorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V -HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda13;->run()V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda17;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/recipients/RecipientId;Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda17;->run()V -HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda1;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V -HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda1;->run()V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda20;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda20;->run()V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda23;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver;)V @@ -23359,35 +23419,21 @@ HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambd HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda29;->run()V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda30;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver;JLorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda30;->run()V -HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda32;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver;)V -HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda32;->run()V -HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda36;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver;)V -HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda36;->run()V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda37;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda37;->run()V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda40;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Ljava/lang/Runnable;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda40;->run()V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda4;->(Lorg/thoughtcrime/securesms/recipients/RecipientId;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda4;->run()V -HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda5;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/recipients/RecipientId;)V -HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda5;->run()V -HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda6;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver;J)V -HSPLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda6;->run()V -HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$6H_TtixOHSa7Tr30medlqcHry2c(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$6mdIgDDCV4XFVFnyxH8Vj4a6MqU(Lorg/thoughtcrime/securesms/database/DatabaseObserver;JLorg/thoughtcrime/securesms/database/DatabaseObserver$MessageObserver;)V -HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$8PzBCQMLi_6Y7FOR98cRbpXw-Xk(Lorg/thoughtcrime/securesms/database/DatabaseObserver;JLorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$Aq7iz6-OcN5qdEpvMz8WyoOoHtc(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Ljava/lang/Runnable;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$FLqOSncPM9UHAHmQfH7ITyYgYis(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$P-H8JPj8WgBa8EorlTkjTC0yG1E(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/recipients/RecipientId;Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$Q9T3e0x03-9UyovUEacfv32ZkYs(Lorg/thoughtcrime/securesms/recipients/RecipientId;)V -HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$XcpL0fyOGdTr1sc4d0z4i8eoe14(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/recipients/RecipientId;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$XpAe1b_YlxfSEkV3hD_v20iDkHw(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/database/DatabaseObserver$MessageObserver;)V -HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$ZjxWKgbWA1SSTmnWoVneQana_Lk(Lorg/thoughtcrime/securesms/database/DatabaseObserver;J)V -HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$dh6RWMfCAixhY74q-duAcBwIwmU(Lorg/thoughtcrime/securesms/database/DatabaseObserver;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$mv9tymw4eNQuLtAMo52Pot0i2c4(Lorg/thoughtcrime/securesms/database/DatabaseObserver;J)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$nM9Xevlg3i5jd4hhWqCSJ8V0APs(Lorg/thoughtcrime/securesms/database/DatabaseObserver;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$oXFDlhvhHFY1OBIQHYp3Oanmq-k(Lorg/thoughtcrime/securesms/database/DatabaseObserver;JLorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V -HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$v9-I7k7VKIptUuQHIpRZcaVjlwY(Lorg/thoughtcrime/securesms/database/DatabaseObserver;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$wnm9BEANNc03FZmWKcqOLSgrT_U(Lorg/thoughtcrime/securesms/database/DatabaseObserver;JLorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$wtkgxGON7fTcqqEso3BleXuYIA8(Lorg/thoughtcrime/securesms/database/DatabaseObserver;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$zacSulZCbj18KAJ4fsL5guxghT4(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V @@ -23396,30 +23442,20 @@ HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$notifyAttachme HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$notifyConversationListListeners$22()V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$notifyConversationListeners$19(J)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$notifyRecipientChanged$34(Lorg/thoughtcrime/securesms/recipients/RecipientId;)V -HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$notifyStickerObservers$26()V -HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$notifyStickerPackObservers$27()V -HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$notifyStoryObservers$35(Lorg/thoughtcrime/securesms/recipients/RecipientId;)V -HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$notifyVerboseConversationListeners$20(J)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$registerConversationListObserver$0(Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$registerConversationObserver$1(JLorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$registerMessageInsertObserver$11(JLorg/thoughtcrime/securesms/database/DatabaseObserver$MessageObserver;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$registerMessageUpdateObserver$10(Lorg/thoughtcrime/securesms/database/DatabaseObserver$MessageObserver;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$registerNotificationProfileObserver$12(Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V -HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$registerScheduledMessageObserver$14(JLorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$registerStoryObserver$13(Lorg/thoughtcrime/securesms/recipients/RecipientId;Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$registerVerboseConversationObserver$2(JLorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$runPostSuccessfulTransaction$40(Ljava/lang/Runnable;)V -HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$unregisterObserver$17(Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->notifyAttachmentObservers()V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->notifyConversationListListeners()V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->notifyConversationListeners(J)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->notifyMapped(Ljava/util/Map;Ljava/lang/Object;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->notifyRecipientChanged(Lorg/thoughtcrime/securesms/recipients/RecipientId;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->notifySet(Ljava/util/Set;)V -HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->notifyStickerObservers()V -HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->notifyStickerPackObservers()V -HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->notifyStoryObservers(Lorg/thoughtcrime/securesms/recipients/RecipientId;)V -HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->notifyVerboseConversationListeners(Ljava/util/Set;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->registerConversationListObserver(Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->registerConversationObserver(JLorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->registerMapped(Ljava/util/Map;Ljava/lang/Object;Ljava/lang/Object;)V @@ -23430,8 +23466,6 @@ HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->registerScheduledMess HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->registerStoryObserver(Lorg/thoughtcrime/securesms/recipients/RecipientId;Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->registerVerboseConversationObserver(JLorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->runPostSuccessfulTransaction(Ljava/lang/String;Ljava/lang/Runnable;)V -HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->unregisterMapped(Ljava/util/Map;Ljava/lang/Object;)V -HSPLorg/thoughtcrime/securesms/database/DatabaseObserver;->unregisterObserver(Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/DatabaseTable;->()V HSPLorg/thoughtcrime/securesms/database/DatabaseTable;->(Landroid/content/Context;Lorg/thoughtcrime/securesms/database/SignalDatabase;)V HSPLorg/thoughtcrime/securesms/database/DatabaseTable;->getReadableDatabase()Lorg/thoughtcrime/securesms/database/SQLiteDatabase; @@ -23439,9 +23473,6 @@ HSPLorg/thoughtcrime/securesms/database/DatabaseTable;->getWritableDatabase()Lor HSPLorg/thoughtcrime/securesms/database/DatabaseTable;->notifyAttachmentListeners()V HSPLorg/thoughtcrime/securesms/database/DatabaseTable;->notifyConversationListListeners()V HSPLorg/thoughtcrime/securesms/database/DatabaseTable;->notifyConversationListeners(J)V -HSPLorg/thoughtcrime/securesms/database/DatabaseTable;->notifyStickerListeners()V -HSPLorg/thoughtcrime/securesms/database/DatabaseTable;->notifyStickerPackListeners()V -HSPLorg/thoughtcrime/securesms/database/DatabaseTable;->notifyVerboseConversationListeners(Ljava/util/Set;)V HSPLorg/thoughtcrime/securesms/database/DistributionListTables$Companion;->()V HSPLorg/thoughtcrime/securesms/database/DistributionListTables$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/database/DistributionListTables$ListTable;->()V @@ -23460,15 +23491,12 @@ HSPLorg/thoughtcrime/securesms/database/DraftTable$Companion;->()V HSPLorg/thoughtcrime/securesms/database/DraftTable$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/database/DraftTable$Drafts;->()V HSPLorg/thoughtcrime/securesms/database/DraftTable$Drafts;->(Ljava/util/List;)V -HSPLorg/thoughtcrime/securesms/database/DraftTable$Drafts;->(Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V -HSPLorg/thoughtcrime/securesms/database/DraftTable$Drafts;->addIfNotNull(Lorg/thoughtcrime/securesms/database/DraftTable$Draft;)V HSPLorg/thoughtcrime/securesms/database/DraftTable$Drafts;->getDraftOfType(Ljava/lang/String;)Lorg/thoughtcrime/securesms/database/DraftTable$Draft; HSPLorg/thoughtcrime/securesms/database/DraftTable$Drafts;->getSize()I HSPLorg/thoughtcrime/securesms/database/DraftTable$Drafts;->size()I HSPLorg/thoughtcrime/securesms/database/DraftTable;->()V HSPLorg/thoughtcrime/securesms/database/DraftTable;->(Landroid/content/Context;Lorg/thoughtcrime/securesms/database/SignalDatabase;)V HSPLorg/thoughtcrime/securesms/database/DraftTable;->asDrafts(Ljava/util/List;)Lorg/thoughtcrime/securesms/database/DraftTable$Drafts; -HSPLorg/thoughtcrime/securesms/database/DraftTable;->clearDrafts(J)V HSPLorg/thoughtcrime/securesms/database/DraftTable;->getDrafts(J)Lorg/thoughtcrime/securesms/database/DraftTable$Drafts; HSPLorg/thoughtcrime/securesms/database/EarlyDeliveryReceiptCache;->()V HSPLorg/thoughtcrime/securesms/database/EarlyDeliveryReceiptCache;->()V @@ -23476,6 +23504,7 @@ HSPLorg/thoughtcrime/securesms/database/EmojiSearchTable$Companion;->()V HSPLorg/thoughtcrime/securesms/database/EmojiSearchTable$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/database/EmojiSearchTable$setSearchIndex$1;->(Lorg/thoughtcrime/securesms/database/SQLiteDatabase;Ljava/util/List;)V HSPLorg/thoughtcrime/securesms/database/EmojiSearchTable$setSearchIndex$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/database/EmojiSearchTable$setSearchIndex$1;->invoke(Lorg/thoughtcrime/securesms/database/SQLiteDatabase;)V HSPLorg/thoughtcrime/securesms/database/EmojiSearchTable;->()V HSPLorg/thoughtcrime/securesms/database/EmojiSearchTable;->(Landroid/content/Context;Lorg/thoughtcrime/securesms/database/SignalDatabase;)V HSPLorg/thoughtcrime/securesms/database/EmojiSearchTable;->setSearchIndex(Ljava/util/List;)V @@ -23533,9 +23562,6 @@ HSPLorg/thoughtcrime/securesms/database/JobDatabase$deleteJobs$1;->invoke(Lnet/z HSPLorg/thoughtcrime/securesms/database/JobDatabase$insertJobs$2;->(Ljava/util/List;Lorg/thoughtcrime/securesms/database/JobDatabase;)V HSPLorg/thoughtcrime/securesms/database/JobDatabase$insertJobs$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/database/JobDatabase$insertJobs$2;->invoke(Lnet/zetetic/database/sqlcipher/SQLiteDatabase;)V -HSPLorg/thoughtcrime/securesms/database/JobDatabase$updateJobs$2;->(Ljava/util/List;)V -HSPLorg/thoughtcrime/securesms/database/JobDatabase$updateJobs$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object; -HSPLorg/thoughtcrime/securesms/database/JobDatabase$updateJobs$2;->invoke(Lnet/zetetic/database/sqlcipher/SQLiteDatabase;)V HSPLorg/thoughtcrime/securesms/database/JobDatabase;->$r8$lambda$ou_p531IVGikC2LNueT6qnVrWyc(Lorg/thoughtcrime/securesms/database/JobDatabase;)V HSPLorg/thoughtcrime/securesms/database/JobDatabase;->()V HSPLorg/thoughtcrime/securesms/database/JobDatabase;->(Landroid/app/Application;Lorg/thoughtcrime/securesms/crypto/DatabaseSecret;)V @@ -23544,7 +23570,6 @@ HSPLorg/thoughtcrime/securesms/database/JobDatabase;->access$insertConstraintSpe HSPLorg/thoughtcrime/securesms/database/JobDatabase;->access$insertDependencySpecs(Lorg/thoughtcrime/securesms/database/JobDatabase;Lnet/zetetic/database/sqlcipher/SQLiteDatabase;Ljava/util/List;)V HSPLorg/thoughtcrime/securesms/database/JobDatabase;->access$insertJobSpec(Lorg/thoughtcrime/securesms/database/JobDatabase;Lnet/zetetic/database/sqlcipher/SQLiteDatabase;Lorg/thoughtcrime/securesms/jobmanager/persistence/JobSpec;)V HSPLorg/thoughtcrime/securesms/database/JobDatabase;->access$setInstance$cp(Lorg/thoughtcrime/securesms/database/JobDatabase;)V -HSPLorg/thoughtcrime/securesms/database/JobDatabase;->constraintSpecFromCursor(Landroid/database/Cursor;)Lorg/thoughtcrime/securesms/jobmanager/persistence/ConstraintSpec; HSPLorg/thoughtcrime/securesms/database/JobDatabase;->deleteJobs(Ljava/util/List;)V HSPLorg/thoughtcrime/securesms/database/JobDatabase;->dropTableIfPresent(Ljava/lang/String;)V HSPLorg/thoughtcrime/securesms/database/JobDatabase;->getAllConstraintSpecs()Ljava/util/List; @@ -23555,13 +23580,10 @@ HSPLorg/thoughtcrime/securesms/database/JobDatabase;->insertConstraintSpecs(Lnet HSPLorg/thoughtcrime/securesms/database/JobDatabase;->insertDependencySpecs(Lnet/zetetic/database/sqlcipher/SQLiteDatabase;Ljava/util/List;)V HSPLorg/thoughtcrime/securesms/database/JobDatabase;->insertJobSpec(Lnet/zetetic/database/sqlcipher/SQLiteDatabase;Lorg/thoughtcrime/securesms/jobmanager/persistence/JobSpec;)V HSPLorg/thoughtcrime/securesms/database/JobDatabase;->insertJobs(Ljava/util/List;)V -HSPLorg/thoughtcrime/securesms/database/JobDatabase;->jobSpecFromCursor(Landroid/database/Cursor;)Lorg/thoughtcrime/securesms/jobmanager/persistence/JobSpec; HSPLorg/thoughtcrime/securesms/database/JobDatabase;->markJobAsRunning(Ljava/lang/String;J)V HSPLorg/thoughtcrime/securesms/database/JobDatabase;->onOpen$lambda$0(Lorg/thoughtcrime/securesms/database/JobDatabase;)V HSPLorg/thoughtcrime/securesms/database/JobDatabase;->onOpen(Lnet/zetetic/database/sqlcipher/SQLiteDatabase;)V HSPLorg/thoughtcrime/securesms/database/JobDatabase;->updateAllJobsToBePending()V -HSPLorg/thoughtcrime/securesms/database/JobDatabase;->updateJobAfterRetry(Ljava/lang/String;JIJ[B)V -HSPLorg/thoughtcrime/securesms/database/JobDatabase;->updateJobs(Ljava/util/List;)V HSPLorg/thoughtcrime/securesms/database/KeyValueDatabase$$ExternalSyntheticLambda0;->()V HSPLorg/thoughtcrime/securesms/database/KeyValueDatabase$$ExternalSyntheticLambda0;->run()V HSPLorg/thoughtcrime/securesms/database/KeyValueDatabase$1;->()V @@ -23739,7 +23761,7 @@ HSPLorg/thoughtcrime/securesms/database/MessageTable$MmsReader;->close()V HSPLorg/thoughtcrime/securesms/database/MessageTable$MmsReader;->getCurrent()Lorg/thoughtcrime/securesms/database/model/MessageRecord; HSPLorg/thoughtcrime/securesms/database/MessageTable$MmsReader;->getCursor()Landroid/database/Cursor; HSPLorg/thoughtcrime/securesms/database/MessageTable$MmsReader;->getFailures(Ljava/lang/String;)Ljava/util/Set; -HSPLorg/thoughtcrime/securesms/database/MessageTable$MmsReader;->getMediaMmsMessageRecord(Landroid/database/Cursor;)Lorg/thoughtcrime/securesms/database/model/MediaMmsMessageRecord; +HSPLorg/thoughtcrime/securesms/database/MessageTable$MmsReader;->getMediaMmsMessageRecord(Landroid/database/Cursor;)Lorg/thoughtcrime/securesms/database/model/MmsMessageRecord; HSPLorg/thoughtcrime/securesms/database/MessageTable$MmsReader;->getMismatchedIdentities(Ljava/lang/String;)Ljava/util/Set; HSPLorg/thoughtcrime/securesms/database/MessageTable$MmsReader;->getNext()Lorg/thoughtcrime/securesms/database/model/MessageRecord; HSPLorg/thoughtcrime/securesms/database/MessageTable$MmsReader;->getQuote(Landroid/database/Cursor;)Lorg/thoughtcrime/securesms/database/model/Quote; @@ -23765,14 +23787,12 @@ HSPLorg/thoughtcrime/securesms/database/MessageTable;->access$getSerializedLinkP HSPLorg/thoughtcrime/securesms/database/MessageTable;->access$getSerializedSharedContacts(Lorg/thoughtcrime/securesms/database/MessageTable;Ljava/util/Map;Ljava/util/List;)Ljava/lang/String; HSPLorg/thoughtcrime/securesms/database/MessageTable;->buildMeaningfulMessagesQuery(J)Lorg/signal/core/util/SqlUtil$Query; HSPLorg/thoughtcrime/securesms/database/MessageTable;->getAllRateLimitedMessageIds()Ljava/util/Set; -HSPLorg/thoughtcrime/securesms/database/MessageTable;->getAllStoriesFor(Lorg/thoughtcrime/securesms/recipients/RecipientId;I)Lorg/thoughtcrime/securesms/database/MessageTable$Reader; HSPLorg/thoughtcrime/securesms/database/MessageTable;->getConversation(JJJ)Landroid/database/Cursor; HSPLorg/thoughtcrime/securesms/database/MessageTable;->getConversationSnippet(J)Lorg/thoughtcrime/securesms/database/model/MessageRecord; HSPLorg/thoughtcrime/securesms/database/MessageTable;->getConversationSnippetCursor(J)Landroid/database/Cursor; HSPLorg/thoughtcrime/securesms/database/MessageTable;->getExpirationStartedMessages()Landroid/database/Cursor; HSPLorg/thoughtcrime/securesms/database/MessageTable;->getMessageCountForThread(J)I HSPLorg/thoughtcrime/securesms/database/MessageTable;->getMessageRecord(J)Lorg/thoughtcrime/securesms/database/model/MessageRecord; -HSPLorg/thoughtcrime/securesms/database/MessageTable;->getMessagesForNotificationState(Ljava/util/Collection;)Landroid/database/Cursor; HSPLorg/thoughtcrime/securesms/database/MessageTable;->getNearestExpiringViewOnceMessage()Lorg/thoughtcrime/securesms/revealable/ViewOnceExpirationInfo; HSPLorg/thoughtcrime/securesms/database/MessageTable;->getOldestScheduledSendTimestamp()Lorg/thoughtcrime/securesms/database/model/MessageRecord; HSPLorg/thoughtcrime/securesms/database/MessageTable;->getOldestStorySendTimestamp(Z)Ljava/lang/Long; @@ -23790,8 +23810,8 @@ HSPLorg/thoughtcrime/securesms/database/MessageTable;->getUnreadStoryThreadRecip HSPLorg/thoughtcrime/securesms/database/MessageTable;->hasFailedOutgoingStory()Z HSPLorg/thoughtcrime/securesms/database/MessageTable;->hasMeaningfulMessage(J)Z HSPLorg/thoughtcrime/securesms/database/MessageTable;->insertMediaMessage(JLjava/lang/String;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;Lorg/thoughtcrime/securesms/database/model/databaseprotos/BodyRangeList;Landroid/content/ContentValues;Lorg/thoughtcrime/securesms/database/MessageTable$InsertListener;ZZ)Lkotlin/Pair; -HSPLorg/thoughtcrime/securesms/database/MessageTable;->insertMessageInbox$default(Lorg/thoughtcrime/securesms/database/MessageTable;Lorg/thoughtcrime/securesms/mms/IncomingMessage;JLorg/thoughtcrime/securesms/database/model/MediaMmsMessageRecord;ZILjava/lang/Object;)Lj$/util/Optional; -HSPLorg/thoughtcrime/securesms/database/MessageTable;->insertMessageInbox(Lorg/thoughtcrime/securesms/mms/IncomingMessage;JLorg/thoughtcrime/securesms/database/model/MediaMmsMessageRecord;Z)Lj$/util/Optional; +HSPLorg/thoughtcrime/securesms/database/MessageTable;->insertMessageInbox$default(Lorg/thoughtcrime/securesms/database/MessageTable;Lorg/thoughtcrime/securesms/mms/IncomingMessage;JLorg/thoughtcrime/securesms/database/model/MmsMessageRecord;ZILjava/lang/Object;)Lj$/util/Optional; +HSPLorg/thoughtcrime/securesms/database/MessageTable;->insertMessageInbox(Lorg/thoughtcrime/securesms/mms/IncomingMessage;JLorg/thoughtcrime/securesms/database/model/MmsMessageRecord;Z)Lj$/util/Optional; HSPLorg/thoughtcrime/securesms/database/MessageTable;->isQuoted(Ljava/util/Collection;)Ljava/util/Set; HSPLorg/thoughtcrime/securesms/database/MessageTable;->mmsReaderFor(Landroid/database/Cursor;)Lorg/thoughtcrime/securesms/database/MessageTable$MmsReader; HSPLorg/thoughtcrime/securesms/database/MessageTable;->rawQueryWithAttachments$default(Lorg/thoughtcrime/securesms/database/MessageTable;Ljava/lang/String;[Ljava/lang/String;ZJILjava/lang/Object;)Landroid/database/Cursor; @@ -23799,8 +23819,6 @@ HSPLorg/thoughtcrime/securesms/database/MessageTable;->rawQueryWithAttachments(L HSPLorg/thoughtcrime/securesms/database/MessageTable;->rawQueryWithAttachments([Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;ZJ)Landroid/database/Cursor; HSPLorg/thoughtcrime/securesms/database/MessageTable;->setAllMessagesRead()Ljava/util/List; HSPLorg/thoughtcrime/securesms/database/MessageTable;->setMessagesRead(Ljava/lang/String;[Ljava/lang/String;)Ljava/util/List; -HSPLorg/thoughtcrime/securesms/database/MessageTable;->setMessagesReadSince(JJ)Ljava/util/List; -HSPLorg/thoughtcrime/securesms/database/MessageTable;->setReactionsSeen(JJ)V HSPLorg/thoughtcrime/securesms/database/MessageTable;->toMessageType(Lorg/thoughtcrime/securesms/mms/IncomingMessage;)J HSPLorg/thoughtcrime/securesms/database/MessageTable;->updatePendingSelfData(Lorg/thoughtcrime/securesms/recipients/RecipientId;Lorg/thoughtcrime/securesms/recipients/RecipientId;)V HSPLorg/thoughtcrime/securesms/database/MessageType;->$values()[Lorg/thoughtcrime/securesms/database/MessageType; @@ -23920,9 +23938,6 @@ HSPLorg/thoughtcrime/securesms/database/PnpIdResolver$PnpInsert;->(Ljava/l HSPLorg/thoughtcrime/securesms/database/PnpIdResolver$PnpInsert;->getAci()Lorg/whispersystems/signalservice/api/push/ServiceId$ACI; HSPLorg/thoughtcrime/securesms/database/PnpIdResolver$PnpInsert;->getE164()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/database/PnpIdResolver$PnpInsert;->getPni()Lorg/whispersystems/signalservice/api/push/ServiceId$PNI; -HSPLorg/thoughtcrime/securesms/database/PnpIdResolver$PnpNoopId;->()V -HSPLorg/thoughtcrime/securesms/database/PnpIdResolver$PnpNoopId;->(Lorg/thoughtcrime/securesms/recipients/RecipientId;)V -HSPLorg/thoughtcrime/securesms/database/PnpIdResolver$PnpNoopId;->getRecipientId()Lorg/thoughtcrime/securesms/recipients/RecipientId; HSPLorg/thoughtcrime/securesms/database/PnpIdResolver;->()V HSPLorg/thoughtcrime/securesms/database/PnpIdResolver;->()V HSPLorg/thoughtcrime/securesms/database/PnpIdResolver;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -23931,18 +23946,11 @@ HSPLorg/thoughtcrime/securesms/database/ReactionTable$Companion;->(Lkotlin HSPLorg/thoughtcrime/securesms/database/ReactionTable;->()V HSPLorg/thoughtcrime/securesms/database/ReactionTable;->(Landroid/content/Context;Lorg/thoughtcrime/securesms/database/SignalDatabase;)V HSPLorg/thoughtcrime/securesms/database/ReactionTable;->getReactionsForMessages(Ljava/util/Collection;)Ljava/util/Map; -HSPLorg/thoughtcrime/securesms/database/RecipientTable$$ExternalSyntheticLambda0;->(Lkotlin/jvm/functions/Function1;)V -HSPLorg/thoughtcrime/securesms/database/RecipientTable$$ExternalSyntheticLambda0;->apply(Ljava/lang/Object;)Ljava/lang/Object; -HSPLorg/thoughtcrime/securesms/database/RecipientTable$$ExternalSyntheticLambda11;->(Lkotlin/jvm/functions/Function1;)V -HSPLorg/thoughtcrime/securesms/database/RecipientTable$$ExternalSyntheticLambda1;->(Lkotlin/jvm/functions/Function1;)V -HSPLorg/thoughtcrime/securesms/database/RecipientTable$$ExternalSyntheticLambda2;->(Lkotlin/jvm/functions/Function1;)V -HSPLorg/thoughtcrime/securesms/database/RecipientTable$$ExternalSyntheticLambda3;->(Lkotlin/jvm/functions/Function1;)V -HSPLorg/thoughtcrime/securesms/database/RecipientTable$$ExternalSyntheticLambda5;->()V -HSPLorg/thoughtcrime/securesms/database/RecipientTable$$ExternalSyntheticLambda5;->run()V +HSPLorg/thoughtcrime/securesms/database/RecipientTable$$ExternalSyntheticLambda1;->()V +HSPLorg/thoughtcrime/securesms/database/RecipientTable$$ExternalSyntheticLambda1;->run()V HSPLorg/thoughtcrime/securesms/database/RecipientTable$Companion;->()V HSPLorg/thoughtcrime/securesms/database/RecipientTable$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V -HSPLorg/thoughtcrime/securesms/database/RecipientTable$GetOrInsertResult;->(Lorg/thoughtcrime/securesms/recipients/RecipientId;Z)V -HSPLorg/thoughtcrime/securesms/database/RecipientTable$GetOrInsertResult;->getRecipientId()Lorg/thoughtcrime/securesms/recipients/RecipientId; +HSPLorg/thoughtcrime/securesms/database/RecipientTable$Companion;->maskCapabilitiesToLong(Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$Capabilities;)J HSPLorg/thoughtcrime/securesms/database/RecipientTable$MentionSetting$Companion;->()V HSPLorg/thoughtcrime/securesms/database/RecipientTable$MentionSetting$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/database/RecipientTable$MentionSetting$Companion;->fromId(I)Lorg/thoughtcrime/securesms/database/RecipientTable$MentionSetting; @@ -23951,6 +23959,14 @@ HSPLorg/thoughtcrime/securesms/database/RecipientTable$MentionSetting;-> HSPLorg/thoughtcrime/securesms/database/RecipientTable$MentionSetting;->(Ljava/lang/String;II)V HSPLorg/thoughtcrime/securesms/database/RecipientTable$MentionSetting;->getId()I HSPLorg/thoughtcrime/securesms/database/RecipientTable$MentionSetting;->values()[Lorg/thoughtcrime/securesms/database/RecipientTable$MentionSetting; +HSPLorg/thoughtcrime/securesms/database/RecipientTable$PhoneNumberSharingState$Companion;->()V +HSPLorg/thoughtcrime/securesms/database/RecipientTable$PhoneNumberSharingState$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/database/RecipientTable$PhoneNumberSharingState$Companion;->fromId(I)Lorg/thoughtcrime/securesms/database/RecipientTable$PhoneNumberSharingState; +HSPLorg/thoughtcrime/securesms/database/RecipientTable$PhoneNumberSharingState;->$values()[Lorg/thoughtcrime/securesms/database/RecipientTable$PhoneNumberSharingState; +HSPLorg/thoughtcrime/securesms/database/RecipientTable$PhoneNumberSharingState;->()V +HSPLorg/thoughtcrime/securesms/database/RecipientTable$PhoneNumberSharingState;->(Ljava/lang/String;II)V +HSPLorg/thoughtcrime/securesms/database/RecipientTable$PhoneNumberSharingState;->getId()I +HSPLorg/thoughtcrime/securesms/database/RecipientTable$PhoneNumberSharingState;->values()[Lorg/thoughtcrime/securesms/database/RecipientTable$PhoneNumberSharingState; HSPLorg/thoughtcrime/securesms/database/RecipientTable$ProcessPnpTupleResult;->()V HSPLorg/thoughtcrime/securesms/database/RecipientTable$ProcessPnpTupleResult;->(Lorg/thoughtcrime/securesms/recipients/RecipientId;ZLjava/util/Set;Ljava/util/Set;Lorg/thoughtcrime/securesms/recipients/RecipientId;Ljava/util/List;Ljava/util/List;)V HSPLorg/thoughtcrime/securesms/database/RecipientTable$ProcessPnpTupleResult;->getAffectedIds()Ljava/util/Set; @@ -23995,20 +24011,14 @@ HSPLorg/thoughtcrime/securesms/database/RecipientTable$VibrateState;->() HSPLorg/thoughtcrime/securesms/database/RecipientTable$VibrateState;->(Ljava/lang/String;II)V HSPLorg/thoughtcrime/securesms/database/RecipientTable$VibrateState;->getId()I HSPLorg/thoughtcrime/securesms/database/RecipientTable$VibrateState;->values()[Lorg/thoughtcrime/securesms/database/RecipientTable$VibrateState; -HSPLorg/thoughtcrime/securesms/database/RecipientTable$getRecipientExtras$1;->()V -HSPLorg/thoughtcrime/securesms/database/RecipientTable$getRecipientExtras$1;->()V -HSPLorg/thoughtcrime/securesms/database/RecipientTable$getSyncExtras$forcedUnread$1;->()V -HSPLorg/thoughtcrime/securesms/database/RecipientTable$getSyncExtras$forcedUnread$1;->()V -HSPLorg/thoughtcrime/securesms/database/RecipientTable$getSyncExtras$forcedUnread$1;->invoke(I)Ljava/lang/Boolean; -HSPLorg/thoughtcrime/securesms/database/RecipientTable$getSyncExtras$forcedUnread$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; -HSPLorg/thoughtcrime/securesms/database/RecipientTable$getSyncExtras$groupMasterKey$1;->()V -HSPLorg/thoughtcrime/securesms/database/RecipientTable$getSyncExtras$groupMasterKey$1;->()V -HSPLorg/thoughtcrime/securesms/database/RecipientTable$getSyncExtras$identityKey$1;->()V -HSPLorg/thoughtcrime/securesms/database/RecipientTable$getSyncExtras$identityKey$1;->()V -HSPLorg/thoughtcrime/securesms/database/RecipientTable$getSyncExtras$identityStatus$1;->()V -HSPLorg/thoughtcrime/securesms/database/RecipientTable$getSyncExtras$identityStatus$1;->()V -HSPLorg/thoughtcrime/securesms/database/RecipientTable;->$r8$lambda$I9NN4kN7VPSkM3QPO5OVTF3d8X8(Lkotlin/jvm/functions/Function1;Ljava/lang/Object;)Ljava/lang/Boolean; -HSPLorg/thoughtcrime/securesms/database/RecipientTable;->$r8$lambda$SzDz3F5GmkEQ7n-yGmY62to6e5Y()V +HSPLorg/thoughtcrime/securesms/database/RecipientTable$getAndPossiblyMerge$3$$ExternalSyntheticLambda0;->(Lkotlin/jvm/internal/Ref$ObjectRef;)V +HSPLorg/thoughtcrime/securesms/database/RecipientTable$getAndPossiblyMerge$3$$ExternalSyntheticLambda0;->run()V +HSPLorg/thoughtcrime/securesms/database/RecipientTable$getAndPossiblyMerge$3;->$r8$lambda$VUPjpJzVVE8vlQGmPfSlIlqx-mg(Lkotlin/jvm/internal/Ref$ObjectRef;)V +HSPLorg/thoughtcrime/securesms/database/RecipientTable$getAndPossiblyMerge$3;->(Lkotlin/jvm/internal/Ref$ObjectRef;Lorg/thoughtcrime/securesms/database/RecipientTable;Ljava/lang/String;Lorg/whispersystems/signalservice/api/push/ServiceId$PNI;Lorg/whispersystems/signalservice/api/push/ServiceId$ACI;ZZLorg/thoughtcrime/securesms/database/SQLiteDatabase;)V +HSPLorg/thoughtcrime/securesms/database/RecipientTable$getAndPossiblyMerge$3;->invoke$lambda$2(Lkotlin/jvm/internal/Ref$ObjectRef;)V +HSPLorg/thoughtcrime/securesms/database/RecipientTable$getAndPossiblyMerge$3;->invoke(Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/database/RecipientTable$getAndPossiblyMerge$3;->invoke(Lorg/thoughtcrime/securesms/database/SQLiteDatabase;)V +HSPLorg/thoughtcrime/securesms/database/RecipientTable;->$r8$lambda$aWzIz5YHb9jNapMRCuQVry5PhmE()V HSPLorg/thoughtcrime/securesms/database/RecipientTable;->()V HSPLorg/thoughtcrime/securesms/database/RecipientTable;->(Landroid/content/Context;Lorg/thoughtcrime/securesms/database/SignalDatabase;)V HSPLorg/thoughtcrime/securesms/database/RecipientTable;->buildContentValuesForNewUser(Ljava/lang/String;Lorg/whispersystems/signalservice/api/push/ServiceId$PNI;Lorg/whispersystems/signalservice/api/push/ServiceId$ACI;)Landroid/content/ContentValues; @@ -24022,28 +24032,19 @@ HSPLorg/thoughtcrime/securesms/database/RecipientTable;->getByColumn(Ljava/lang/ HSPLorg/thoughtcrime/securesms/database/RecipientTable;->getByE164(Ljava/lang/String;)Lj$/util/Optional; HSPLorg/thoughtcrime/securesms/database/RecipientTable;->getByPni(Lorg/whispersystems/signalservice/api/push/ServiceId$PNI;)Lj$/util/Optional; HSPLorg/thoughtcrime/securesms/database/RecipientTable;->getByServiceId(Lorg/whispersystems/signalservice/api/push/ServiceId;)Lj$/util/Optional; -HSPLorg/thoughtcrime/securesms/database/RecipientTable;->getExtras(Landroid/database/Cursor;)Lorg/thoughtcrime/securesms/recipients/Recipient$Extras; -HSPLorg/thoughtcrime/securesms/database/RecipientTable;->getRecipientExtras(Landroid/database/Cursor;)Lorg/thoughtcrime/securesms/database/model/databaseprotos/RecipientExtras; +HSPLorg/thoughtcrime/securesms/database/RecipientTable;->getRecipientIdIfAllFieldsMatch(Lorg/whispersystems/signalservice/api/push/ServiceId$ACI;Lorg/whispersystems/signalservice/api/push/ServiceId$PNI;Ljava/lang/String;)Lorg/thoughtcrime/securesms/recipients/RecipientId; HSPLorg/thoughtcrime/securesms/database/RecipientTable;->getRecipientsWithNotificationChannels()Lorg/thoughtcrime/securesms/database/RecipientTable$RecipientReader; -HSPLorg/thoughtcrime/securesms/database/RecipientTable;->getRecord(Landroid/content/Context;Landroid/database/Cursor;)Lorg/thoughtcrime/securesms/database/model/RecipientRecord; -HSPLorg/thoughtcrime/securesms/database/RecipientTable;->getRecord(Landroid/content/Context;Landroid/database/Cursor;Ljava/lang/String;)Lorg/thoughtcrime/securesms/database/model/RecipientRecord; HSPLorg/thoughtcrime/securesms/database/RecipientTable;->getRecord(Lorg/thoughtcrime/securesms/recipients/RecipientId;)Lorg/thoughtcrime/securesms/database/model/RecipientRecord; -HSPLorg/thoughtcrime/securesms/database/RecipientTable;->getSyncExtras$lambda$136(Lkotlin/jvm/functions/Function1;Ljava/lang/Object;)Ljava/lang/Boolean; -HSPLorg/thoughtcrime/securesms/database/RecipientTable;->getSyncExtras(Landroid/database/Cursor;)Lorg/thoughtcrime/securesms/database/model/RecipientRecord$SyncExtras; -HSPLorg/thoughtcrime/securesms/database/RecipientTable;->insertReleaseChannelRecipient()Lorg/thoughtcrime/securesms/recipients/RecipientId; +HSPLorg/thoughtcrime/securesms/database/RecipientTable;->getTAG()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/database/RecipientTable;->linkIdsForSelf(Lorg/whispersystems/signalservice/api/push/ServiceId$ACI;Lorg/whispersystems/signalservice/api/push/ServiceId$PNI;Ljava/lang/String;)V HSPLorg/thoughtcrime/securesms/database/RecipientTable;->markNeedsSync(Lorg/thoughtcrime/securesms/recipients/RecipientId;)V HSPLorg/thoughtcrime/securesms/database/RecipientTable;->markRegistered(Lorg/thoughtcrime/securesms/recipients/RecipientId;Lorg/whispersystems/signalservice/api/push/ServiceId;)Z HSPLorg/thoughtcrime/securesms/database/RecipientTable;->markRegisteredOrThrow(Lorg/thoughtcrime/securesms/recipients/RecipientId;Lorg/whispersystems/signalservice/api/push/ServiceId;)V -HSPLorg/thoughtcrime/securesms/database/RecipientTable;->parseBadgeList([B)Ljava/util/List; HSPLorg/thoughtcrime/securesms/database/RecipientTable;->processPnpTuple(Ljava/lang/String;Lorg/whispersystems/signalservice/api/push/ServiceId$PNI;Lorg/whispersystems/signalservice/api/push/ServiceId$ACI;ZZ)Lorg/thoughtcrime/securesms/database/RecipientTable$ProcessPnpTupleResult; HSPLorg/thoughtcrime/securesms/database/RecipientTable;->processPnpTupleToChangeSet(Ljava/lang/String;Lorg/whispersystems/signalservice/api/push/ServiceId$PNI;Lorg/whispersystems/signalservice/api/push/ServiceId$ACI;ZZ)Lorg/thoughtcrime/securesms/database/PnpChangeSet; -HSPLorg/thoughtcrime/securesms/database/RecipientTable;->readCapabilities(Landroid/database/Cursor;)Lorg/thoughtcrime/securesms/database/model/RecipientRecord$Capabilities; HSPLorg/thoughtcrime/securesms/database/RecipientTable;->rotateStorageId(Lorg/thoughtcrime/securesms/recipients/RecipientId;)V HSPLorg/thoughtcrime/securesms/database/RecipientTable;->setCapabilities(Lorg/thoughtcrime/securesms/recipients/RecipientId;Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$Capabilities;)V -HSPLorg/thoughtcrime/securesms/database/RecipientTable;->setMuted(Lorg/thoughtcrime/securesms/recipients/RecipientId;J)V -HSPLorg/thoughtcrime/securesms/database/RecipientTable;->setProfileAvatar(Lorg/thoughtcrime/securesms/recipients/RecipientId;Ljava/lang/String;)V -HSPLorg/thoughtcrime/securesms/database/RecipientTable;->setProfileKey$lambda$54()V +HSPLorg/thoughtcrime/securesms/database/RecipientTable;->setProfileKey$lambda$58()V HSPLorg/thoughtcrime/securesms/database/RecipientTable;->setProfileKey(Lorg/thoughtcrime/securesms/recipients/RecipientId;Lorg/signal/libsignal/zkgroup/profiles/ProfileKey;)Z HSPLorg/thoughtcrime/securesms/database/RecipientTable;->setProfileKeyIfAbsent(Lorg/thoughtcrime/securesms/recipients/RecipientId;Lorg/signal/libsignal/zkgroup/profiles/ProfileKey;)Z HSPLorg/thoughtcrime/securesms/database/RecipientTable;->setProfileName(Lorg/thoughtcrime/securesms/recipients/RecipientId;Lorg/thoughtcrime/securesms/profiles/ProfileName;)V @@ -24052,6 +24053,33 @@ HSPLorg/thoughtcrime/securesms/database/RecipientTable;->update(Lorg/signal/core HSPLorg/thoughtcrime/securesms/database/RecipientTable;->update(Lorg/thoughtcrime/securesms/recipients/RecipientId;Landroid/content/ContentValues;)Z HSPLorg/thoughtcrime/securesms/database/RecipientTable;->updatePendingSelfData(Lorg/thoughtcrime/securesms/recipients/RecipientId;)V HSPLorg/thoughtcrime/securesms/database/RecipientTable;->writePnpChangeSetToDisk(Lorg/thoughtcrime/securesms/database/PnpChangeSet;Lorg/whispersystems/signalservice/api/push/ServiceId$PNI;)Lorg/thoughtcrime/securesms/recipients/RecipientId; +HSPLorg/thoughtcrime/securesms/database/RecipientTableCursorUtil$$ExternalSyntheticLambda0;->(Lkotlin/jvm/functions/Function1;)V +HSPLorg/thoughtcrime/securesms/database/RecipientTableCursorUtil$$ExternalSyntheticLambda0;->apply(Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/database/RecipientTableCursorUtil$$ExternalSyntheticLambda1;->(Lkotlin/jvm/functions/Function1;)V +HSPLorg/thoughtcrime/securesms/database/RecipientTableCursorUtil$$ExternalSyntheticLambda2;->(Lkotlin/jvm/functions/Function1;)V +HSPLorg/thoughtcrime/securesms/database/RecipientTableCursorUtil$$ExternalSyntheticLambda3;->(Lkotlin/jvm/functions/Function1;)V +HSPLorg/thoughtcrime/securesms/database/RecipientTableCursorUtil$$ExternalSyntheticLambda4;->(Lkotlin/jvm/functions/Function1;)V +HSPLorg/thoughtcrime/securesms/database/RecipientTableCursorUtil$getRecipientExtras$1;->()V +HSPLorg/thoughtcrime/securesms/database/RecipientTableCursorUtil$getRecipientExtras$1;->()V +HSPLorg/thoughtcrime/securesms/database/RecipientTableCursorUtil$getSyncExtras$forcedUnread$1;->()V +HSPLorg/thoughtcrime/securesms/database/RecipientTableCursorUtil$getSyncExtras$forcedUnread$1;->()V +HSPLorg/thoughtcrime/securesms/database/RecipientTableCursorUtil$getSyncExtras$forcedUnread$1;->invoke(I)Ljava/lang/Boolean; +HSPLorg/thoughtcrime/securesms/database/RecipientTableCursorUtil$getSyncExtras$forcedUnread$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/database/RecipientTableCursorUtil$getSyncExtras$groupMasterKey$1;->()V +HSPLorg/thoughtcrime/securesms/database/RecipientTableCursorUtil$getSyncExtras$groupMasterKey$1;->()V +HSPLorg/thoughtcrime/securesms/database/RecipientTableCursorUtil$getSyncExtras$identityKey$1;->()V +HSPLorg/thoughtcrime/securesms/database/RecipientTableCursorUtil$getSyncExtras$identityKey$1;->()V +HSPLorg/thoughtcrime/securesms/database/RecipientTableCursorUtil$getSyncExtras$identityStatus$1;->()V +HSPLorg/thoughtcrime/securesms/database/RecipientTableCursorUtil$getSyncExtras$identityStatus$1;->()V +HSPLorg/thoughtcrime/securesms/database/RecipientTableCursorUtil;->$r8$lambda$3gC4IvWj9S7nLeLK-eONY0kYlPA(Lkotlin/jvm/functions/Function1;Ljava/lang/Object;)Ljava/lang/Boolean; +HSPLorg/thoughtcrime/securesms/database/RecipientTableCursorUtil;->()V +HSPLorg/thoughtcrime/securesms/database/RecipientTableCursorUtil;->()V +HSPLorg/thoughtcrime/securesms/database/RecipientTableCursorUtil;->getExtras(Landroid/database/Cursor;)Lorg/thoughtcrime/securesms/recipients/Recipient$Extras; +HSPLorg/thoughtcrime/securesms/database/RecipientTableCursorUtil;->getRecipientExtras(Landroid/database/Cursor;)Lorg/thoughtcrime/securesms/database/model/databaseprotos/RecipientExtras; +HSPLorg/thoughtcrime/securesms/database/RecipientTableCursorUtil;->getRecord(Landroid/content/Context;Landroid/database/Cursor;)Lorg/thoughtcrime/securesms/database/model/RecipientRecord; +HSPLorg/thoughtcrime/securesms/database/RecipientTableCursorUtil;->getSyncExtras$lambda$2(Lkotlin/jvm/functions/Function1;Ljava/lang/Object;)Ljava/lang/Boolean; +HSPLorg/thoughtcrime/securesms/database/RecipientTableCursorUtil;->parseBadgeList([B)Ljava/util/List; +HSPLorg/thoughtcrime/securesms/database/RecipientTableCursorUtil;->readCapabilities(Landroid/database/Cursor;)Lorg/thoughtcrime/securesms/database/model/RecipientRecord$Capabilities; HSPLorg/thoughtcrime/securesms/database/RemappedRecordTables$Companion;->()V HSPLorg/thoughtcrime/securesms/database/RemappedRecordTables$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/database/RemappedRecordTables;->()V @@ -24065,7 +24093,6 @@ HSPLorg/thoughtcrime/securesms/database/RxDatabaseObserver$$ExternalSyntheticLam HSPLorg/thoughtcrime/securesms/database/RxDatabaseObserver$$ExternalSyntheticLambda0;->subscribe(Lio/reactivex/rxjava3/core/FlowableEmitter;)V HSPLorg/thoughtcrime/securesms/database/RxDatabaseObserver$$ExternalSyntheticLambda1;->(Lorg/thoughtcrime/securesms/database/RxDatabaseObserver$RxObserver;)V HSPLorg/thoughtcrime/securesms/database/RxDatabaseObserver$RxObserver;->(Lio/reactivex/rxjava3/core/Emitter;)V -HSPLorg/thoughtcrime/securesms/database/RxDatabaseObserver$RxObserver;->onChanged()V HSPLorg/thoughtcrime/securesms/database/RxDatabaseObserver$RxObserver;->prime()V HSPLorg/thoughtcrime/securesms/database/RxDatabaseObserver$conversation$1;->(J)V HSPLorg/thoughtcrime/securesms/database/RxDatabaseObserver$conversation$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; @@ -24189,6 +24216,7 @@ HSPLorg/thoughtcrime/securesms/database/SQLiteDatabase;->runPostSuccessfulTransa HSPLorg/thoughtcrime/securesms/database/SQLiteDatabase;->setTransactionSuccessful()V HSPLorg/thoughtcrime/securesms/database/SQLiteDatabase;->trace(Ljava/lang/String;Ljava/lang/Runnable;)V HSPLorg/thoughtcrime/securesms/database/SQLiteDatabase;->traceLockEnd()V +HSPLorg/thoughtcrime/securesms/database/SQLiteDatabase;->traceLockStart()V HSPLorg/thoughtcrime/securesms/database/SQLiteDatabase;->traceSql(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLorg/thoughtcrime/securesms/database/SQLiteDatabase$Returnable;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/database/SQLiteDatabase;->traceSql(Ljava/lang/String;Ljava/lang/String;ZLjava/lang/Runnable;)V HSPLorg/thoughtcrime/securesms/database/SQLiteDatabase;->traceSql(Ljava/lang/String;Ljava/lang/String;ZLorg/thoughtcrime/securesms/database/SQLiteDatabase$Returnable;)Ljava/lang/Object; @@ -24320,17 +24348,9 @@ HSPLorg/thoughtcrime/securesms/database/SqlCipherErrorHandler;->(Ljava/lan HSPLorg/thoughtcrime/securesms/database/SqlCipherLibraryLoader;->()V HSPLorg/thoughtcrime/securesms/database/SqlCipherLibraryLoader;->()V HSPLorg/thoughtcrime/securesms/database/SqlCipherLibraryLoader;->load()V -HSPLorg/thoughtcrime/securesms/database/StickerTable$StickerPackRecordReader;->(Landroid/database/Cursor;)V -HSPLorg/thoughtcrime/securesms/database/StickerTable$StickerPackRecordReader;->getNext()Lorg/thoughtcrime/securesms/database/model/StickerPackRecord; HSPLorg/thoughtcrime/securesms/database/StickerTable;->()V HSPLorg/thoughtcrime/securesms/database/StickerTable;->(Landroid/content/Context;Lorg/thoughtcrime/securesms/database/SignalDatabase;Lorg/thoughtcrime/securesms/crypto/AttachmentSecret;)V -HSPLorg/thoughtcrime/securesms/database/StickerTable;->deleteOrphanedPacks()V -HSPLorg/thoughtcrime/securesms/database/StickerTable;->deleteStickersInPackExceptCover(Lorg/thoughtcrime/securesms/database/SQLiteDatabase;Ljava/lang/String;)V HSPLorg/thoughtcrime/securesms/database/StickerTable;->getAllStickerPacks(Ljava/lang/String;)Landroid/database/Cursor; -HSPLorg/thoughtcrime/securesms/database/StickerTable;->getStickerPack(Ljava/lang/String;)Lorg/thoughtcrime/securesms/database/model/StickerPackRecord; -HSPLorg/thoughtcrime/securesms/database/StickerTable;->isPackAvailableAsReference(Ljava/lang/String;)Z -HSPLorg/thoughtcrime/securesms/database/StickerTable;->uninstallPack(Ljava/lang/String;)V -HSPLorg/thoughtcrime/securesms/database/StickerTable;->updatePackInstalled(Lorg/thoughtcrime/securesms/database/SQLiteDatabase;Ljava/lang/String;ZZ)V HSPLorg/thoughtcrime/securesms/database/StorySendTable$Companion;->()V HSPLorg/thoughtcrime/securesms/database/StorySendTable$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/database/StorySendTable;->()V @@ -24381,15 +24401,13 @@ HSPLorg/thoughtcrime/securesms/database/ThreadTable$Reader;->(Lorg/thought HSPLorg/thoughtcrime/securesms/database/ThreadTable$StaticReader;->()V HSPLorg/thoughtcrime/securesms/database/ThreadTable$StaticReader;->(Landroid/database/Cursor;Landroid/content/Context;)V HSPLorg/thoughtcrime/securesms/database/ThreadTable$StaticReader;->close()V +HSPLorg/thoughtcrime/securesms/database/ThreadTable$StaticReader;->getCurrent()Lorg/thoughtcrime/securesms/database/model/ThreadRecord; HSPLorg/thoughtcrime/securesms/database/ThreadTable$StaticReader;->getNext()Lorg/thoughtcrime/securesms/database/model/ThreadRecord; HSPLorg/thoughtcrime/securesms/database/ThreadTable$StaticReader;->getSnippetUri(Landroid/database/Cursor;)Landroid/net/Uri; HSPLorg/thoughtcrime/securesms/database/ThreadTable$WhenMappings;->()V HSPLorg/thoughtcrime/securesms/database/ThreadTable$getOrCreateThreadIdFor$1;->(Lorg/thoughtcrime/securesms/database/ThreadTable;Lorg/thoughtcrime/securesms/recipients/RecipientId;ZI)V HSPLorg/thoughtcrime/securesms/database/ThreadTable$getOrCreateThreadIdFor$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/database/ThreadTable$getOrCreateThreadIdFor$1;->invoke(Lorg/thoughtcrime/securesms/database/SQLiteDatabase;)Ljava/lang/Long; -HSPLorg/thoughtcrime/securesms/database/ThreadTable$setReadSince$1;->(Ljava/util/Map;Lorg/thoughtcrime/securesms/database/ThreadTable;Ljava/util/List;ZLkotlin/jvm/internal/Ref$BooleanRef;)V -HSPLorg/thoughtcrime/securesms/database/ThreadTable$setReadSince$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; -HSPLorg/thoughtcrime/securesms/database/ThreadTable$setReadSince$1;->invoke(Lorg/thoughtcrime/securesms/database/SQLiteDatabase;)V HSPLorg/thoughtcrime/securesms/database/ThreadTable$update$1$isPinned$2;->(Lorg/thoughtcrime/securesms/database/ThreadTable;J)V HSPLorg/thoughtcrime/securesms/database/ThreadTable$update$1$shouldDelete$2;->(ZJLkotlin/Lazy;)V HSPLorg/thoughtcrime/securesms/database/ThreadTable$update$1;->(JLorg/thoughtcrime/securesms/database/ThreadTable;ZZZ)V @@ -24404,7 +24422,6 @@ HSPLorg/thoughtcrime/securesms/database/ThreadTable;->access$getExtrasFor(Lorg/t HSPLorg/thoughtcrime/securesms/database/ThreadTable;->access$hasMoreRecentDraft(Lorg/thoughtcrime/securesms/database/ThreadTable;JJ)Z HSPLorg/thoughtcrime/securesms/database/ThreadTable;->access$updateThread(Lorg/thoughtcrime/securesms/database/ThreadTable;JZLjava/lang/String;Landroid/net/Uri;Ljava/lang/String;Lorg/thoughtcrime/securesms/database/ThreadTable$Extra;JIIJZJIII)V HSPLorg/thoughtcrime/securesms/database/ThreadTable;->allowedToUnarchive(J)Z -HSPLorg/thoughtcrime/securesms/database/ThreadTable;->createQuery(Ljava/lang/String;J)Ljava/lang/String; HSPLorg/thoughtcrime/securesms/database/ThreadTable;->createQuery(Ljava/lang/String;JJZ)Ljava/lang/String; HSPLorg/thoughtcrime/securesms/database/ThreadTable;->createQuery(Ljava/lang/String;Ljava/lang/String;JJ)Ljava/lang/String; HSPLorg/thoughtcrime/securesms/database/ThreadTable;->createThreadForRecipient(Lorg/thoughtcrime/securesms/recipients/RecipientId;ZI)J @@ -24423,7 +24440,6 @@ HSPLorg/thoughtcrime/securesms/database/ThreadTable;->getRecipientForThreadId(J) HSPLorg/thoughtcrime/securesms/database/ThreadTable;->getRecipientIdForThreadId(J)Lorg/thoughtcrime/securesms/recipients/RecipientId; HSPLorg/thoughtcrime/securesms/database/ThreadTable;->getThreadIdFor(Lorg/thoughtcrime/securesms/recipients/RecipientId;)Ljava/lang/Long; HSPLorg/thoughtcrime/securesms/database/ThreadTable;->getThreadIdIfExistsFor(Lorg/thoughtcrime/securesms/recipients/RecipientId;)J -HSPLorg/thoughtcrime/securesms/database/ThreadTable;->getThreadRecord(Ljava/lang/Long;)Lorg/thoughtcrime/securesms/database/model/ThreadRecord; HSPLorg/thoughtcrime/securesms/database/ThreadTable;->getUnarchivedConversationList(Lorg/thoughtcrime/securesms/conversationlist/model/ConversationFilter;ZJJ)Landroid/database/Cursor; HSPLorg/thoughtcrime/securesms/database/ThreadTable;->getUnarchivedConversationListCount(Lorg/thoughtcrime/securesms/conversationlist/model/ConversationFilter;)I HSPLorg/thoughtcrime/securesms/database/ThreadTable;->getUnreadMessageCount()J @@ -24432,12 +24448,8 @@ HSPLorg/thoughtcrime/securesms/database/ThreadTable;->incrementUnread(JII)V HSPLorg/thoughtcrime/securesms/database/ThreadTable;->markAsActiveEarly(J)V HSPLorg/thoughtcrime/securesms/database/ThreadTable;->readerFor(Landroid/database/Cursor;)Lorg/thoughtcrime/securesms/database/ThreadTable$Reader; HSPLorg/thoughtcrime/securesms/database/ThreadTable;->setLastScrolled(JJ)V -HSPLorg/thoughtcrime/securesms/database/ThreadTable;->setReadSince(JZJ)Ljava/util/List; -HSPLorg/thoughtcrime/securesms/database/ThreadTable;->setReadSince(Ljava/util/Map;Z)Ljava/util/List; -HSPLorg/thoughtcrime/securesms/database/ThreadTable;->setReadSince(Lorg/thoughtcrime/securesms/notifications/v2/ConversationId;ZJ)Ljava/util/List; HSPLorg/thoughtcrime/securesms/database/ThreadTable;->toQuery(Lorg/thoughtcrime/securesms/conversationlist/model/ConversationFilter;)Ljava/lang/String; HSPLorg/thoughtcrime/securesms/database/ThreadTable;->update(JZ)Z -HSPLorg/thoughtcrime/securesms/database/ThreadTable;->update(JZZ)Z HSPLorg/thoughtcrime/securesms/database/ThreadTable;->update(JZZZ)Z HSPLorg/thoughtcrime/securesms/database/ThreadTable;->updateThread(JZLjava/lang/String;Landroid/net/Uri;Ljava/lang/String;Lorg/thoughtcrime/securesms/database/ThreadTable$Extra;JIIJZJIII)V HSPLorg/thoughtcrime/securesms/database/UnknownStorageIdTable;->()V @@ -24464,17 +24476,16 @@ HSPLorg/thoughtcrime/securesms/database/model/AvatarPickerDatabase$Companion;->< HSPLorg/thoughtcrime/securesms/database/model/AvatarPickerDatabase;->()V HSPLorg/thoughtcrime/securesms/database/model/AvatarPickerDatabase;->(Landroid/content/Context;Lorg/thoughtcrime/securesms/database/SignalDatabase;)V HSPLorg/thoughtcrime/securesms/database/model/AvatarPickerDatabase;->getAllAvatars()Ljava/util/List; -HSPLorg/thoughtcrime/securesms/database/model/DisplayRecord;->(Ljava/lang/String;Lorg/thoughtcrime/securesms/recipients/Recipient;Lorg/thoughtcrime/securesms/recipients/Recipient;JJJIIJII)V +HSPLorg/thoughtcrime/securesms/database/model/DisplayRecord;->(Ljava/lang/String;Lorg/thoughtcrime/securesms/recipients/Recipient;Lorg/thoughtcrime/securesms/recipients/Recipient;JJJIZJZZ)V HSPLorg/thoughtcrime/securesms/database/model/DisplayRecord;->getBody()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/database/model/DisplayRecord;->getDateReceived()J HSPLorg/thoughtcrime/securesms/database/model/DisplayRecord;->getDateSent()J -HSPLorg/thoughtcrime/securesms/database/model/DisplayRecord;->getDeliveryReceiptCount()I HSPLorg/thoughtcrime/securesms/database/model/DisplayRecord;->getDeliveryStatus()I HSPLorg/thoughtcrime/securesms/database/model/DisplayRecord;->getFromRecipient()Lorg/thoughtcrime/securesms/recipients/Recipient; -HSPLorg/thoughtcrime/securesms/database/model/DisplayRecord;->getReadReceiptCount()I HSPLorg/thoughtcrime/securesms/database/model/DisplayRecord;->getThreadId()J HSPLorg/thoughtcrime/securesms/database/model/DisplayRecord;->getToRecipient()Lorg/thoughtcrime/securesms/recipients/Recipient; -HSPLorg/thoughtcrime/securesms/database/model/DisplayRecord;->getViewedReceiptCount()I +HSPLorg/thoughtcrime/securesms/database/model/DisplayRecord;->hasDeliveryReceipt()Z +HSPLorg/thoughtcrime/securesms/database/model/DisplayRecord;->hasReadReceipt()Z HSPLorg/thoughtcrime/securesms/database/model/DisplayRecord;->isBoostRequest()Z HSPLorg/thoughtcrime/securesms/database/model/DisplayRecord;->isCallLog()Z HSPLorg/thoughtcrime/securesms/database/model/DisplayRecord;->isChangeNumber()Z @@ -24500,6 +24511,7 @@ HSPLorg/thoughtcrime/securesms/database/model/DisplayRecord;->isPaymentsRequestT HSPLorg/thoughtcrime/securesms/database/model/DisplayRecord;->isPending()Z HSPLorg/thoughtcrime/securesms/database/model/DisplayRecord;->isPendingInsecureSmsFallback()Z HSPLorg/thoughtcrime/securesms/database/model/DisplayRecord;->isProfileChange()Z +HSPLorg/thoughtcrime/securesms/database/model/DisplayRecord;->isViewed()Z HSPLorg/thoughtcrime/securesms/database/model/DistributionListId$1;->()V HSPLorg/thoughtcrime/securesms/database/model/DistributionListId;->()V HSPLorg/thoughtcrime/securesms/database/model/DistributionListId;->(J)V @@ -24539,38 +24551,10 @@ HSPLorg/thoughtcrime/securesms/database/model/LocalMetricsSplit;->getDuration()J HSPLorg/thoughtcrime/securesms/database/model/LocalMetricsSplit;->getName()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/database/model/LocalMetricsSplit;->toString()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/database/model/LogEntry;->()V +HSPLorg/thoughtcrime/securesms/database/model/LogEntry;->(JZLjava/lang/String;)V HSPLorg/thoughtcrime/securesms/database/model/LogEntry;->getBody()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/database/model/LogEntry;->getCreatedAt()J HSPLorg/thoughtcrime/securesms/database/model/LogEntry;->getKeepLonger()Z -HSPLorg/thoughtcrime/securesms/database/model/MediaMmsMessageRecord$$ExternalSyntheticLambda0;->()V -HSPLorg/thoughtcrime/securesms/database/model/MediaMmsMessageRecord$$ExternalSyntheticLambda1;->()V -HSPLorg/thoughtcrime/securesms/database/model/MediaMmsMessageRecord$$ExternalSyntheticLambda2;->()V -HSPLorg/thoughtcrime/securesms/database/model/MediaMmsMessageRecord$$ExternalSyntheticLambda3;->()V -HSPLorg/thoughtcrime/securesms/database/model/MediaMmsMessageRecord$$ExternalSyntheticLambda4;->()V -HSPLorg/thoughtcrime/securesms/database/model/MediaMmsMessageRecord$$ExternalSyntheticLambda5;->(Ljava/util/Set;)V -HSPLorg/thoughtcrime/securesms/database/model/MediaMmsMessageRecord$$ExternalSyntheticLambda5;->test(Ljava/lang/Object;)Z -HSPLorg/thoughtcrime/securesms/database/model/MediaMmsMessageRecord$$ExternalSyntheticLambda6;->(Ljava/util/Set;)V -HSPLorg/thoughtcrime/securesms/database/model/MediaMmsMessageRecord$$ExternalSyntheticLambda6;->test(Ljava/lang/Object;)Z -HSPLorg/thoughtcrime/securesms/database/model/MediaMmsMessageRecord$$ExternalSyntheticLambda7;->(Ljava/util/Map;)V -HSPLorg/thoughtcrime/securesms/database/model/MediaMmsMessageRecord$$ExternalSyntheticLambda8;->(Ljava/util/Map;)V -HSPLorg/thoughtcrime/securesms/database/model/MediaMmsMessageRecord;->$r8$lambda$cBkbuMSmzz3TuVkB3EmeJL0n8_E(Ljava/util/Set;Lorg/thoughtcrime/securesms/attachments/DatabaseAttachment;)Z -HSPLorg/thoughtcrime/securesms/database/model/MediaMmsMessageRecord;->$r8$lambda$up8PZ00yIipxNCBebesipIcJWhE(Ljava/util/Set;Lorg/thoughtcrime/securesms/attachments/DatabaseAttachment;)Z -HSPLorg/thoughtcrime/securesms/database/model/MediaMmsMessageRecord;->()V -HSPLorg/thoughtcrime/securesms/database/model/MediaMmsMessageRecord;->(JLorg/thoughtcrime/securesms/recipients/Recipient;ILorg/thoughtcrime/securesms/recipients/Recipient;JJJIJLjava/lang/String;Lorg/thoughtcrime/securesms/mms/SlideDeck;JLjava/util/Set;Ljava/util/Set;IJJZILorg/thoughtcrime/securesms/database/model/Quote;Ljava/util/List;Ljava/util/List;ZLjava/util/List;ZZJIJLorg/thoughtcrime/securesms/database/model/databaseprotos/BodyRangeList;Lorg/thoughtcrime/securesms/database/model/StoryType;Lorg/thoughtcrime/securesms/database/model/ParentStoryId;Lorg/thoughtcrime/securesms/database/model/databaseprotos/GiftBadge;Lorg/thoughtcrime/securesms/payments/Payment;Lorg/thoughtcrime/securesms/database/CallTable$Call;JLorg/thoughtcrime/securesms/database/model/MessageId;Lorg/thoughtcrime/securesms/database/model/MessageId;I)V -HSPLorg/thoughtcrime/securesms/database/model/MediaMmsMessageRecord;->getCall()Lorg/thoughtcrime/securesms/database/CallTable$Call; -HSPLorg/thoughtcrime/securesms/database/model/MediaMmsMessageRecord;->getDisplayBody(Landroid/content/Context;)Landroid/text/SpannableString; -HSPLorg/thoughtcrime/securesms/database/model/MediaMmsMessageRecord;->getLatestRevisionId()Lorg/thoughtcrime/securesms/database/model/MessageId; -HSPLorg/thoughtcrime/securesms/database/model/MediaMmsMessageRecord;->getMessageRanges()Lorg/thoughtcrime/securesms/database/model/databaseprotos/BodyRangeList; -HSPLorg/thoughtcrime/securesms/database/model/MediaMmsMessageRecord;->getPayment()Lorg/thoughtcrime/securesms/payments/Payment; -HSPLorg/thoughtcrime/securesms/database/model/MediaMmsMessageRecord;->getScheduledDate()J -HSPLorg/thoughtcrime/securesms/database/model/MediaMmsMessageRecord;->getUpdateDisplayBody(Landroid/content/Context;Lj$/util/function/Consumer;)Lorg/thoughtcrime/securesms/database/model/UpdateDescription; -HSPLorg/thoughtcrime/securesms/database/model/MediaMmsMessageRecord;->isMmsNotification()Z -HSPLorg/thoughtcrime/securesms/database/model/MediaMmsMessageRecord;->lambda$withAttachments$0(Ljava/util/Set;Lorg/thoughtcrime/securesms/attachments/DatabaseAttachment;)Z -HSPLorg/thoughtcrime/securesms/database/model/MediaMmsMessageRecord;->lambda$withAttachments$1(Ljava/util/Set;Lorg/thoughtcrime/securesms/attachments/DatabaseAttachment;)Z -HSPLorg/thoughtcrime/securesms/database/model/MediaMmsMessageRecord;->updateContacts(Ljava/util/List;Ljava/util/Map;)Ljava/util/List; -HSPLorg/thoughtcrime/securesms/database/model/MediaMmsMessageRecord;->updateLinkPreviews(Ljava/util/List;Ljava/util/Map;)Ljava/util/List; -HSPLorg/thoughtcrime/securesms/database/model/MediaMmsMessageRecord;->updateQuote(Lorg/thoughtcrime/securesms/database/model/Quote;Ljava/util/List;)Lorg/thoughtcrime/securesms/database/model/Quote; -HSPLorg/thoughtcrime/securesms/database/model/MediaMmsMessageRecord;->withAttachments(Ljava/util/List;)Lorg/thoughtcrime/securesms/database/model/MediaMmsMessageRecord; HSPLorg/thoughtcrime/securesms/database/model/MegaphoneRecord;->(Lorg/thoughtcrime/securesms/megaphone/Megaphones$Event;IJJZ)V HSPLorg/thoughtcrime/securesms/database/model/MegaphoneRecord;->getEvent()Lorg/thoughtcrime/securesms/megaphone/Megaphones$Event; HSPLorg/thoughtcrime/securesms/database/model/MegaphoneRecord;->getFirstVisible()J @@ -24583,7 +24567,7 @@ HSPLorg/thoughtcrime/securesms/database/model/MessageId$Creator;->()V HSPLorg/thoughtcrime/securesms/database/model/MessageId;->()V HSPLorg/thoughtcrime/securesms/database/model/MessageId;->(J)V HSPLorg/thoughtcrime/securesms/database/model/MessageRecord;->()V -HSPLorg/thoughtcrime/securesms/database/model/MessageRecord;->(JLjava/lang/String;Lorg/thoughtcrime/securesms/recipients/Recipient;ILorg/thoughtcrime/securesms/recipients/Recipient;JJJJIIJLjava/util/Set;Ljava/util/Set;IJJIZLjava/util/List;ZJIJLorg/thoughtcrime/securesms/database/model/MessageId;I)V +HSPLorg/thoughtcrime/securesms/database/model/MessageRecord;->(JLjava/lang/String;Lorg/thoughtcrime/securesms/recipients/Recipient;ILorg/thoughtcrime/securesms/recipients/Recipient;JJJJIZJLjava/util/Set;Ljava/util/Set;IJJZZLjava/util/List;ZJZJLorg/thoughtcrime/securesms/database/model/MessageId;I)V HSPLorg/thoughtcrime/securesms/database/model/MessageRecord;->getDisplayBody(Landroid/content/Context;)Landroid/text/SpannableString; HSPLorg/thoughtcrime/securesms/database/model/MessageRecord;->getDisplayBody(Landroid/content/Context;Lj$/util/function/Consumer;)Landroid/text/SpannableString; HSPLorg/thoughtcrime/securesms/database/model/MessageRecord;->getExpireStarted()J @@ -24624,16 +24608,46 @@ HSPLorg/thoughtcrime/securesms/database/model/MessageRecord;->isThreadMergeEvent HSPLorg/thoughtcrime/securesms/database/model/MessageRecord;->isUnidentified()Z HSPLorg/thoughtcrime/securesms/database/model/MessageRecord;->isUpdate()Z HSPLorg/thoughtcrime/securesms/database/model/MessageRecordExtensionsKt;->withAttachments(Lorg/thoughtcrime/securesms/database/model/MessageRecord;Ljava/util/List;)Lorg/thoughtcrime/securesms/database/model/MessageRecord; -HSPLorg/thoughtcrime/securesms/database/model/MmsMessageRecord;->(JLjava/lang/String;Lorg/thoughtcrime/securesms/recipients/Recipient;ILorg/thoughtcrime/securesms/recipients/Recipient;JJJJIIJLjava/util/Set;Ljava/util/Set;IJJZLorg/thoughtcrime/securesms/mms/SlideDeck;ILorg/thoughtcrime/securesms/database/model/Quote;Ljava/util/List;Ljava/util/List;ZLjava/util/List;ZJIJLorg/thoughtcrime/securesms/database/model/StoryType;Lorg/thoughtcrime/securesms/database/model/ParentStoryId;Lorg/thoughtcrime/securesms/database/model/databaseprotos/GiftBadge;Lorg/thoughtcrime/securesms/database/model/MessageId;I)V +HSPLorg/thoughtcrime/securesms/database/model/MmsMessageRecord$$ExternalSyntheticLambda1;->(Ljava/util/Map;)V +HSPLorg/thoughtcrime/securesms/database/model/MmsMessageRecord$$ExternalSyntheticLambda2;->(Ljava/util/Map;)V +HSPLorg/thoughtcrime/securesms/database/model/MmsMessageRecord$$ExternalSyntheticLambda3;->()V +HSPLorg/thoughtcrime/securesms/database/model/MmsMessageRecord$$ExternalSyntheticLambda4;->()V +HSPLorg/thoughtcrime/securesms/database/model/MmsMessageRecord$$ExternalSyntheticLambda5;->()V +HSPLorg/thoughtcrime/securesms/database/model/MmsMessageRecord$$ExternalSyntheticLambda6;->()V +HSPLorg/thoughtcrime/securesms/database/model/MmsMessageRecord$$ExternalSyntheticLambda7;->()V +HSPLorg/thoughtcrime/securesms/database/model/MmsMessageRecord$$ExternalSyntheticLambda8;->(Ljava/util/Set;)V +HSPLorg/thoughtcrime/securesms/database/model/MmsMessageRecord$$ExternalSyntheticLambda8;->test(Ljava/lang/Object;)Z +HSPLorg/thoughtcrime/securesms/database/model/MmsMessageRecord$$ExternalSyntheticLambda9;->(Ljava/util/Set;)V +HSPLorg/thoughtcrime/securesms/database/model/MmsMessageRecord$$ExternalSyntheticLambda9;->test(Ljava/lang/Object;)Z +HSPLorg/thoughtcrime/securesms/database/model/MmsMessageRecord;->$r8$lambda$61cfVwuT9NopES3EvrpU58ByOrs(Ljava/util/Set;Lorg/thoughtcrime/securesms/attachments/DatabaseAttachment;)Z +HSPLorg/thoughtcrime/securesms/database/model/MmsMessageRecord;->$r8$lambda$yFErtnyhgG1o4FUJh3c90u9IkrI(Ljava/util/Set;Lorg/thoughtcrime/securesms/attachments/DatabaseAttachment;)Z +HSPLorg/thoughtcrime/securesms/database/model/MmsMessageRecord;->()V +HSPLorg/thoughtcrime/securesms/database/model/MmsMessageRecord;->(JLorg/thoughtcrime/securesms/recipients/Recipient;ILorg/thoughtcrime/securesms/recipients/Recipient;JJJZJLjava/lang/String;Lorg/thoughtcrime/securesms/mms/SlideDeck;JLjava/util/Set;Ljava/util/Set;IJJZZLorg/thoughtcrime/securesms/database/model/Quote;Ljava/util/List;Ljava/util/List;ZLjava/util/List;ZZJZJLorg/thoughtcrime/securesms/database/model/databaseprotos/BodyRangeList;Lorg/thoughtcrime/securesms/database/model/StoryType;Lorg/thoughtcrime/securesms/database/model/ParentStoryId;Lorg/thoughtcrime/securesms/database/model/databaseprotos/GiftBadge;Lorg/thoughtcrime/securesms/payments/Payment;Lorg/thoughtcrime/securesms/database/CallTable$Call;JLorg/thoughtcrime/securesms/database/model/MessageId;Lorg/thoughtcrime/securesms/database/model/MessageId;IZ)V +HSPLorg/thoughtcrime/securesms/database/model/MmsMessageRecord;->getCall()Lorg/thoughtcrime/securesms/database/CallTable$Call; +HSPLorg/thoughtcrime/securesms/database/model/MmsMessageRecord;->getDisplayBody(Landroid/content/Context;)Landroid/text/SpannableString; HSPLorg/thoughtcrime/securesms/database/model/MmsMessageRecord;->getGiftBadge()Lorg/thoughtcrime/securesms/database/model/databaseprotos/GiftBadge; +HSPLorg/thoughtcrime/securesms/database/model/MmsMessageRecord;->getLatestRevisionId()Lorg/thoughtcrime/securesms/database/model/MessageId; HSPLorg/thoughtcrime/securesms/database/model/MmsMessageRecord;->getLinkPreviews()Ljava/util/List; +HSPLorg/thoughtcrime/securesms/database/model/MmsMessageRecord;->getMessageRanges()Lorg/thoughtcrime/securesms/database/model/databaseprotos/BodyRangeList; HSPLorg/thoughtcrime/securesms/database/model/MmsMessageRecord;->getParentStoryId()Lorg/thoughtcrime/securesms/database/model/ParentStoryId; +HSPLorg/thoughtcrime/securesms/database/model/MmsMessageRecord;->getPayment()Lorg/thoughtcrime/securesms/payments/Payment; HSPLorg/thoughtcrime/securesms/database/model/MmsMessageRecord;->getQuote()Lorg/thoughtcrime/securesms/database/model/Quote; +HSPLorg/thoughtcrime/securesms/database/model/MmsMessageRecord;->getScheduledDate()J HSPLorg/thoughtcrime/securesms/database/model/MmsMessageRecord;->getSharedContacts()Ljava/util/List; HSPLorg/thoughtcrime/securesms/database/model/MmsMessageRecord;->getSlideDeck()Lorg/thoughtcrime/securesms/mms/SlideDeck; HSPLorg/thoughtcrime/securesms/database/model/MmsMessageRecord;->getStoryType()Lorg/thoughtcrime/securesms/database/model/StoryType; +HSPLorg/thoughtcrime/securesms/database/model/MmsMessageRecord;->getUpdateDisplayBody(Landroid/content/Context;Lj$/util/function/Consumer;)Lorg/thoughtcrime/securesms/database/model/UpdateDescription; +HSPLorg/thoughtcrime/securesms/database/model/MmsMessageRecord;->isMediaPending()Z HSPLorg/thoughtcrime/securesms/database/model/MmsMessageRecord;->isMms()Z +HSPLorg/thoughtcrime/securesms/database/model/MmsMessageRecord;->isMmsNotification()Z +HSPLorg/thoughtcrime/securesms/database/model/MmsMessageRecord;->isRead()Z HSPLorg/thoughtcrime/securesms/database/model/MmsMessageRecord;->isViewOnce()Z +HSPLorg/thoughtcrime/securesms/database/model/MmsMessageRecord;->lambda$withAttachments$0(Ljava/util/Set;Lorg/thoughtcrime/securesms/attachments/DatabaseAttachment;)Z +HSPLorg/thoughtcrime/securesms/database/model/MmsMessageRecord;->lambda$withAttachments$1(Ljava/util/Set;Lorg/thoughtcrime/securesms/attachments/DatabaseAttachment;)Z +HSPLorg/thoughtcrime/securesms/database/model/MmsMessageRecord;->updateContacts(Ljava/util/List;Ljava/util/Map;)Ljava/util/List; +HSPLorg/thoughtcrime/securesms/database/model/MmsMessageRecord;->updateLinkPreviews(Ljava/util/List;Ljava/util/Map;)Ljava/util/List; +HSPLorg/thoughtcrime/securesms/database/model/MmsMessageRecord;->updateQuote(Lorg/thoughtcrime/securesms/database/model/Quote;Ljava/util/List;)Lorg/thoughtcrime/securesms/database/model/Quote; +HSPLorg/thoughtcrime/securesms/database/model/MmsMessageRecord;->withAttachments(Ljava/util/List;)Lorg/thoughtcrime/securesms/database/model/MmsMessageRecord; HSPLorg/thoughtcrime/securesms/database/model/ParentStoryId$Companion;->()V HSPLorg/thoughtcrime/securesms/database/model/ParentStoryId$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/database/model/ParentStoryId$Companion;->deserialize(J)Lorg/thoughtcrime/securesms/database/model/ParentStoryId; @@ -24652,7 +24666,7 @@ HSPLorg/thoughtcrime/securesms/database/model/RecipientRecord$Capabilities;->()V HSPLorg/thoughtcrime/securesms/database/model/RecipientRecord$SyncExtras;->([BLorg/signal/libsignal/zkgroup/groups/GroupMasterKey;[BLorg/thoughtcrime/securesms/database/IdentityTable$VerifiedStatus;ZZJLjava/lang/String;)V HSPLorg/thoughtcrime/securesms/database/model/RecipientRecord;->()V -HSPLorg/thoughtcrime/securesms/database/model/RecipientRecord;->(Lorg/thoughtcrime/securesms/recipients/RecipientId;Lorg/whispersystems/signalservice/api/push/ServiceId$ACI;Lorg/whispersystems/signalservice/api/push/ServiceId$PNI;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lorg/thoughtcrime/securesms/groups/GroupId;Lorg/thoughtcrime/securesms/database/model/DistributionListId;Lorg/thoughtcrime/securesms/database/RecipientTable$RecipientType;ZJLorg/thoughtcrime/securesms/database/RecipientTable$VibrateState;Lorg/thoughtcrime/securesms/database/RecipientTable$VibrateState;Landroid/net/Uri;Landroid/net/Uri;ILorg/thoughtcrime/securesms/database/RecipientTable$RegisteredState;[BLorg/signal/libsignal/zkgroup/profiles/ExpiringProfileKeyCredential;Lorg/thoughtcrime/securesms/profiles/ProfileName;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lorg/thoughtcrime/securesms/profiles/ProfileName;Ljava/lang/String;Lorg/thoughtcrime/securesms/database/model/ProfileAvatarFileDetails;ZJLjava/lang/String;Lorg/thoughtcrime/securesms/database/RecipientTable$UnidentifiedAccessMode;Lorg/thoughtcrime/securesms/database/model/RecipientRecord$Capabilities;[BLorg/thoughtcrime/securesms/database/RecipientTable$MentionSetting;Lorg/thoughtcrime/securesms/wallpaper/ChatWallpaper;Lorg/thoughtcrime/securesms/conversation/colors/ChatColors;Lorg/thoughtcrime/securesms/conversation/colors/AvatarColor;Ljava/lang/String;Ljava/lang/String;Lorg/thoughtcrime/securesms/database/model/RecipientRecord$SyncExtras;Lorg/thoughtcrime/securesms/recipients/Recipient$Extras;ZLjava/util/List;ZLorg/thoughtcrime/securesms/recipients/Recipient$HiddenState;Lorg/thoughtcrime/securesms/service/webrtc/links/CallLinkRoomId;)V +HSPLorg/thoughtcrime/securesms/database/model/RecipientRecord;->(Lorg/thoughtcrime/securesms/recipients/RecipientId;Lorg/whispersystems/signalservice/api/push/ServiceId$ACI;Lorg/whispersystems/signalservice/api/push/ServiceId$PNI;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lorg/thoughtcrime/securesms/groups/GroupId;Lorg/thoughtcrime/securesms/database/model/DistributionListId;Lorg/thoughtcrime/securesms/database/RecipientTable$RecipientType;ZJLorg/thoughtcrime/securesms/database/RecipientTable$VibrateState;Lorg/thoughtcrime/securesms/database/RecipientTable$VibrateState;Landroid/net/Uri;Landroid/net/Uri;ILorg/thoughtcrime/securesms/database/RecipientTable$RegisteredState;[BLorg/signal/libsignal/zkgroup/profiles/ExpiringProfileKeyCredential;Lorg/thoughtcrime/securesms/profiles/ProfileName;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lorg/thoughtcrime/securesms/profiles/ProfileName;Ljava/lang/String;Lorg/thoughtcrime/securesms/database/model/ProfileAvatarFileDetails;ZJLjava/lang/String;Lorg/thoughtcrime/securesms/database/RecipientTable$UnidentifiedAccessMode;Lorg/thoughtcrime/securesms/database/model/RecipientRecord$Capabilities;[BLorg/thoughtcrime/securesms/database/RecipientTable$MentionSetting;Lorg/thoughtcrime/securesms/wallpaper/ChatWallpaper;Lorg/thoughtcrime/securesms/conversation/colors/ChatColors;Lorg/thoughtcrime/securesms/conversation/colors/AvatarColor;Ljava/lang/String;Ljava/lang/String;Lorg/thoughtcrime/securesms/database/model/RecipientRecord$SyncExtras;Lorg/thoughtcrime/securesms/recipients/Recipient$Extras;ZLjava/util/List;ZLorg/thoughtcrime/securesms/recipients/Recipient$HiddenState;Lorg/thoughtcrime/securesms/service/webrtc/links/CallLinkRoomId;Lorg/thoughtcrime/securesms/database/RecipientTable$PhoneNumberSharingState;)V HSPLorg/thoughtcrime/securesms/database/model/RecipientRecord;->getAbout()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/database/model/RecipientRecord;->getAboutEmoji()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/database/model/RecipientRecord;->getAci()Lorg/whispersystems/signalservice/api/push/ServiceId$ACI; @@ -24678,6 +24692,7 @@ HSPLorg/thoughtcrime/securesms/database/model/RecipientRecord;->getMessageRingto HSPLorg/thoughtcrime/securesms/database/model/RecipientRecord;->getMessageVibrateState()Lorg/thoughtcrime/securesms/database/RecipientTable$VibrateState; HSPLorg/thoughtcrime/securesms/database/model/RecipientRecord;->getMuteUntil()J HSPLorg/thoughtcrime/securesms/database/model/RecipientRecord;->getNotificationChannel()Ljava/lang/String; +HSPLorg/thoughtcrime/securesms/database/model/RecipientRecord;->getPhoneNumberSharing()Lorg/thoughtcrime/securesms/database/RecipientTable$PhoneNumberSharingState; HSPLorg/thoughtcrime/securesms/database/model/RecipientRecord;->getPni()Lorg/whispersystems/signalservice/api/push/ServiceId$PNI; HSPLorg/thoughtcrime/securesms/database/model/RecipientRecord;->getProfileAvatar()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/database/model/RecipientRecord;->getProfileAvatarFileDetails()Lorg/thoughtcrime/securesms/database/model/ProfileAvatarFileDetails; @@ -24716,13 +24731,10 @@ HSPLorg/thoughtcrime/securesms/database/model/StoryViewState$Companion$$External HSPLorg/thoughtcrime/securesms/database/model/StoryViewState$Companion$$ExternalSyntheticLambda1;->subscribe(Lio/reactivex/rxjava3/core/ObservableEmitter;)V HSPLorg/thoughtcrime/securesms/database/model/StoryViewState$Companion$$ExternalSyntheticLambda2;->(Lio/reactivex/rxjava3/core/ObservableEmitter;Lorg/thoughtcrime/securesms/recipients/RecipientId;)V HSPLorg/thoughtcrime/securesms/database/model/StoryViewState$Companion$$ExternalSyntheticLambda3;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V -HSPLorg/thoughtcrime/securesms/database/model/StoryViewState$Companion$$ExternalSyntheticLambda3;->cancel()V -HSPLorg/thoughtcrime/securesms/database/model/StoryViewState$Companion;->$r8$lambda$_YM1i9V93JIKhbRirbAeb_98VJw(Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/model/StoryViewState$Companion;->$r8$lambda$qlVsO3gJogFXwiR82wyUg4D6NdU(Lorg/thoughtcrime/securesms/recipients/RecipientId;Lio/reactivex/rxjava3/core/ObservableEmitter;)V HSPLorg/thoughtcrime/securesms/database/model/StoryViewState$Companion;->()V HSPLorg/thoughtcrime/securesms/database/model/StoryViewState$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/database/model/StoryViewState$Companion;->getForRecipientId(Lorg/thoughtcrime/securesms/recipients/RecipientId;)Lio/reactivex/rxjava3/core/Observable; -HSPLorg/thoughtcrime/securesms/database/model/StoryViewState$Companion;->getState$lambda$3$lambda$2(Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V HSPLorg/thoughtcrime/securesms/database/model/StoryViewState$Companion;->getState$lambda$3$refresh(Lio/reactivex/rxjava3/core/ObservableEmitter;Lorg/thoughtcrime/securesms/recipients/RecipientId;)V HSPLorg/thoughtcrime/securesms/database/model/StoryViewState$Companion;->getState$lambda$3(Lorg/thoughtcrime/securesms/recipients/RecipientId;Lio/reactivex/rxjava3/core/ObservableEmitter;)V HSPLorg/thoughtcrime/securesms/database/model/StoryViewState$Companion;->getState(Lorg/thoughtcrime/securesms/recipients/RecipientId;)Lio/reactivex/rxjava3/core/Observable; @@ -24734,16 +24746,16 @@ HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->-$$Nest$fge HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->-$$Nest$fgetbody(Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;)Ljava/lang/String; HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->-$$Nest$fgetcontentType(Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;)Ljava/lang/String; HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->-$$Nest$fgetdate(Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;)J -HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->-$$Nest$fgetdeliveryReceiptCount(Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;)I HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->-$$Nest$fgetdeliveryStatus(Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;)J HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->-$$Nest$fgetdistributionType(Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;)I HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->-$$Nest$fgetexpiresIn(Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;)J HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->-$$Nest$fgetextra(Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;)Lorg/thoughtcrime/securesms/database/ThreadTable$Extra; HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->-$$Nest$fgetforcedUnread(Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;)Z +HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->-$$Nest$fgethasDeliveryReceipt(Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;)Z +HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->-$$Nest$fgethasReadReceipt(Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;)Z HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->-$$Nest$fgetisPinned(Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;)Z HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->-$$Nest$fgetlastSeen(Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;)J HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->-$$Nest$fgetmeaningfulMessages(Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;)Z -HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->-$$Nest$fgetreadReceiptCount(Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;)I HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->-$$Nest$fgetrecipient(Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;)Lorg/thoughtcrime/securesms/recipients/Recipient; HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->-$$Nest$fgetsnippetUri(Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;)Landroid/net/Uri; HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->-$$Nest$fgetthreadId(Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;)J @@ -24756,16 +24768,16 @@ HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->setArchived HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->setBody(Ljava/lang/String;)Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder; HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->setContentType(Ljava/lang/String;)Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder; HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->setDate(J)Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder; -HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->setDeliveryReceiptCount(I)Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder; HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->setDeliveryStatus(J)Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder; HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->setDistributionType(I)Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder; HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->setExpiresIn(J)Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder; HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->setExtra(Lorg/thoughtcrime/securesms/database/ThreadTable$Extra;)Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder; HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->setForcedUnread(Z)Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder; +HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->setHasDeliveryReceipt(Z)Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder; +HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->setHasReadReceipt(Z)Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder; HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->setLastSeen(J)Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder; HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->setMeaningfulMessages(Z)Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder; HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->setPinned(Z)Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder; -HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->setReadReceiptCount(I)Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder; HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->setRecipient(Lorg/thoughtcrime/securesms/recipients/Recipient;)Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder; HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->setSnippetUri(Landroid/net/Uri;)Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder; HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->setType(J)Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder; @@ -24773,7 +24785,6 @@ HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->setUnreadCo HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;->setUnreadSelfMentionsCount(I)Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder; HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord;->(Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;)V HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord;->(Lorg/thoughtcrime/securesms/database/model/ThreadRecord$Builder;Lorg/thoughtcrime/securesms/database/model/ThreadRecord-IA;)V -HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord;->equals(Ljava/lang/Object;)Z HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord;->getBody()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord;->getBodyRanges()Lorg/thoughtcrime/securesms/database/model/databaseprotos/BodyRangeList; HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord;->getDate()J @@ -24788,7 +24799,6 @@ HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord;->getThreadId()J HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord;->getType()J HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord;->getUnreadCount()I HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord;->isArchived()Z -HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord;->isForcedUnread()Z HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord;->isMessageRequestAccepted()Z HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord;->isOutgoing()Z HSPLorg/thoughtcrime/securesms/database/model/ThreadRecord;->isRead()Z @@ -24797,6 +24807,8 @@ HSPLorg/thoughtcrime/securesms/database/model/databaseprotos/PendingOneTimeDonat HSPLorg/thoughtcrime/securesms/database/model/databaseprotos/PendingOneTimeDonation$Companion;->()V HSPLorg/thoughtcrime/securesms/database/model/databaseprotos/PendingOneTimeDonation$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/database/model/databaseprotos/PendingOneTimeDonation;->()V +HSPLorg/thoughtcrime/securesms/databinding/ConversationHeaderViewBinding;->(Landroid/view/View;Lorg/thoughtcrime/securesms/components/emoji/EmojiTextView;Lorg/thoughtcrime/securesms/components/AvatarImageView;Landroid/widget/LinearLayout;Lorg/thoughtcrime/securesms/badges/BadgeImageView;Lorg/thoughtcrime/securesms/components/emoji/EmojiTextView;Landroid/view/View;Landroidx/constraintlayout/widget/ConstraintLayout;Landroid/view/View;Landroid/widget/TextView;Lorg/thoughtcrime/securesms/components/emoji/EmojiTextView;)V +HSPLorg/thoughtcrime/securesms/databinding/ConversationHeaderViewBinding;->bind(Landroid/view/View;)Lorg/thoughtcrime/securesms/databinding/ConversationHeaderViewBinding; HSPLorg/thoughtcrime/securesms/databinding/ConversationInputPanelBinding;->(Lorg/thoughtcrime/securesms/components/InputPanel;Landroid/widget/ImageButton;Landroidx/constraintlayout/widget/Barrier;Lorg/thoughtcrime/securesms/components/InputPanel;Lorg/thoughtcrime/securesms/components/AnimatingToggle;Landroid/view/View;Landroidx/constraintlayout/widget/Barrier;Lcom/google/android/material/imageview/ShapeableImageView;Landroidx/appcompat/widget/AppCompatTextView;Lorg/thoughtcrime/securesms/components/ComposeText;Lorg/thoughtcrime/securesms/components/emoji/EmojiToggle;Landroid/widget/ImageButton;Lorg/thoughtcrime/securesms/components/HidingLinearLayout;Landroidx/appcompat/widget/AppCompatImageButton;Landroidx/recyclerview/widget/RecyclerView;Lorg/thoughtcrime/securesms/components/LinkPreviewView;Lorg/thoughtcrime/securesms/components/HidingLinearLayout;Landroid/widget/ImageButton;Lorg/thoughtcrime/securesms/components/QuoteView;Lorg/thoughtcrime/securesms/components/MicrophoneRecorderView;Lorg/thoughtcrime/securesms/components/SendButton;Landroid/widget/ImageButton;Lorg/thoughtcrime/securesms/conversation/VoiceNoteDraftView;)V HSPLorg/thoughtcrime/securesms/databinding/ConversationInputPanelBinding;->bind(Landroid/view/View;)Lorg/thoughtcrime/securesms/databinding/ConversationInputPanelBinding; HSPLorg/thoughtcrime/securesms/databinding/ConversationInputPanelBinding;->getRoot()Lorg/thoughtcrime/securesms/components/InputPanel; @@ -24820,6 +24832,9 @@ HSPLorg/thoughtcrime/securesms/databinding/TransferControlsViewBinding;->inflate HSPLorg/thoughtcrime/securesms/databinding/V2ConversationFragmentBinding;->(Lorg/thoughtcrime/securesms/components/InputAwareConstraintLayout;Landroid/view/ViewStub;Landroid/view/View;Lorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView;Landroid/widget/FrameLayout;Lorg/thoughtcrime/securesms/components/menu/SignalBottomActionBar;Landroidx/constraintlayout/widget/Barrier;Lorg/thoughtcrime/securesms/conversation/v2/DisabledInputView;Lcom/google/android/material/button/MaterialButton;Lorg/thoughtcrime/securesms/databinding/ConversationInputPanelBinding;Landroidx/constraintlayout/widget/Barrier;Landroid/widget/TextView;Lorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectRecyclerView;Landroid/widget/FrameLayout;Landroid/view/ViewStub;Lorg/thoughtcrime/securesms/databinding/ConversationSearchNavBinding;Lorg/thoughtcrime/securesms/databinding/ConversationTitleViewBinding;Landroid/widget/FrameLayout;Landroid/widget/ImageView;Landroid/view/View;Landroidx/fragment/app/FragmentContainerView;Landroidx/fragment/app/FragmentContainerView;Landroidx/fragment/app/FragmentContainerView;Landroid/widget/FrameLayout;Landroid/view/ViewStub;Landroid/view/ViewStub;Landroid/view/ViewStub;Landroid/widget/TextView;Lorg/thoughtcrime/securesms/components/ConversationScrollToView;Lorg/thoughtcrime/securesms/components/ConversationScrollToView;Lorg/thoughtcrime/securesms/util/views/DarkOverflowToolbar;Landroid/view/View;Landroid/view/ViewStub;Landroid/view/ViewStub;)V HSPLorg/thoughtcrime/securesms/databinding/V2ConversationFragmentBinding;->bind(Landroid/view/View;)Lorg/thoughtcrime/securesms/databinding/V2ConversationFragmentBinding; HSPLorg/thoughtcrime/securesms/databinding/V2ConversationFragmentBinding;->getRoot()Lorg/thoughtcrime/securesms/components/InputAwareConstraintLayout; +HSPLorg/thoughtcrime/securesms/databinding/V2ConversationItemTextOnlyIncomingBinding;->(Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout;Lorg/thoughtcrime/securesms/badges/BadgeImageView;Lorg/thoughtcrime/securesms/components/AvatarImageView;Lorg/thoughtcrime/securesms/components/emoji/EmojiTextView;Landroid/widget/LinearLayout;Lorg/thoughtcrime/securesms/components/ExpirationTimerView;Landroid/view/View;Landroid/widget/TextView;Lorg/thoughtcrime/securesms/reactions/ReactionsConversationView;Lcom/google/android/material/imageview/ShapeableImageView;Landroid/widget/Space;Lorg/thoughtcrime/securesms/components/emoji/EmojiTextView;)V +HSPLorg/thoughtcrime/securesms/databinding/V2ConversationItemTextOnlyIncomingBinding;->bind(Landroid/view/View;)Lorg/thoughtcrime/securesms/databinding/V2ConversationItemTextOnlyIncomingBinding; +HSPLorg/thoughtcrime/securesms/databinding/V2ConversationItemTextOnlyIncomingBinding;->getRoot()Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout; HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencies$$ExternalSyntheticLambda0;->()V HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencies$$ExternalSyntheticLambda0;->get()Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencies;->$r8$lambda$TTNxYGZvGlMOp1oidmVJeKiRxZs()Lorg/whispersystems/signalservice/internal/configuration/SignalServiceConfiguration; @@ -24844,14 +24859,12 @@ HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencies;->getMessage HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencies;->getOkHttpClient()Lokhttp3/OkHttpClient; HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencies;->getPendingRetryReceiptCache()Lorg/thoughtcrime/securesms/database/PendingRetryReceiptCache; HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencies;->getPendingRetryReceiptManager()Lorg/thoughtcrime/securesms/service/PendingRetryReceiptManager; -HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencies;->getProfileService()Lorg/whispersystems/signalservice/api/services/ProfileService; HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencies;->getProtocolStore()Lorg/thoughtcrime/securesms/crypto/storage/SignalServiceDataStoreImpl; HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencies;->getRecipientCache()Lorg/thoughtcrime/securesms/recipients/LiveRecipientCache; HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencies;->getScheduledMessageManager()Lorg/thoughtcrime/securesms/service/ScheduledMessageManager; HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencies;->getShakeToReport()Lorg/thoughtcrime/securesms/shakereport/ShakeToReport; HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencies;->getSignalOkHttpClient()Lokhttp3/OkHttpClient; HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencies;->getSignalServiceAccountManager()Lorg/whispersystems/signalservice/api/SignalServiceAccountManager; -HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencies;->getSignalServiceMessageReceiver()Lorg/whispersystems/signalservice/api/SignalServiceMessageReceiver; HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencies;->getSignalServiceNetworkAccess()Lorg/thoughtcrime/securesms/push/SignalServiceNetworkAccess; HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencies;->getSignalWebSocket()Lorg/whispersystems/signalservice/api/SignalWebSocket; HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencies;->getTypingStatusRepository()Lorg/thoughtcrime/securesms/components/TypingStatusRepository; @@ -24892,13 +24905,11 @@ HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider;->prov HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider;->provideMessageNotifier()Lorg/thoughtcrime/securesms/notifications/MessageNotifier; HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider;->providePendingRetryReceiptCache()Lorg/thoughtcrime/securesms/database/PendingRetryReceiptCache; HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider;->providePendingRetryReceiptManager()Lorg/thoughtcrime/securesms/service/PendingRetryReceiptManager; -HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider;->provideProfileService(Lorg/signal/libsignal/zkgroup/profiles/ClientZkProfileOperations;Lorg/whispersystems/signalservice/api/SignalServiceMessageReceiver;Lorg/whispersystems/signalservice/api/SignalWebSocket;)Lorg/whispersystems/signalservice/api/services/ProfileService; HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider;->provideProtocolStore()Lorg/thoughtcrime/securesms/crypto/storage/SignalServiceDataStoreImpl; HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider;->provideRecipientCache()Lorg/thoughtcrime/securesms/recipients/LiveRecipientCache; HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider;->provideScheduledMessageManager()Lorg/thoughtcrime/securesms/service/ScheduledMessageManager; HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider;->provideShakeToReport()Lorg/thoughtcrime/securesms/shakereport/ShakeToReport; HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider;->provideSignalServiceAccountManager(Lorg/whispersystems/signalservice/internal/configuration/SignalServiceConfiguration;Lorg/whispersystems/signalservice/api/groupsv2/GroupsV2Operations;)Lorg/whispersystems/signalservice/api/SignalServiceAccountManager; -HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider;->provideSignalServiceMessageReceiver(Lorg/whispersystems/signalservice/internal/configuration/SignalServiceConfiguration;)Lorg/whispersystems/signalservice/api/SignalServiceMessageReceiver; HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider;->provideSignalServiceNetworkAccess()Lorg/thoughtcrime/securesms/push/SignalServiceNetworkAccess; HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider;->provideSignalWebSocket(Lj$/util/function/Supplier;)Lorg/whispersystems/signalservice/api/SignalWebSocket; HSPLorg/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider;->provideTypingStatusRepository()Lorg/thoughtcrime/securesms/components/TypingStatusRepository; @@ -24963,10 +24974,8 @@ HSPLorg/thoughtcrime/securesms/emoji/EmojiFiles$Version$Companion;->isVersionVal HSPLorg/thoughtcrime/securesms/emoji/EmojiFiles$Version$Companion;->readVersion$default(Lorg/thoughtcrime/securesms/emoji/EmojiFiles$Version$Companion;Landroid/content/Context;ZILjava/lang/Object;)Lorg/thoughtcrime/securesms/emoji/EmojiFiles$Version; HSPLorg/thoughtcrime/securesms/emoji/EmojiFiles$Version$Companion;->readVersion(Landroid/content/Context;)Lorg/thoughtcrime/securesms/emoji/EmojiFiles$Version; HSPLorg/thoughtcrime/securesms/emoji/EmojiFiles$Version$Companion;->readVersion(Landroid/content/Context;Z)Lorg/thoughtcrime/securesms/emoji/EmojiFiles$Version; -HSPLorg/thoughtcrime/securesms/emoji/EmojiFiles$Version$Companion;->writeVersion(Landroid/content/Context;Lorg/thoughtcrime/securesms/emoji/EmojiFiles$Version;)V HSPLorg/thoughtcrime/securesms/emoji/EmojiFiles$Version;->()V HSPLorg/thoughtcrime/securesms/emoji/EmojiFiles$Version;->(ILjava/util/UUID;Ljava/lang/String;)V -HSPLorg/thoughtcrime/securesms/emoji/EmojiFiles$Version;->access$getObjectMapper$cp()Lcom/fasterxml/jackson/databind/ObjectMapper; HSPLorg/thoughtcrime/securesms/emoji/EmojiFiles$Version;->getDensity()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/emoji/EmojiFiles$Version;->getDirectory(Landroid/content/Context;)Ljava/io/File; HSPLorg/thoughtcrime/securesms/emoji/EmojiFiles$Version;->getFile(Landroid/content/Context;Ljava/util/UUID;)Ljava/io/File; @@ -24974,17 +24983,14 @@ HSPLorg/thoughtcrime/securesms/emoji/EmojiFiles$Version;->getUuid()Ljava/util/UU HSPLorg/thoughtcrime/securesms/emoji/EmojiFiles$Version;->getVersion()I HSPLorg/thoughtcrime/securesms/emoji/EmojiFiles$Version;->isVersionValid(Landroid/content/Context;Lorg/thoughtcrime/securesms/emoji/EmojiFiles$Version;)Z HSPLorg/thoughtcrime/securesms/emoji/EmojiFiles$Version;->readVersion(Landroid/content/Context;)Lorg/thoughtcrime/securesms/emoji/EmojiFiles$Version; -HSPLorg/thoughtcrime/securesms/emoji/EmojiFiles$Version;->writeVersion(Landroid/content/Context;Lorg/thoughtcrime/securesms/emoji/EmojiFiles$Version;)V HSPLorg/thoughtcrime/securesms/emoji/EmojiFiles$getLatestEmojiData$1$1;->()V HSPLorg/thoughtcrime/securesms/emoji/EmojiFiles$getLatestEmojiData$1$1;->()V HSPLorg/thoughtcrime/securesms/emoji/EmojiFiles$getLatestEmojiData$1$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/emoji/EmojiFiles$getLatestEmojiData$1$1;->invoke(Ljava/lang/String;Ljava/lang/String;)Landroid/net/Uri; HSPLorg/thoughtcrime/securesms/emoji/EmojiFiles;->()V HSPLorg/thoughtcrime/securesms/emoji/EmojiFiles;->()V -HSPLorg/thoughtcrime/securesms/emoji/EmojiFiles;->getBaseDirectory(Landroid/content/Context;)Ljava/io/File; HSPLorg/thoughtcrime/securesms/emoji/EmojiFiles;->getLatestEmojiData(Landroid/content/Context;Lorg/thoughtcrime/securesms/emoji/EmojiFiles$Version;)Lorg/thoughtcrime/securesms/emoji/ParsedEmojiData; HSPLorg/thoughtcrime/securesms/emoji/EmojiFiles;->getMd5(Landroid/content/Context;Lorg/thoughtcrime/securesms/emoji/EmojiFiles$Version;Ljava/util/UUID;)[B -HSPLorg/thoughtcrime/securesms/emoji/EmojiFiles;->openForReading(Landroid/content/Context;Ljava/lang/String;)Ljava/io/InputStream; HSPLorg/thoughtcrime/securesms/emoji/EmojiFiles;->openForWriting(Landroid/content/Context;Lorg/thoughtcrime/securesms/emoji/EmojiFiles$Version;Ljava/util/UUID;)Ljava/io/OutputStream; HSPLorg/thoughtcrime/securesms/emoji/EmojiFilesKt;->access$getEmojiDirectory(Landroid/content/Context;)Ljava/io/File; HSPLorg/thoughtcrime/securesms/emoji/EmojiFilesKt;->access$getFilesUri(Ljava/lang/String;Ljava/lang/String;)Landroid/net/Uri; @@ -25049,12 +25055,12 @@ HSPLorg/thoughtcrime/securesms/emoji/EmojiMetrics;->(III)V HSPLorg/thoughtcrime/securesms/emoji/EmojiMetrics;->getPerRow()I HSPLorg/thoughtcrime/securesms/emoji/EmojiMetrics;->getRawHeight()I HSPLorg/thoughtcrime/securesms/emoji/EmojiMetrics;->getRawWidth()I -HSPLorg/thoughtcrime/securesms/emoji/EmojiPage$Disk;->()V -HSPLorg/thoughtcrime/securesms/emoji/EmojiPage$Disk;->(Landroid/net/Uri;)V -HSPLorg/thoughtcrime/securesms/emoji/EmojiPage$Disk;->equals(Ljava/lang/Object;)Z -HSPLorg/thoughtcrime/securesms/emoji/EmojiPage$Disk;->getUri()Landroid/net/Uri; -HSPLorg/thoughtcrime/securesms/emoji/EmojiPage$Disk;->hashCode()I -HSPLorg/thoughtcrime/securesms/emoji/EmojiPage$Disk;->toString()Ljava/lang/String; +HSPLorg/thoughtcrime/securesms/emoji/EmojiPage$Asset;->()V +HSPLorg/thoughtcrime/securesms/emoji/EmojiPage$Asset;->(Landroid/net/Uri;)V +HSPLorg/thoughtcrime/securesms/emoji/EmojiPage$Asset;->equals(Ljava/lang/Object;)Z +HSPLorg/thoughtcrime/securesms/emoji/EmojiPage$Asset;->getUri()Landroid/net/Uri; +HSPLorg/thoughtcrime/securesms/emoji/EmojiPage$Asset;->hashCode()I +HSPLorg/thoughtcrime/securesms/emoji/EmojiPage$Asset;->toString()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/emoji/EmojiPage;->()V HSPLorg/thoughtcrime/securesms/emoji/EmojiPage;->(Landroid/net/Uri;)V HSPLorg/thoughtcrime/securesms/emoji/EmojiPage;->(Landroid/net/Uri;Lkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -25081,7 +25087,6 @@ HSPLorg/thoughtcrime/securesms/emoji/EmojiPageCache;->$r8$lambda$NLSLe3cZ9aQvIzn HSPLorg/thoughtcrime/securesms/emoji/EmojiPageCache;->$r8$lambda$prHxAb0TF9CtP8w7EJ9vg507J70(Lorg/thoughtcrime/securesms/util/ListenableFutureTask;Lorg/thoughtcrime/securesms/emoji/EmojiPageCache$EmojiPageRequest;Lkotlin/Unit;)V HSPLorg/thoughtcrime/securesms/emoji/EmojiPageCache;->()V HSPLorg/thoughtcrime/securesms/emoji/EmojiPageCache;->()V -HSPLorg/thoughtcrime/securesms/emoji/EmojiPageCache;->clear()V HSPLorg/thoughtcrime/securesms/emoji/EmojiPageCache;->load$lambda$0(Lorg/thoughtcrime/securesms/emoji/EmojiPageCache$EmojiPageRequest;Landroid/content/Context;)Landroid/graphics/Bitmap; HSPLorg/thoughtcrime/securesms/emoji/EmojiPageCache;->load$lambda$1(Lorg/thoughtcrime/securesms/util/ListenableFutureTask;Lorg/thoughtcrime/securesms/emoji/EmojiPageCache$EmojiPageRequest;Lkotlin/Unit;)V HSPLorg/thoughtcrime/securesms/emoji/EmojiPageCache;->load$run__proxy(Lorg/thoughtcrime/securesms/util/ListenableFutureTask;)Lkotlin/Unit; @@ -25094,14 +25099,12 @@ HSPLorg/thoughtcrime/securesms/emoji/EmojiRemote;->getObject(Lorg/thoughtcrime/s HSPLorg/thoughtcrime/securesms/emoji/EmojiRemote;->getVersion()I HSPLorg/thoughtcrime/securesms/emoji/EmojiSource$Companion$loadAssetBasedEmojis$1$1;->()V HSPLorg/thoughtcrime/securesms/emoji/EmojiSource$Companion$loadAssetBasedEmojis$1$1;->()V +HSPLorg/thoughtcrime/securesms/emoji/EmojiSource$Companion$loadAssetBasedEmojis$1$1;->invoke(Landroid/net/Uri;)Lorg/thoughtcrime/securesms/emoji/EmojiPage; +HSPLorg/thoughtcrime/securesms/emoji/EmojiSource$Companion$loadAssetBasedEmojis$1$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/emoji/EmojiSource$Companion$loadAssetBasedEmojis$1$parsedData$1;->()V HSPLorg/thoughtcrime/securesms/emoji/EmojiSource$Companion$loadAssetBasedEmojis$1$parsedData$1;->()V HSPLorg/thoughtcrime/securesms/emoji/EmojiSource$Companion$loadAssetBasedEmojis$1$parsedData$1;->invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/emoji/EmojiSource$Companion$loadAssetBasedEmojis$1$parsedData$1;->invoke(Ljava/lang/String;Ljava/lang/String;)Landroid/net/Uri; -HSPLorg/thoughtcrime/securesms/emoji/EmojiSource$Companion$loadRemoteBasedEmojis$1$1;->()V -HSPLorg/thoughtcrime/securesms/emoji/EmojiSource$Companion$loadRemoteBasedEmojis$1$1;->()V -HSPLorg/thoughtcrime/securesms/emoji/EmojiSource$Companion$loadRemoteBasedEmojis$1$1;->invoke(Landroid/net/Uri;)Lorg/thoughtcrime/securesms/emoji/EmojiPage; -HSPLorg/thoughtcrime/securesms/emoji/EmojiSource$Companion$loadRemoteBasedEmojis$1$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/emoji/EmojiSource$Companion;->()V HSPLorg/thoughtcrime/securesms/emoji/EmojiSource$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/emoji/EmojiSource$Companion;->getEmojiSource()Lorg/thoughtcrime/securesms/emoji/EmojiSource; @@ -25137,15 +25140,11 @@ HSPLorg/thoughtcrime/securesms/emoji/EmojiSourceKt;->access$getPAGE_EMOTICONS$p( HSPLorg/thoughtcrime/securesms/emoji/EmojiSourceKt;->access$maxOrZero(Ljava/util/List;)I HSPLorg/thoughtcrime/securesms/emoji/EmojiSourceKt;->getAssetsUri(Ljava/lang/String;Ljava/lang/String;)Landroid/net/Uri; HSPLorg/thoughtcrime/securesms/emoji/EmojiSourceKt;->maxOrZero(Ljava/util/List;)I -HSPLorg/thoughtcrime/securesms/emoji/JumboEmoji$$ExternalSyntheticLambda4;->(Lorg/thoughtcrime/securesms/emoji/EmojiFiles$Version;)V -HSPLorg/thoughtcrime/securesms/emoji/JumboEmoji$$ExternalSyntheticLambda4;->run()V HSPLorg/thoughtcrime/securesms/emoji/JumboEmoji$$ExternalSyntheticLambda5;->(Landroid/content/Context;)V HSPLorg/thoughtcrime/securesms/emoji/JumboEmoji$$ExternalSyntheticLambda5;->run()V -HSPLorg/thoughtcrime/securesms/emoji/JumboEmoji;->$r8$lambda$1MZfAT8L5tWrN2t8lIV1c9aeWgA(Lorg/thoughtcrime/securesms/emoji/EmojiFiles$Version;)V HSPLorg/thoughtcrime/securesms/emoji/JumboEmoji;->$r8$lambda$U8tEMIKRjYoTvIByfnS46m01TB0(Landroid/content/Context;)V HSPLorg/thoughtcrime/securesms/emoji/JumboEmoji;->()V HSPLorg/thoughtcrime/securesms/emoji/JumboEmoji;->()V -HSPLorg/thoughtcrime/securesms/emoji/JumboEmoji;->updateCurrentVersion$lambda$1$lambda$0(Lorg/thoughtcrime/securesms/emoji/EmojiFiles$Version;)V HSPLorg/thoughtcrime/securesms/emoji/JumboEmoji;->updateCurrentVersion$lambda$1(Landroid/content/Context;)V HSPLorg/thoughtcrime/securesms/emoji/JumboEmoji;->updateCurrentVersion(Landroid/content/Context;)V HSPLorg/thoughtcrime/securesms/emoji/ParsedEmojiData;->()V @@ -25163,13 +25162,8 @@ HSPLorg/thoughtcrime/securesms/fonts/FontFileMap$Companion;->()V HSPLorg/thoughtcrime/securesms/fonts/FontFileMap$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/fonts/FontFileMap$Companion;->getMap(Landroid/content/Context;Lorg/thoughtcrime/securesms/fonts/FontVersion;)Lorg/thoughtcrime/securesms/fonts/FontFileMap; HSPLorg/thoughtcrime/securesms/fonts/FontFileMap$Companion;->getNameOnDisk(Landroid/content/Context;Lorg/thoughtcrime/securesms/fonts/FontVersion;Ljava/lang/String;)Ljava/lang/String; -HSPLorg/thoughtcrime/securesms/fonts/FontFileMap$Companion;->put(Landroid/content/Context;Lorg/thoughtcrime/securesms/fonts/FontVersion;Ljava/lang/String;Ljava/lang/String;)V -HSPLorg/thoughtcrime/securesms/fonts/FontFileMap$Companion;->setMap(Landroid/content/Context;Lorg/thoughtcrime/securesms/fonts/FontVersion;Lorg/thoughtcrime/securesms/fonts/FontFileMap;)V HSPLorg/thoughtcrime/securesms/fonts/FontFileMap;->()V -HSPLorg/thoughtcrime/securesms/fonts/FontFileMap;->(Ljava/util/Map;)V -HSPLorg/thoughtcrime/securesms/fonts/FontFileMap;->access$getObjectMapper$cp()Lcom/fasterxml/jackson/databind/ObjectMapper; HSPLorg/thoughtcrime/securesms/fonts/FontFileMap;->access$getTAG$cp()Ljava/lang/String; -HSPLorg/thoughtcrime/securesms/fonts/FontFileMap;->getMap()Ljava/util/Map; HSPLorg/thoughtcrime/securesms/fonts/FontManifest$Companion;->()V HSPLorg/thoughtcrime/securesms/fonts/FontManifest$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/fonts/FontManifest$Companion;->fromDisk(Landroid/content/Context;Lorg/thoughtcrime/securesms/fonts/FontVersion;)Lorg/thoughtcrime/securesms/fonts/FontManifest; @@ -25238,7 +25232,6 @@ HSPLorg/thoughtcrime/securesms/fonts/Fonts;->downloadLatestVersionLong()J HSPLorg/thoughtcrime/securesms/fonts/Fonts;->getDirectory(Landroid/content/Context;)Ljava/io/File; HSPLorg/thoughtcrime/securesms/fonts/Fonts;->getScriptPath(Lorg/thoughtcrime/securesms/fonts/TextFont;Lorg/thoughtcrime/securesms/fonts/FontManifest$FontScript;)Ljava/lang/String; HSPLorg/thoughtcrime/securesms/fonts/Fonts;->getSupportedScript(Ljava/util/List;Lorg/thoughtcrime/securesms/fonts/SupportedScript;)Lorg/thoughtcrime/securesms/fonts/SupportedScript; -HSPLorg/thoughtcrime/securesms/fonts/Fonts;->loadFontIntoTypeface(Landroid/content/Context;Lorg/thoughtcrime/securesms/fonts/FontVersion;Ljava/lang/String;)Landroid/graphics/Typeface; HSPLorg/thoughtcrime/securesms/fonts/Fonts;->resolveFont$lambda$2$lambda$1(Landroid/content/Context;Lorg/thoughtcrime/securesms/fonts/SupportedScript;Lorg/thoughtcrime/securesms/fonts/TextFont;Lorg/thoughtcrime/securesms/fonts/FontVersion;Lorg/thoughtcrime/securesms/fonts/FontManifest;Lorg/thoughtcrime/securesms/fonts/Fonts$FontResult$Immediate;Lorg/thoughtcrime/securesms/fonts/Fonts$FontDownloadKey;)Landroid/graphics/Typeface; HSPLorg/thoughtcrime/securesms/fonts/Fonts;->resolveFont(Landroid/content/Context;Lorg/thoughtcrime/securesms/fonts/TextFont;Lorg/thoughtcrime/securesms/fonts/SupportedScript;)Lorg/thoughtcrime/securesms/fonts/Fonts$FontResult; HSPLorg/thoughtcrime/securesms/fonts/Fonts;->resolveFontScriptFromScriptName(Lorg/thoughtcrime/securesms/fonts/SupportedScript;Lorg/thoughtcrime/securesms/fonts/FontManifest;)Lorg/thoughtcrime/securesms/fonts/FontManifest$FontScript; @@ -25359,12 +25352,8 @@ HSPLorg/thoughtcrime/securesms/jobmanager/CompositeScheduler;->schedule(JLjava/u HSPLorg/thoughtcrime/securesms/jobmanager/Constraint$-CC;->$default$getJobSchedulerKeyPart(Lorg/thoughtcrime/securesms/jobmanager/Constraint;)Ljava/lang/String; HSPLorg/thoughtcrime/securesms/jobmanager/ConstraintInstantiator;->(Ljava/util/Map;)V HSPLorg/thoughtcrime/securesms/jobmanager/ConstraintInstantiator;->instantiate(Ljava/lang/String;)Lorg/thoughtcrime/securesms/jobmanager/Constraint; -HSPLorg/thoughtcrime/securesms/jobmanager/InAppScheduler$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/jobmanager/InAppScheduler;)V -HSPLorg/thoughtcrime/securesms/jobmanager/InAppScheduler$$ExternalSyntheticLambda0;->run()V -HSPLorg/thoughtcrime/securesms/jobmanager/InAppScheduler;->$r8$lambda$hBEn_48T9NK-BhOmjEIwF4k281g(Lorg/thoughtcrime/securesms/jobmanager/InAppScheduler;)V HSPLorg/thoughtcrime/securesms/jobmanager/InAppScheduler;->()V HSPLorg/thoughtcrime/securesms/jobmanager/InAppScheduler;->(Lorg/thoughtcrime/securesms/jobmanager/JobManager;)V -HSPLorg/thoughtcrime/securesms/jobmanager/InAppScheduler;->lambda$schedule$0()V HSPLorg/thoughtcrime/securesms/jobmanager/InAppScheduler;->schedule(JLjava/util/List;)V HSPLorg/thoughtcrime/securesms/jobmanager/Job$1;->()V HSPLorg/thoughtcrime/securesms/jobmanager/Job$Parameters$Builder;->()V @@ -25379,7 +25368,6 @@ HSPLorg/thoughtcrime/securesms/jobmanager/Job$Parameters$Builder;->setLifespan(J HSPLorg/thoughtcrime/securesms/jobmanager/Job$Parameters$Builder;->setMaxAttempts(I)Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters$Builder; HSPLorg/thoughtcrime/securesms/jobmanager/Job$Parameters$Builder;->setMaxInstancesForFactory(I)Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters$Builder; HSPLorg/thoughtcrime/securesms/jobmanager/Job$Parameters$Builder;->setMaxInstancesForQueue(I)Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters$Builder; -HSPLorg/thoughtcrime/securesms/jobmanager/Job$Parameters$Builder;->setPriority(I)Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters$Builder; HSPLorg/thoughtcrime/securesms/jobmanager/Job$Parameters$Builder;->setQueue(Ljava/lang/String;)Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters$Builder; HSPLorg/thoughtcrime/securesms/jobmanager/Job$Parameters;->(Ljava/lang/String;JJIIILjava/lang/String;Ljava/util/List;[BZI)V HSPLorg/thoughtcrime/securesms/jobmanager/Job$Parameters;->(Ljava/lang/String;JJIIILjava/lang/String;Ljava/util/List;[BZILorg/thoughtcrime/securesms/jobmanager/Job$Parameters-IA;)V @@ -25399,14 +25387,11 @@ HSPLorg/thoughtcrime/securesms/jobmanager/Job$Result$ResultType;->(Ljava/l HSPLorg/thoughtcrime/securesms/jobmanager/Job$Result$ResultType;->values()[Lorg/thoughtcrime/securesms/jobmanager/Job$Result$ResultType; HSPLorg/thoughtcrime/securesms/jobmanager/Job$Result;->()V HSPLorg/thoughtcrime/securesms/jobmanager/Job$Result;->(Lorg/thoughtcrime/securesms/jobmanager/Job$Result$ResultType;Ljava/lang/RuntimeException;[BJ)V -HSPLorg/thoughtcrime/securesms/jobmanager/Job$Result;->failure()Lorg/thoughtcrime/securesms/jobmanager/Job$Result; -HSPLorg/thoughtcrime/securesms/jobmanager/Job$Result;->getBackoffInterval()J HSPLorg/thoughtcrime/securesms/jobmanager/Job$Result;->getException()Ljava/lang/RuntimeException; HSPLorg/thoughtcrime/securesms/jobmanager/Job$Result;->getOutputData()[B HSPLorg/thoughtcrime/securesms/jobmanager/Job$Result;->isFailure()Z HSPLorg/thoughtcrime/securesms/jobmanager/Job$Result;->isRetry()Z HSPLorg/thoughtcrime/securesms/jobmanager/Job$Result;->isSuccess()Z -HSPLorg/thoughtcrime/securesms/jobmanager/Job$Result;->retry(J)Lorg/thoughtcrime/securesms/jobmanager/Job$Result; HSPLorg/thoughtcrime/securesms/jobmanager/Job$Result;->success([B)Lorg/thoughtcrime/securesms/jobmanager/Job$Result; HSPLorg/thoughtcrime/securesms/jobmanager/Job$Result;->toString()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/jobmanager/Job;->()V @@ -25418,22 +25403,11 @@ HSPLorg/thoughtcrime/securesms/jobmanager/Job;->getParameters()Lorg/thoughtcrime HSPLorg/thoughtcrime/securesms/jobmanager/Job;->getRunAttempt()I HSPLorg/thoughtcrime/securesms/jobmanager/Job;->isCanceled()Z HSPLorg/thoughtcrime/securesms/jobmanager/Job;->onAdded()V -HSPLorg/thoughtcrime/securesms/jobmanager/Job;->onRetry()V HSPLorg/thoughtcrime/securesms/jobmanager/Job;->onSubmit()V HSPLorg/thoughtcrime/securesms/jobmanager/Job;->setContext(Landroid/content/Context;)V HSPLorg/thoughtcrime/securesms/jobmanager/Job;->setLastRunAttemptTime(J)V HSPLorg/thoughtcrime/securesms/jobmanager/Job;->setNextBackoffInterval(J)V HSPLorg/thoughtcrime/securesms/jobmanager/Job;->setRunAttempt(I)V -HSPLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda0;->()V -HSPLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda0;->accept(Ljava/lang/Object;)V -HSPLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda13;->()V -HSPLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda13;->apply(Ljava/lang/Object;)Ljava/lang/Object; -HSPLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda14;->(Lorg/thoughtcrime/securesms/jobmanager/persistence/JobStorage;)V -HSPLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda14;->apply(Ljava/lang/Object;)Ljava/lang/Object; -HSPLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda16;->(Lorg/thoughtcrime/securesms/jobmanager/JobController;)V -HSPLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda16;->apply(Ljava/lang/Object;)Ljava/lang/Object; -HSPLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda17;->(Lorg/thoughtcrime/securesms/jobmanager/JobController;)V -HSPLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda17;->accept(Ljava/lang/Object;)V HSPLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda19;->(Lorg/thoughtcrime/securesms/jobmanager/ConstraintInstantiator;)V HSPLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda19;->apply(Ljava/lang/Object;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda1;->(Lorg/thoughtcrime/securesms/jobmanager/JobController;)V @@ -25454,11 +25428,9 @@ HSPLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda HSPLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda7;->accept(Ljava/lang/Object;)V HSPLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda8;->()V HSPLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda8;->apply(Ljava/lang/Object;)Ljava/lang/Object; -HSPLorg/thoughtcrime/securesms/jobmanager/JobController;->$r8$lambda$AFTJhIYz2IJLxa5cdEvspxyDnpk(Lorg/thoughtcrime/securesms/jobmanager/JobController;Lorg/thoughtcrime/securesms/jobmanager/persistence/JobSpec;)Lorg/thoughtcrime/securesms/jobmanager/Job; HSPLorg/thoughtcrime/securesms/jobmanager/JobController;->$r8$lambda$FnYUQJ9nqVuTdiLe8l57LDvJzOE(Lorg/thoughtcrime/securesms/jobmanager/JobController;Ljava/util/List;)V HSPLorg/thoughtcrime/securesms/jobmanager/JobController;->$r8$lambda$Ka2rmpP3tDCT7kzx5KQlHQUrhA4(Lorg/thoughtcrime/securesms/jobmanager/persistence/JobSpec;Ljava/lang/String;)Lorg/thoughtcrime/securesms/jobmanager/persistence/ConstraintSpec; HSPLorg/thoughtcrime/securesms/jobmanager/JobController;->$r8$lambda$YXMN5Ij_rYm1dFbxzMPP4rhbPPI(Lorg/thoughtcrime/securesms/jobmanager/JobController;Lorg/thoughtcrime/securesms/jobmanager/Job;)V -HSPLorg/thoughtcrime/securesms/jobmanager/JobController;->$r8$lambda$_WG2KEwBlGL1eFDhBUpj1kHB45I(Lorg/thoughtcrime/securesms/jobmanager/JobController;Lorg/thoughtcrime/securesms/jobmanager/Job;)V HSPLorg/thoughtcrime/securesms/jobmanager/JobController;->$r8$lambda$cs0r6RT3Ty9GlYLxMBZkIHg08dM(Lorg/thoughtcrime/securesms/jobmanager/JobController;Lorg/thoughtcrime/securesms/jobmanager/Job;Ljava/lang/String;)Lorg/thoughtcrime/securesms/jobmanager/persistence/DependencySpec; HSPLorg/thoughtcrime/securesms/jobmanager/JobController;->()V HSPLorg/thoughtcrime/securesms/jobmanager/JobController;->(Landroid/app/Application;Lorg/thoughtcrime/securesms/jobmanager/persistence/JobStorage;Lorg/thoughtcrime/securesms/jobmanager/JobInstantiator;Lorg/thoughtcrime/securesms/jobmanager/ConstraintInstantiator;Lorg/thoughtcrime/securesms/jobmanager/JobTracker;Lorg/thoughtcrime/securesms/jobmanager/Scheduler;Lorg/thoughtcrime/securesms/util/Debouncer;Lorg/thoughtcrime/securesms/jobmanager/JobController$Callback;)V @@ -25472,13 +25444,9 @@ HSPLorg/thoughtcrime/securesms/jobmanager/JobController;->init()V HSPLorg/thoughtcrime/securesms/jobmanager/JobController;->insertJobChain(Ljava/util/List;)V HSPLorg/thoughtcrime/securesms/jobmanager/JobController;->lambda$buildFullSpec$10(Lorg/thoughtcrime/securesms/jobmanager/persistence/JobSpec;Ljava/lang/String;)Lorg/thoughtcrime/securesms/jobmanager/persistence/ConstraintSpec; HSPLorg/thoughtcrime/securesms/jobmanager/JobController;->lambda$buildFullSpec$11(Lorg/thoughtcrime/securesms/jobmanager/Job;Ljava/lang/String;)Lorg/thoughtcrime/securesms/jobmanager/persistence/DependencySpec; -HSPLorg/thoughtcrime/securesms/jobmanager/JobController;->lambda$onFailure$3(Lorg/thoughtcrime/securesms/jobmanager/persistence/JobSpec;)Lorg/thoughtcrime/securesms/jobmanager/Job; -HSPLorg/thoughtcrime/securesms/jobmanager/JobController;->lambda$onFailure$4(Lorg/thoughtcrime/securesms/jobmanager/Job;)V HSPLorg/thoughtcrime/securesms/jobmanager/JobController;->lambda$triggerOnSubmit$8(Lorg/thoughtcrime/securesms/jobmanager/Job;)V HSPLorg/thoughtcrime/securesms/jobmanager/JobController;->lambda$triggerOnSubmit$9(Ljava/util/List;)V -HSPLorg/thoughtcrime/securesms/jobmanager/JobController;->onFailure(Lorg/thoughtcrime/securesms/jobmanager/Job;)Ljava/util/List; HSPLorg/thoughtcrime/securesms/jobmanager/JobController;->onJobFinished(Lorg/thoughtcrime/securesms/jobmanager/Job;)V -HSPLorg/thoughtcrime/securesms/jobmanager/JobController;->onRetry(Lorg/thoughtcrime/securesms/jobmanager/Job;J)V HSPLorg/thoughtcrime/securesms/jobmanager/JobController;->onSuccess(Lorg/thoughtcrime/securesms/jobmanager/Job;[B)V HSPLorg/thoughtcrime/securesms/jobmanager/JobController;->pullNextEligibleJobForExecution(Lorg/thoughtcrime/securesms/jobmanager/JobPredicate;)Lorg/thoughtcrime/securesms/jobmanager/Job; HSPLorg/thoughtcrime/securesms/jobmanager/JobController;->scheduleJobs(Ljava/util/List;)V @@ -25559,6 +25527,7 @@ HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->runOnExecutor(Ljava/lang/ HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->startChain(Lorg/thoughtcrime/securesms/jobmanager/Job;)Lorg/thoughtcrime/securesms/jobmanager/JobManager$Chain; HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->waitUntilInitialized()V HSPLorg/thoughtcrime/securesms/jobmanager/JobManager;->wakeUp()V +HSPLorg/thoughtcrime/securesms/jobmanager/JobMigration;->()V HSPLorg/thoughtcrime/securesms/jobmanager/JobMigration;->(I)V HSPLorg/thoughtcrime/securesms/jobmanager/JobMigration;->getEndVersion()I HSPLorg/thoughtcrime/securesms/jobmanager/JobMigrator;->()V @@ -25613,16 +25582,9 @@ HSPLorg/thoughtcrime/securesms/jobmanager/JsonJobData;->()V HSPLorg/thoughtcrime/securesms/jobmanager/JsonJobData;->(Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;)V HSPLorg/thoughtcrime/securesms/jobmanager/JsonJobData;->(Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;Lorg/thoughtcrime/securesms/jobmanager/JsonJobData-IA;)V HSPLorg/thoughtcrime/securesms/jobmanager/JsonJobData;->deserialize([B)Lorg/thoughtcrime/securesms/jobmanager/JsonJobData; -HSPLorg/thoughtcrime/securesms/jobmanager/JsonJobData;->getBoolean(Ljava/lang/String;)Z -HSPLorg/thoughtcrime/securesms/jobmanager/JsonJobData;->getBooleanOrDefault(Ljava/lang/String;Z)Z -HSPLorg/thoughtcrime/securesms/jobmanager/JsonJobData;->getLong(Ljava/lang/String;)J -HSPLorg/thoughtcrime/securesms/jobmanager/JsonJobData;->getString(Ljava/lang/String;)Ljava/lang/String; -HSPLorg/thoughtcrime/securesms/jobmanager/JsonJobData;->hasBoolean(Ljava/lang/String;)Z HSPLorg/thoughtcrime/securesms/jobmanager/JsonJobData;->hasInt(Ljava/lang/String;)Z -HSPLorg/thoughtcrime/securesms/jobmanager/JsonJobData;->hasString(Ljava/lang/String;)Z HSPLorg/thoughtcrime/securesms/jobmanager/JsonJobData;->isEmpty()Z HSPLorg/thoughtcrime/securesms/jobmanager/JsonJobData;->serialize()[B -HSPLorg/thoughtcrime/securesms/jobmanager/JsonJobData;->throwIfAbsent(Ljava/util/Map;Ljava/lang/String;)V HSPLorg/thoughtcrime/securesms/jobmanager/impl/AutoDownloadEmojiConstraint$Factory;->(Landroid/content/Context;)V HSPLorg/thoughtcrime/securesms/jobmanager/impl/AutoDownloadEmojiConstraint$Factory;->create()Lorg/thoughtcrime/securesms/jobmanager/Constraint; HSPLorg/thoughtcrime/securesms/jobmanager/impl/AutoDownloadEmojiConstraint$Factory;->create()Lorg/thoughtcrime/securesms/jobmanager/impl/AutoDownloadEmojiConstraint; @@ -25707,6 +25669,8 @@ HSPLorg/thoughtcrime/securesms/jobmanager/impl/SqlCipherMigrationConstraint$Fact HSPLorg/thoughtcrime/securesms/jobmanager/impl/SqlCipherMigrationConstraintObserver;->()V HSPLorg/thoughtcrime/securesms/jobmanager/impl/SqlCipherMigrationConstraintObserver;->()V HSPLorg/thoughtcrime/securesms/jobmanager/impl/SqlCipherMigrationConstraintObserver;->register(Lorg/thoughtcrime/securesms/jobmanager/ConstraintObserver$Notifier;)V +HSPLorg/thoughtcrime/securesms/jobmanager/migrations/DonationReceiptRedemptionJobMigration;->()V +HSPLorg/thoughtcrime/securesms/jobmanager/migrations/DonationReceiptRedemptionJobMigration;->()V HSPLorg/thoughtcrime/securesms/jobmanager/migrations/PushDecryptMessageJobEnvelopeMigration;->()V HSPLorg/thoughtcrime/securesms/jobmanager/migrations/PushProcessMessageJobMigration$Companion;->()V HSPLorg/thoughtcrime/securesms/jobmanager/migrations/PushProcessMessageJobMigration$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -25766,29 +25730,17 @@ HSPLorg/thoughtcrime/securesms/jobs/AccountConsistencyWorkerJob$Companion;->enqueueIfNecessary()V HSPLorg/thoughtcrime/securesms/jobs/AccountConsistencyWorkerJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/AccountConsistencyWorkerJob$Factory;->()V -HSPLorg/thoughtcrime/securesms/jobs/AccountConsistencyWorkerJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobmanager/Job; -HSPLorg/thoughtcrime/securesms/jobs/AccountConsistencyWorkerJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobs/AccountConsistencyWorkerJob; HSPLorg/thoughtcrime/securesms/jobs/AccountConsistencyWorkerJob;->()V HSPLorg/thoughtcrime/securesms/jobs/AccountConsistencyWorkerJob;->()V HSPLorg/thoughtcrime/securesms/jobs/AccountConsistencyWorkerJob;->(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;)V -HSPLorg/thoughtcrime/securesms/jobs/AccountConsistencyWorkerJob;->(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/jobs/AccountConsistencyWorkerJob;->enqueueIfNecessary()V HSPLorg/thoughtcrime/securesms/jobs/AccountConsistencyWorkerJob;->getFactoryKey()Ljava/lang/String; -HSPLorg/thoughtcrime/securesms/jobs/AccountConsistencyWorkerJob;->onRun()V -HSPLorg/thoughtcrime/securesms/jobs/AccountConsistencyWorkerJob;->onShouldRetry(Ljava/lang/Exception;)Z HSPLorg/thoughtcrime/securesms/jobs/AccountConsistencyWorkerJob;->serialize()[B HSPLorg/thoughtcrime/securesms/jobs/ApkUpdateJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/ApkUpdateJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/AttachmentCompressionJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/AttachmentCopyJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/AttachmentDownloadJob$Factory;->()V -HSPLorg/thoughtcrime/securesms/jobs/AttachmentDownloadJob;->()V -HSPLorg/thoughtcrime/securesms/jobs/AttachmentDownloadJob;->(JLorg/thoughtcrime/securesms/attachments/AttachmentId;Z)V -HSPLorg/thoughtcrime/securesms/jobs/AttachmentDownloadJob;->(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;JLorg/thoughtcrime/securesms/attachments/AttachmentId;Z)V -HSPLorg/thoughtcrime/securesms/jobs/AttachmentDownloadJob;->constructQueueString(Lorg/thoughtcrime/securesms/attachments/AttachmentId;)Ljava/lang/String; -HSPLorg/thoughtcrime/securesms/jobs/AttachmentDownloadJob;->getFactoryKey()Ljava/lang/String; -HSPLorg/thoughtcrime/securesms/jobs/AttachmentDownloadJob;->onAdded()V -HSPLorg/thoughtcrime/securesms/jobs/AttachmentDownloadJob;->serialize()[B HSPLorg/thoughtcrime/securesms/jobs/AttachmentMarkUploadedJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/AttachmentUploadJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/AttachmentUploadJob$Factory;->()V @@ -25797,7 +25749,6 @@ HSPLorg/thoughtcrime/securesms/jobs/AvatarGroupsV1DownloadJob$Factory;->() HSPLorg/thoughtcrime/securesms/jobs/AvatarGroupsV2DownloadJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/BaseJob;->()V HSPLorg/thoughtcrime/securesms/jobs/BaseJob;->(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;)V -HSPLorg/thoughtcrime/securesms/jobs/BaseJob;->getNextRunAttemptBackoff(ILjava/lang/Exception;)J HSPLorg/thoughtcrime/securesms/jobs/BaseJob;->run()Lorg/thoughtcrime/securesms/jobmanager/Job$Result; HSPLorg/thoughtcrime/securesms/jobs/BaseJob;->shouldTrace()Z HSPLorg/thoughtcrime/securesms/jobs/BaseJob;->warn(Ljava/lang/String;Ljava/lang/String;)V @@ -25822,53 +25773,26 @@ HSPLorg/thoughtcrime/securesms/jobs/CleanPreKeysJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/ConversationShortcutRankingUpdateJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/ConversationShortcutRankingUpdateJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/ConversationShortcutUpdateJob$Factory;->()V -HSPLorg/thoughtcrime/securesms/jobs/ConversationShortcutUpdateJob;->()V -HSPLorg/thoughtcrime/securesms/jobs/ConversationShortcutUpdateJob;->()V -HSPLorg/thoughtcrime/securesms/jobs/ConversationShortcutUpdateJob;->(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;)V -HSPLorg/thoughtcrime/securesms/jobs/ConversationShortcutUpdateJob;->enqueue()V -HSPLorg/thoughtcrime/securesms/jobs/ConversationShortcutUpdateJob;->getFactoryKey()Ljava/lang/String; -HSPLorg/thoughtcrime/securesms/jobs/ConversationShortcutUpdateJob;->serialize()[B HSPLorg/thoughtcrime/securesms/jobs/CreateReleaseChannelJob$Companion;->()V HSPLorg/thoughtcrime/securesms/jobs/CreateReleaseChannelJob$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/jobs/CreateReleaseChannelJob$Companion;->create()Lorg/thoughtcrime/securesms/jobs/CreateReleaseChannelJob; HSPLorg/thoughtcrime/securesms/jobs/CreateReleaseChannelJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/CreateReleaseChannelJob$Factory;->()V -HSPLorg/thoughtcrime/securesms/jobs/CreateReleaseChannelJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobmanager/Job; -HSPLorg/thoughtcrime/securesms/jobs/CreateReleaseChannelJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobs/CreateReleaseChannelJob; -HSPLorg/thoughtcrime/securesms/jobs/CreateReleaseChannelJob$setAvatar$1;->(Lorg/thoughtcrime/securesms/jobs/CreateReleaseChannelJob;Lorg/thoughtcrime/securesms/recipients/RecipientId;Ljava/util/concurrent/CountDownLatch;)V -HSPLorg/thoughtcrime/securesms/jobs/CreateReleaseChannelJob$setAvatar$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; -HSPLorg/thoughtcrime/securesms/jobs/CreateReleaseChannelJob$setAvatar$1;->invoke(Lorg/thoughtcrime/securesms/mediasend/Media;)V -HSPLorg/thoughtcrime/securesms/jobs/CreateReleaseChannelJob$setAvatar$2;->(Ljava/util/concurrent/CountDownLatch;)V HSPLorg/thoughtcrime/securesms/jobs/CreateReleaseChannelJob;->()V HSPLorg/thoughtcrime/securesms/jobs/CreateReleaseChannelJob;->(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;)V HSPLorg/thoughtcrime/securesms/jobs/CreateReleaseChannelJob;->(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;Lkotlin/jvm/internal/DefaultConstructorMarker;)V -HSPLorg/thoughtcrime/securesms/jobs/CreateReleaseChannelJob;->access$getContext$p$s1046862181(Lorg/thoughtcrime/securesms/jobs/CreateReleaseChannelJob;)Landroid/content/Context; HSPLorg/thoughtcrime/securesms/jobs/CreateReleaseChannelJob;->getFactoryKey()Ljava/lang/String; -HSPLorg/thoughtcrime/securesms/jobs/CreateReleaseChannelJob;->onRun()V HSPLorg/thoughtcrime/securesms/jobs/CreateReleaseChannelJob;->serialize()[B -HSPLorg/thoughtcrime/securesms/jobs/CreateReleaseChannelJob;->setAvatar(Lorg/thoughtcrime/securesms/recipients/RecipientId;)V HSPLorg/thoughtcrime/securesms/jobs/DirectoryRefreshJob$Factory;->()V -HSPLorg/thoughtcrime/securesms/jobs/DirectoryRefreshJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobmanager/Job; -HSPLorg/thoughtcrime/securesms/jobs/DirectoryRefreshJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobs/DirectoryRefreshJob; HSPLorg/thoughtcrime/securesms/jobs/DirectoryRefreshJob;->()V HSPLorg/thoughtcrime/securesms/jobs/DirectoryRefreshJob;->(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;Lorg/thoughtcrime/securesms/recipients/Recipient;Z)V -HSPLorg/thoughtcrime/securesms/jobs/DirectoryRefreshJob;->(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;Lorg/thoughtcrime/securesms/recipients/Recipient;ZLorg/thoughtcrime/securesms/jobs/DirectoryRefreshJob-IA;)V HSPLorg/thoughtcrime/securesms/jobs/DirectoryRefreshJob;->(Lorg/thoughtcrime/securesms/recipients/Recipient;Z)V HSPLorg/thoughtcrime/securesms/jobs/DirectoryRefreshJob;->(Z)V HSPLorg/thoughtcrime/securesms/jobs/DirectoryRefreshJob;->getFactoryKey()Ljava/lang/String; -HSPLorg/thoughtcrime/securesms/jobs/DirectoryRefreshJob;->onRun()V HSPLorg/thoughtcrime/securesms/jobs/DirectoryRefreshJob;->serialize()[B -HSPLorg/thoughtcrime/securesms/jobs/DirectoryRefreshJob;->shouldTrace()Z HSPLorg/thoughtcrime/securesms/jobs/DonationReceiptRedemptionJob$Factory;->()V -HSPLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob$$ExternalSyntheticLambda0;->()V -HSPLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob$$ExternalSyntheticLambda0;->test(Ljava/lang/Object;)Z HSPLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob$$ExternalSyntheticLambda10;->(Lorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob;)V HSPLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob$$ExternalSyntheticLambda10;->produce()Ljava/lang/Object; -HSPLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob$$ExternalSyntheticLambda1;->(Ljava/lang/String;)V -HSPLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob$$ExternalSyntheticLambda1;->test(Ljava/lang/Object;)Z -HSPLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob$$ExternalSyntheticLambda2;->(Ljava/lang/String;)V -HSPLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob$$ExternalSyntheticLambda2;->test(Ljava/lang/Object;)Z -HSPLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob$$ExternalSyntheticLambda3;->()V HSPLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob$$ExternalSyntheticLambda4;->(I)V HSPLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob$$ExternalSyntheticLambda4;->compare(Ljava/lang/Object;Ljava/lang/Object;)I HSPLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob$$ExternalSyntheticLambda5;->()V @@ -25883,23 +25807,17 @@ HSPLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob$$ExternalSyntheti HSPLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobmanager/Job; HSPLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob; -HSPLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob;->$r8$lambda$QoH7dcHnoANVuwa796Z9UVq8P10(Ljava/lang/String;Ljava/io/File;)Z HSPLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob;->$r8$lambda$dkoZGu-68WkVhszwd5d_cWNZrJU(ILcom/annimon/stream/IntPair;Lcom/annimon/stream/IntPair;)I -HSPLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob;->$r8$lambda$svSYKFZ54wbzF4p08Dr861zOX4Y(Ljava/lang/String;Ljava/io/File;)Z HSPLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob;->()V HSPLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob;->(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;Lorg/thoughtcrime/securesms/emoji/EmojiFiles$Version;)V HSPLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob;->(Z)V HSPLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob;->assertRemoteDownloadConstraints(Landroid/content/Context;)V -HSPLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob;->clearOldEmojiData(Landroid/content/Context;Lorg/thoughtcrime/securesms/emoji/EmojiFiles$Version;)V HSPLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob;->downloadImages(Landroid/content/Context;Lorg/thoughtcrime/securesms/emoji/EmojiFiles$Version;Ljava/util/List;Ljava/lang/String;Lorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob$Producer;)V HSPLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob;->downloadJson(Landroid/content/Context;Lorg/thoughtcrime/securesms/emoji/EmojiFiles$Version;)Lorg/thoughtcrime/securesms/emoji/EmojiData; HSPLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob;->getDesiredRemoteBucketForDensity(Lorg/thoughtcrime/securesms/util/ScreenDensity;)Ljava/lang/String; HSPLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob;->getFactoryKey()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob;->getRemoteImageHash(Lorg/thoughtcrime/securesms/emoji/EmojiFiles$Version;Ljava/lang/String;Ljava/lang/String;)[B -HSPLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob;->lambda$clearOldEmojiData$2(Ljava/lang/String;Ljava/io/File;)Z -HSPLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob;->lambda$clearOldEmojiData$3(Ljava/lang/String;Ljava/io/File;)Z HSPLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob;->lambda$resolveDensity$0(ILcom/annimon/stream/IntPair;Lcom/annimon/stream/IntPair;)I -HSPLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob;->markComplete(Lorg/thoughtcrime/securesms/emoji/EmojiFiles$Version;)V HSPLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob;->onRun()V HSPLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob;->resolveDensity(Ljava/util/List;Ljava/lang/String;)Ljava/lang/String; HSPLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob;->scheduleIfNecessary(Landroid/content/Context;)V @@ -25938,9 +25856,11 @@ HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage$deleteJobs$1;->(Ljava/u HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage$deleteJobs$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage$deleteJobs$1;->invoke(Lorg/thoughtcrime/securesms/jobmanager/persistence/JobSpec;)Ljava/lang/Boolean; HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage$getPendingJobsWithNoDependenciesInCreatedOrder$$inlined$sortedBy$1;->()V +HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage$getPendingJobsWithNoDependenciesInCreatedOrder$$inlined$sortedBy$1;->compare(Ljava/lang/Object;Ljava/lang/Object;)I HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage$getPendingJobsWithNoDependenciesInCreatedOrder$$inlined$sortedByDescending$1;->()V HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage$getPendingJobsWithNoDependenciesInCreatedOrder$$inlined$sortedByDescending$1;->compare(Ljava/lang/Object;Ljava/lang/Object;)I HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage$getPendingJobsWithNoDependenciesInCreatedOrder$lambda$8$$inlined$sortedBy$1;->()V +HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage$getPendingJobsWithNoDependenciesInCreatedOrder$lambda$8$$inlined$sortedBy$1;->compare(Ljava/lang/Object;Ljava/lang/Object;)I HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage;->$r8$lambda$Xu-rS-eV7EMHJJ0ct-GtD7_wnkc(Lkotlin/jvm/functions/Function1;Ljava/lang/Object;)Z HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage;->()V HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage;->(Lorg/thoughtcrime/securesms/database/JobDatabase;)V @@ -25949,20 +25869,16 @@ HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage;->deleteJobs$lambda$28(Lkotli HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage;->deleteJobs(Ljava/util/List;)V HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage;->getAllJobSpecs()Ljava/util/List; HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage;->getConstraintSpecs(Ljava/lang/String;)Ljava/util/List; -HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage;->getDependencySpecsThatDependOnJob(Ljava/lang/String;)Ljava/util/List; HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage;->getJobById(Ljava/lang/String;)Lorg/thoughtcrime/securesms/jobmanager/persistence/JobSpec; HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage;->getJobCountForFactory(Ljava/lang/String;)I HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage;->getJobCountForFactoryAndQueue(Ljava/lang/String;Ljava/lang/String;)I HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage;->getJobSpec(Ljava/lang/String;)Lorg/thoughtcrime/securesms/jobmanager/persistence/JobSpec; HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage;->getMigrationJob()Lorg/thoughtcrime/securesms/jobmanager/persistence/JobSpec; -HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage;->getPendingJobsWithNoDependenciesInCreatedOrder(J)Ljava/util/List; -HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage;->getSingleLayerOfDependencySpecsThatDependOnJob(Ljava/lang/String;)Ljava/util/List; HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage;->hasEligibleRunTime(Lorg/thoughtcrime/securesms/jobmanager/persistence/JobSpec;J)Z HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage;->init()V HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage;->insertJobs(Ljava/util/List;)V HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage;->markJobAsRunning(Ljava/lang/String;J)V HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage;->updateAllJobsToBePending()V -HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage;->updateJobAfterRetry(Ljava/lang/String;JIJ[B)V HSPLorg/thoughtcrime/securesms/jobs/FastJobStorage;->updateJobs(Ljava/util/List;)V HSPLorg/thoughtcrime/securesms/jobs/FcmRefreshJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/FetchRemoteMegaphoneImageJob$Factory;->()V @@ -25974,8 +25890,6 @@ HSPLorg/thoughtcrime/securesms/jobs/FontDownloaderJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/FontDownloaderJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobmanager/Job; HSPLorg/thoughtcrime/securesms/jobs/FontDownloaderJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobs/FontDownloaderJob; HSPLorg/thoughtcrime/securesms/jobs/FontDownloaderJob$onRun$listener$1;->(Ljava/util/concurrent/CountDownLatch;Ljava/util/concurrent/atomic/AtomicInteger;)V -HSPLorg/thoughtcrime/securesms/jobs/FontDownloaderJob$onRun$listener$1;->onSuccess(Landroid/graphics/Typeface;)V -HSPLorg/thoughtcrime/securesms/jobs/FontDownloaderJob$onRun$listener$1;->onSuccess(Ljava/lang/Object;)V HSPLorg/thoughtcrime/securesms/jobs/FontDownloaderJob;->()V HSPLorg/thoughtcrime/securesms/jobs/FontDownloaderJob;->()V HSPLorg/thoughtcrime/securesms/jobs/FontDownloaderJob;->(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;)V @@ -25992,7 +25906,17 @@ HSPLorg/thoughtcrime/securesms/jobs/GiftSendJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/GroupCallPeekJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/GroupCallPeekWorkerJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/GroupCallUpdateSendJob$Factory;->()V -HSPLorg/thoughtcrime/securesms/jobs/GroupV1MigrationJob$Factory;->()V +HSPLorg/thoughtcrime/securesms/jobs/GroupRingCleanupJob$Companion;->()V +HSPLorg/thoughtcrime/securesms/jobs/GroupRingCleanupJob$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/jobs/GroupRingCleanupJob$Companion;->enqueue()V +HSPLorg/thoughtcrime/securesms/jobs/GroupRingCleanupJob$Factory;->()V +HSPLorg/thoughtcrime/securesms/jobs/GroupRingCleanupJob$Factory;->()V +HSPLorg/thoughtcrime/securesms/jobs/GroupRingCleanupJob;->()V +HSPLorg/thoughtcrime/securesms/jobs/GroupRingCleanupJob;->(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;)V +HSPLorg/thoughtcrime/securesms/jobs/GroupRingCleanupJob;->(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;Lkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/jobs/GroupRingCleanupJob;->enqueue()V +HSPLorg/thoughtcrime/securesms/jobs/GroupRingCleanupJob;->getFactoryKey()Ljava/lang/String; +HSPLorg/thoughtcrime/securesms/jobs/GroupRingCleanupJob;->serialize()[B HSPLorg/thoughtcrime/securesms/jobs/GroupV2UpdateSelfProfileKeyJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/GroupV2UpdateSelfProfileKeyJob;->()V HSPLorg/thoughtcrime/securesms/jobs/GroupV2UpdateSelfProfileKeyJob;->enqueueForGroupsIfNecessary()V @@ -26025,14 +25949,10 @@ HSPLorg/thoughtcrime/securesms/jobs/MultiDeviceMessageRequestResponseJob$Factory HSPLorg/thoughtcrime/securesms/jobs/MultiDeviceOutgoingPaymentSyncJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/MultiDeviceProfileContentUpdateJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/MultiDeviceProfileKeyUpdateJob$Factory;->()V -HSPLorg/thoughtcrime/securesms/jobs/MultiDeviceProfileKeyUpdateJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobmanager/Job; -HSPLorg/thoughtcrime/securesms/jobs/MultiDeviceProfileKeyUpdateJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobs/MultiDeviceProfileKeyUpdateJob; HSPLorg/thoughtcrime/securesms/jobs/MultiDeviceProfileKeyUpdateJob;->()V HSPLorg/thoughtcrime/securesms/jobs/MultiDeviceProfileKeyUpdateJob;->()V HSPLorg/thoughtcrime/securesms/jobs/MultiDeviceProfileKeyUpdateJob;->(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;)V -HSPLorg/thoughtcrime/securesms/jobs/MultiDeviceProfileKeyUpdateJob;->(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;Lorg/thoughtcrime/securesms/jobs/MultiDeviceProfileKeyUpdateJob-IA;)V HSPLorg/thoughtcrime/securesms/jobs/MultiDeviceProfileKeyUpdateJob;->getFactoryKey()Ljava/lang/String; -HSPLorg/thoughtcrime/securesms/jobs/MultiDeviceProfileKeyUpdateJob;->onFailure()V HSPLorg/thoughtcrime/securesms/jobs/MultiDeviceProfileKeyUpdateJob;->serialize()[B HSPLorg/thoughtcrime/securesms/jobs/MultiDeviceReadUpdateJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/MultiDeviceStickerPackOperationJob$Factory;->()V @@ -26045,8 +25965,6 @@ HSPLorg/thoughtcrime/securesms/jobs/MultiDeviceSubscriptionSyncRequestJob$Factor HSPLorg/thoughtcrime/securesms/jobs/MultiDeviceVerifiedUpdateJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/MultiDeviceViewOnceOpenJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/MultiDeviceViewedUpdateJob$Factory;->()V -HSPLorg/thoughtcrime/securesms/jobs/NewRegistrationUsernameSyncJob$Factory;->()V -HSPLorg/thoughtcrime/securesms/jobs/NewRegistrationUsernameSyncJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/NullMessageSendJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/OptimizeMessageSearchIndexJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/OptimizeMessageSearchIndexJob$Factory;->()V @@ -26080,21 +25998,14 @@ HSPLorg/thoughtcrime/securesms/jobs/PreKeysSyncJob;->access$getTAG$cp()Ljava/lan HSPLorg/thoughtcrime/securesms/jobs/PreKeysSyncJob;->enqueue()V HSPLorg/thoughtcrime/securesms/jobs/PreKeysSyncJob;->enqueueIfNeeded()V HSPLorg/thoughtcrime/securesms/jobs/PreKeysSyncJob;->getFactoryKey()Ljava/lang/String; -HSPLorg/thoughtcrime/securesms/jobs/PreKeysSyncJob;->onFailure()V HSPLorg/thoughtcrime/securesms/jobs/PreKeysSyncJob;->onRun()V -HSPLorg/thoughtcrime/securesms/jobs/PreKeysSyncJob;->onShouldRetry(Ljava/lang/Exception;)Z HSPLorg/thoughtcrime/securesms/jobs/PreKeysSyncJob;->serialize()[B -HSPLorg/thoughtcrime/securesms/jobs/PreKeysSyncJob;->syncPreKeys(Lorg/whispersystems/signalservice/api/push/ServiceIdType;Lorg/whispersystems/signalservice/api/push/ServiceId;Lorg/whispersystems/signalservice/api/SignalServiceAccountDataStore;Lorg/thoughtcrime/securesms/crypto/storage/PreKeyMetadataStore;)V HSPLorg/thoughtcrime/securesms/jobs/ProfileKeySendJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/ProfileUploadJob$Factory;->()V -HSPLorg/thoughtcrime/securesms/jobs/ProfileUploadJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobmanager/Job; -HSPLorg/thoughtcrime/securesms/jobs/ProfileUploadJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobs/ProfileUploadJob; HSPLorg/thoughtcrime/securesms/jobs/ProfileUploadJob;->()V HSPLorg/thoughtcrime/securesms/jobs/ProfileUploadJob;->()V HSPLorg/thoughtcrime/securesms/jobs/ProfileUploadJob;->(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;)V -HSPLorg/thoughtcrime/securesms/jobs/ProfileUploadJob;->(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;Lorg/thoughtcrime/securesms/jobs/ProfileUploadJob-IA;)V HSPLorg/thoughtcrime/securesms/jobs/ProfileUploadJob;->getFactoryKey()Ljava/lang/String; -HSPLorg/thoughtcrime/securesms/jobs/ProfileUploadJob;->onFailure()V HSPLorg/thoughtcrime/securesms/jobs/ProfileUploadJob;->serialize()[B HSPLorg/thoughtcrime/securesms/jobs/PushDistributionListSendJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/PushGroupSendJob$Factory;->()V @@ -26108,18 +26019,14 @@ HSPLorg/thoughtcrime/securesms/jobs/PushProcessMessageJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/ReactionSendJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/RebuildMessageSearchIndexJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/RebuildMessageSearchIndexJob$Factory;->()V +HSPLorg/thoughtcrime/securesms/jobs/ReclaimUsernameAndLinkJob$Factory;->()V +HSPLorg/thoughtcrime/securesms/jobs/ReclaimUsernameAndLinkJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/RefreshAttributesJob$Factory;->()V -HSPLorg/thoughtcrime/securesms/jobs/RefreshAttributesJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobmanager/Job; -HSPLorg/thoughtcrime/securesms/jobs/RefreshAttributesJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobs/RefreshAttributesJob; HSPLorg/thoughtcrime/securesms/jobs/RefreshAttributesJob;->()V HSPLorg/thoughtcrime/securesms/jobs/RefreshAttributesJob;->()V HSPLorg/thoughtcrime/securesms/jobs/RefreshAttributesJob;->(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;Z)V -HSPLorg/thoughtcrime/securesms/jobs/RefreshAttributesJob;->(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;ZLorg/thoughtcrime/securesms/jobs/RefreshAttributesJob-IA;)V HSPLorg/thoughtcrime/securesms/jobs/RefreshAttributesJob;->(Z)V HSPLorg/thoughtcrime/securesms/jobs/RefreshAttributesJob;->getFactoryKey()Ljava/lang/String; -HSPLorg/thoughtcrime/securesms/jobs/RefreshAttributesJob;->onFailure()V -HSPLorg/thoughtcrime/securesms/jobs/RefreshAttributesJob;->onRun()V -HSPLorg/thoughtcrime/securesms/jobs/RefreshAttributesJob;->onShouldRetry(Ljava/lang/Exception;)Z HSPLorg/thoughtcrime/securesms/jobs/RefreshAttributesJob;->serialize()[B HSPLorg/thoughtcrime/securesms/jobs/RefreshCallLinkDetailsJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/RefreshCallLinkDetailsJob$Factory;->()V @@ -26146,13 +26053,18 @@ HSPLorg/thoughtcrime/securesms/jobs/ResetSvrGuessCountJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/ResetSvrGuessCountJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/ResumableUploadSpecJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/RetrieveProfileAvatarJob$Factory;->()V +HSPLorg/thoughtcrime/securesms/jobs/RetrieveProfileJob$Companion;->()V +HSPLorg/thoughtcrime/securesms/jobs/RetrieveProfileJob$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/jobs/RetrieveProfileJob$Companion;->enqueue(Lorg/thoughtcrime/securesms/recipients/RecipientId;)V +HSPLorg/thoughtcrime/securesms/jobs/RetrieveProfileJob$Companion;->enqueueRoutineFetchIfNecessary()V +HSPLorg/thoughtcrime/securesms/jobs/RetrieveProfileJob$Companion;->forRecipients(Ljava/util/Set;)Ljava/util/List; +HSPLorg/thoughtcrime/securesms/jobs/RetrieveProfileJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/RetrieveProfileJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/RetrieveProfileJob;->()V HSPLorg/thoughtcrime/securesms/jobs/RetrieveProfileJob;->(Ljava/util/Set;)V HSPLorg/thoughtcrime/securesms/jobs/RetrieveProfileJob;->(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;Ljava/util/Set;)V -HSPLorg/thoughtcrime/securesms/jobs/RetrieveProfileJob;->enqueue(Lorg/thoughtcrime/securesms/recipients/RecipientId;)V -HSPLorg/thoughtcrime/securesms/jobs/RetrieveProfileJob;->enqueueRoutineFetchIfNecessary(Landroid/app/Application;)V -HSPLorg/thoughtcrime/securesms/jobs/RetrieveProfileJob;->forRecipient(Lorg/thoughtcrime/securesms/recipients/RecipientId;)Lorg/thoughtcrime/securesms/jobmanager/Job; +HSPLorg/thoughtcrime/securesms/jobs/RetrieveProfileJob;->access$getTAG$cp()Ljava/lang/String; +HSPLorg/thoughtcrime/securesms/jobs/RetrieveProfileJob;->enqueueRoutineFetchIfNecessary()V HSPLorg/thoughtcrime/securesms/jobs/RetrieveProfileJob;->getFactoryKey()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/jobs/RetrieveProfileJob;->serialize()[B HSPLorg/thoughtcrime/securesms/jobs/RetrieveRemoteAnnouncementsJob$Companion;->()V @@ -26165,7 +26077,6 @@ HSPLorg/thoughtcrime/securesms/jobs/RetrieveRemoteAnnouncementsJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/RetrieveRemoteAnnouncementsJob;->access$getTAG$cp()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/jobs/RetrieveRemoteAnnouncementsJob;->enqueue()V -HSPLorg/thoughtcrime/securesms/jobs/RotateCertificateJob$1;->()V HSPLorg/thoughtcrime/securesms/jobs/RotateCertificateJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/RotateCertificateJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobmanager/Job; HSPLorg/thoughtcrime/securesms/jobs/RotateCertificateJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobs/RotateCertificateJob; @@ -26175,9 +26086,7 @@ HSPLorg/thoughtcrime/securesms/jobs/RotateCertificateJob;->(Lorg/thoughtcr HSPLorg/thoughtcrime/securesms/jobs/RotateCertificateJob;->(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;Lorg/thoughtcrime/securesms/jobs/RotateCertificateJob-IA;)V HSPLorg/thoughtcrime/securesms/jobs/RotateCertificateJob;->getFactoryKey()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/jobs/RotateCertificateJob;->onAdded()V -HSPLorg/thoughtcrime/securesms/jobs/RotateCertificateJob;->onFailure()V HSPLorg/thoughtcrime/securesms/jobs/RotateCertificateJob;->onRun()V -HSPLorg/thoughtcrime/securesms/jobs/RotateCertificateJob;->onShouldRetry(Ljava/lang/Exception;)Z HSPLorg/thoughtcrime/securesms/jobs/RotateCertificateJob;->serialize()[B HSPLorg/thoughtcrime/securesms/jobs/RotateProfileKeyJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/RotateProfileKeyJob;->()V @@ -26193,14 +26102,6 @@ HSPLorg/thoughtcrime/securesms/jobs/SmsSendJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/SmsSentJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/StickerDownloadJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/StickerPackDownloadJob$Factory;->()V -HSPLorg/thoughtcrime/securesms/jobs/StickerPackDownloadJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobmanager/Job; -HSPLorg/thoughtcrime/securesms/jobs/StickerPackDownloadJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobs/StickerPackDownloadJob; -HSPLorg/thoughtcrime/securesms/jobs/StickerPackDownloadJob;->()V -HSPLorg/thoughtcrime/securesms/jobs/StickerPackDownloadJob;->(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;Ljava/lang/String;Ljava/lang/String;ZZ)V -HSPLorg/thoughtcrime/securesms/jobs/StickerPackDownloadJob;->(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;Ljava/lang/String;Ljava/lang/String;ZZLorg/thoughtcrime/securesms/jobs/StickerPackDownloadJob-IA;)V -HSPLorg/thoughtcrime/securesms/jobs/StickerPackDownloadJob;->onFailure()V -HSPLorg/thoughtcrime/securesms/jobs/StickerPackDownloadJob;->onRun()V -HSPLorg/thoughtcrime/securesms/jobs/StickerPackDownloadJob;->onShouldRetry(Ljava/lang/Exception;)Z HSPLorg/thoughtcrime/securesms/jobs/StorageAccountRestoreJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/StorageAccountRestoreJob;->()V HSPLorg/thoughtcrime/securesms/jobs/StorageForcePushJob$Factory;->()V @@ -26211,15 +26112,11 @@ HSPLorg/thoughtcrime/securesms/jobs/StoryOnboardingDownloadJob$Companion;->creat HSPLorg/thoughtcrime/securesms/jobs/StoryOnboardingDownloadJob$Companion;->enqueueIfNeeded()V HSPLorg/thoughtcrime/securesms/jobs/StoryOnboardingDownloadJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/StoryOnboardingDownloadJob$Factory;->()V -HSPLorg/thoughtcrime/securesms/jobs/StoryOnboardingDownloadJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobmanager/Job; -HSPLorg/thoughtcrime/securesms/jobs/StoryOnboardingDownloadJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobs/StoryOnboardingDownloadJob; HSPLorg/thoughtcrime/securesms/jobs/StoryOnboardingDownloadJob;->()V HSPLorg/thoughtcrime/securesms/jobs/StoryOnboardingDownloadJob;->(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;)V HSPLorg/thoughtcrime/securesms/jobs/StoryOnboardingDownloadJob;->(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/jobs/StoryOnboardingDownloadJob;->access$getTAG$cp()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/jobs/StoryOnboardingDownloadJob;->getFactoryKey()Ljava/lang/String; -HSPLorg/thoughtcrime/securesms/jobs/StoryOnboardingDownloadJob;->getLocaleCodes()Ljava/util/List; -HSPLorg/thoughtcrime/securesms/jobs/StoryOnboardingDownloadJob;->onRun()V HSPLorg/thoughtcrime/securesms/jobs/StoryOnboardingDownloadJob;->serialize()[B HSPLorg/thoughtcrime/securesms/jobs/SubmitRateLimitPushChallengeJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/SubscriptionKeepAliveJob$Factory;->()V @@ -26238,16 +26135,12 @@ HSPLorg/thoughtcrime/securesms/jobs/SyncSystemContactLinksJob$Factory;->() HSPLorg/thoughtcrime/securesms/jobs/ThreadUpdateJob$$ExternalSyntheticLambda0;->(J)V HSPLorg/thoughtcrime/securesms/jobs/ThreadUpdateJob$$ExternalSyntheticLambda0;->run()V HSPLorg/thoughtcrime/securesms/jobs/ThreadUpdateJob$Factory;->()V -HSPLorg/thoughtcrime/securesms/jobs/ThreadUpdateJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobmanager/Job; -HSPLorg/thoughtcrime/securesms/jobs/ThreadUpdateJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobs/ThreadUpdateJob; HSPLorg/thoughtcrime/securesms/jobs/ThreadUpdateJob;->$r8$lambda$JSigGKNJwOMmimqwpu8xPBnW5UQ(J)V HSPLorg/thoughtcrime/securesms/jobs/ThreadUpdateJob;->(J)V HSPLorg/thoughtcrime/securesms/jobs/ThreadUpdateJob;->(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;J)V -HSPLorg/thoughtcrime/securesms/jobs/ThreadUpdateJob;->(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;JLorg/thoughtcrime/securesms/jobs/ThreadUpdateJob-IA;)V HSPLorg/thoughtcrime/securesms/jobs/ThreadUpdateJob;->enqueue(J)V HSPLorg/thoughtcrime/securesms/jobs/ThreadUpdateJob;->getFactoryKey()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/jobs/ThreadUpdateJob;->lambda$enqueue$0(J)V -HSPLorg/thoughtcrime/securesms/jobs/ThreadUpdateJob;->onRun()V HSPLorg/thoughtcrime/securesms/jobs/ThreadUpdateJob;->serialize()[B HSPLorg/thoughtcrime/securesms/jobs/TrimThreadJob$Factory;->()V HSPLorg/thoughtcrime/securesms/jobs/TypingSendJob$Factory;->()V @@ -26266,11 +26159,6 @@ HSPLorg/thoughtcrime/securesms/keyboard/emoji/search/EmojiSearchRepository;->(Landroid/content/Context;)V HSPLorg/thoughtcrime/securesms/keyvalue/AccountValues$Companion;->()V HSPLorg/thoughtcrime/securesms/keyvalue/AccountValues$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V -HSPLorg/thoughtcrime/securesms/keyvalue/AccountValues$UsernameSyncState$Companion;->()V -HSPLorg/thoughtcrime/securesms/keyvalue/AccountValues$UsernameSyncState$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V -HSPLorg/thoughtcrime/securesms/keyvalue/AccountValues$UsernameSyncState;->$values()[Lorg/thoughtcrime/securesms/keyvalue/AccountValues$UsernameSyncState; -HSPLorg/thoughtcrime/securesms/keyvalue/AccountValues$UsernameSyncState;->()V -HSPLorg/thoughtcrime/securesms/keyvalue/AccountValues$UsernameSyncState;->(Ljava/lang/String;IJ)V HSPLorg/thoughtcrime/securesms/keyvalue/AccountValues$aciPreKeys$1;->()V HSPLorg/thoughtcrime/securesms/keyvalue/AccountValues$aciPreKeys$1;->(Lorg/thoughtcrime/securesms/keyvalue/AccountValues;)V HSPLorg/thoughtcrime/securesms/keyvalue/AccountValues$aciPreKeys$1;->getNextKyberPreKeyId()I @@ -26295,7 +26183,6 @@ HSPLorg/thoughtcrime/securesms/keyvalue/AccountValues$pniPreKeys$1;->setLastSign HSPLorg/thoughtcrime/securesms/keyvalue/AccountValues$pniPreKeys$1;->setNextKyberPreKeyId(I)V HSPLorg/thoughtcrime/securesms/keyvalue/AccountValues$pniPreKeys$1;->setNextSignedPreKeyId(I)V HSPLorg/thoughtcrime/securesms/keyvalue/AccountValues$pniPreKeys$1;->setSignedPreKeyRegistered(Z)V -HSPLorg/thoughtcrime/securesms/keyvalue/AccountValues$usernameSyncState$2;->()V HSPLorg/thoughtcrime/securesms/keyvalue/AccountValues;->()V HSPLorg/thoughtcrime/securesms/keyvalue/AccountValues;->(Lorg/thoughtcrime/securesms/keyvalue/KeyValueStore;)V HSPLorg/thoughtcrime/securesms/keyvalue/AccountValues;->aciPreKeys()Lorg/thoughtcrime/securesms/crypto/storage/PreKeyMetadataStore; @@ -26304,7 +26191,6 @@ HSPLorg/thoughtcrime/securesms/keyvalue/AccountValues;->generatePniIdentityKeyIf HSPLorg/thoughtcrime/securesms/keyvalue/AccountValues;->getAci()Lorg/whispersystems/signalservice/api/push/ServiceId$ACI; HSPLorg/thoughtcrime/securesms/keyvalue/AccountValues;->getAciIdentityKey()Lorg/signal/libsignal/protocol/IdentityKeyPair; HSPLorg/thoughtcrime/securesms/keyvalue/AccountValues;->getDeviceId()I -HSPLorg/thoughtcrime/securesms/keyvalue/AccountValues;->getDeviceName()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/keyvalue/AccountValues;->getE164()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/keyvalue/AccountValues;->getPni()Lorg/whispersystems/signalservice/api/push/ServiceId$PNI; HSPLorg/thoughtcrime/securesms/keyvalue/AccountValues;->getPniIdentityKey()Lorg/signal/libsignal/protocol/IdentityKeyPair; @@ -26332,6 +26218,10 @@ HSPLorg/thoughtcrime/securesms/keyvalue/ApkUpdateValues$Companion;->()V HSPLorg/thoughtcrime/securesms/keyvalue/ApkUpdateValues$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/keyvalue/ApkUpdateValues;->()V HSPLorg/thoughtcrime/securesms/keyvalue/ApkUpdateValues;->(Lorg/thoughtcrime/securesms/keyvalue/KeyValueStore;)V +HSPLorg/thoughtcrime/securesms/keyvalue/BackupValues$Companion;->()V +HSPLorg/thoughtcrime/securesms/keyvalue/BackupValues$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/keyvalue/BackupValues;->()V +HSPLorg/thoughtcrime/securesms/keyvalue/BackupValues;->(Lorg/thoughtcrime/securesms/keyvalue/KeyValueStore;)V HSPLorg/thoughtcrime/securesms/keyvalue/BlobValue;->(Ljava/lang/String;[BLorg/thoughtcrime/securesms/keyvalue/KeyValueStore;)V HSPLorg/thoughtcrime/securesms/keyvalue/BooleanValue;->(Ljava/lang/String;ZLorg/thoughtcrime/securesms/keyvalue/KeyValueStore;)V HSPLorg/thoughtcrime/securesms/keyvalue/BooleanValue;->getValue$Signal_Android_playProdBenchmark(Lorg/thoughtcrime/securesms/keyvalue/KeyValueStore;)Ljava/lang/Boolean; @@ -26341,7 +26231,6 @@ HSPLorg/thoughtcrime/securesms/keyvalue/BooleanValue;->setValue$Signal_Android_p HSPLorg/thoughtcrime/securesms/keyvalue/CertificateType;->$values()[Lorg/thoughtcrime/securesms/keyvalue/CertificateType; HSPLorg/thoughtcrime/securesms/keyvalue/CertificateType;->()V HSPLorg/thoughtcrime/securesms/keyvalue/CertificateType;->(Ljava/lang/String;I)V -HSPLorg/thoughtcrime/securesms/keyvalue/CertificateType;->values()[Lorg/thoughtcrime/securesms/keyvalue/CertificateType; HSPLorg/thoughtcrime/securesms/keyvalue/CertificateValues;->(Lorg/thoughtcrime/securesms/keyvalue/KeyValueStore;)V HSPLorg/thoughtcrime/securesms/keyvalue/ChatColorsValues$Companion;->()V HSPLorg/thoughtcrime/securesms/keyvalue/ChatColorsValues$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -26371,7 +26260,6 @@ HSPLorg/thoughtcrime/securesms/keyvalue/DonationsValues;->setPending3DSData(Lorg HSPLorg/thoughtcrime/securesms/keyvalue/DonationsValues;->setVerifiedSubscription3DSData(Lorg/thoughtcrime/securesms/components/settings/app/subscription/donate/stripe/Stripe3DSData;)V HSPLorg/thoughtcrime/securesms/keyvalue/EmojiValues;->()V HSPLorg/thoughtcrime/securesms/keyvalue/EmojiValues;->(Lorg/thoughtcrime/securesms/keyvalue/KeyValueStore;)V -HSPLorg/thoughtcrime/securesms/keyvalue/EmojiValues;->getJumboEmojiSheets(I)Ljava/util/HashSet; HSPLorg/thoughtcrime/securesms/keyvalue/EmojiValues;->getLastSearchIndexCheck()J HSPLorg/thoughtcrime/securesms/keyvalue/EmojiValues;->getNextScheduledImageCheck()J HSPLorg/thoughtcrime/securesms/keyvalue/EmojiValues;->getSearchVersion()I @@ -26389,6 +26277,7 @@ HSPLorg/thoughtcrime/securesms/keyvalue/IntValue;->getValue$Signal_Android_playP HSPLorg/thoughtcrime/securesms/keyvalue/IntValue;->setValue$Signal_Android_playProdBenchmark(Lorg/thoughtcrime/securesms/keyvalue/KeyValueStore;I)V HSPLorg/thoughtcrime/securesms/keyvalue/IntValue;->setValue$Signal_Android_playProdBenchmark(Lorg/thoughtcrime/securesms/keyvalue/KeyValueStore;Ljava/lang/Object;)V HSPLorg/thoughtcrime/securesms/keyvalue/InternalValues;->(Lorg/thoughtcrime/securesms/keyvalue/KeyValueStore;)V +HSPLorg/thoughtcrime/securesms/keyvalue/InternalValues;->callingDisableLBRed()Z HSPLorg/thoughtcrime/securesms/keyvalue/InternalValues;->forceBuiltInEmoji()Z HSPLorg/thoughtcrime/securesms/keyvalue/InternalValues;->isWebsocketModeForced()Z HSPLorg/thoughtcrime/securesms/keyvalue/InternalValues;->shakeToReport()Z @@ -26415,7 +26304,6 @@ HSPLorg/thoughtcrime/securesms/keyvalue/KeyValueDataSet;->putLong(Ljava/lang/Str HSPLorg/thoughtcrime/securesms/keyvalue/KeyValueDataSet;->putString(Ljava/lang/String;Ljava/lang/String;)V HSPLorg/thoughtcrime/securesms/keyvalue/KeyValueDataSet;->readValueAsType(Ljava/lang/String;Ljava/lang/Class;Z)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/keyvalue/KeyValueDataSet;->removeAll(Ljava/util/Collection;)V -HSPLorg/thoughtcrime/securesms/keyvalue/KeyValueEnumValue;->(Ljava/lang/String;Ljava/lang/Object;Lorg/signal/core/util/LongSerializer;Lorg/thoughtcrime/securesms/keyvalue/KeyValueStore;)V HSPLorg/thoughtcrime/securesms/keyvalue/KeyValueProtoValue;->(Ljava/lang/String;Lcom/squareup/wire/ProtoAdapter;Lorg/thoughtcrime/securesms/keyvalue/KeyValueStore;)V HSPLorg/thoughtcrime/securesms/keyvalue/KeyValueStore$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/keyvalue/KeyValueStore;Lorg/thoughtcrime/securesms/keyvalue/KeyValueDataSet;Ljava/util/Collection;)V HSPLorg/thoughtcrime/securesms/keyvalue/KeyValueStore$$ExternalSyntheticLambda0;->run()V @@ -26491,12 +26379,10 @@ HSPLorg/thoughtcrime/securesms/keyvalue/PhoneNumberPrivacyValues$PhoneNumberList HSPLorg/thoughtcrime/securesms/keyvalue/PhoneNumberPrivacyValues$PhoneNumberListingMode;->()V HSPLorg/thoughtcrime/securesms/keyvalue/PhoneNumberPrivacyValues$PhoneNumberListingMode;->(Ljava/lang/String;II)V HSPLorg/thoughtcrime/securesms/keyvalue/PhoneNumberPrivacyValues$PhoneNumberListingMode;->deserialize(I)Lorg/thoughtcrime/securesms/keyvalue/PhoneNumberPrivacyValues$PhoneNumberListingMode; -HSPLorg/thoughtcrime/securesms/keyvalue/PhoneNumberPrivacyValues$PhoneNumberListingMode;->isDiscoverable()Z HSPLorg/thoughtcrime/securesms/keyvalue/PhoneNumberPrivacyValues$PhoneNumberListingMode;->serialize()I HSPLorg/thoughtcrime/securesms/keyvalue/PhoneNumberPrivacyValues$PhoneNumberListingMode;->values()[Lorg/thoughtcrime/securesms/keyvalue/PhoneNumberPrivacyValues$PhoneNumberListingMode; HSPLorg/thoughtcrime/securesms/keyvalue/PhoneNumberPrivacyValues;->()V HSPLorg/thoughtcrime/securesms/keyvalue/PhoneNumberPrivacyValues;->(Lorg/thoughtcrime/securesms/keyvalue/KeyValueStore;)V -HSPLorg/thoughtcrime/securesms/keyvalue/PhoneNumberPrivacyValues;->getAllCertificateTypes()Ljava/util/Collection; HSPLorg/thoughtcrime/securesms/keyvalue/PhoneNumberPrivacyValues;->getPhoneNumberListingMode()Lorg/thoughtcrime/securesms/keyvalue/PhoneNumberPrivacyValues$PhoneNumberListingMode; HSPLorg/thoughtcrime/securesms/keyvalue/PhoneNumberPrivacyValues;->getPhoneNumberListingModeTimestamp()J HSPLorg/thoughtcrime/securesms/keyvalue/PinValues;->()V @@ -26517,7 +26403,6 @@ HSPLorg/thoughtcrime/securesms/keyvalue/ReleaseChannelValues$Companion;->( HSPLorg/thoughtcrime/securesms/keyvalue/ReleaseChannelValues;->()V HSPLorg/thoughtcrime/securesms/keyvalue/ReleaseChannelValues;->(Lorg/thoughtcrime/securesms/keyvalue/KeyValueStore;)V HSPLorg/thoughtcrime/securesms/keyvalue/ReleaseChannelValues;->getReleaseChannelRecipientId()Lorg/thoughtcrime/securesms/recipients/RecipientId; -HSPLorg/thoughtcrime/securesms/keyvalue/ReleaseChannelValues;->setReleaseChannelRecipientId(Lorg/thoughtcrime/securesms/recipients/RecipientId;)V HSPLorg/thoughtcrime/securesms/keyvalue/RemoteConfigValues;->()V HSPLorg/thoughtcrime/securesms/keyvalue/RemoteConfigValues;->(Lorg/thoughtcrime/securesms/keyvalue/KeyValueStore;)V HSPLorg/thoughtcrime/securesms/keyvalue/RemoteConfigValues;->getCurrentConfig()Ljava/lang/String; @@ -26534,6 +26419,7 @@ HSPLorg/thoughtcrime/securesms/keyvalue/SettingsValues$Theme;->$values()[Lorg/th HSPLorg/thoughtcrime/securesms/keyvalue/SettingsValues$Theme;->()V HSPLorg/thoughtcrime/securesms/keyvalue/SettingsValues$Theme;->(Ljava/lang/String;ILjava/lang/String;)V HSPLorg/thoughtcrime/securesms/keyvalue/SettingsValues$Theme;->deserialize(Ljava/lang/String;)Lorg/thoughtcrime/securesms/keyvalue/SettingsValues$Theme; +HSPLorg/thoughtcrime/securesms/keyvalue/SettingsValues$Theme;->values()[Lorg/thoughtcrime/securesms/keyvalue/SettingsValues$Theme; HSPLorg/thoughtcrime/securesms/keyvalue/SettingsValues;->()V HSPLorg/thoughtcrime/securesms/keyvalue/SettingsValues;->(Lorg/thoughtcrime/securesms/keyvalue/KeyValueStore;)V HSPLorg/thoughtcrime/securesms/keyvalue/SettingsValues;->getCensorshipCircumventionEnabled()Lorg/thoughtcrime/securesms/keyvalue/SettingsValues$CensorshipCircumventionEnabled; @@ -26584,7 +26470,6 @@ HSPLorg/thoughtcrime/securesms/keyvalue/SignalStoreValueDelegate;->getValue(Ljav HSPLorg/thoughtcrime/securesms/keyvalue/SignalStoreValueDelegate;->setValue(Ljava/lang/Object;Lkotlin/reflect/KProperty;Ljava/lang/Object;)V HSPLorg/thoughtcrime/securesms/keyvalue/SignalStoreValueDelegatesKt;->blobValue(Lorg/thoughtcrime/securesms/keyvalue/SignalStoreValues;Ljava/lang/String;[B)Lorg/thoughtcrime/securesms/keyvalue/SignalStoreValueDelegate; HSPLorg/thoughtcrime/securesms/keyvalue/SignalStoreValueDelegatesKt;->booleanValue(Lorg/thoughtcrime/securesms/keyvalue/SignalStoreValues;Ljava/lang/String;Z)Lorg/thoughtcrime/securesms/keyvalue/SignalStoreValueDelegate; -HSPLorg/thoughtcrime/securesms/keyvalue/SignalStoreValueDelegatesKt;->enumValue(Lorg/thoughtcrime/securesms/keyvalue/SignalStoreValues;Ljava/lang/String;Ljava/lang/Object;Lorg/signal/core/util/LongSerializer;)Lorg/thoughtcrime/securesms/keyvalue/SignalStoreValueDelegate; HSPLorg/thoughtcrime/securesms/keyvalue/SignalStoreValueDelegatesKt;->integerValue(Lorg/thoughtcrime/securesms/keyvalue/SignalStoreValues;Ljava/lang/String;I)Lorg/thoughtcrime/securesms/keyvalue/SignalStoreValueDelegate; HSPLorg/thoughtcrime/securesms/keyvalue/SignalStoreValueDelegatesKt;->longValue(Lorg/thoughtcrime/securesms/keyvalue/SignalStoreValues;Ljava/lang/String;J)Lorg/thoughtcrime/securesms/keyvalue/SignalStoreValueDelegate; HSPLorg/thoughtcrime/securesms/keyvalue/SignalStoreValueDelegatesKt;->protoValue(Lorg/thoughtcrime/securesms/keyvalue/SignalStoreValues;Ljava/lang/String;Lcom/squareup/wire/ProtoAdapter;)Lorg/thoughtcrime/securesms/keyvalue/SignalStoreValueDelegate; @@ -26615,7 +26500,6 @@ HSPLorg/thoughtcrime/securesms/keyvalue/StoryValues;->getLastFontVersionCheck()J HSPLorg/thoughtcrime/securesms/keyvalue/StoryValues;->getLatestActiveStorySendTimestamps(J)Ljava/util/List; HSPLorg/thoughtcrime/securesms/keyvalue/StoryValues;->getUserHasViewedOnboardingStory()Z HSPLorg/thoughtcrime/securesms/keyvalue/StoryValues;->isFeatureDisabled()Z -HSPLorg/thoughtcrime/securesms/keyvalue/StoryValues;->setHasDownloadedOnboardingStory(Z)V HSPLorg/thoughtcrime/securesms/keyvalue/StoryValues;->setLastFontVersionCheck(J)V HSPLorg/thoughtcrime/securesms/keyvalue/StringValue;->(Ljava/lang/String;Ljava/lang/String;Lorg/thoughtcrime/securesms/keyvalue/KeyValueStore;)V HSPLorg/thoughtcrime/securesms/keyvalue/StringValue;->getValue$Signal_Android_playProdBenchmark(Lorg/thoughtcrime/securesms/keyvalue/KeyValueStore;)Ljava/lang/Object; @@ -26623,11 +26507,9 @@ HSPLorg/thoughtcrime/securesms/keyvalue/StringValue;->getValue$Signal_Android_pl HSPLorg/thoughtcrime/securesms/keyvalue/SvrValues;->(Lorg/thoughtcrime/securesms/keyvalue/KeyValueStore;)V HSPLorg/thoughtcrime/securesms/keyvalue/SvrValues;->clearRegistrationLockAndPin()V HSPLorg/thoughtcrime/securesms/keyvalue/SvrValues;->getLocalPinHash()Ljava/lang/String; -HSPLorg/thoughtcrime/securesms/keyvalue/SvrValues;->getMasterKey()Lorg/whispersystems/signalservice/api/kbs/MasterKey; -HSPLorg/thoughtcrime/securesms/keyvalue/SvrValues;->getRecoveryPassword()Ljava/lang/String; +HSPLorg/thoughtcrime/securesms/keyvalue/SvrValues;->getOrCreateMasterKey()Lorg/whispersystems/signalservice/api/kbs/MasterKey; HSPLorg/thoughtcrime/securesms/keyvalue/SvrValues;->hasOptedOut()Z HSPLorg/thoughtcrime/securesms/keyvalue/SvrValues;->hasPin()Z -HSPLorg/thoughtcrime/securesms/keyvalue/SvrValues;->isRegistrationLockEnabled()Z HSPLorg/thoughtcrime/securesms/keyvalue/SvrValues;->lastPinCreateFailed()Z HSPLorg/thoughtcrime/securesms/keyvalue/SvrValues;->optOut()V HSPLorg/thoughtcrime/securesms/keyvalue/TooltipValues;->(Lorg/thoughtcrime/securesms/keyvalue/KeyValueStore;)V @@ -26649,6 +26531,7 @@ HSPLorg/thoughtcrime/securesms/linkpreview/LinkPreviewState;->()V HSPLorg/thoughtcrime/securesms/linkpreview/LinkPreviewState;->(Ljava/lang/String;ZZLorg/thoughtcrime/securesms/linkpreview/LinkPreview;Lorg/thoughtcrime/securesms/linkpreview/LinkPreviewRepository$Error;)V HSPLorg/thoughtcrime/securesms/linkpreview/LinkPreviewState;->(Ljava/lang/String;ZZLorg/thoughtcrime/securesms/linkpreview/LinkPreview;Lorg/thoughtcrime/securesms/linkpreview/LinkPreviewRepository$Error;Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/linkpreview/LinkPreviewState;->hasContent()Z +HSPLorg/thoughtcrime/securesms/linkpreview/LinkPreviewState;->hasLinks()Z HSPLorg/thoughtcrime/securesms/linkpreview/LinkPreviewViewModelV2$Companion;->()V HSPLorg/thoughtcrime/securesms/linkpreview/LinkPreviewViewModelV2$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/linkpreview/LinkPreviewViewModelV2$savedLinkPreviewState$2;->()V @@ -26656,17 +26539,20 @@ HSPLorg/thoughtcrime/securesms/linkpreview/LinkPreviewViewModelV2$savedLinkPrevi HSPLorg/thoughtcrime/securesms/linkpreview/LinkPreviewViewModelV2$savedLinkPreviewState$2;->invoke()Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/linkpreview/LinkPreviewViewModelV2$savedLinkPreviewState$2;->invoke()Lorg/thoughtcrime/securesms/linkpreview/LinkPreviewState; HSPLorg/thoughtcrime/securesms/linkpreview/LinkPreviewViewModelV2$savedStateDisposable$1;->(Lorg/thoughtcrime/securesms/linkpreview/LinkPreviewViewModelV2;)V +HSPLorg/thoughtcrime/securesms/linkpreview/LinkPreviewViewModelV2$savedStateDisposable$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/linkpreview/LinkPreviewViewModelV2$savedStateDisposable$1;->invoke(Lorg/thoughtcrime/securesms/linkpreview/LinkPreviewState;)V HSPLorg/thoughtcrime/securesms/linkpreview/LinkPreviewViewModelV2;->()V HSPLorg/thoughtcrime/securesms/linkpreview/LinkPreviewViewModelV2;->(Landroidx/lifecycle/SavedStateHandle;Lorg/thoughtcrime/securesms/linkpreview/LinkPreviewRepository;Z)V HSPLorg/thoughtcrime/securesms/linkpreview/LinkPreviewViewModelV2;->(Landroidx/lifecycle/SavedStateHandle;Lorg/thoughtcrime/securesms/linkpreview/LinkPreviewRepository;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/linkpreview/LinkPreviewViewModelV2;->access$setSavedLinkPreviewState(Lorg/thoughtcrime/securesms/linkpreview/LinkPreviewViewModelV2;Lorg/thoughtcrime/securesms/linkpreview/LinkPreviewState;)V HSPLorg/thoughtcrime/securesms/linkpreview/LinkPreviewViewModelV2;->getLinkPreviewState()Lio/reactivex/rxjava3/core/Flowable; HSPLorg/thoughtcrime/securesms/linkpreview/LinkPreviewViewModelV2;->getSavedLinkPreviewState()Lorg/thoughtcrime/securesms/linkpreview/LinkPreviewState; +HSPLorg/thoughtcrime/securesms/linkpreview/LinkPreviewViewModelV2;->setSavedLinkPreviewState(Lorg/thoughtcrime/securesms/linkpreview/LinkPreviewState;)V HSPLorg/thoughtcrime/securesms/logging/CustomSignalProtocolLogger;->()V HSPLorg/thoughtcrime/securesms/logging/CustomSignalProtocolLogger;->log(ILjava/lang/String;Ljava/lang/String;)V HSPLorg/thoughtcrime/securesms/logging/PersistentLogger$Companion;->()V HSPLorg/thoughtcrime/securesms/logging/PersistentLogger$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/logging/PersistentLogger$LogRequest;->(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JLjava/lang/String;Ljava/lang/Throwable;Z)V -HSPLorg/thoughtcrime/securesms/logging/PersistentLogger$LogRequest;->getCreateTime()J HSPLorg/thoughtcrime/securesms/logging/PersistentLogger$LogRequest;->getKeepLonger()Z HSPLorg/thoughtcrime/securesms/logging/PersistentLogger$LogRequest;->getLevel()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/logging/PersistentLogger$LogRequest;->getMessage()Ljava/lang/String; @@ -26690,6 +26576,7 @@ HSPLorg/thoughtcrime/securesms/logging/PersistentLogger;->getThreadString()Ljava HSPLorg/thoughtcrime/securesms/logging/PersistentLogger;->i(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;Z)V HSPLorg/thoughtcrime/securesms/logging/PersistentLogger;->w(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;Z)V HSPLorg/thoughtcrime/securesms/logging/PersistentLogger;->write(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;Z)V +HSPLorg/thoughtcrime/securesms/logsubmit/LogSectionNotifications$$ExternalSyntheticApiModelOutline2;->m(Landroid/app/NotificationChannel;)Z HSPLorg/thoughtcrime/securesms/main/MainActivityListHostFragment$$ExternalSyntheticLambda0;->()V HSPLorg/thoughtcrime/securesms/main/MainActivityListHostFragment$$ExternalSyntheticLambda0;->run()Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/main/MainActivityListHostFragment$$ExternalSyntheticLambda1;->(Lorg/thoughtcrime/securesms/main/MainActivityListHostFragment;)V @@ -26741,10 +26628,6 @@ HSPLorg/thoughtcrime/securesms/main/MainActivityListHostFragment;->presentToolba HSPLorg/thoughtcrime/securesms/main/MainActivityListHostFragment;->presentToolbarForDestination(Landroidx/navigation/NavDestination;)V HSPLorg/thoughtcrime/securesms/main/MainActivityListHostFragment;->updateNotificationProfileStatus(Ljava/util/List;)V HSPLorg/thoughtcrime/securesms/main/MainActivityListHostFragment;->updateProxyStatus(Lorg/whispersystems/signalservice/api/websocket/WebSocketConnectionState;)V -HSPLorg/thoughtcrime/securesms/mediasend/Media$1;->()V -HSPLorg/thoughtcrime/securesms/mediasend/Media;->()V -HSPLorg/thoughtcrime/securesms/mediasend/Media;->(Landroid/net/Uri;Ljava/lang/String;JIIJJZZLj$/util/Optional;Lj$/util/Optional;Lj$/util/Optional;)V -HSPLorg/thoughtcrime/securesms/mediasend/Media;->getUri()Landroid/net/Uri; HSPLorg/thoughtcrime/securesms/megaphone/ForeverSchedule;->(Z)V HSPLorg/thoughtcrime/securesms/megaphone/ForeverSchedule;->shouldDisplay(IJJJ)Z HSPLorg/thoughtcrime/securesms/megaphone/Megaphone$Builder;->-$$Nest$fgetbodyText(Lorg/thoughtcrime/securesms/megaphone/Megaphone$Builder;)Lorg/thoughtcrime/securesms/megaphone/MegaphoneText; @@ -26811,24 +26694,24 @@ HSPLorg/thoughtcrime/securesms/megaphone/MegaphoneRepository;->resetDatabaseCach HSPLorg/thoughtcrime/securesms/megaphone/MegaphoneViewBuilder$1;->()V HSPLorg/thoughtcrime/securesms/megaphone/MegaphoneViewBuilder;->build(Landroid/content/Context;Lorg/thoughtcrime/securesms/megaphone/Megaphone;Lorg/thoughtcrime/securesms/megaphone/MegaphoneActionController;)Landroid/view/View; HSPLorg/thoughtcrime/securesms/megaphone/MegaphoneViewBuilder;->buildOnboardingMegaphone(Landroid/content/Context;Lorg/thoughtcrime/securesms/megaphone/Megaphone;Lorg/thoughtcrime/securesms/megaphone/MegaphoneActionController;)Landroid/view/View; -HSPLorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda22;->(Ljava/util/Map;J)V -HSPLorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda22;->test(Ljava/lang/Object;)Z -HSPLorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda23;->()V -HSPLorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda23;->apply(Ljava/lang/Object;)Ljava/lang/Object; -HSPLorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda24;->(Ljava/util/Map;)V -HSPLorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda24;->apply(Ljava/lang/Object;)Ljava/lang/Object; -HSPLorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda25;->(Landroid/content/Context;)V -HSPLorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda25;->apply(Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda18;->(Ljava/util/Map;J)V +HSPLorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda18;->test(Ljava/lang/Object;)Z +HSPLorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda19;->()V +HSPLorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda19;->apply(Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda20;->(Ljava/util/Map;)V +HSPLorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda20;->apply(Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda21;->(Landroid/content/Context;)V +HSPLorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda21;->apply(Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda2;->()V +HSPLorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda2;->test(Ljava/lang/Object;)Z +HSPLorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda3;->(Lorg/thoughtcrime/securesms/megaphone/Megaphones$Event;)V +HSPLorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda3;->test(Ljava/lang/Object;)Z HSPLorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda4;->()V -HSPLorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda4;->test(Ljava/lang/Object;)Z -HSPLorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda5;->(Lorg/thoughtcrime/securesms/megaphone/Megaphones$Event;)V +HSPLorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda4;->apply(Ljava/lang/Object;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda5;->()V HSPLorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda5;->test(Ljava/lang/Object;)Z HSPLorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda6;->()V HSPLorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda6;->apply(Ljava/lang/Object;)Ljava/lang/Object; -HSPLorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda7;->()V -HSPLorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda7;->test(Ljava/lang/Object;)Z -HSPLorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda8;->()V -HSPLorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda8;->apply(Ljava/lang/Object;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/megaphone/Megaphones$1;->(Landroid/content/Context;Ljava/util/Map;)V HSPLorg/thoughtcrime/securesms/megaphone/Megaphones$3;->()V HSPLorg/thoughtcrime/securesms/megaphone/Megaphones$Event;->$values()[Lorg/thoughtcrime/securesms/megaphone/Megaphones$Event; @@ -26838,17 +26721,16 @@ HSPLorg/thoughtcrime/securesms/megaphone/Megaphones$Event;->fromKey(Ljava/lang/S HSPLorg/thoughtcrime/securesms/megaphone/Megaphones$Event;->getKey()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/megaphone/Megaphones$Event;->hasKey(Ljava/lang/String;)Z HSPLorg/thoughtcrime/securesms/megaphone/Megaphones$Event;->values()[Lorg/thoughtcrime/securesms/megaphone/Megaphones$Event; -HSPLorg/thoughtcrime/securesms/megaphone/Megaphones;->$r8$lambda$7sPLFJ0mZQgg6EeUEtMht2tdXm4(Ljava/util/Map$Entry;)Ljava/lang/Long; -HSPLorg/thoughtcrime/securesms/megaphone/Megaphones;->$r8$lambda$B1XlDhQ6tosxLxayssTsu8VtR0s(Lorg/thoughtcrime/securesms/megaphone/Megaphones$Event;Ljava/util/Map$Entry;)Z -HSPLorg/thoughtcrime/securesms/megaphone/Megaphones;->$r8$lambda$UAYlPLzeF7x02TJyju0yAFJVJ0A(Ljava/util/Map$Entry;)Z -HSPLorg/thoughtcrime/securesms/megaphone/Megaphones;->$r8$lambda$VSBT6WXoSfleduPmM3q6MB3oK3E(Ljava/lang/Long;)Z +HSPLorg/thoughtcrime/securesms/megaphone/Megaphones;->$r8$lambda$57Iylt9yWAB65XCua6kCFNHv3Ho(Ljava/util/Map$Entry;)Ljava/lang/Long; +HSPLorg/thoughtcrime/securesms/megaphone/Megaphones;->$r8$lambda$Jd3YY1Tctc615OoR-BunWBsX5AM(Ljava/lang/Long;)Z +HSPLorg/thoughtcrime/securesms/megaphone/Megaphones;->$r8$lambda$StkmuqSPG82Z8-YWKFWZC31YUUs(Ljava/util/Map$Entry;)Z +HSPLorg/thoughtcrime/securesms/megaphone/Megaphones;->$r8$lambda$bfAOsFTMV105nBLDPhyo5-_RYW0(Lorg/thoughtcrime/securesms/megaphone/Megaphones$Event;Ljava/util/Map$Entry;)Z HSPLorg/thoughtcrime/securesms/megaphone/Megaphones;->$r8$lambda$jKOYs4kAlzGKOSd0Gb2wil_axMM(Ljava/util/Map;JLjava/util/Map$Entry;)Z HSPLorg/thoughtcrime/securesms/megaphone/Megaphones;->$r8$lambda$ya9b6y2SFOc06_b4NHtqa2lYZ44(Landroid/content/Context;Lorg/thoughtcrime/securesms/database/model/MegaphoneRecord;)Lorg/thoughtcrime/securesms/megaphone/Megaphone; HSPLorg/thoughtcrime/securesms/megaphone/Megaphones;->-$$Nest$sfgetALWAYS()Lorg/thoughtcrime/securesms/megaphone/MegaphoneSchedule; HSPLorg/thoughtcrime/securesms/megaphone/Megaphones;->-$$Nest$sfgetNEVER()Lorg/thoughtcrime/securesms/megaphone/MegaphoneSchedule; HSPLorg/thoughtcrime/securesms/megaphone/Megaphones;->-$$Nest$smshouldShowAddAProfilePhotoMegaphone(Landroid/content/Context;)Z HSPLorg/thoughtcrime/securesms/megaphone/Megaphones;->-$$Nest$smshouldShowBackupSchedulePermissionMegaphone(Landroid/content/Context;)Z -HSPLorg/thoughtcrime/securesms/megaphone/Megaphones;->-$$Nest$smshouldShowDonateMegaphone(Landroid/content/Context;Lorg/thoughtcrime/securesms/megaphone/Megaphones$Event;Ljava/util/Map;)Z HSPLorg/thoughtcrime/securesms/megaphone/Megaphones;->-$$Nest$smshouldShowGrantFullScreenIntentPermission(Landroid/content/Context;)Z HSPLorg/thoughtcrime/securesms/megaphone/Megaphones;->-$$Nest$smshouldShowNotificationsMegaphone(Landroid/content/Context;)Z HSPLorg/thoughtcrime/securesms/megaphone/Megaphones;->-$$Nest$smshouldShowOnboardingMegaphone(Landroid/content/Context;)Z @@ -26862,13 +26744,12 @@ HSPLorg/thoughtcrime/securesms/megaphone/Megaphones;->forRecord(Landroid/content HSPLorg/thoughtcrime/securesms/megaphone/Megaphones;->getNextMegaphone(Landroid/content/Context;Ljava/util/Map;)Lorg/thoughtcrime/securesms/megaphone/Megaphone; HSPLorg/thoughtcrime/securesms/megaphone/Megaphones;->lambda$getNextMegaphone$0(Ljava/util/Map;JLjava/util/Map$Entry;)Z HSPLorg/thoughtcrime/securesms/megaphone/Megaphones;->lambda$getNextMegaphone$1(Landroid/content/Context;Lorg/thoughtcrime/securesms/database/model/MegaphoneRecord;)Lorg/thoughtcrime/securesms/megaphone/Megaphone; -HSPLorg/thoughtcrime/securesms/megaphone/Megaphones;->lambda$timeSinceLastDonatePrompt$27(Ljava/util/Map$Entry;)Z -HSPLorg/thoughtcrime/securesms/megaphone/Megaphones;->lambda$timeSinceLastDonatePrompt$28(Lorg/thoughtcrime/securesms/megaphone/Megaphones$Event;Ljava/util/Map$Entry;)Z -HSPLorg/thoughtcrime/securesms/megaphone/Megaphones;->lambda$timeSinceLastDonatePrompt$29(Ljava/util/Map$Entry;)Ljava/lang/Long; -HSPLorg/thoughtcrime/securesms/megaphone/Megaphones;->lambda$timeSinceLastDonatePrompt$30(Ljava/lang/Long;)Z +HSPLorg/thoughtcrime/securesms/megaphone/Megaphones;->lambda$timeSinceLastDonatePrompt$22(Ljava/util/Map$Entry;)Z +HSPLorg/thoughtcrime/securesms/megaphone/Megaphones;->lambda$timeSinceLastDonatePrompt$23(Lorg/thoughtcrime/securesms/megaphone/Megaphones$Event;Ljava/util/Map$Entry;)Z +HSPLorg/thoughtcrime/securesms/megaphone/Megaphones;->lambda$timeSinceLastDonatePrompt$24(Ljava/util/Map$Entry;)Ljava/lang/Long; +HSPLorg/thoughtcrime/securesms/megaphone/Megaphones;->lambda$timeSinceLastDonatePrompt$25(Ljava/lang/Long;)Z HSPLorg/thoughtcrime/securesms/megaphone/Megaphones;->shouldShowAddAProfilePhotoMegaphone(Landroid/content/Context;)Z HSPLorg/thoughtcrime/securesms/megaphone/Megaphones;->shouldShowBackupSchedulePermissionMegaphone(Landroid/content/Context;)Z -HSPLorg/thoughtcrime/securesms/megaphone/Megaphones;->shouldShowDonateMegaphone(Landroid/content/Context;Lorg/thoughtcrime/securesms/megaphone/Megaphones$Event;Ljava/util/Map;)Z HSPLorg/thoughtcrime/securesms/megaphone/Megaphones;->shouldShowGrantFullScreenIntentPermission(Landroid/content/Context;)Z HSPLorg/thoughtcrime/securesms/megaphone/Megaphones;->shouldShowNotificationsMegaphone(Landroid/content/Context;)Z HSPLorg/thoughtcrime/securesms/megaphone/Megaphones;->shouldShowOnboardingMegaphone(Landroid/content/Context;)Z @@ -26912,6 +26793,8 @@ HSPLorg/thoughtcrime/securesms/megaphone/PinsForAllSchedule;->shouldDisplay(IJJJ HSPLorg/thoughtcrime/securesms/megaphone/RecurringSchedule;->([J)V HSPLorg/thoughtcrime/securesms/megaphone/RemoteMegaphoneRepository$donate$1;->()V HSPLorg/thoughtcrime/securesms/megaphone/RemoteMegaphoneRepository$donate$1;->()V +HSPLorg/thoughtcrime/securesms/megaphone/RemoteMegaphoneRepository$donateForFriend$1;->()V +HSPLorg/thoughtcrime/securesms/megaphone/RemoteMegaphoneRepository$donateForFriend$1;->()V HSPLorg/thoughtcrime/securesms/megaphone/RemoteMegaphoneRepository$finish$1;->()V HSPLorg/thoughtcrime/securesms/megaphone/RemoteMegaphoneRepository$finish$1;->()V HSPLorg/thoughtcrime/securesms/megaphone/RemoteMegaphoneRepository$getRemoteMegaphoneToShow$1;->()V @@ -26995,7 +26878,6 @@ HSPLorg/thoughtcrime/securesms/messages/IncomingMessageObserver;->access$setDecr HSPLorg/thoughtcrime/securesms/messages/IncomingMessageObserver;->access$waitForConnectionNecessary(Lorg/thoughtcrime/securesms/messages/IncomingMessageObserver;)V HSPLorg/thoughtcrime/securesms/messages/IncomingMessageObserver;->addDecryptionDrainedListener(Ljava/lang/Runnable;)V HSPLorg/thoughtcrime/securesms/messages/IncomingMessageObserver;->disconnect()V -HSPLorg/thoughtcrime/securesms/messages/IncomingMessageObserver;->getDecryptionDrained()Z HSPLorg/thoughtcrime/securesms/messages/IncomingMessageObserver;->isConnectionNecessary()Z HSPLorg/thoughtcrime/securesms/messages/IncomingMessageObserver;->notifyRegistrationChanged()V HSPLorg/thoughtcrime/securesms/messages/IncomingMessageObserver;->onAppForegrounded()V @@ -27071,15 +26953,17 @@ HSPLorg/thoughtcrime/securesms/migrations/RebuildMessageSearchIndexMigrationJob$ HSPLorg/thoughtcrime/securesms/migrations/RecheckPaymentsMigrationJob$Factory;->()V HSPLorg/thoughtcrime/securesms/migrations/RecheckPaymentsMigrationJob$Factory;->()V HSPLorg/thoughtcrime/securesms/migrations/RecipientSearchMigrationJob$Factory;->()V +HSPLorg/thoughtcrime/securesms/migrations/SelfRegisteredStateMigrationJob$Factory;->()V +HSPLorg/thoughtcrime/securesms/migrations/SelfRegisteredStateMigrationJob$Factory;->()V HSPLorg/thoughtcrime/securesms/migrations/StickerAdditionMigrationJob$Factory;->()V HSPLorg/thoughtcrime/securesms/migrations/StickerDayByDayMigrationJob$Factory;->()V HSPLorg/thoughtcrime/securesms/migrations/StickerLaunchMigrationJob$Factory;->()V HSPLorg/thoughtcrime/securesms/migrations/StickerMyDailyLifeMigrationJob$Factory;->()V HSPLorg/thoughtcrime/securesms/migrations/StorageCapabilityMigrationJob$Factory;->()V +HSPLorg/thoughtcrime/securesms/migrations/StorageFixLocalUnknownMigrationJob$Factory;->()V +HSPLorg/thoughtcrime/securesms/migrations/StorageFixLocalUnknownMigrationJob$Factory;->()V HSPLorg/thoughtcrime/securesms/migrations/StorageServiceMigrationJob$Factory;->()V HSPLorg/thoughtcrime/securesms/migrations/StorageServiceSystemNameMigrationJob$Factory;->()V -HSPLorg/thoughtcrime/securesms/migrations/StoryReadStateMigrationJob$Factory;->()V -HSPLorg/thoughtcrime/securesms/migrations/StoryReadStateMigrationJob$Factory;->()V HSPLorg/thoughtcrime/securesms/migrations/StoryViewedReceiptsStateMigrationJob$Factory;->()V HSPLorg/thoughtcrime/securesms/migrations/StoryViewedReceiptsStateMigrationJob$Factory;->()V HSPLorg/thoughtcrime/securesms/migrations/Svr2MirrorMigrationJob$Factory;->()V @@ -27094,6 +26978,7 @@ HSPLorg/thoughtcrime/securesms/migrations/UserNotificationMigrationJob$Factory;- HSPLorg/thoughtcrime/securesms/migrations/UuidMigrationJob$Factory;->()V HSPLorg/thoughtcrime/securesms/mms/AttachmentManager;->()V HSPLorg/thoughtcrime/securesms/mms/AttachmentManager;->(Landroid/content/Context;Landroid/view/View;Lorg/thoughtcrime/securesms/mms/AttachmentManager$AttachmentListener;)V +HSPLorg/thoughtcrime/securesms/mms/AttachmentManager;->isAttachmentPresent()Z HSPLorg/thoughtcrime/securesms/mms/AttachmentStreamUriLoader$Factory;->()V HSPLorg/thoughtcrime/securesms/mms/DecryptableStreamUriLoader$Factory;->(Landroid/content/Context;)V HSPLorg/thoughtcrime/securesms/mms/GlideApp;->get(Landroid/content/Context;)Lcom/bumptech/glide/Glide; @@ -27155,7 +27040,6 @@ HSPLorg/thoughtcrime/securesms/mms/IncomingMessage;->isGroupMessage()Z HSPLorg/thoughtcrime/securesms/mms/IncomingMessage;->isUnidentified()Z HSPLorg/thoughtcrime/securesms/mms/IncomingMessage;->isViewOnce()Z HSPLorg/thoughtcrime/securesms/mms/PartAuthority;->()V -HSPLorg/thoughtcrime/securesms/mms/PartAuthority;->getEmojiFilename(Landroid/net/Uri;)Ljava/lang/String; HSPLorg/thoughtcrime/securesms/mms/PartAuthority;->getEmojiUri(Ljava/lang/String;)Landroid/net/Uri; HSPLorg/thoughtcrime/securesms/mms/SentMediaQuality;->$values()[Lorg/thoughtcrime/securesms/mms/SentMediaQuality; HSPLorg/thoughtcrime/securesms/mms/SentMediaQuality;->()V @@ -27187,6 +27071,8 @@ HSPLorg/thoughtcrime/securesms/mms/Slide;->hasDocument()Z HSPLorg/thoughtcrime/securesms/mms/Slide;->hasSticker()Z HSPLorg/thoughtcrime/securesms/mms/Slide;->hasVideo()Z HSPLorg/thoughtcrime/securesms/mms/Slide;->hashCode()I +HSPLorg/thoughtcrime/securesms/mms/Slide;->isInProgress()Z +HSPLorg/thoughtcrime/securesms/mms/Slide;->isPendingDownload()Z HSPLorg/thoughtcrime/securesms/mms/Slide;->isVideoGif()Z HSPLorg/thoughtcrime/securesms/mms/SlideDeck$$ExternalSyntheticLambda0;->()V HSPLorg/thoughtcrime/securesms/mms/SlideDeck$$ExternalSyntheticLambda0;->test(Ljava/lang/Object;)Z @@ -27266,12 +27152,6 @@ HSPLorg/thoughtcrime/securesms/notifications/Configuration;->getMinimumMessageLa HSPLorg/thoughtcrime/securesms/notifications/Configuration;->getMinimumServiceEventCount()I HSPLorg/thoughtcrime/securesms/notifications/Configuration;->getServiceStartFailurePercentage()F HSPLorg/thoughtcrime/securesms/notifications/Configuration;->getWeeklyFailedQueueDrains()I -HSPLorg/thoughtcrime/securesms/notifications/MarkReadReceiver;->()V -HSPLorg/thoughtcrime/securesms/notifications/MarkReadReceiver;->process(Ljava/util/List;)V -HSPLorg/thoughtcrime/securesms/notifications/NotificationCancellationHelper$$ExternalSyntheticApiModelOutline0;->m(Landroid/app/NotificationManager;)[Landroid/service/notification/StatusBarNotification; -HSPLorg/thoughtcrime/securesms/notifications/NotificationCancellationHelper;->()V -HSPLorg/thoughtcrime/securesms/notifications/NotificationCancellationHelper;->cancelAllMessageNotifications(Landroid/content/Context;Ljava/util/Set;)V -HSPLorg/thoughtcrime/securesms/notifications/NotificationCancellationHelper;->cancelLegacy(Landroid/content/Context;I)V HSPLorg/thoughtcrime/securesms/notifications/NotificationChannels$$ExternalSyntheticApiModelOutline0;->m(Landroid/app/NotificationManager;Ljava/lang/String;)Landroid/app/NotificationChannelGroup; HSPLorg/thoughtcrime/securesms/notifications/NotificationChannels$$ExternalSyntheticApiModelOutline1;->m(Landroid/app/NotificationChannelGroup;)Z HSPLorg/thoughtcrime/securesms/notifications/NotificationChannels$$ExternalSyntheticApiModelOutline2;->m(Landroid/app/NotificationChannel;)Ljava/lang/String; @@ -27299,23 +27179,9 @@ HSPLorg/thoughtcrime/securesms/notifications/NotificationChannels;->onUpgrade(La HSPLorg/thoughtcrime/securesms/notifications/NotificationChannels;->setLedPreference(Landroid/app/NotificationChannel;Ljava/lang/String;)V HSPLorg/thoughtcrime/securesms/notifications/NotificationChannels;->setVibrationEnabled(Landroid/app/NotificationChannel;Z)V HSPLorg/thoughtcrime/securesms/notifications/NotificationChannels;->supported()Z -HSPLorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier$$ExternalSyntheticLambda17;->(Ljava/lang/Runnable;Ljava/lang/Throwable;)V -HSPLorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier$$ExternalSyntheticLambda17;->run()V -HSPLorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier$$ExternalSyntheticLambda3;->(Lorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier;Landroid/content/Context;)V -HSPLorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier$$ExternalSyntheticLambda3;->run()V -HSPLorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier$$ExternalSyntheticLambda9;->(Lorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier;Landroid/content/Context;)V -HSPLorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier$$ExternalSyntheticLambda9;->run()V -HSPLorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier;->$r8$lambda$98B6rel4XDu4lm2pCI-DVofE9WY(Lorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier;Landroid/content/Context;)V -HSPLorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier;->$r8$lambda$ImmKEyV9mnT3aXASBPTNtbwseFY(Ljava/lang/Runnable;Ljava/lang/Throwable;)V -HSPLorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier;->$r8$lambda$mDf5wVelzwBtc0Vgo342P1gCo0c(Lorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier;Landroid/content/Context;)V HSPLorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier;->(Landroid/app/Application;)V HSPLorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier;->getNotifier()Lorg/thoughtcrime/securesms/notifications/MessageNotifier; -HSPLorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier;->lambda$runOnLimiter$17(Ljava/lang/Runnable;Ljava/lang/Throwable;)V -HSPLorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier;->lambda$updateNotification$4(Landroid/content/Context;)V -HSPLorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier;->lambda$updateNotification$5(Landroid/content/Context;)V -HSPLorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier;->runOnLimiter(Ljava/lang/Runnable;)V HSPLorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier;->setVisibleThread(Lorg/thoughtcrime/securesms/notifications/v2/ConversationId;)V -HSPLorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier;->updateNotification(Landroid/content/Context;)V HSPLorg/thoughtcrime/securesms/notifications/SlowNotificationHeuristics;->()V HSPLorg/thoughtcrime/securesms/notifications/SlowNotificationHeuristics;->()V HSPLorg/thoughtcrime/securesms/notifications/SlowNotificationHeuristics;->getConfiguration()Lorg/thoughtcrime/securesms/notifications/Configuration; @@ -27353,25 +27219,12 @@ HSPLorg/thoughtcrime/securesms/notifications/v2/ConversationId$Companion;->forCo HSPLorg/thoughtcrime/securesms/notifications/v2/ConversationId$Creator;->()V HSPLorg/thoughtcrime/securesms/notifications/v2/ConversationId;->()V HSPLorg/thoughtcrime/securesms/notifications/v2/ConversationId;->(JLjava/lang/Long;)V -HSPLorg/thoughtcrime/securesms/notifications/v2/ConversationId;->getGroupStoryId()Ljava/lang/Long; -HSPLorg/thoughtcrime/securesms/notifications/v2/ConversationId;->getThreadId()J HSPLorg/thoughtcrime/securesms/notifications/v2/ConversationId;->hashCode()I HSPLorg/thoughtcrime/securesms/notifications/v2/DefaultMessageNotifier$Companion;->()V HSPLorg/thoughtcrime/securesms/notifications/v2/DefaultMessageNotifier$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V -HSPLorg/thoughtcrime/securesms/notifications/v2/DefaultMessageNotifier$Companion;->access$updateBadge(Lorg/thoughtcrime/securesms/notifications/v2/DefaultMessageNotifier$Companion;Landroid/content/Context;I)V -HSPLorg/thoughtcrime/securesms/notifications/v2/DefaultMessageNotifier$Companion;->updateBadge(Landroid/content/Context;I)V -HSPLorg/thoughtcrime/securesms/notifications/v2/DefaultMessageNotifier$updateNotification$7;->(Ljava/util/Set;)V HSPLorg/thoughtcrime/securesms/notifications/v2/DefaultMessageNotifier;->()V HSPLorg/thoughtcrime/securesms/notifications/v2/DefaultMessageNotifier;->(Landroid/app/Application;)V -HSPLorg/thoughtcrime/securesms/notifications/v2/DefaultMessageNotifier;->clearReminderInternal(Landroid/content/Context;)V HSPLorg/thoughtcrime/securesms/notifications/v2/DefaultMessageNotifier;->setVisibleThread(Lorg/thoughtcrime/securesms/notifications/v2/ConversationId;)V -HSPLorg/thoughtcrime/securesms/notifications/v2/DefaultMessageNotifier;->updateNotification(Landroid/content/Context;)V -HSPLorg/thoughtcrime/securesms/notifications/v2/DefaultMessageNotifier;->updateNotification(Landroid/content/Context;Lorg/thoughtcrime/securesms/notifications/v2/ConversationId;ZILorg/thoughtcrime/securesms/util/BubbleUtil$BubbleState;)V -HSPLorg/thoughtcrime/securesms/notifications/v2/DefaultMessageNotifierKt;->access$getDisplayedNotificationIds(Landroid/app/NotificationManager;)Ljava/lang/Object; -HSPLorg/thoughtcrime/securesms/notifications/v2/DefaultMessageNotifierKt;->getDisplayedNotificationIds(Landroid/app/NotificationManager;)Ljava/lang/Object; -HSPLorg/thoughtcrime/securesms/notifications/v2/NotificationPendingIntentHelper;->()V -HSPLorg/thoughtcrime/securesms/notifications/v2/NotificationPendingIntentHelper;->()V -HSPLorg/thoughtcrime/securesms/notifications/v2/NotificationPendingIntentHelper;->getBroadcast(Landroid/content/Context;ILandroid/content/Intent;I)Landroid/app/PendingIntent; HSPLorg/thoughtcrime/securesms/notifications/v2/NotificationState$Companion;->()V HSPLorg/thoughtcrime/securesms/notifications/v2/NotificationState$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/notifications/v2/NotificationState$Companion;->getEMPTY()Lorg/thoughtcrime/securesms/notifications/v2/NotificationState; @@ -27381,22 +27234,6 @@ HSPLorg/thoughtcrime/securesms/notifications/v2/NotificationState$notificationIt HSPLorg/thoughtcrime/securesms/notifications/v2/NotificationState;->()V HSPLorg/thoughtcrime/securesms/notifications/v2/NotificationState;->(Ljava/util/List;Ljava/util/List;Ljava/util/List;)V HSPLorg/thoughtcrime/securesms/notifications/v2/NotificationState;->access$getEMPTY$cp()Lorg/thoughtcrime/securesms/notifications/v2/NotificationState; -HSPLorg/thoughtcrime/securesms/notifications/v2/NotificationState;->getConversations()Ljava/util/List; -HSPLorg/thoughtcrime/securesms/notifications/v2/NotificationState;->getMuteFilteredMessages()Ljava/util/List; -HSPLorg/thoughtcrime/securesms/notifications/v2/NotificationState;->getProfileFilteredMessages()Ljava/util/List; -HSPLorg/thoughtcrime/securesms/notifications/v2/NotificationState;->getThreadsWithMostRecentNotificationFromSelf()Ljava/util/Set; -HSPLorg/thoughtcrime/securesms/notifications/v2/NotificationState;->isEmpty()Z -HSPLorg/thoughtcrime/securesms/notifications/v2/NotificationState;->toString()Ljava/lang/String; -HSPLorg/thoughtcrime/securesms/notifications/v2/NotificationStateProvider;->()V -HSPLorg/thoughtcrime/securesms/notifications/v2/NotificationStateProvider;->()V -HSPLorg/thoughtcrime/securesms/notifications/v2/NotificationStateProvider;->constructNotificationState(Ljava/util/Map;Lorg/thoughtcrime/securesms/notifications/profiles/NotificationProfile;)Lorg/thoughtcrime/securesms/notifications/v2/NotificationState; -HSPLorg/thoughtcrime/securesms/permissions/Permissions$$ExternalSyntheticLambda0;->(Landroid/content/Context;)V -HSPLorg/thoughtcrime/securesms/permissions/Permissions$$ExternalSyntheticLambda0;->test(Ljava/lang/Object;)Z -HSPLorg/thoughtcrime/securesms/permissions/Permissions;->$r8$lambda$Q0AcdMcPXUgr1QQ_HDTcoSx0sHo(Landroid/content/Context;Ljava/lang/String;)Z -HSPLorg/thoughtcrime/securesms/permissions/Permissions;->()V -HSPLorg/thoughtcrime/securesms/permissions/Permissions;->hasAll(Landroid/content/Context;[Ljava/lang/String;)Z -HSPLorg/thoughtcrime/securesms/permissions/Permissions;->isRuntimePermissionsRequired()Z -HSPLorg/thoughtcrime/securesms/permissions/Permissions;->lambda$hasAll$2(Landroid/content/Context;Ljava/lang/String;)Z HSPLorg/thoughtcrime/securesms/phonenumbers/PhoneNumberFormatter$PhoneNumber;->-$$Nest$fgetcountryCode(Lorg/thoughtcrime/securesms/phonenumbers/PhoneNumberFormatter$PhoneNumber;)I HSPLorg/thoughtcrime/securesms/phonenumbers/PhoneNumberFormatter$PhoneNumber;->(Ljava/lang/String;ILjava/lang/String;)V HSPLorg/thoughtcrime/securesms/phonenumbers/PhoneNumberFormatter$PhoneNumber;->getCountryCode()I @@ -27410,16 +27247,15 @@ HSPLorg/thoughtcrime/securesms/pin/SvrRepository;->()V HSPLorg/thoughtcrime/securesms/pin/SvrRepository;->()V HSPLorg/thoughtcrime/securesms/pin/SvrRepository;->onRegistrationComplete(Lorg/whispersystems/signalservice/api/kbs/MasterKey;Ljava/lang/String;ZZ)V HSPLorg/thoughtcrime/securesms/preferences/widgets/NotificationPrivacyPreference;->(Ljava/lang/String;)V -HSPLorg/thoughtcrime/securesms/preferences/widgets/NotificationPrivacyPreference;->equals(Ljava/lang/Object;)Z +HSPLorg/thoughtcrime/securesms/preferences/widgets/NotificationPrivacyPreference;->isDisplayContact()Z HSPLorg/thoughtcrime/securesms/preferences/widgets/NotificationPrivacyPreference;->toString()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/profiles/AvatarHelper;->()V HSPLorg/thoughtcrime/securesms/profiles/AvatarHelper;->getAvatar(Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/RecipientId;)Ljava/io/InputStream; HSPLorg/thoughtcrime/securesms/profiles/AvatarHelper;->getAvatarDirectory(Landroid/content/Context;)Ljava/io/File; HSPLorg/thoughtcrime/securesms/profiles/AvatarHelper;->getAvatarFile(Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/RecipientId;)Ljava/io/File; +HSPLorg/thoughtcrime/securesms/profiles/AvatarHelper;->getAvatarFile(Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/RecipientId;Z)Ljava/io/File; HSPLorg/thoughtcrime/securesms/profiles/AvatarHelper;->getAvatarFileDetails(Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/RecipientId;)Lorg/thoughtcrime/securesms/database/model/ProfileAvatarFileDetails; -HSPLorg/thoughtcrime/securesms/profiles/AvatarHelper;->getOutputStream(Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/RecipientId;Z)Ljava/io/OutputStream; HSPLorg/thoughtcrime/securesms/profiles/AvatarHelper;->hasAvatar(Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/RecipientId;)Z -HSPLorg/thoughtcrime/securesms/profiles/AvatarHelper;->setAvatar(Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/RecipientId;Ljava/io/InputStream;)V HSPLorg/thoughtcrime/securesms/profiles/ProfileName$$ExternalSyntheticLambda0;->()V HSPLorg/thoughtcrime/securesms/profiles/ProfileName$$ExternalSyntheticLambda0;->test(Ljava/lang/Object;)Z HSPLorg/thoughtcrime/securesms/profiles/ProfileName$$ExternalSyntheticLambda1;->()V @@ -27428,8 +27264,6 @@ HSPLorg/thoughtcrime/securesms/profiles/ProfileName$1;->()V HSPLorg/thoughtcrime/securesms/profiles/ProfileName;->$r8$lambda$pNHvm3E5R2_hKbty_0luXfn7Cik(Ljava/lang/Boolean;Ljava/lang/String;)Ljava/lang/Boolean; HSPLorg/thoughtcrime/securesms/profiles/ProfileName;->()V HSPLorg/thoughtcrime/securesms/profiles/ProfileName;->(Ljava/lang/String;Ljava/lang/String;)V -HSPLorg/thoughtcrime/securesms/profiles/ProfileName;->asGiven(Ljava/lang/String;)Lorg/thoughtcrime/securesms/profiles/ProfileName; -HSPLorg/thoughtcrime/securesms/profiles/ProfileName;->equals(Ljava/lang/Object;)Z HSPLorg/thoughtcrime/securesms/profiles/ProfileName;->fromParts(Ljava/lang/String;Ljava/lang/String;)Lorg/thoughtcrime/securesms/profiles/ProfileName; HSPLorg/thoughtcrime/securesms/profiles/ProfileName;->getFamilyName()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/profiles/ProfileName;->getGivenName()Ljava/lang/String; @@ -27449,69 +27283,17 @@ HSPLorg/thoughtcrime/securesms/providers/BaseContentProvider;->attachInfo(Landro HSPLorg/thoughtcrime/securesms/providers/BlobContentProvider;->()V HSPLorg/thoughtcrime/securesms/providers/BlobContentProvider;->()V HSPLorg/thoughtcrime/securesms/providers/BlobContentProvider;->onCreate()Z -HSPLorg/thoughtcrime/securesms/providers/BlobProvider$$ExternalSyntheticLambda10;->(Lorg/thoughtcrime/securesms/providers/BlobProvider;Landroid/content/Context;J)V -HSPLorg/thoughtcrime/securesms/providers/BlobProvider$$ExternalSyntheticLambda10;->apply(Ljava/lang/Object;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/providers/BlobProvider$$ExternalSyntheticLambda11;->(Lorg/thoughtcrime/securesms/providers/BlobProvider;Landroid/content/Context;)V HSPLorg/thoughtcrime/securesms/providers/BlobProvider$$ExternalSyntheticLambda11;->run()V -HSPLorg/thoughtcrime/securesms/providers/BlobProvider$$ExternalSyntheticLambda1;->(Lorg/thoughtcrime/securesms/providers/BlobProvider;Lorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;Ljava/io/OutputStream;Landroid/net/Uri;Landroid/content/Context;)V -HSPLorg/thoughtcrime/securesms/providers/BlobProvider$$ExternalSyntheticLambda1;->call()Ljava/lang/Object; -HSPLorg/thoughtcrime/securesms/providers/BlobProvider$$ExternalSyntheticLambda9;->(JLandroid/net/Uri;)V HSPLorg/thoughtcrime/securesms/providers/BlobProvider$1;->(I)V -HSPLorg/thoughtcrime/securesms/providers/BlobProvider$2;->()V -HSPLorg/thoughtcrime/securesms/providers/BlobProvider$BlobBuilder;->(Lorg/thoughtcrime/securesms/providers/BlobProvider;Ljava/io/InputStream;J)V -HSPLorg/thoughtcrime/securesms/providers/BlobProvider$BlobBuilder;->(Lorg/thoughtcrime/securesms/providers/BlobProvider;Ljava/io/InputStream;JLorg/thoughtcrime/securesms/providers/BlobProvider$BlobBuilder-IA;)V -HSPLorg/thoughtcrime/securesms/providers/BlobProvider$BlobBuilder;->buildBlobSpec(Lorg/thoughtcrime/securesms/providers/BlobProvider$StorageType;)Lorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec; -HSPLorg/thoughtcrime/securesms/providers/BlobProvider$BlobBuilder;->createForSingleSessionOnDisk(Landroid/content/Context;)Landroid/net/Uri; -HSPLorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;->-$$Nest$fgetid(Lorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;)Ljava/lang/String; -HSPLorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;->-$$Nest$mgetData(Lorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;)Ljava/io/InputStream; -HSPLorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;->-$$Nest$mgetFileName(Lorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;)Ljava/lang/String; -HSPLorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;->-$$Nest$mgetFileSize(Lorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;)J -HSPLorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;->-$$Nest$mgetId(Lorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;)Ljava/lang/String; -HSPLorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;->-$$Nest$mgetMimeType(Lorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;)Ljava/lang/String; -HSPLorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;->-$$Nest$mgetStorageType(Lorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;)Lorg/thoughtcrime/securesms/providers/BlobProvider$StorageType; -HSPLorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;->(Ljava/io/InputStream;Ljava/lang/String;Lorg/thoughtcrime/securesms/providers/BlobProvider$StorageType;Ljava/lang/String;Ljava/lang/String;J)V -HSPLorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;->(Ljava/io/InputStream;Ljava/lang/String;Lorg/thoughtcrime/securesms/providers/BlobProvider$StorageType;Ljava/lang/String;Ljava/lang/String;JLorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec-IA;)V -HSPLorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;->getData()Ljava/io/InputStream; -HSPLorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;->getFileName()Ljava/lang/String; -HSPLorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;->getFileSize()J -HSPLorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;->getId()Ljava/lang/String; -HSPLorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;->getMimeType()Ljava/lang/String; -HSPLorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;->getStorageType()Lorg/thoughtcrime/securesms/providers/BlobProvider$StorageType; -HSPLorg/thoughtcrime/securesms/providers/BlobProvider$StorageType;->$values()[Lorg/thoughtcrime/securesms/providers/BlobProvider$StorageType; -HSPLorg/thoughtcrime/securesms/providers/BlobProvider$StorageType;->-$$Nest$mencode(Lorg/thoughtcrime/securesms/providers/BlobProvider$StorageType;)Ljava/lang/String; -HSPLorg/thoughtcrime/securesms/providers/BlobProvider$StorageType;->-$$Nest$misMemory(Lorg/thoughtcrime/securesms/providers/BlobProvider$StorageType;)Z -HSPLorg/thoughtcrime/securesms/providers/BlobProvider$StorageType;->-$$Nest$smdecode(Ljava/lang/String;)Lorg/thoughtcrime/securesms/providers/BlobProvider$StorageType; -HSPLorg/thoughtcrime/securesms/providers/BlobProvider$StorageType;->()V -HSPLorg/thoughtcrime/securesms/providers/BlobProvider$StorageType;->(Ljava/lang/String;ILjava/lang/String;Z)V -HSPLorg/thoughtcrime/securesms/providers/BlobProvider$StorageType;->decode(Ljava/lang/String;)Lorg/thoughtcrime/securesms/providers/BlobProvider$StorageType; -HSPLorg/thoughtcrime/securesms/providers/BlobProvider$StorageType;->encode()Ljava/lang/String; -HSPLorg/thoughtcrime/securesms/providers/BlobProvider$StorageType;->isMemory()Z -HSPLorg/thoughtcrime/securesms/providers/BlobProvider$StorageType;->values()[Lorg/thoughtcrime/securesms/providers/BlobProvider$StorageType; HSPLorg/thoughtcrime/securesms/providers/BlobProvider;->$r8$lambda$ODJ5xh2VOR1mLM-AV1r-HG7pEAk(Lorg/thoughtcrime/securesms/providers/BlobProvider;Landroid/content/Context;)V -HSPLorg/thoughtcrime/securesms/providers/BlobProvider;->$r8$lambda$hszeKLE-J5rLAuyYe0jTaWcOLg4(Lorg/thoughtcrime/securesms/providers/BlobProvider;Landroid/content/Context;JLjava/io/File;)Ljava/io/InputStream; -HSPLorg/thoughtcrime/securesms/providers/BlobProvider;->$r8$lambda$vEFatZFK1evY8Xp8zObuRkcU_8Q(Lorg/thoughtcrime/securesms/providers/BlobProvider;Lorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;Ljava/io/OutputStream;Landroid/net/Uri;Landroid/content/Context;)Landroid/net/Uri; -HSPLorg/thoughtcrime/securesms/providers/BlobProvider;->-$$Nest$mwriteBlobSpecToDisk(Lorg/thoughtcrime/securesms/providers/BlobProvider;Landroid/content/Context;Lorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;)Landroid/net/Uri; HSPLorg/thoughtcrime/securesms/providers/BlobProvider;->()V HSPLorg/thoughtcrime/securesms/providers/BlobProvider;->()V -HSPLorg/thoughtcrime/securesms/providers/BlobProvider;->buildFileName(Ljava/lang/String;)Ljava/lang/String; -HSPLorg/thoughtcrime/securesms/providers/BlobProvider;->buildUri(Lorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;)Landroid/net/Uri; HSPLorg/thoughtcrime/securesms/providers/BlobProvider;->deleteOrphanedDraftFiles(Landroid/content/Context;)V -HSPLorg/thoughtcrime/securesms/providers/BlobProvider;->forData(Ljava/io/InputStream;J)Lorg/thoughtcrime/securesms/providers/BlobProvider$BlobBuilder; -HSPLorg/thoughtcrime/securesms/providers/BlobProvider;->getAttachmentSecret(Landroid/content/Context;)Lorg/thoughtcrime/securesms/crypto/AttachmentSecret; -HSPLorg/thoughtcrime/securesms/providers/BlobProvider;->getBlobRepresentation(Landroid/content/Context;Landroid/net/Uri;Lorg/thoughtcrime/securesms/util/IOFunction;Lorg/thoughtcrime/securesms/util/IOFunction;)Ljava/lang/Object; -HSPLorg/thoughtcrime/securesms/providers/BlobProvider;->getDirectory(Lorg/thoughtcrime/securesms/providers/BlobProvider$StorageType;)Ljava/lang/String; HSPLorg/thoughtcrime/securesms/providers/BlobProvider;->getInstance()Lorg/thoughtcrime/securesms/providers/BlobProvider; HSPLorg/thoughtcrime/securesms/providers/BlobProvider;->getOrCreateDirectory(Landroid/content/Context;Ljava/lang/String;)Ljava/io/File; -HSPLorg/thoughtcrime/securesms/providers/BlobProvider;->getStream(Landroid/content/Context;Landroid/net/Uri;)Ljava/io/InputStream; -HSPLorg/thoughtcrime/securesms/providers/BlobProvider;->getStream(Landroid/content/Context;Landroid/net/Uri;J)Ljava/io/InputStream; HSPLorg/thoughtcrime/securesms/providers/BlobProvider;->initialize(Landroid/content/Context;)V -HSPLorg/thoughtcrime/securesms/providers/BlobProvider;->isAuthority(Landroid/net/Uri;)Z -HSPLorg/thoughtcrime/securesms/providers/BlobProvider;->lambda$getStream$1(Landroid/content/Context;JLjava/io/File;)Ljava/io/InputStream; HSPLorg/thoughtcrime/securesms/providers/BlobProvider;->lambda$initialize$3(Landroid/content/Context;)V -HSPLorg/thoughtcrime/securesms/providers/BlobProvider;->lambda$writeBlobSpecToDiskAsync$4(Lorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;Ljava/io/OutputStream;Landroid/net/Uri;Landroid/content/Context;)Landroid/net/Uri; -HSPLorg/thoughtcrime/securesms/providers/BlobProvider;->waitUntilInitialized()V -HSPLorg/thoughtcrime/securesms/providers/BlobProvider;->writeBlobSpecToDisk(Landroid/content/Context;Lorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;)Landroid/net/Uri; -HSPLorg/thoughtcrime/securesms/providers/BlobProvider;->writeBlobSpecToDiskAsync(Landroid/content/Context;Lorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;)Ljava/util/concurrent/Future; HSPLorg/thoughtcrime/securesms/providers/MmsBodyProvider;->()V HSPLorg/thoughtcrime/securesms/providers/MmsBodyProvider;->()V HSPLorg/thoughtcrime/securesms/providers/MmsBodyProvider;->onCreate()Z @@ -27552,12 +27334,13 @@ HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda3;->(Lorg/thoughtcrime/securesms/recipients/LiveRecipient;)V HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda3;->onChanged(Ljava/lang/Object;)V HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda4;->()V -HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda4;->contentsMatch(Ljava/lang/Object;Ljava/lang/Object;)Z HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda5;->()V HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda5;->apply(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda6;->()V HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda9;->(Lorg/thoughtcrime/securesms/recipients/LiveRecipient;Lorg/thoughtcrime/securesms/recipients/Recipient;)V +HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda9;->run()V HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient;->$r8$lambda$4o-q--s8xb4fbde9teliyQxlyww(Lorg/thoughtcrime/securesms/recipients/LiveRecipient;Lorg/thoughtcrime/securesms/recipients/Recipient;)V +HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient;->$r8$lambda$CYcq6dHxZW6RfEGMe0s6kvofKaE(Lorg/thoughtcrime/securesms/recipients/LiveRecipient;Lorg/thoughtcrime/securesms/recipients/Recipient;)V HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient;->$r8$lambda$bhowCzW_4HRIO1hvMslBpl08AJE(Lorg/thoughtcrime/securesms/recipients/Recipient;Ljava/lang/Object;)Lorg/thoughtcrime/securesms/recipients/Recipient; HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient;->$r8$lambda$pGM0bNiB06y_fkMUloVDwF8BcLs(Lorg/thoughtcrime/securesms/recipients/LiveRecipient;Lorg/thoughtcrime/securesms/recipients/RecipientForeverObserver;)V HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient;->()V @@ -27566,6 +27349,7 @@ HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient;->fetchAndCacheRecipient HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient;->get()Lorg/thoughtcrime/securesms/recipients/Recipient; HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient;->getId()Lorg/thoughtcrime/securesms/recipients/RecipientId; HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient;->getLiveData()Landroidx/lifecycle/LiveData; +HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient;->lambda$new$0(Lorg/thoughtcrime/securesms/recipients/Recipient;)V HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient;->lambda$new$1(Lorg/thoughtcrime/securesms/recipients/Recipient;)V HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient;->lambda$new$2(Lorg/thoughtcrime/securesms/recipients/Recipient;Ljava/lang/Object;)Lorg/thoughtcrime/securesms/recipients/Recipient; HSPLorg/thoughtcrime/securesms/recipients/LiveRecipient;->lambda$observeForever$6(Lorg/thoughtcrime/securesms/recipients/RecipientForeverObserver;)V @@ -27601,7 +27385,6 @@ HSPLorg/thoughtcrime/securesms/recipients/LiveRecipientCache;->lambda$addToCache HSPLorg/thoughtcrime/securesms/recipients/LiveRecipientCache;->lambda$new$0()Z HSPLorg/thoughtcrime/securesms/recipients/LiveRecipientCache;->lambda$warmUp$3(Lorg/signal/core/util/Stopwatch;)V HSPLorg/thoughtcrime/securesms/recipients/LiveRecipientCache;->warmUp()V -HSPLorg/thoughtcrime/securesms/recipients/Recipient$$ExternalSyntheticLambda3;->()V HSPLorg/thoughtcrime/securesms/recipients/Recipient$Capability;->$values()[Lorg/thoughtcrime/securesms/recipients/Recipient$Capability; HSPLorg/thoughtcrime/securesms/recipients/Recipient$Capability;->()V HSPLorg/thoughtcrime/securesms/recipients/Recipient$Capability;->(Ljava/lang/String;II)V @@ -27625,6 +27408,7 @@ HSPLorg/thoughtcrime/securesms/recipients/Recipient;->getBadges()Ljava/util/List HSPLorg/thoughtcrime/securesms/recipients/Recipient;->getChatColors()Lorg/thoughtcrime/securesms/conversation/colors/ChatColors; HSPLorg/thoughtcrime/securesms/recipients/Recipient;->getCombinedAboutAndEmoji()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/recipients/Recipient;->getContactPhoto()Lorg/thoughtcrime/securesms/contacts/avatars/ContactPhoto; +HSPLorg/thoughtcrime/securesms/recipients/Recipient;->getContactUri()Landroid/net/Uri; HSPLorg/thoughtcrime/securesms/recipients/Recipient;->getDisplayName(Landroid/content/Context;)Ljava/lang/String; HSPLorg/thoughtcrime/securesms/recipients/Recipient;->getDisplayNameOrUsername(Landroid/content/Context;)Ljava/lang/String; HSPLorg/thoughtcrime/securesms/recipients/Recipient;->getE164()Lj$/util/Optional; @@ -27634,19 +27418,20 @@ HSPLorg/thoughtcrime/securesms/recipients/Recipient;->getFallbackContactPhotoDra HSPLorg/thoughtcrime/securesms/recipients/Recipient;->getGroupName(Landroid/content/Context;)Ljava/lang/String; HSPLorg/thoughtcrime/securesms/recipients/Recipient;->getId()Lorg/thoughtcrime/securesms/recipients/RecipientId; HSPLorg/thoughtcrime/securesms/recipients/Recipient;->getNameFromLocalData(Landroid/content/Context;)Ljava/lang/String; +HSPLorg/thoughtcrime/securesms/recipients/Recipient;->getNotificationChannel()Ljava/lang/String; +HSPLorg/thoughtcrime/securesms/recipients/Recipient;->getPhoneNumberSharing()Lorg/thoughtcrime/securesms/database/RecipientTable$PhoneNumberSharingState; HSPLorg/thoughtcrime/securesms/recipients/Recipient;->getProfileAvatar()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/recipients/Recipient;->getProfileAvatarFileDetails()Lorg/thoughtcrime/securesms/database/model/ProfileAvatarFileDetails; -HSPLorg/thoughtcrime/securesms/recipients/Recipient;->getProfileKey()[B HSPLorg/thoughtcrime/securesms/recipients/Recipient;->getProfileName()Lorg/thoughtcrime/securesms/profiles/ProfileName; HSPLorg/thoughtcrime/securesms/recipients/Recipient;->getRegistered()Lorg/thoughtcrime/securesms/database/RecipientTable$RegisteredState; HSPLorg/thoughtcrime/securesms/recipients/Recipient;->getServiceId()Lj$/util/Optional; HSPLorg/thoughtcrime/securesms/recipients/Recipient;->getSmallFallbackContactPhotoDrawable(Landroid/content/Context;ZLorg/thoughtcrime/securesms/recipients/Recipient$FallbackPhotoProvider;I)Landroid/graphics/drawable/Drawable; HSPLorg/thoughtcrime/securesms/recipients/Recipient;->getWallpaper()Lorg/thoughtcrime/securesms/wallpaper/ChatWallpaper; HSPLorg/thoughtcrime/securesms/recipients/Recipient;->hasE164()Z -HSPLorg/thoughtcrime/securesms/recipients/Recipient;->hasSameContent(Lorg/thoughtcrime/securesms/recipients/Recipient;)Z HSPLorg/thoughtcrime/securesms/recipients/Recipient;->hasServiceId()Z HSPLorg/thoughtcrime/securesms/recipients/Recipient;->hasWallpaper()Z HSPLorg/thoughtcrime/securesms/recipients/Recipient;->hashCode()I +HSPLorg/thoughtcrime/securesms/recipients/Recipient;->isActiveGroup()Z HSPLorg/thoughtcrime/securesms/recipients/Recipient;->isBlocked()Z HSPLorg/thoughtcrime/securesms/recipients/Recipient;->isCallLink()Z HSPLorg/thoughtcrime/securesms/recipients/Recipient;->isDistributionList()Z @@ -27675,7 +27460,7 @@ HSPLorg/thoughtcrime/securesms/recipients/Recipient;->resolved(Lorg/thoughtcrime HSPLorg/thoughtcrime/securesms/recipients/Recipient;->resolvedList(Ljava/util/Collection;)Ljava/util/List; HSPLorg/thoughtcrime/securesms/recipients/Recipient;->self()Lorg/thoughtcrime/securesms/recipients/Recipient; HSPLorg/thoughtcrime/securesms/recipients/Recipient;->shouldBlurAvatar()Z -HSPLorg/thoughtcrime/securesms/recipients/Recipient;->shouldHideStory()Z +HSPLorg/thoughtcrime/securesms/recipients/Recipient;->shouldShowE164()Z HSPLorg/thoughtcrime/securesms/recipients/Recipient;->showVerified()Z HSPLorg/thoughtcrime/securesms/recipients/Recipient;->trustedPush(Lorg/whispersystems/signalservice/api/push/ServiceId$ACI;Lorg/whispersystems/signalservice/api/push/ServiceId$PNI;Ljava/lang/String;)Lorg/thoughtcrime/securesms/recipients/Recipient; HSPLorg/thoughtcrime/securesms/recipients/RecipientDetails$$ExternalSyntheticLambda0;->(Lkotlin/jvm/functions/Function1;)V @@ -27689,9 +27474,8 @@ HSPLorg/thoughtcrime/securesms/recipients/RecipientDetails$Companion;->(Lk HSPLorg/thoughtcrime/securesms/recipients/RecipientDetails$Companion;->forIndividual(Landroid/content/Context;Lorg/thoughtcrime/securesms/database/model/RecipientRecord;)Lorg/thoughtcrime/securesms/recipients/RecipientDetails; HSPLorg/thoughtcrime/securesms/recipients/RecipientDetails$Companion;->forUnknown()Lorg/thoughtcrime/securesms/recipients/RecipientDetails; HSPLorg/thoughtcrime/securesms/recipients/RecipientDetails;->()V -HSPLorg/thoughtcrime/securesms/recipients/RecipientDetails;->(Ljava/lang/String;Ljava/lang/String;ZLorg/thoughtcrime/securesms/database/RecipientTable$RegisteredState;Lorg/thoughtcrime/securesms/database/model/RecipientRecord;Ljava/util/List;ZLorg/thoughtcrime/securesms/conversation/colors/AvatarColor;Lj$/util/Optional;)V -HSPLorg/thoughtcrime/securesms/recipients/RecipientDetails;->(Lorg/whispersystems/signalservice/api/push/ServiceId$ACI;Lorg/whispersystems/signalservice/api/push/ServiceId$PNI;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lorg/thoughtcrime/securesms/groups/GroupId;Lorg/thoughtcrime/securesms/database/model/DistributionListId;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Landroid/net/Uri;Landroid/net/Uri;Lj$/util/Optional;Landroid/net/Uri;Landroid/net/Uri;JLorg/thoughtcrime/securesms/database/RecipientTable$VibrateState;Lorg/thoughtcrime/securesms/database/RecipientTable$VibrateState;ZILjava/util/List;Lorg/thoughtcrime/securesms/profiles/ProfileName;Lorg/thoughtcrime/securesms/database/RecipientTable$RegisteredState;[BLorg/signal/libsignal/zkgroup/profiles/ExpiringProfileKeyCredential;Ljava/lang/String;Lorg/thoughtcrime/securesms/database/model/ProfileAvatarFileDetails;ZLorg/thoughtcrime/securesms/recipients/Recipient$HiddenState;ZJZLjava/lang/String;Lorg/thoughtcrime/securesms/database/RecipientTable$UnidentifiedAccessMode;Lorg/thoughtcrime/securesms/database/model/RecipientRecord$Capabilities;[BLorg/thoughtcrime/securesms/database/RecipientTable$MentionSetting;Lorg/thoughtcrime/securesms/wallpaper/ChatWallpaper;Lorg/thoughtcrime/securesms/conversation/colors/ChatColors;Lorg/thoughtcrime/securesms/conversation/colors/AvatarColor;Ljava/lang/String;Ljava/lang/String;Lorg/thoughtcrime/securesms/profiles/ProfileName;Lj$/util/Optional;ZLjava/util/List;ZZLorg/thoughtcrime/securesms/service/webrtc/links/CallLinkRoomId;Lj$/util/Optional;)V -HSPLorg/thoughtcrime/securesms/recipients/RecipientDetails;->(Lorg/whispersystems/signalservice/api/push/ServiceId$ACI;Lorg/whispersystems/signalservice/api/push/ServiceId$PNI;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lorg/thoughtcrime/securesms/groups/GroupId;Lorg/thoughtcrime/securesms/database/model/DistributionListId;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Landroid/net/Uri;Landroid/net/Uri;Lj$/util/Optional;Landroid/net/Uri;Landroid/net/Uri;JLorg/thoughtcrime/securesms/database/RecipientTable$VibrateState;Lorg/thoughtcrime/securesms/database/RecipientTable$VibrateState;ZILjava/util/List;Lorg/thoughtcrime/securesms/profiles/ProfileName;Lorg/thoughtcrime/securesms/database/RecipientTable$RegisteredState;[BLorg/signal/libsignal/zkgroup/profiles/ExpiringProfileKeyCredential;Ljava/lang/String;Lorg/thoughtcrime/securesms/database/model/ProfileAvatarFileDetails;ZLorg/thoughtcrime/securesms/recipients/Recipient$HiddenState;ZJZLjava/lang/String;Lorg/thoughtcrime/securesms/database/RecipientTable$UnidentifiedAccessMode;Lorg/thoughtcrime/securesms/database/model/RecipientRecord$Capabilities;[BLorg/thoughtcrime/securesms/database/RecipientTable$MentionSetting;Lorg/thoughtcrime/securesms/wallpaper/ChatWallpaper;Lorg/thoughtcrime/securesms/conversation/colors/ChatColors;Lorg/thoughtcrime/securesms/conversation/colors/AvatarColor;Ljava/lang/String;Ljava/lang/String;Lorg/thoughtcrime/securesms/profiles/ProfileName;Lj$/util/Optional;ZLjava/util/List;ZZLorg/thoughtcrime/securesms/service/webrtc/links/CallLinkRoomId;Lj$/util/Optional;Lkotlin/jvm/internal/DefaultConstructorMarker;)V +HSPLorg/thoughtcrime/securesms/recipients/RecipientDetails;->(Lorg/whispersystems/signalservice/api/push/ServiceId$ACI;Lorg/whispersystems/signalservice/api/push/ServiceId$PNI;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lorg/thoughtcrime/securesms/groups/GroupId;Lorg/thoughtcrime/securesms/database/model/DistributionListId;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Landroid/net/Uri;Landroid/net/Uri;Lj$/util/Optional;Landroid/net/Uri;Landroid/net/Uri;JLorg/thoughtcrime/securesms/database/RecipientTable$VibrateState;Lorg/thoughtcrime/securesms/database/RecipientTable$VibrateState;ZILjava/util/List;Lorg/thoughtcrime/securesms/profiles/ProfileName;Lorg/thoughtcrime/securesms/database/RecipientTable$RegisteredState;[BLorg/signal/libsignal/zkgroup/profiles/ExpiringProfileKeyCredential;Ljava/lang/String;Lorg/thoughtcrime/securesms/database/model/ProfileAvatarFileDetails;ZLorg/thoughtcrime/securesms/recipients/Recipient$HiddenState;ZJZLjava/lang/String;Lorg/thoughtcrime/securesms/database/RecipientTable$UnidentifiedAccessMode;Lorg/thoughtcrime/securesms/database/model/RecipientRecord$Capabilities;[BLorg/thoughtcrime/securesms/database/RecipientTable$MentionSetting;Lorg/thoughtcrime/securesms/wallpaper/ChatWallpaper;Lorg/thoughtcrime/securesms/conversation/colors/ChatColors;Lorg/thoughtcrime/securesms/conversation/colors/AvatarColor;Ljava/lang/String;Ljava/lang/String;Lorg/thoughtcrime/securesms/profiles/ProfileName;Lj$/util/Optional;ZLjava/util/List;ZZLorg/thoughtcrime/securesms/service/webrtc/links/CallLinkRoomId;Lj$/util/Optional;Lorg/thoughtcrime/securesms/database/RecipientTable$PhoneNumberSharingState;)V +HSPLorg/thoughtcrime/securesms/recipients/RecipientDetails;->(Lorg/whispersystems/signalservice/api/push/ServiceId$ACI;Lorg/whispersystems/signalservice/api/push/ServiceId$PNI;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lorg/thoughtcrime/securesms/groups/GroupId;Lorg/thoughtcrime/securesms/database/model/DistributionListId;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Landroid/net/Uri;Landroid/net/Uri;Lj$/util/Optional;Landroid/net/Uri;Landroid/net/Uri;JLorg/thoughtcrime/securesms/database/RecipientTable$VibrateState;Lorg/thoughtcrime/securesms/database/RecipientTable$VibrateState;ZILjava/util/List;Lorg/thoughtcrime/securesms/profiles/ProfileName;Lorg/thoughtcrime/securesms/database/RecipientTable$RegisteredState;[BLorg/signal/libsignal/zkgroup/profiles/ExpiringProfileKeyCredential;Ljava/lang/String;Lorg/thoughtcrime/securesms/database/model/ProfileAvatarFileDetails;ZLorg/thoughtcrime/securesms/recipients/Recipient$HiddenState;ZJZLjava/lang/String;Lorg/thoughtcrime/securesms/database/RecipientTable$UnidentifiedAccessMode;Lorg/thoughtcrime/securesms/database/model/RecipientRecord$Capabilities;[BLorg/thoughtcrime/securesms/database/RecipientTable$MentionSetting;Lorg/thoughtcrime/securesms/wallpaper/ChatWallpaper;Lorg/thoughtcrime/securesms/conversation/colors/ChatColors;Lorg/thoughtcrime/securesms/conversation/colors/AvatarColor;Ljava/lang/String;Ljava/lang/String;Lorg/thoughtcrime/securesms/profiles/ProfileName;Lj$/util/Optional;ZLjava/util/List;ZZLorg/thoughtcrime/securesms/service/webrtc/links/CallLinkRoomId;Lj$/util/Optional;Lorg/thoughtcrime/securesms/database/RecipientTable$PhoneNumberSharingState;Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/thoughtcrime/securesms/recipients/RecipientDetails;->forIndividual(Landroid/content/Context;Lorg/thoughtcrime/securesms/database/model/RecipientRecord;)Lorg/thoughtcrime/securesms/recipients/RecipientDetails; HSPLorg/thoughtcrime/securesms/recipients/RecipientDetails;->forUnknown()Lorg/thoughtcrime/securesms/recipients/RecipientDetails; HSPLorg/thoughtcrime/securesms/recipients/RecipientId$1;->()V @@ -27708,6 +27492,7 @@ HSPLorg/thoughtcrime/securesms/recipients/RecipientId;->from(Lorg/whispersystems HSPLorg/thoughtcrime/securesms/recipients/RecipientId;->hashCode()I HSPLorg/thoughtcrime/securesms/recipients/RecipientId;->isUnknown()Z HSPLorg/thoughtcrime/securesms/recipients/RecipientId;->serialize()Ljava/lang/String; +HSPLorg/thoughtcrime/securesms/recipients/RecipientId;->toLong()J HSPLorg/thoughtcrime/securesms/recipients/RecipientId;->toString()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/recipients/RecipientIdCache$1;->(Lorg/thoughtcrime/securesms/recipients/RecipientIdCache;IFZI)V HSPLorg/thoughtcrime/securesms/recipients/RecipientIdCache$1;->removeEldestEntry(Ljava/util/Map$Entry;)Z @@ -27721,7 +27506,6 @@ HSPLorg/thoughtcrime/securesms/recipients/RecipientUtil;->isLegacyProfileSharing HSPLorg/thoughtcrime/securesms/recipients/RecipientUtil;->isMessageRequestAccepted(Landroid/content/Context;J)Z HSPLorg/thoughtcrime/securesms/recipients/RecipientUtil;->isMessageRequestAccepted(Ljava/lang/Long;Lorg/thoughtcrime/securesms/recipients/Recipient;)Z HSPLorg/thoughtcrime/securesms/recipients/RecipientUtil;->isRecipientHidden(J)Z -HSPLorg/thoughtcrime/securesms/recipients/RecipientUtil;->toSignalServiceAddress(Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/Recipient;)Lorg/whispersystems/signalservice/api/push/SignalServiceAddress; HSPLorg/thoughtcrime/securesms/registration/RegistrationData;->()V HSPLorg/thoughtcrime/securesms/registration/RegistrationData;->(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILorg/signal/libsignal/zkgroup/profiles/ProfileKey;Ljava/lang/String;ILjava/lang/String;)V HSPLorg/thoughtcrime/securesms/registration/RegistrationData;->getE164()Ljava/lang/String; @@ -27755,12 +27539,6 @@ HSPLorg/thoughtcrime/securesms/registration/VerifyResponse;->getMasterKey()Lorg/ HSPLorg/thoughtcrime/securesms/registration/VerifyResponse;->getPin()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/registration/VerifyResponse;->getPniPreKeyCollection()Lorg/whispersystems/signalservice/api/account/PreKeyCollection; HSPLorg/thoughtcrime/securesms/registration/VerifyResponse;->getVerifyAccountResponse()Lorg/whispersystems/signalservice/internal/push/VerifyAccountResponse; -HSPLorg/thoughtcrime/securesms/registration/viewmodel/RegistrationViewModel$$ExternalSyntheticLambda3;->()V -HSPLorg/thoughtcrime/securesms/registration/viewmodel/RegistrationViewModel$$ExternalSyntheticLambda3;->apply(Ljava/lang/Object;)Ljava/lang/Object; -HSPLorg/thoughtcrime/securesms/releasechannel/ReleaseChannel;->()V -HSPLorg/thoughtcrime/securesms/releasechannel/ReleaseChannel;->()V -HSPLorg/thoughtcrime/securesms/releasechannel/ReleaseChannel;->insertReleaseChannelMessage$default(Lorg/thoughtcrime/securesms/releasechannel/ReleaseChannel;Lorg/thoughtcrime/securesms/recipients/RecipientId;Ljava/lang/String;JLjava/lang/String;IILjava/lang/String;Ljava/lang/String;Lorg/thoughtcrime/securesms/database/model/databaseprotos/BodyRangeList;Lorg/thoughtcrime/securesms/database/model/StoryType;ILjava/lang/Object;)Lorg/thoughtcrime/securesms/database/MessageTable$InsertResult; -HSPLorg/thoughtcrime/securesms/releasechannel/ReleaseChannel;->insertReleaseChannelMessage(Lorg/thoughtcrime/securesms/recipients/RecipientId;Ljava/lang/String;JLjava/lang/String;IILjava/lang/String;Ljava/lang/String;Lorg/thoughtcrime/securesms/database/model/databaseprotos/BodyRangeList;Lorg/thoughtcrime/securesms/database/model/StoryType;)Lorg/thoughtcrime/securesms/database/MessageTable$InsertResult; HSPLorg/thoughtcrime/securesms/revealable/ViewOnceMessageManager;->()V HSPLorg/thoughtcrime/securesms/revealable/ViewOnceMessageManager;->(Landroid/app/Application;)V HSPLorg/thoughtcrime/securesms/revealable/ViewOnceMessageManager;->getNextClosestEvent()Ljava/lang/Object; @@ -27810,7 +27588,6 @@ HSPLorg/thoughtcrime/securesms/service/ExpiringStoriesManager;->getNextClosestEv HSPLorg/thoughtcrime/securesms/service/KeyCachingService;->()V HSPLorg/thoughtcrime/securesms/service/KeyCachingService;->isLocked(Landroid/content/Context;)Z HSPLorg/thoughtcrime/securesms/service/KeyCachingService;->onAppForegrounded(Landroid/content/Context;)V -HSPLorg/thoughtcrime/securesms/service/LocalBackupListener$$ExternalSyntheticBackport0;->m(J)I HSPLorg/thoughtcrime/securesms/service/LocalBackupListener;->()V HSPLorg/thoughtcrime/securesms/service/LocalBackupListener;->schedule(Landroid/content/Context;)V HSPLorg/thoughtcrime/securesms/service/PendingRetryReceiptManager;->()V @@ -27862,11 +27639,6 @@ HSPLorg/thoughtcrime/securesms/shakereport/ShakeToReport;->()V HSPLorg/thoughtcrime/securesms/shakereport/ShakeToReport;->(Landroid/app/Application;)V HSPLorg/thoughtcrime/securesms/shakereport/ShakeToReport;->enable()V HSPLorg/thoughtcrime/securesms/shakereport/ShakeToReport;->registerActivity(Landroidx/appcompat/app/AppCompatActivity;)V -HSPLorg/thoughtcrime/securesms/stickers/BlessedPacks$1;->()V -HSPLorg/thoughtcrime/securesms/stickers/BlessedPacks$Pack;->(Ljava/lang/String;Ljava/lang/String;)V -HSPLorg/thoughtcrime/securesms/stickers/BlessedPacks$Pack;->getPackId()Ljava/lang/String; -HSPLorg/thoughtcrime/securesms/stickers/BlessedPacks;->()V -HSPLorg/thoughtcrime/securesms/stickers/BlessedPacks;->contains(Ljava/lang/String;)Z HSPLorg/thoughtcrime/securesms/stickers/StickerRemoteUriLoader$Factory;->()V HSPLorg/thoughtcrime/securesms/stickers/StickerSearchRepository$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/stickers/StickerSearchRepository;Lorg/thoughtcrime/securesms/stickers/StickerSearchRepository$Callback;)V HSPLorg/thoughtcrime/securesms/stickers/StickerSearchRepository$$ExternalSyntheticLambda0;->run()V @@ -28031,6 +27803,7 @@ HSPLorg/thoughtcrime/securesms/util/AppForegroundObserver;->()V HSPLorg/thoughtcrime/securesms/util/AppForegroundObserver;->addListener(Lorg/thoughtcrime/securesms/util/AppForegroundObserver$Listener;)V HSPLorg/thoughtcrime/securesms/util/AppForegroundObserver;->begin()V HSPLorg/thoughtcrime/securesms/util/AppForegroundObserver;->onForeground()V +HSPLorg/thoughtcrime/securesms/util/AppForegroundObserver;->removeListener(Lorg/thoughtcrime/securesms/util/AppForegroundObserver$Listener;)V HSPLorg/thoughtcrime/securesms/util/AppStartup$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/util/AppStartup;)V HSPLorg/thoughtcrime/securesms/util/AppStartup$$ExternalSyntheticLambda1;->(Lorg/thoughtcrime/securesms/util/AppStartup;)V HSPLorg/thoughtcrime/securesms/util/AppStartup$Task;->(Lorg/thoughtcrime/securesms/util/AppStartup;Ljava/lang/String;Ljava/lang/Runnable;)V @@ -28053,9 +27826,12 @@ HSPLorg/thoughtcrime/securesms/util/AvatarUtil;->loadIconIntoImageView(Lorg/thou HSPLorg/thoughtcrime/securesms/util/AvatarUtil;->request(Lorg/thoughtcrime/securesms/mms/GlideRequest;Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/Recipient;ILcom/bumptech/glide/load/resource/bitmap/BitmapTransformation;)Lorg/thoughtcrime/securesms/mms/GlideRequest; HSPLorg/thoughtcrime/securesms/util/AvatarUtil;->request(Lorg/thoughtcrime/securesms/mms/GlideRequest;Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/Recipient;ZILcom/bumptech/glide/load/resource/bitmap/BitmapTransformation;)Lorg/thoughtcrime/securesms/mms/GlideRequest; HSPLorg/thoughtcrime/securesms/util/AvatarUtil;->requestCircle(Lorg/thoughtcrime/securesms/mms/GlideRequest;Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/Recipient;I)Lorg/thoughtcrime/securesms/mms/GlideRequest; -HSPLorg/thoughtcrime/securesms/util/BubbleUtil$BubbleState;->$values()[Lorg/thoughtcrime/securesms/util/BubbleUtil$BubbleState; -HSPLorg/thoughtcrime/securesms/util/BubbleUtil$BubbleState;->()V -HSPLorg/thoughtcrime/securesms/util/BubbleUtil$BubbleState;->(Ljava/lang/String;I)V +HSPLorg/thoughtcrime/securesms/util/BubbleUtil$$ExternalSyntheticApiModelOutline0;->m(Landroid/app/NotificationManager;Ljava/lang/String;Ljava/lang/String;)Landroid/app/NotificationChannel; +HSPLorg/thoughtcrime/securesms/util/BubbleUtil$$ExternalSyntheticApiModelOutline1;->m(Landroid/app/NotificationManager;)Z +HSPLorg/thoughtcrime/securesms/util/BubbleUtil$$ExternalSyntheticApiModelOutline2;->m(Landroid/app/NotificationManager;)I +HSPLorg/thoughtcrime/securesms/util/BubbleUtil$$ExternalSyntheticApiModelOutline3;->m(Landroid/app/NotificationManager;)Z +HSPLorg/thoughtcrime/securesms/util/BubbleUtil;->()V +HSPLorg/thoughtcrime/securesms/util/BubbleUtil;->canBubble(Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/Recipient;Ljava/lang/Long;)Z HSPLorg/thoughtcrime/securesms/util/ByteUnit$1;->(Ljava/lang/String;I)V HSPLorg/thoughtcrime/securesms/util/ByteUnit$1;->(Ljava/lang/String;ILorg/thoughtcrime/securesms/util/ByteUnit$1-IA;)V HSPLorg/thoughtcrime/securesms/util/ByteUnit$2;->(Ljava/lang/String;I)V @@ -28090,6 +27866,9 @@ HSPLorg/thoughtcrime/securesms/util/CachedInflater;->cacheUntilLimit(ILandroid/v HSPLorg/thoughtcrime/securesms/util/CachedInflater;->clear()V HSPLorg/thoughtcrime/securesms/util/CachedInflater;->from(Landroid/content/Context;)Lorg/thoughtcrime/securesms/util/CachedInflater; HSPLorg/thoughtcrime/securesms/util/CachedInflater;->inflate(ILandroid/view/ViewGroup;Z)Landroid/view/View; +HSPLorg/thoughtcrime/securesms/util/CenteredImageSpan;->(Landroid/graphics/drawable/Drawable;)V +HSPLorg/thoughtcrime/securesms/util/CenteredImageSpan;->draw(Landroid/graphics/Canvas;Ljava/lang/CharSequence;IIFIIILandroid/graphics/Paint;)V +HSPLorg/thoughtcrime/securesms/util/CenteredImageSpan;->getSize(Landroid/graphics/Paint;Ljava/lang/CharSequence;IILandroid/graphics/Paint$FontMetricsInt;)I HSPLorg/thoughtcrime/securesms/util/CharacterCalculator;->()V HSPLorg/thoughtcrime/securesms/util/ConfigurationUtil;->getFontScale(Landroid/content/res/Configuration;)F HSPLorg/thoughtcrime/securesms/util/ConfigurationUtil;->getNightModeConfiguration(Landroid/content/Context;)I @@ -28097,7 +27876,8 @@ HSPLorg/thoughtcrime/securesms/util/ConfigurationUtil;->getNightModeConfiguratio HSPLorg/thoughtcrime/securesms/util/ContextUtil;->requireDrawable(Landroid/content/Context;I)Landroid/graphics/drawable/Drawable; HSPLorg/thoughtcrime/securesms/util/ConversationShortcutPhoto$Loader$Factory;->(Landroid/content/Context;)V HSPLorg/thoughtcrime/securesms/util/ConversationUtil;->()V -HSPLorg/thoughtcrime/securesms/util/ConversationUtil;->refreshRecipientShortcuts()V +HSPLorg/thoughtcrime/securesms/util/ConversationUtil;->getChannelId(Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/Recipient;)Ljava/lang/String; +HSPLorg/thoughtcrime/securesms/util/ConversationUtil;->getShortcutId(Lorg/thoughtcrime/securesms/recipients/RecipientId;)Ljava/lang/String; HSPLorg/thoughtcrime/securesms/util/DateUtils$sameDayDateFormat$2;->()V HSPLorg/thoughtcrime/securesms/util/DateUtils$sameDayDateFormat$2;->()V HSPLorg/thoughtcrime/securesms/util/DateUtils$sameDayDateFormat$2;->invoke()Ljava/lang/Object; @@ -28107,8 +27887,8 @@ HSPLorg/thoughtcrime/securesms/util/DateUtils;->()V HSPLorg/thoughtcrime/securesms/util/DateUtils;->getBriefRelativeTimeSpanString(Landroid/content/Context;Ljava/util/Locale;J)Ljava/lang/String; HSPLorg/thoughtcrime/securesms/util/DateUtils;->getConversationDateHeaderString(Landroid/content/Context;Ljava/util/Locale;J)Ljava/lang/String; HSPLorg/thoughtcrime/securesms/util/DateUtils;->getDatelessRelativeTimeSpanFormattedDate(Landroid/content/Context;Ljava/util/Locale;J)Lorg/thoughtcrime/securesms/conversation/v2/computed/FormattedDate; -HSPLorg/thoughtcrime/securesms/util/DateUtils;->getDatelessRelativeTimeSpanString(Landroid/content/Context;Ljava/util/Locale;J)Ljava/lang/String; HSPLorg/thoughtcrime/securesms/util/DateUtils;->getSameDayDateFormat()Ljava/text/SimpleDateFormat; +HSPLorg/thoughtcrime/securesms/util/DateUtils;->isNow(J)Z HSPLorg/thoughtcrime/securesms/util/DateUtils;->isSameDay(JJ)Z HSPLorg/thoughtcrime/securesms/util/DateUtils;->isSameExtendedRelativeTimestamp(JJ)Z HSPLorg/thoughtcrime/securesms/util/DateUtils;->isWithin-HG0u8IE(JJ)Z @@ -28120,6 +27900,7 @@ HSPLorg/thoughtcrime/securesms/util/DefaultSavedStateHandleDelegate$lazyDefault$ HSPLorg/thoughtcrime/securesms/util/DefaultSavedStateHandleDelegate;->(Landroidx/lifecycle/SavedStateHandle;Ljava/lang/String;Lkotlin/jvm/functions/Function0;)V HSPLorg/thoughtcrime/securesms/util/DefaultSavedStateHandleDelegate;->getLazyDefault()Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/util/DefaultSavedStateHandleDelegate;->getValue(Ljava/lang/Object;Lkotlin/reflect/KProperty;)Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/util/DefaultSavedStateHandleDelegate;->setValue(Ljava/lang/Object;Lkotlin/reflect/KProperty;Ljava/lang/Object;)V HSPLorg/thoughtcrime/securesms/util/DefaultValueLiveData;->(Ljava/lang/Object;)V HSPLorg/thoughtcrime/securesms/util/DefaultValueLiveData;->getValue()Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/util/DefaultValueLiveData;->postValue(Ljava/lang/Object;)V @@ -28156,7 +27937,6 @@ HSPLorg/thoughtcrime/securesms/util/FeatureFlags;->crashPromptConfig()Ljava/lang HSPLorg/thoughtcrime/securesms/util/FeatureFlags;->delayedNotificationsPromptConfig()Ljava/lang/String; HSPLorg/thoughtcrime/securesms/util/FeatureFlags;->getBackgroundMessageProcessInterval()J HSPLorg/thoughtcrime/securesms/util/FeatureFlags;->getBoolean(Ljava/lang/String;Z)Z -HSPLorg/thoughtcrime/securesms/util/FeatureFlags;->getDefaultMaxBackoff()J HSPLorg/thoughtcrime/securesms/util/FeatureFlags;->getInteger(Ljava/lang/String;I)I HSPLorg/thoughtcrime/securesms/util/FeatureFlags;->getLong(Ljava/lang/String;J)J HSPLorg/thoughtcrime/securesms/util/FeatureFlags;->getString(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; @@ -28172,7 +27952,6 @@ HSPLorg/thoughtcrime/securesms/util/FeatureFlags;->refreshIfNecessary()V HSPLorg/thoughtcrime/securesms/util/FeatureFlags;->retryReceipts()Z HSPLorg/thoughtcrime/securesms/util/FeatureFlags;->retryRespondMaxAge()J HSPLorg/thoughtcrime/securesms/util/FeatureFlags;->triggerFlagChangeListeners(Ljava/util/Map;)V -HSPLorg/thoughtcrime/securesms/util/FeatureFlags;->useTextOnlyConversationItemV2()Z HSPLorg/thoughtcrime/securesms/util/FeatureFlags;->usernames()Z HSPLorg/thoughtcrime/securesms/util/FrameRateTracker$1;->(Lorg/thoughtcrime/securesms/util/FrameRateTracker;)V HSPLorg/thoughtcrime/securesms/util/FrameRateTracker$1;->doFrame(J)V @@ -28203,15 +27982,12 @@ HSPLorg/thoughtcrime/securesms/util/JsonUtils$SaneJSONObject;->getString(Ljava/l HSPLorg/thoughtcrime/securesms/util/JsonUtils$SaneJSONObject;->isNull(Ljava/lang/String;)Z HSPLorg/thoughtcrime/securesms/util/JsonUtils;->()V HSPLorg/thoughtcrime/securesms/util/JsonUtils;->fromJson(Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object; -HSPLorg/thoughtcrime/securesms/util/JsonUtils;->fromJson([BLjava/lang/Class;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/util/JsonUtils;->getMapper()Lcom/fasterxml/jackson/databind/ObjectMapper; HSPLorg/thoughtcrime/securesms/util/JsonUtils;->toJson(Ljava/lang/Object;)Ljava/lang/String; HSPLorg/thoughtcrime/securesms/util/LRUCache;->(I)V HSPLorg/thoughtcrime/securesms/util/LRUCache;->removeEldestEntry(Ljava/util/Map$Entry;)Z -HSPLorg/thoughtcrime/securesms/util/LeakyBucketLimiter$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/util/LeakyBucketLimiter;)V HSPLorg/thoughtcrime/securesms/util/LeakyBucketLimiter;->()V HSPLorg/thoughtcrime/securesms/util/LeakyBucketLimiter;->(IJLandroid/os/Handler;)V -HSPLorg/thoughtcrime/securesms/util/LeakyBucketLimiter;->run(Ljava/lang/Runnable;)V HSPLorg/thoughtcrime/securesms/util/ListenableFutureTask$2;->(Lorg/thoughtcrime/securesms/util/ListenableFutureTask;)V HSPLorg/thoughtcrime/securesms/util/ListenableFutureTask$2;->run()V HSPLorg/thoughtcrime/securesms/util/ListenableFutureTask;->-$$Nest$fgetlisteners(Lorg/thoughtcrime/securesms/util/ListenableFutureTask;)Ljava/util/List; @@ -28272,6 +28048,7 @@ HSPLorg/thoughtcrime/securesms/util/Material3OnScrollHelper$3;->onCreate(Landroi HSPLorg/thoughtcrime/securesms/util/Material3OnScrollHelper$3;->onPause(Landroidx/lifecycle/LifecycleOwner;)V HSPLorg/thoughtcrime/securesms/util/Material3OnScrollHelper$3;->onResume(Landroidx/lifecycle/LifecycleOwner;)V HSPLorg/thoughtcrime/securesms/util/Material3OnScrollHelper$3;->onStart(Landroidx/lifecycle/LifecycleOwner;)V +HSPLorg/thoughtcrime/securesms/util/Material3OnScrollHelper$3;->onStop(Landroidx/lifecycle/LifecycleOwner;)V HSPLorg/thoughtcrime/securesms/util/Material3OnScrollHelper$ColorSet;->()V HSPLorg/thoughtcrime/securesms/util/Material3OnScrollHelper$ColorSet;->(I)V HSPLorg/thoughtcrime/securesms/util/Material3OnScrollHelper$ColorSet;->(II)V @@ -28305,7 +28082,6 @@ HSPLorg/thoughtcrime/securesms/util/MediaUtil;->isGif(Ljava/lang/String;)Z HSPLorg/thoughtcrime/securesms/util/MediaUtil;->isImageType(Ljava/lang/String;)Z HSPLorg/thoughtcrime/securesms/util/MediaUtil;->isInstantVideoSupported(Lorg/thoughtcrime/securesms/mms/Slide;)Z HSPLorg/thoughtcrime/securesms/util/MediaUtil;->isLongTextType(Ljava/lang/String;)Z -HSPLorg/thoughtcrime/securesms/util/MediaUtil;->isVideo(Ljava/lang/String;)Z HSPLorg/thoughtcrime/securesms/util/MessageRecordUtil;->hasAudio(Lorg/thoughtcrime/securesms/database/model/MessageRecord;)Z HSPLorg/thoughtcrime/securesms/util/MessageRecordUtil;->hasDocument(Lorg/thoughtcrime/securesms/database/model/MessageRecord;)Z HSPLorg/thoughtcrime/securesms/util/MessageRecordUtil;->hasExtraText(Lorg/thoughtcrime/securesms/database/model/MessageRecord;)Z @@ -28334,28 +28110,15 @@ HSPLorg/thoughtcrime/securesms/util/NetworkUtil;->isConnected(Landroid/content/C HSPLorg/thoughtcrime/securesms/util/NetworkUtil;->isConnectedWifi(Landroid/content/Context;)Z HSPLorg/thoughtcrime/securesms/util/NoCrossfadeChangeDefaultAnimator;->()V HSPLorg/thoughtcrime/securesms/util/NullableSavedStateHandleDelegate;->(Landroidx/lifecycle/SavedStateHandle;Ljava/lang/String;)V -HSPLorg/thoughtcrime/securesms/util/ProfileUtil$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/recipients/Recipient;)V -HSPLorg/thoughtcrime/securesms/util/ProfileUtil$$ExternalSyntheticLambda0;->apply(Ljava/lang/Object;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/util/ProfileUtil$$ExternalSyntheticLambda2;->()V -HSPLorg/thoughtcrime/securesms/util/ProfileUtil$$ExternalSyntheticLambda3;->(Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/Recipient;)V -HSPLorg/thoughtcrime/securesms/util/ProfileUtil$$ExternalSyntheticLambda3;->call()Ljava/lang/Object; -HSPLorg/thoughtcrime/securesms/util/ProfileUtil$$ExternalSyntheticLambda4;->(Lorg/whispersystems/signalservice/api/services/ProfileService;Lj$/util/Optional;Lj$/util/Optional;Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;Lorg/thoughtcrime/securesms/recipients/Recipient;)V -HSPLorg/thoughtcrime/securesms/util/ProfileUtil$$ExternalSyntheticLambda4;->apply(Ljava/lang/Object;)Ljava/lang/Object; -HSPLorg/thoughtcrime/securesms/util/ProfileUtil$$ExternalSyntheticLambda5;->(Lorg/thoughtcrime/securesms/recipients/Recipient;)V -HSPLorg/thoughtcrime/securesms/util/ProfileUtil;->$r8$lambda$64DRPwLhDKidiYVBrJ1oGsaeSVY(Lorg/thoughtcrime/securesms/recipients/Recipient;Lorg/whispersystems/signalservice/internal/ServiceResponse;)Lorg/signal/libsignal/protocol/util/Pair; -HSPLorg/thoughtcrime/securesms/util/ProfileUtil;->$r8$lambda$cqO5Ws54dRBOxkD_sPlVLlSYwIg(Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/Recipient;)Lorg/whispersystems/signalservice/api/push/SignalServiceAddress; -HSPLorg/thoughtcrime/securesms/util/ProfileUtil;->$r8$lambda$dDuBqdOM1yCYB_18NZWtjJd7BlA(Lorg/whispersystems/signalservice/api/services/ProfileService;Lj$/util/Optional;Lj$/util/Optional;Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;Lorg/thoughtcrime/securesms/recipients/Recipient;Lorg/whispersystems/signalservice/api/push/SignalServiceAddress;)Lio/reactivex/rxjava3/core/SingleSource; HSPLorg/thoughtcrime/securesms/util/ProfileUtil;->()V HSPLorg/thoughtcrime/securesms/util/ProfileUtil;->handleSelfProfileKeyChange()V -HSPLorg/thoughtcrime/securesms/util/ProfileUtil;->lambda$retrieveProfile$0(Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/Recipient;)Lorg/whispersystems/signalservice/api/push/SignalServiceAddress; -HSPLorg/thoughtcrime/securesms/util/ProfileUtil;->lambda$retrieveProfile$1(Lorg/thoughtcrime/securesms/recipients/Recipient;Lorg/whispersystems/signalservice/internal/ServiceResponse;)Lorg/signal/libsignal/protocol/util/Pair; -HSPLorg/thoughtcrime/securesms/util/ProfileUtil;->lambda$retrieveProfile$2(Lorg/whispersystems/signalservice/api/services/ProfileService;Lj$/util/Optional;Lj$/util/Optional;Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;Lorg/thoughtcrime/securesms/recipients/Recipient;Lorg/whispersystems/signalservice/api/push/SignalServiceAddress;)Lio/reactivex/rxjava3/core/SingleSource; -HSPLorg/thoughtcrime/securesms/util/ProfileUtil;->retrieveProfile(Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/Recipient;Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;Z)Lio/reactivex/rxjava3/core/Single; -HSPLorg/thoughtcrime/securesms/util/ProfileUtil;->retrieveProfileSync(Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/Recipient;Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;Z)Lorg/whispersystems/signalservice/api/profiles/ProfileAndCredential; -HSPLorg/thoughtcrime/securesms/util/ProfileUtil;->toSignalServiceAddress(Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/Recipient;)Lorg/whispersystems/signalservice/api/push/SignalServiceAddress; HSPLorg/thoughtcrime/securesms/util/Projection$Corners;->()V HSPLorg/thoughtcrime/securesms/util/Projection$Corners;->(F)V HSPLorg/thoughtcrime/securesms/util/Projection$Corners;->(FFFF)V +HSPLorg/thoughtcrime/securesms/util/Projection$Corners;->([F)V +HSPLorg/thoughtcrime/securesms/util/Projection$Corners;->toRadii()[F +HSPLorg/thoughtcrime/securesms/util/Projection$Corners;->toRelativeRadii(Z)[F HSPLorg/thoughtcrime/securesms/util/ProjectionList;->()V HSPLorg/thoughtcrime/securesms/util/ProjectionList;->()V HSPLorg/thoughtcrime/securesms/util/ProjectionList;->(I)V @@ -28444,11 +28207,17 @@ HSPLorg/thoughtcrime/securesms/util/SoftHashMap$SoftValue;->(Ljava/lang/Ob HSPLorg/thoughtcrime/securesms/util/SoftHashMap;->()V HSPLorg/thoughtcrime/securesms/util/SoftHashMap;->(I)V HSPLorg/thoughtcrime/securesms/util/SoftHashMap;->addToStrongReferences(Ljava/lang/Object;)V -HSPLorg/thoughtcrime/securesms/util/SoftHashMap;->clear()V HSPLorg/thoughtcrime/securesms/util/SoftHashMap;->get(Ljava/lang/Object;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/util/SoftHashMap;->processQueue()V HSPLorg/thoughtcrime/securesms/util/SoftHashMap;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; HSPLorg/thoughtcrime/securesms/util/SoftHashMap;->trimStrongReferencesIfNecessary()V +HSPLorg/thoughtcrime/securesms/util/SpanUtil;->()V +HSPLorg/thoughtcrime/securesms/util/SpanUtil;->buildCenteredImageSpan(Landroid/graphics/drawable/Drawable;)Ljava/lang/CharSequence; +HSPLorg/thoughtcrime/securesms/util/SpanUtil;->space(ILorg/signal/core/util/DimensionUnit;)Ljava/lang/CharSequence; +HSPLorg/thoughtcrime/securesms/util/SplashScreenUtil$$ExternalSyntheticApiModelOutline0;->m(Landroid/app/Activity;)Landroid/window/SplashScreen; +HSPLorg/thoughtcrime/securesms/util/SplashScreenUtil$$ExternalSyntheticApiModelOutline1;->m(Landroid/window/SplashScreen;I)V +HSPLorg/thoughtcrime/securesms/util/SplashScreenUtil$1;->()V +HSPLorg/thoughtcrime/securesms/util/SplashScreenUtil;->setSplashScreenThemeIfNecessary(Landroid/app/Activity;Lorg/thoughtcrime/securesms/keyvalue/SettingsValues$Theme;)V HSPLorg/thoughtcrime/securesms/util/StorageUtil;->getCleanFileName(Ljava/lang/String;)Ljava/lang/String; HSPLorg/thoughtcrime/securesms/util/TextSecurePreferences$MediaKeyboardMode;->$values()[Lorg/thoughtcrime/securesms/util/TextSecurePreferences$MediaKeyboardMode; HSPLorg/thoughtcrime/securesms/util/TextSecurePreferences$MediaKeyboardMode;->()V @@ -28501,7 +28270,6 @@ HSPLorg/thoughtcrime/securesms/util/TextSecurePreferences;->isScreenLockEnabled( HSPLorg/thoughtcrime/securesms/util/TextSecurePreferences;->isScreenSecurityEnabled(Landroid/content/Context;)Z HSPLorg/thoughtcrime/securesms/util/TextSecurePreferences;->isSystemEmojiPreferred(Landroid/content/Context;)Z HSPLorg/thoughtcrime/securesms/util/TextSecurePreferences;->isUnauthorizedReceived(Landroid/content/Context;)Z -HSPLorg/thoughtcrime/securesms/util/TextSecurePreferences;->isUniversalUnidentifiedAccess(Landroid/content/Context;)Z HSPLorg/thoughtcrime/securesms/util/TextSecurePreferences;->needsFullContactSync(Landroid/content/Context;)Z HSPLorg/thoughtcrime/securesms/util/TextSecurePreferences;->setBooleanPreference(Landroid/content/Context;Ljava/lang/String;Z)V HSPLorg/thoughtcrime/securesms/util/TextSecurePreferences;->setDirectoryRefreshTime(Landroid/content/Context;J)V @@ -28549,7 +28317,10 @@ HSPLorg/thoughtcrime/securesms/util/VersionTracker;->getLastSeenVersion(Landroid HSPLorg/thoughtcrime/securesms/util/VersionTracker;->updateLastSeenVersion(Landroid/content/Context;)V HSPLorg/thoughtcrime/securesms/util/ViewExtensionsKt;->drawAsTopItemDecoration(Landroid/view/View;Landroid/graphics/Canvas;Landroid/view/View;Landroid/view/View;I)V HSPLorg/thoughtcrime/securesms/util/ViewExtensionsKt;->getLifecycle(Landroid/view/View;)Landroidx/lifecycle/Lifecycle; +HSPLorg/thoughtcrime/securesms/util/ViewExtensionsKt;->getVisible(Landroid/view/View;)Z HSPLorg/thoughtcrime/securesms/util/ViewExtensionsKt;->layoutIn(Landroid/view/View;Landroid/view/View;)V +HSPLorg/thoughtcrime/securesms/util/ViewExtensionsKt;->padding$default(Landroid/view/View;IIIIILjava/lang/Object;)V +HSPLorg/thoughtcrime/securesms/util/ViewExtensionsKt;->padding(Landroid/view/View;IIII)V HSPLorg/thoughtcrime/securesms/util/ViewExtensionsKt;->setVisible(Landroid/view/View;Z)V HSPLorg/thoughtcrime/securesms/util/ViewModelFactory$Companion$factoryProducer$1;->(Lkotlin/jvm/functions/Function0;)V HSPLorg/thoughtcrime/securesms/util/ViewModelFactory$Companion$factoryProducer$1;->invoke()Ljava/lang/Object; @@ -28588,8 +28359,14 @@ HSPLorg/thoughtcrime/securesms/util/ViewModelFactoryKt$viewModel$$inlined$viewMo HSPLorg/thoughtcrime/securesms/util/ViewModelFactoryKt$viewModel$$inlined$viewModels$default$4;->(Lkotlin/jvm/functions/Function0;Lkotlin/Lazy;)V HSPLorg/thoughtcrime/securesms/util/ViewModelFactoryKt$viewModel$$inlined$viewModels$default$4;->invoke()Landroidx/lifecycle/viewmodel/CreationExtras; HSPLorg/thoughtcrime/securesms/util/ViewModelFactoryKt$viewModel$$inlined$viewModels$default$4;->invoke()Ljava/lang/Object; +HSPLorg/thoughtcrime/securesms/util/ViewUtil;->animateOut(Landroid/view/View;Landroid/view/animation/Animation;I)Lorg/thoughtcrime/securesms/util/concurrent/ListenableFuture; HSPLorg/thoughtcrime/securesms/util/ViewUtil;->dpToPx(I)I +HSPLorg/thoughtcrime/securesms/util/ViewUtil;->fadeOut(Landroid/view/View;I)Lorg/thoughtcrime/securesms/util/concurrent/ListenableFuture; +HSPLorg/thoughtcrime/securesms/util/ViewUtil;->fadeOut(Landroid/view/View;II)Lorg/thoughtcrime/securesms/util/concurrent/ListenableFuture; HSPLorg/thoughtcrime/securesms/util/ViewUtil;->findStubById(Landroid/view/View;I)Lorg/thoughtcrime/securesms/util/views/Stub; +HSPLorg/thoughtcrime/securesms/util/ViewUtil;->getAlphaAnimation(FFI)Landroid/view/animation/Animation; +HSPLorg/thoughtcrime/securesms/util/ViewUtil;->getLeftMargin(Landroid/view/View;)I +HSPLorg/thoughtcrime/securesms/util/ViewUtil;->getRightMargin(Landroid/view/View;)I HSPLorg/thoughtcrime/securesms/util/ViewUtil;->getTopMargin(Landroid/view/View;)I HSPLorg/thoughtcrime/securesms/util/ViewUtil;->getWidth(Landroid/view/View;)I HSPLorg/thoughtcrime/securesms/util/ViewUtil;->isLtr(Landroid/content/Context;)Z @@ -28637,10 +28414,6 @@ HSPLorg/thoughtcrime/securesms/util/adapter/mapping/MappingAdapter;->onViewAttac HSPLorg/thoughtcrime/securesms/util/adapter/mapping/MappingAdapter;->registerFactory(Ljava/lang/Class;Lj$/util/function/Function;I)V HSPLorg/thoughtcrime/securesms/util/adapter/mapping/MappingAdapter;->registerFactory(Ljava/lang/Class;Lorg/thoughtcrime/securesms/util/adapter/mapping/Factory;)V HSPLorg/thoughtcrime/securesms/util/adapter/mapping/MappingDiffCallback;->()V -HSPLorg/thoughtcrime/securesms/util/adapter/mapping/MappingDiffCallback;->areContentsTheSame(Ljava/lang/Object;Ljava/lang/Object;)Z -HSPLorg/thoughtcrime/securesms/util/adapter/mapping/MappingDiffCallback;->areContentsTheSame(Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingModel;Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingModel;)Z -HSPLorg/thoughtcrime/securesms/util/adapter/mapping/MappingDiffCallback;->areItemsTheSame(Ljava/lang/Object;Ljava/lang/Object;)Z -HSPLorg/thoughtcrime/securesms/util/adapter/mapping/MappingDiffCallback;->areItemsTheSame(Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingModel;Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingModel;)Z HSPLorg/thoughtcrime/securesms/util/adapter/mapping/MappingModelList;->(Ljava/util/Collection;)V HSPLorg/thoughtcrime/securesms/util/adapter/mapping/MappingViewHolder;->(Landroid/view/View;)V HSPLorg/thoughtcrime/securesms/util/adapter/mapping/MappingViewHolder;->onAttachedToWindow()V @@ -28664,12 +28437,9 @@ HSPLorg/thoughtcrime/securesms/util/concurrent/SerialExecutor;->execute(Ljava/la HSPLorg/thoughtcrime/securesms/util/concurrent/SerialExecutor;->lambda$execute$0(Ljava/lang/Runnable;)V HSPLorg/thoughtcrime/securesms/util/concurrent/SerialExecutor;->scheduleNext()V HSPLorg/thoughtcrime/securesms/util/concurrent/SerialMonoLifoExecutor$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/util/concurrent/SerialMonoLifoExecutor;Ljava/lang/Runnable;)V -HSPLorg/thoughtcrime/securesms/util/concurrent/SerialMonoLifoExecutor$$ExternalSyntheticLambda0;->run()V -HSPLorg/thoughtcrime/securesms/util/concurrent/SerialMonoLifoExecutor;->$r8$lambda$axI96jKiGgASw-5DyS1pXfSexxk(Lorg/thoughtcrime/securesms/util/concurrent/SerialMonoLifoExecutor;Ljava/lang/Runnable;)V HSPLorg/thoughtcrime/securesms/util/concurrent/SerialMonoLifoExecutor;->(Ljava/util/concurrent/Executor;)V HSPLorg/thoughtcrime/securesms/util/concurrent/SerialMonoLifoExecutor;->enqueue(Ljava/lang/Runnable;)Z HSPLorg/thoughtcrime/securesms/util/concurrent/SerialMonoLifoExecutor;->execute(Ljava/lang/Runnable;)V -HSPLorg/thoughtcrime/securesms/util/concurrent/SerialMonoLifoExecutor;->lambda$enqueue$0(Ljava/lang/Runnable;)V HSPLorg/thoughtcrime/securesms/util/concurrent/SerialMonoLifoExecutor;->scheduleNext()V HSPLorg/thoughtcrime/securesms/util/concurrent/SettableFuture;->()V HSPLorg/thoughtcrime/securesms/util/concurrent/SettableFuture;->(Ljava/lang/Object;)V @@ -28815,14 +28585,7 @@ HSPLorg/webrtc/PeerConnectionFactory;->initialize(Lorg/webrtc/PeerConnectionFact HSPLorg/webrtc/WebRtcClassLoader;->getClassLoader()Ljava/lang/Object; HSPLorg/whispersystems/signalservice/api/SignalServiceAccountManager;->()V HSPLorg/whispersystems/signalservice/api/SignalServiceAccountManager;->(Lorg/whispersystems/signalservice/internal/configuration/SignalServiceConfiguration;Lorg/whispersystems/signalservice/api/util/CredentialsProvider;Ljava/lang/String;Lorg/whispersystems/signalservice/api/groupsv2/GroupsV2Operations;Z)V -HSPLorg/whispersystems/signalservice/api/SignalServiceAccountManager;->getPreKeyCounts(Lorg/whispersystems/signalservice/api/push/ServiceIdType;)Lorg/whispersystems/signalservice/internal/push/OneTimePreKeyCounts; HSPLorg/whispersystems/signalservice/api/SignalServiceAccountManager;->getSecureValueRecoveryV2(Ljava/lang/String;)Lorg/whispersystems/signalservice/api/svr/SecureValueRecoveryV2; -HSPLorg/whispersystems/signalservice/api/SignalServiceAccountManager;->getSenderCertificate()[B -HSPLorg/whispersystems/signalservice/api/SignalServiceAccountManager;->setAccountAttributes(Lorg/whispersystems/signalservice/api/account/AccountAttributes;)V -HSPLorg/whispersystems/signalservice/api/SignalServiceMessageReceiver$$ExternalSyntheticLambda0;->()V -HSPLorg/whispersystems/signalservice/api/SignalServiceMessageReceiver;->(Lorg/whispersystems/signalservice/internal/configuration/SignalServiceConfiguration;Lorg/whispersystems/signalservice/api/util/CredentialsProvider;Ljava/lang/String;Lorg/signal/libsignal/zkgroup/profiles/ClientZkProfileOperations;Z)V -HSPLorg/whispersystems/signalservice/api/SignalServiceMessageReceiver;->retrieveProfile(Lorg/whispersystems/signalservice/api/push/SignalServiceAddress;Lj$/util/Optional;Lj$/util/Optional;Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;Ljava/util/Locale;)Lorg/whispersystems/signalservice/internal/util/concurrent/ListenableFuture; -HSPLorg/whispersystems/signalservice/api/SignalServiceMessageReceiver;->retrieveStickerManifest([B[B)Lorg/whispersystems/signalservice/api/messages/SignalServiceStickerManifest; HSPLorg/whispersystems/signalservice/api/SignalWebSocket$$ExternalSyntheticLambda0;->(Lio/reactivex/rxjava3/subjects/BehaviorSubject;)V HSPLorg/whispersystems/signalservice/api/SignalWebSocket$$ExternalSyntheticLambda0;->accept(Ljava/lang/Object;)V HSPLorg/whispersystems/signalservice/api/SignalWebSocket;->()V @@ -28836,39 +28599,10 @@ HSPLorg/whispersystems/signalservice/api/SignalWebSocket;->getUnidentifiedWebSoc HSPLorg/whispersystems/signalservice/api/SignalWebSocket;->getWebSocket()Lorg/whispersystems/signalservice/internal/websocket/WebSocketConnection; HSPLorg/whispersystems/signalservice/api/SignalWebSocket;->getWebSocketState()Lio/reactivex/rxjava3/core/Observable; HSPLorg/whispersystems/signalservice/api/SignalWebSocket;->readMessageBatch(JILorg/whispersystems/signalservice/api/SignalWebSocket$MessageReceivedCallback;)Z -HSPLorg/whispersystems/signalservice/api/SignalWebSocket;->request(Lorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage;)Lio/reactivex/rxjava3/core/Single; -HSPLorg/whispersystems/signalservice/api/SignalWebSocket;->request(Lorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage;Lj$/util/Optional;)Lio/reactivex/rxjava3/core/Single; HSPLorg/whispersystems/signalservice/api/SignalWebSocket;->waitForSingleMessage(J)Lj$/util/Optional; -HSPLorg/whispersystems/signalservice/api/account/AccountAttributes$Capabilities;->(ZZZZZZZZ)V -HSPLorg/whispersystems/signalservice/api/account/AccountAttributes$Capabilities;->getAnnouncementGroup()Z -HSPLorg/whispersystems/signalservice/api/account/AccountAttributes$Capabilities;->getChangeNumber()Z -HSPLorg/whispersystems/signalservice/api/account/AccountAttributes$Capabilities;->getGiftBadges()Z -HSPLorg/whispersystems/signalservice/api/account/AccountAttributes$Capabilities;->getPaymentActivation()Z -HSPLorg/whispersystems/signalservice/api/account/AccountAttributes$Capabilities;->getPni()Z -HSPLorg/whispersystems/signalservice/api/account/AccountAttributes$Capabilities;->getSenderKey()Z -HSPLorg/whispersystems/signalservice/api/account/AccountAttributes$Capabilities;->getStorage()Z -HSPLorg/whispersystems/signalservice/api/account/AccountAttributes$Capabilities;->getStories()Z -HSPLorg/whispersystems/signalservice/api/account/AccountAttributes$Capabilities;->toString()Ljava/lang/String; -HSPLorg/whispersystems/signalservice/api/account/AccountAttributes;->(Ljava/lang/String;IZLjava/lang/String;[BZLorg/whispersystems/signalservice/api/account/AccountAttributes$Capabilities;ZLjava/lang/String;ILjava/lang/String;)V -HSPLorg/whispersystems/signalservice/api/account/AccountAttributes;->(Ljava/lang/String;IZZZLjava/lang/String;[BZZLorg/whispersystems/signalservice/api/account/AccountAttributes$Capabilities;Ljava/lang/String;ILjava/lang/String;)V -HSPLorg/whispersystems/signalservice/api/account/AccountAttributes;->getCapabilities()Lorg/whispersystems/signalservice/api/account/AccountAttributes$Capabilities; -HSPLorg/whispersystems/signalservice/api/account/AccountAttributes;->getDiscoverableByPhoneNumber()Z -HSPLorg/whispersystems/signalservice/api/account/AccountAttributes;->getFetchesMessages()Z -HSPLorg/whispersystems/signalservice/api/account/AccountAttributes;->getName()Ljava/lang/String; -HSPLorg/whispersystems/signalservice/api/account/AccountAttributes;->getPniRegistrationId()I -HSPLorg/whispersystems/signalservice/api/account/AccountAttributes;->getRecoveryPassword()Ljava/lang/String; -HSPLorg/whispersystems/signalservice/api/account/AccountAttributes;->getRegistrationId()I -HSPLorg/whispersystems/signalservice/api/account/AccountAttributes;->getRegistrationLock()Ljava/lang/String; -HSPLorg/whispersystems/signalservice/api/account/AccountAttributes;->getSignalingKey()Ljava/lang/String; -HSPLorg/whispersystems/signalservice/api/account/AccountAttributes;->getUnidentifiedAccessKey()[B -HSPLorg/whispersystems/signalservice/api/account/AccountAttributes;->getUnrestrictedUnidentifiedAccess()Z -HSPLorg/whispersystems/signalservice/api/account/AccountAttributes;->getVideo()Z -HSPLorg/whispersystems/signalservice/api/account/AccountAttributes;->getVoice()Z HSPLorg/whispersystems/signalservice/api/account/PreKeyCollection;->(Lorg/signal/libsignal/protocol/IdentityKey;Lorg/signal/libsignal/protocol/state/SignedPreKeyRecord;Lorg/signal/libsignal/protocol/state/KyberPreKeyRecord;)V HSPLorg/whispersystems/signalservice/api/account/PreKeyCollection;->getLastResortKyberPreKey()Lorg/signal/libsignal/protocol/state/KyberPreKeyRecord; HSPLorg/whispersystems/signalservice/api/account/PreKeyCollection;->getSignedPreKey()Lorg/signal/libsignal/protocol/state/SignedPreKeyRecord; -HSPLorg/whispersystems/signalservice/api/crypto/UnidentifiedAccess;->createEmptyByteArray(I)[B -HSPLorg/whispersystems/signalservice/api/crypto/UnidentifiedAccess;->deriveAccessKeyFrom(Lorg/signal/libsignal/zkgroup/profiles/ProfileKey;)[B HSPLorg/whispersystems/signalservice/api/groupsv2/ClientZkOperations;->(Lorg/signal/libsignal/zkgroup/ServerPublicParams;)V HSPLorg/whispersystems/signalservice/api/groupsv2/ClientZkOperations;->create(Lorg/whispersystems/signalservice/internal/configuration/SignalServiceConfiguration;)Lorg/whispersystems/signalservice/api/groupsv2/ClientZkOperations; HSPLorg/whispersystems/signalservice/api/groupsv2/ClientZkOperations;->getAuthOperations()Lorg/signal/libsignal/zkgroup/auth/ClientZkAuthOperations; @@ -28879,6 +28613,8 @@ HSPLorg/whispersystems/signalservice/api/groupsv2/GroupsV2Operations;->(Lo HSPLorg/whispersystems/signalservice/api/groupsv2/GroupsV2Operations;->getProfileOperations()Lorg/signal/libsignal/zkgroup/profiles/ClientZkProfileOperations; HSPLorg/whispersystems/signalservice/api/kbs/MasterKey;->([B)V HSPLorg/whispersystems/signalservice/api/kbs/MasterKey;->createNew(Ljava/security/SecureRandom;)Lorg/whispersystems/signalservice/api/kbs/MasterKey; +HSPLorg/whispersystems/signalservice/api/kbs/MasterKey;->derive(Ljava/lang/String;)[B +HSPLorg/whispersystems/signalservice/api/kbs/MasterKey;->deriveLoggingKey()[B HSPLorg/whispersystems/signalservice/api/kbs/MasterKey;->serialize()[B HSPLorg/whispersystems/signalservice/api/messages/SignalServiceAttachment;->(Ljava/lang/String;)V HSPLorg/whispersystems/signalservice/api/messages/SignalServiceAttachment;->asPointer()Lorg/whispersystems/signalservice/api/messages/SignalServiceAttachmentPointer; @@ -28925,9 +28661,6 @@ HSPLorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$Capabilit HSPLorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$Capabilities;->isPnp()Z HSPLorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$Capabilities;->isSenderKey()Z HSPLorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$Capabilities;->isStories()Z -HSPLorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;->$values()[Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType; -HSPLorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;->()V -HSPLorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;->(Ljava/lang/String;I)V HSPLorg/whispersystems/signalservice/api/push/ServiceId$ACI$Companion;->()V HSPLorg/whispersystems/signalservice/api/push/ServiceId$ACI$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/whispersystems/signalservice/api/push/ServiceId$ACI$Companion;->from(Ljava/util/UUID;)Lorg/whispersystems/signalservice/api/push/ServiceId$ACI; @@ -28936,7 +28669,6 @@ HSPLorg/whispersystems/signalservice/api/push/ServiceId$ACI$Companion;->parseOrT HSPLorg/whispersystems/signalservice/api/push/ServiceId$ACI;->()V HSPLorg/whispersystems/signalservice/api/push/ServiceId$ACI;->(Lorg/signal/libsignal/protocol/ServiceId$Aci;)V HSPLorg/whispersystems/signalservice/api/push/ServiceId$ACI;->equals(Ljava/lang/Object;)Z -HSPLorg/whispersystems/signalservice/api/push/ServiceId$ACI;->getLibSignalAci()Lorg/signal/libsignal/protocol/ServiceId$Aci; HSPLorg/whispersystems/signalservice/api/push/ServiceId$ACI;->hashCode()I HSPLorg/whispersystems/signalservice/api/push/ServiceId$ACI;->parseOrThrow(Ljava/lang/String;)Lorg/whispersystems/signalservice/api/push/ServiceId$ACI; HSPLorg/whispersystems/signalservice/api/push/ServiceId$ACI;->toString()Ljava/lang/String; @@ -28962,36 +28694,13 @@ HSPLorg/whispersystems/signalservice/api/push/ServiceId;->(Lorg/signal/lib HSPLorg/whispersystems/signalservice/api/push/ServiceId;->fromLibSignal(Lorg/signal/libsignal/protocol/ServiceId;)Lorg/whispersystems/signalservice/api/push/ServiceId; HSPLorg/whispersystems/signalservice/api/push/ServiceId;->isUnknown()Z HSPLorg/whispersystems/signalservice/api/push/ServiceId;->toString()Ljava/lang/String; -HSPLorg/whispersystems/signalservice/api/push/ServiceIdType;->$values()[Lorg/whispersystems/signalservice/api/push/ServiceIdType; -HSPLorg/whispersystems/signalservice/api/push/ServiceIdType;->()V -HSPLorg/whispersystems/signalservice/api/push/ServiceIdType;->(Ljava/lang/String;ILjava/lang/String;)V -HSPLorg/whispersystems/signalservice/api/push/ServiceIdType;->queryParam()Ljava/lang/String; HSPLorg/whispersystems/signalservice/api/push/SignalServiceAddress;->(Lorg/whispersystems/signalservice/api/push/ServiceId;Lj$/util/Optional;)V HSPLorg/whispersystems/signalservice/api/push/SignalServiceAddress;->(Lorg/whispersystems/signalservice/api/push/ServiceId;Ljava/lang/String;)V HSPLorg/whispersystems/signalservice/api/push/SignalServiceAddress;->getNumber()Lj$/util/Optional; HSPLorg/whispersystems/signalservice/api/push/SignalServiceAddress;->getServiceId()Lorg/whispersystems/signalservice/api/push/ServiceId; -HSPLorg/whispersystems/signalservice/api/push/exceptions/NonSuccessfulResponseCodeException;->(ILjava/lang/String;)V -HSPLorg/whispersystems/signalservice/api/push/exceptions/NonSuccessfulResponseCodeException;->getCode()I HSPLorg/whispersystems/signalservice/api/services/DonationsService;->()V HSPLorg/whispersystems/signalservice/api/services/DonationsService;->(Lorg/whispersystems/signalservice/internal/configuration/SignalServiceConfiguration;Lorg/whispersystems/signalservice/api/util/CredentialsProvider;Ljava/lang/String;Lorg/whispersystems/signalservice/api/groupsv2/GroupsV2Operations;Z)V HSPLorg/whispersystems/signalservice/api/services/DonationsService;->(Lorg/whispersystems/signalservice/internal/push/PushServiceSocket;)V -HSPLorg/whispersystems/signalservice/api/services/MessagingService$$ExternalSyntheticLambda4;->(Lorg/whispersystems/signalservice/internal/websocket/ResponseMapper;)V -HSPLorg/whispersystems/signalservice/api/services/ProfileService$$ExternalSyntheticLambda1;->(Lorg/whispersystems/signalservice/api/services/ProfileService;Lorg/whispersystems/signalservice/api/push/SignalServiceAddress;Lj$/util/Optional;Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;Ljava/util/Locale;)V -HSPLorg/whispersystems/signalservice/api/services/ProfileService$$ExternalSyntheticLambda1;->apply(Ljava/lang/Object;)Ljava/lang/Object; -HSPLorg/whispersystems/signalservice/api/services/ProfileService$$ExternalSyntheticLambda2;->()V -HSPLorg/whispersystems/signalservice/api/services/ProfileService$$ExternalSyntheticLambda3;->(Lorg/whispersystems/signalservice/api/services/ProfileService;Lorg/whispersystems/signalservice/api/push/SignalServiceAddress;Lj$/util/Optional;Lj$/util/Optional;Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;Ljava/util/Locale;)V -HSPLorg/whispersystems/signalservice/api/services/ProfileService$$ExternalSyntheticLambda3;->apply(Ljava/lang/Object;)Ljava/lang/Object; -HSPLorg/whispersystems/signalservice/api/services/ProfileService$ProfileResponseMapper;->(Lorg/whispersystems/signalservice/api/services/ProfileService;Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;Lorg/signal/libsignal/zkgroup/profiles/ProfileKeyCredentialRequestContext;)V -HSPLorg/whispersystems/signalservice/api/services/ProfileService$ProfileResponseProcessor;->(Lorg/whispersystems/signalservice/internal/ServiceResponse;)V -HSPLorg/whispersystems/signalservice/api/services/ProfileService$ProfileResponseProcessor;->getError()Ljava/lang/Throwable; -HSPLorg/whispersystems/signalservice/api/services/ProfileService;->$r8$lambda$qreHJhpvwxlLuI7XzIfP9eBBI24(Lorg/whispersystems/signalservice/api/services/ProfileService;Lorg/whispersystems/signalservice/api/push/SignalServiceAddress;Lj$/util/Optional;Lj$/util/Optional;Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;Ljava/util/Locale;Ljava/lang/Throwable;)Lio/reactivex/rxjava3/core/SingleSource; -HSPLorg/whispersystems/signalservice/api/services/ProfileService;->$r8$lambda$vXxdyhUGBLq7AFV9vG9NvMDxbNI(Lorg/whispersystems/signalservice/api/services/ProfileService;Lorg/whispersystems/signalservice/api/push/SignalServiceAddress;Lj$/util/Optional;Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;Ljava/util/Locale;Ljava/lang/Throwable;)Lio/reactivex/rxjava3/core/SingleSource; -HSPLorg/whispersystems/signalservice/api/services/ProfileService;->()V -HSPLorg/whispersystems/signalservice/api/services/ProfileService;->(Lorg/signal/libsignal/zkgroup/profiles/ClientZkProfileOperations;Lorg/whispersystems/signalservice/api/SignalServiceMessageReceiver;Lorg/whispersystems/signalservice/api/SignalWebSocket;)V -HSPLorg/whispersystems/signalservice/api/services/ProfileService;->getProfile(Lorg/whispersystems/signalservice/api/push/SignalServiceAddress;Lj$/util/Optional;Lj$/util/Optional;Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;Ljava/util/Locale;)Lio/reactivex/rxjava3/core/Single; -HSPLorg/whispersystems/signalservice/api/services/ProfileService;->getProfileRestFallback(Lorg/whispersystems/signalservice/api/push/SignalServiceAddress;Lj$/util/Optional;Lj$/util/Optional;Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;Ljava/util/Locale;)Lio/reactivex/rxjava3/core/Single; -HSPLorg/whispersystems/signalservice/api/services/ProfileService;->lambda$getProfile$0(Lorg/whispersystems/signalservice/api/push/SignalServiceAddress;Lj$/util/Optional;Lj$/util/Optional;Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;Ljava/util/Locale;Ljava/lang/Throwable;)Lio/reactivex/rxjava3/core/SingleSource; -HSPLorg/whispersystems/signalservice/api/services/ProfileService;->lambda$getProfileRestFallback$3(Lorg/whispersystems/signalservice/api/push/SignalServiceAddress;Lj$/util/Optional;Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;Ljava/util/Locale;Ljava/lang/Throwable;)Lio/reactivex/rxjava3/core/SingleSource; HSPLorg/whispersystems/signalservice/api/svr/SecureValueRecoveryV2$Companion;->()V HSPLorg/whispersystems/signalservice/api/svr/SecureValueRecoveryV2$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V HSPLorg/whispersystems/signalservice/api/svr/SecureValueRecoveryV2;->()V @@ -29022,15 +28731,10 @@ HSPLorg/whispersystems/signalservice/api/websocket/WebSocketConnectionState;->is HSPLorg/whispersystems/signalservice/api/websocket/WebSocketConnectionState;->values()[Lorg/whispersystems/signalservice/api/websocket/WebSocketConnectionState; HSPLorg/whispersystems/signalservice/api/websocket/WebSocketUnavailableException;->()V HSPLorg/whispersystems/signalservice/internal/ServiceResponse;->(ILjava/lang/String;Ljava/lang/Object;Ljava/lang/Throwable;Ljava/lang/Throwable;)V -HSPLorg/whispersystems/signalservice/internal/ServiceResponse;->forApplicationError(Ljava/lang/Throwable;ILjava/lang/String;)Lorg/whispersystems/signalservice/internal/ServiceResponse; HSPLorg/whispersystems/signalservice/internal/ServiceResponse;->forResult(Ljava/lang/Object;ILjava/lang/String;)Lorg/whispersystems/signalservice/internal/ServiceResponse; -HSPLorg/whispersystems/signalservice/internal/ServiceResponse;->forUnknownError(Ljava/lang/Throwable;)Lorg/whispersystems/signalservice/internal/ServiceResponse; -HSPLorg/whispersystems/signalservice/internal/ServiceResponse;->getApplicationError()Lj$/util/Optional; -HSPLorg/whispersystems/signalservice/internal/ServiceResponse;->getExecutionError()Lj$/util/Optional; HSPLorg/whispersystems/signalservice/internal/ServiceResponse;->getResult()Lj$/util/Optional; HSPLorg/whispersystems/signalservice/internal/ServiceResponseProcessor$DefaultProcessor;->(Lorg/whispersystems/signalservice/internal/ServiceResponse;)V HSPLorg/whispersystems/signalservice/internal/ServiceResponseProcessor;->(Lorg/whispersystems/signalservice/internal/ServiceResponse;)V -HSPLorg/whispersystems/signalservice/internal/ServiceResponseProcessor;->getError()Ljava/lang/Throwable; HSPLorg/whispersystems/signalservice/internal/ServiceResponseProcessor;->getResult()Ljava/lang/Object; HSPLorg/whispersystems/signalservice/internal/ServiceResponseProcessor;->getResultOrThrow()Ljava/lang/Object; HSPLorg/whispersystems/signalservice/internal/ServiceResponseProcessor;->hasResult()Z @@ -29039,7 +28743,7 @@ HSPLorg/whispersystems/signalservice/internal/configuration/SignalCdnUrl;->(Ljava/lang/String;Ljava/lang/String;Lorg/whispersystems/signalservice/api/push/TrustStore;Lokhttp3/ConnectionSpec;)V HSPLorg/whispersystems/signalservice/internal/configuration/SignalCdsiUrl;->(Ljava/lang/String;Lorg/whispersystems/signalservice/api/push/TrustStore;)V HSPLorg/whispersystems/signalservice/internal/configuration/SignalKeyBackupServiceUrl;->(Ljava/lang/String;Ljava/lang/String;Lorg/whispersystems/signalservice/api/push/TrustStore;Lokhttp3/ConnectionSpec;)V -HSPLorg/whispersystems/signalservice/internal/configuration/SignalServiceConfiguration;->([Lorg/whispersystems/signalservice/internal/configuration/SignalServiceUrl;Ljava/util/Map;[Lorg/whispersystems/signalservice/internal/configuration/SignalStorageUrl;[Lorg/whispersystems/signalservice/internal/configuration/SignalCdsiUrl;[Lorg/whispersystems/signalservice/internal/configuration/SignalSvr2Url;Ljava/util/List;Lj$/util/Optional;Lj$/util/Optional;[B[B)V +HSPLorg/whispersystems/signalservice/internal/configuration/SignalServiceConfiguration;->([Lorg/whispersystems/signalservice/internal/configuration/SignalServiceUrl;Ljava/util/Map;[Lorg/whispersystems/signalservice/internal/configuration/SignalStorageUrl;[Lorg/whispersystems/signalservice/internal/configuration/SignalCdsiUrl;[Lorg/whispersystems/signalservice/internal/configuration/SignalSvr2Url;Ljava/util/List;Lj$/util/Optional;Lj$/util/Optional;[B[B[B)V HSPLorg/whispersystems/signalservice/internal/configuration/SignalServiceConfiguration;->getDns()Lj$/util/Optional; HSPLorg/whispersystems/signalservice/internal/configuration/SignalServiceConfiguration;->getNetworkInterceptors()Ljava/util/List; HSPLorg/whispersystems/signalservice/internal/configuration/SignalServiceConfiguration;->getSignalCdnUrlMap()Ljava/util/Map; @@ -29059,55 +28763,27 @@ HSPLorg/whispersystems/signalservice/internal/configuration/SignalUrl;->getConne HSPLorg/whispersystems/signalservice/internal/configuration/SignalUrl;->getHostHeader()Lj$/util/Optional; HSPLorg/whispersystems/signalservice/internal/configuration/SignalUrl;->getTrustStore()Lorg/whispersystems/signalservice/api/push/TrustStore; HSPLorg/whispersystems/signalservice/internal/configuration/SignalUrl;->getUrl()Ljava/lang/String; +HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket$$ExternalSyntheticLambda10;->()V HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket$$ExternalSyntheticLambda11;->()V HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket$$ExternalSyntheticLambda12;->()V -HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket$$ExternalSyntheticLambda13;->()V -HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket$$ExternalSyntheticLambda15;->()V -HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket$1;->(Lorg/whispersystems/signalservice/internal/push/PushServiceSocket;Lorg/whispersystems/signalservice/internal/util/concurrent/SettableFuture;)V -HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket$1;->onResponse(Lokhttp3/Call;Lokhttp3/Response;)V HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket$2;->()V HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket$ConnectionHolder;->(Lokhttp3/OkHttpClient;Ljava/lang/String;Lj$/util/Optional;)V HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket$ConnectionHolder;->(Lokhttp3/OkHttpClient;Ljava/lang/String;Lj$/util/Optional;Lorg/whispersystems/signalservice/internal/push/PushServiceSocket$ConnectionHolder-IA;)V -HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket$ConnectionHolder;->getClient()Lokhttp3/OkHttpClient; -HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket$ConnectionHolder;->getHostHeader()Lj$/util/Optional; -HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket$ConnectionHolder;->getUrl()Ljava/lang/String; HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket$EmptyResponseCodeHandler;->()V HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket$EmptyResponseCodeHandler;->(Lorg/whispersystems/signalservice/internal/push/PushServiceSocket$EmptyResponseCodeHandler-IA;)V -HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket$EmptyResponseCodeHandler;->handle(ILokhttp3/ResponseBody;)V HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket$ServiceConnectionHolder;->(Lokhttp3/OkHttpClient;Lokhttp3/OkHttpClient;Ljava/lang/String;Lj$/util/Optional;)V HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket$ServiceConnectionHolder;->(Lokhttp3/OkHttpClient;Lokhttp3/OkHttpClient;Ljava/lang/String;Lj$/util/Optional;Lorg/whispersystems/signalservice/internal/push/PushServiceSocket$ServiceConnectionHolder-IA;)V -HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->-$$Nest$mvalidateServiceResponse(Lorg/whispersystems/signalservice/internal/push/PushServiceSocket;Lokhttp3/Response;)Lokhttp3/Response; HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->()V HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->(Lorg/whispersystems/signalservice/internal/configuration/SignalServiceConfiguration;Lorg/whispersystems/signalservice/api/util/CredentialsProvider;Ljava/lang/String;Lorg/signal/libsignal/zkgroup/profiles/ClientZkProfileOperations;Z)V -HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->buildOkHttpClient(Z)Lokhttp3/OkHttpClient; -HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->buildServiceRequest(Ljava/lang/String;Ljava/lang/String;Lokhttp3/RequestBody;Ljava/util/Map;Lj$/util/Optional;Z)Lokhttp3/Request; HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->createCdnClientsMap(Ljava/util/Map;Ljava/util/List;Lj$/util/Optional;Lj$/util/Optional;)Ljava/util/Map; HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->createConnectionClient(Lorg/whispersystems/signalservice/internal/configuration/SignalUrl;Ljava/util/List;Lj$/util/Optional;Lj$/util/Optional;)Lokhttp3/OkHttpClient; HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->createConnectionHolders([Lorg/whispersystems/signalservice/internal/configuration/SignalUrl;Ljava/util/List;Lj$/util/Optional;Lj$/util/Optional;)[Lorg/whispersystems/signalservice/internal/push/PushServiceSocket$ConnectionHolder; HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->createServiceConnectionHolders([Lorg/whispersystems/signalservice/internal/configuration/SignalUrl;Ljava/util/List;Lj$/util/Optional;Lj$/util/Optional;)[Lorg/whispersystems/signalservice/internal/push/PushServiceSocket$ServiceConnectionHolder; -HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->downloadFromCdn(Ljava/io/OutputStream;JILjava/lang/String;JLorg/whispersystems/signalservice/api/messages/SignalServiceAttachment$ProgressListener;)V -HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->getAuthorizationHeader(Lorg/whispersystems/signalservice/api/util/CredentialsProvider;)Ljava/lang/String; -HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->getAvailablePreKeys(Lorg/whispersystems/signalservice/api/push/ServiceIdType;)Lorg/whispersystems/signalservice/internal/push/OneTimePreKeyCounts; -HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->getRandom([Lorg/whispersystems/signalservice/internal/push/PushServiceSocket$ConnectionHolder;Ljava/security/SecureRandom;)Lorg/whispersystems/signalservice/internal/push/PushServiceSocket$ConnectionHolder; -HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->getSenderCertificate()[B -HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->getServiceConnection(Ljava/lang/String;Ljava/lang/String;Lokhttp3/RequestBody;Ljava/util/Map;Lj$/util/Optional;Z)Lokhttp3/Response; -HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->jsonRequestBody(Ljava/lang/String;)Lokhttp3/RequestBody; -HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->makeServiceRequest(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; -HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->makeServiceRequest(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;Lorg/whispersystems/signalservice/internal/push/PushServiceSocket$ResponseCodeHandler;Lj$/util/Optional;)Ljava/lang/String; -HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->makeServiceRequest(Ljava/lang/String;Ljava/lang/String;Lokhttp3/RequestBody;Ljava/util/Map;Lorg/whispersystems/signalservice/internal/push/PushServiceSocket$ResponseCodeHandler;Lj$/util/Optional;Z)Lokhttp3/Response; -HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->retrieveStickerManifest([B)[B -HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->retrieveVersionedProfile(Lorg/whispersystems/signalservice/api/push/ServiceId$ACI;Lorg/signal/libsignal/zkgroup/profiles/ProfileKey;Lj$/util/Optional;Ljava/util/Locale;)Lorg/whispersystems/signalservice/internal/util/concurrent/ListenableFuture; -HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->setAccountAttributes(Lorg/whispersystems/signalservice/api/account/AccountAttributes;)V -HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->submitServiceRequest(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;Lj$/util/Optional;)Lorg/whispersystems/signalservice/internal/util/concurrent/ListenableFuture; HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->validateConfiguration(Ljava/util/Map;)V -HSPLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->validateServiceResponse(Lokhttp3/Response;)Lokhttp3/Response; HSPLorg/whispersystems/signalservice/internal/push/VerifyAccountResponse;->(Ljava/lang/String;Ljava/lang/String;Z)V HSPLorg/whispersystems/signalservice/internal/push/VerifyAccountResponse;->getPni()Ljava/lang/String; HSPLorg/whispersystems/signalservice/internal/push/VerifyAccountResponse;->getUuid()Ljava/lang/String; HSPLorg/whispersystems/signalservice/internal/push/VerifyAccountResponse;->isStorageCapable()Z -HSPLorg/whispersystems/signalservice/internal/push/http/AcceptLanguagesUtil;->formatLanguages(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; -HSPLorg/whispersystems/signalservice/internal/push/http/AcceptLanguagesUtil;->getAcceptLanguageHeader(Ljava/util/Locale;)Ljava/lang/String; -HSPLorg/whispersystems/signalservice/internal/push/http/AcceptLanguagesUtil;->getHeadersWithAcceptLanguage(Ljava/util/Locale;)Ljava/util/Map; HSPLorg/whispersystems/signalservice/internal/util/BlacklistingTrustManager$1;->()V HSPLorg/whispersystems/signalservice/internal/util/BlacklistingTrustManager;->()V HSPLorg/whispersystems/signalservice/internal/util/BlacklistingTrustManager;->(Ljavax/net/ssl/X509TrustManager;)V @@ -29115,35 +28791,10 @@ HSPLorg/whispersystems/signalservice/internal/util/BlacklistingTrustManager;->ch HSPLorg/whispersystems/signalservice/internal/util/BlacklistingTrustManager;->createFor(Lorg/whispersystems/signalservice/api/push/TrustStore;)[Ljavax/net/ssl/TrustManager; HSPLorg/whispersystems/signalservice/internal/util/BlacklistingTrustManager;->createFor([Ljavax/net/ssl/TrustManager;)[Ljavax/net/ssl/TrustManager; HSPLorg/whispersystems/signalservice/internal/util/BlacklistingTrustManager;->getAcceptedIssuers()[Ljava/security/cert/X509Certificate; -HSPLorg/whispersystems/signalservice/internal/util/Hex;->()V -HSPLorg/whispersystems/signalservice/internal/util/Hex;->appendHexChar(Ljava/lang/StringBuffer;I)V -HSPLorg/whispersystems/signalservice/internal/util/Hex;->toStringCondensed([B)Ljava/lang/String; HSPLorg/whispersystems/signalservice/internal/util/JsonUtil;->()V HSPLorg/whispersystems/signalservice/internal/util/JsonUtil;->fromJson(Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object; -HSPLorg/whispersystems/signalservice/internal/util/JsonUtil;->toJson(Ljava/lang/Object;)Ljava/lang/String; HSPLorg/whispersystems/signalservice/internal/util/Util;->immutableList([Ljava/lang/Object;)Ljava/util/List; HSPLorg/whispersystems/signalservice/internal/util/Util;->wait(Ljava/lang/Object;J)V -HSPLorg/whispersystems/signalservice/internal/util/concurrent/FutureMapTransformer;->(Lorg/whispersystems/signalservice/internal/util/concurrent/ListenableFuture;Lorg/whispersystems/signalservice/internal/util/concurrent/FutureTransformers$Transformer;)V -HSPLorg/whispersystems/signalservice/internal/util/concurrent/FutureMapTransformer;->get(JLjava/util/concurrent/TimeUnit;)Ljava/lang/Object; -HSPLorg/whispersystems/signalservice/internal/util/concurrent/FutureTransformers;->map(Lorg/whispersystems/signalservice/internal/util/concurrent/ListenableFuture;Lorg/whispersystems/signalservice/internal/util/concurrent/FutureTransformers$Transformer;)Lorg/whispersystems/signalservice/internal/util/concurrent/ListenableFuture; -HSPLorg/whispersystems/signalservice/internal/util/concurrent/SettableFuture;->()V -HSPLorg/whispersystems/signalservice/internal/util/concurrent/SettableFuture;->get()Ljava/lang/Object; -HSPLorg/whispersystems/signalservice/internal/util/concurrent/SettableFuture;->get(JLjava/util/concurrent/TimeUnit;)Ljava/lang/Object; -HSPLorg/whispersystems/signalservice/internal/util/concurrent/SettableFuture;->notifyAllListeners()V -HSPLorg/whispersystems/signalservice/internal/util/concurrent/SettableFuture;->setException(Ljava/lang/Throwable;)Z -HSPLorg/whispersystems/signalservice/internal/websocket/DefaultErrorMapper$Builder;->()V -HSPLorg/whispersystems/signalservice/internal/websocket/DefaultErrorMapper$Builder;->build()Lorg/whispersystems/signalservice/internal/websocket/ErrorMapper; -HSPLorg/whispersystems/signalservice/internal/websocket/DefaultErrorMapper;->()V -HSPLorg/whispersystems/signalservice/internal/websocket/DefaultErrorMapper;->()V -HSPLorg/whispersystems/signalservice/internal/websocket/DefaultErrorMapper;->(Ljava/util/Map;)V -HSPLorg/whispersystems/signalservice/internal/websocket/DefaultErrorMapper;->(Ljava/util/Map;Lorg/whispersystems/signalservice/internal/websocket/DefaultErrorMapper-IA;)V -HSPLorg/whispersystems/signalservice/internal/websocket/DefaultErrorMapper;->extend()Lorg/whispersystems/signalservice/internal/websocket/DefaultErrorMapper$Builder; -HSPLorg/whispersystems/signalservice/internal/websocket/DefaultResponseMapper$Builder;->(Ljava/lang/Class;)V -HSPLorg/whispersystems/signalservice/internal/websocket/DefaultResponseMapper$Builder;->build()Lorg/whispersystems/signalservice/internal/websocket/ResponseMapper; -HSPLorg/whispersystems/signalservice/internal/websocket/DefaultResponseMapper$Builder;->withResponseMapper(Lorg/whispersystems/signalservice/internal/websocket/DefaultResponseMapper$CustomResponseMapper;)Lorg/whispersystems/signalservice/internal/websocket/DefaultResponseMapper$Builder; -HSPLorg/whispersystems/signalservice/internal/websocket/DefaultResponseMapper;->(Ljava/lang/Class;Lorg/whispersystems/signalservice/internal/websocket/DefaultResponseMapper$CustomResponseMapper;Lorg/whispersystems/signalservice/internal/websocket/ErrorMapper;)V -HSPLorg/whispersystems/signalservice/internal/websocket/DefaultResponseMapper;->(Ljava/lang/Class;Lorg/whispersystems/signalservice/internal/websocket/DefaultResponseMapper$CustomResponseMapper;Lorg/whispersystems/signalservice/internal/websocket/ErrorMapper;Lorg/whispersystems/signalservice/internal/websocket/DefaultResponseMapper-IA;)V -HSPLorg/whispersystems/signalservice/internal/websocket/DefaultResponseMapper;->extend(Ljava/lang/Class;)Lorg/whispersystems/signalservice/internal/websocket/DefaultResponseMapper$Builder; HSPLorg/whispersystems/signalservice/internal/websocket/WebSocketConnection;->()V HSPLorg/whispersystems/signalservice/internal/websocket/WebSocketConnection;->(Ljava/lang/String;Lorg/whispersystems/signalservice/internal/configuration/SignalServiceConfiguration;Lj$/util/Optional;Ljava/lang/String;Lorg/whispersystems/signalservice/api/websocket/HealthMonitor;Ljava/lang/String;Z)V HSPLorg/whispersystems/signalservice/internal/websocket/WebSocketConnection;->(Ljava/lang/String;Lorg/whispersystems/signalservice/internal/configuration/SignalServiceConfiguration;Lj$/util/Optional;Ljava/lang/String;Lorg/whispersystems/signalservice/api/websocket/HealthMonitor;Z)V @@ -29158,17 +28809,7 @@ HSPLorg/whispersystems/signalservice/internal/websocket/WebSocketConnection;->lo HSPLorg/whispersystems/signalservice/internal/websocket/WebSocketConnection;->onFailure(Lokhttp3/WebSocket;Ljava/lang/Throwable;Lokhttp3/Response;)V HSPLorg/whispersystems/signalservice/internal/websocket/WebSocketConnection;->readRequest(J)Lorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage; HSPLorg/whispersystems/signalservice/internal/websocket/WebSocketConnection;->warn(Ljava/lang/String;Ljava/lang/Throwable;)V -HSPLorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage$Builder;->()V -HSPLorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage$Builder;->build()Lorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage; -HSPLorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage$Builder;->headers(Ljava/util/List;)Lorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage$Builder; -HSPLorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage$Builder;->id(Ljava/lang/Long;)Lorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage$Builder; -HSPLorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage$Builder;->path(Ljava/lang/String;)Lorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage$Builder; -HSPLorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage$Builder;->verb(Ljava/lang/String;)Lorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage$Builder; -HSPLorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage$Companion$ADAPTER$1;->(Lcom/squareup/wire/FieldEncoding;Lkotlin/reflect/KClass;Lcom/squareup/wire/Syntax;)V -HSPLorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage$Companion;->()V -HSPLorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V -HSPLorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage;->()V -HSPLorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage;->(Ljava/lang/String;Ljava/lang/String;Lokio/ByteString;Ljava/util/List;Ljava/lang/Long;Lokio/ByteString;)V +HSPLorg/whispersystems/util/StringUtil;->utf8(Ljava/lang/String;)[B HSPLrxdogtag2/DogTagMaybeObserver$$ExternalSyntheticLambda0;->(Lrxdogtag2/DogTagMaybeObserver;)V HSPLrxdogtag2/DogTagMaybeObserver$$ExternalSyntheticLambda1;->(Lrxdogtag2/DogTagMaybeObserver;Lio/reactivex/rxjava3/disposables/Disposable;)V HSPLrxdogtag2/DogTagMaybeObserver$$ExternalSyntheticLambda1;->run()V @@ -29361,7 +29002,6 @@ Landroidx/appcompat/app/AppLocalesMetadataHolderService$Api24Impl; Landroidx/appcompat/app/AppLocalesMetadataHolderService; Landroidx/appcompat/app/AppLocalesStorageHelper$SerialExecutor; Landroidx/appcompat/app/AppLocalesStorageHelper$ThreadPerTaskExecutor; -Landroidx/appcompat/app/LayoutIncludeDetector; Landroidx/appcompat/app/ToolbarActionBar$1; Landroidx/appcompat/app/ToolbarActionBar$2; Landroidx/appcompat/app/ToolbarActionBar$ActionMenuPresenterCallback; @@ -29374,11 +29014,14 @@ Landroidx/appcompat/resources/R$drawable; Landroidx/appcompat/view/ActionBarPolicy; Landroidx/appcompat/view/ActionMode$Callback; Landroidx/appcompat/view/ActionMode; +Landroidx/appcompat/view/CollapsibleActionView; Landroidx/appcompat/view/ContextThemeWrapper; Landroidx/appcompat/view/SupportMenuInflater$MenuState; Landroidx/appcompat/view/SupportMenuInflater; Landroidx/appcompat/view/WindowCallbackWrapper; Landroidx/appcompat/view/menu/ActionMenuItem; +Landroidx/appcompat/view/menu/ActionMenuItemView$PopupCallback; +Landroidx/appcompat/view/menu/ActionMenuItemView; Landroidx/appcompat/view/menu/BaseMenuPresenter; Landroidx/appcompat/view/menu/MenuBuilder$Callback; Landroidx/appcompat/view/menu/MenuBuilder$ItemInvoker; @@ -29386,8 +29029,11 @@ Landroidx/appcompat/view/menu/MenuBuilder; Landroidx/appcompat/view/menu/MenuItemImpl; Landroidx/appcompat/view/menu/MenuPresenter$Callback; Landroidx/appcompat/view/menu/MenuPresenter; +Landroidx/appcompat/view/menu/MenuView$ItemView; Landroidx/appcompat/view/menu/MenuView; +Landroidx/appcompat/view/menu/SubMenuBuilder; Landroidx/appcompat/widget/ActionBarOverlayLayout$ActionBarVisibilityCallback; +Landroidx/appcompat/widget/ActionMenuPresenter$ActionMenuPopupCallback; Landroidx/appcompat/widget/ActionMenuPresenter$OverflowMenuButton$1; Landroidx/appcompat/widget/ActionMenuPresenter$OverflowMenuButton; Landroidx/appcompat/widget/ActionMenuPresenter$PopupPresenterCallback; @@ -29398,6 +29044,7 @@ Landroidx/appcompat/widget/ActionMenuView$LayoutParams; Landroidx/appcompat/widget/ActionMenuView$MenuBuilderCallback; Landroidx/appcompat/widget/ActionMenuView$OnMenuItemClickListener; Landroidx/appcompat/widget/ActionMenuView; +Landroidx/appcompat/widget/AppCompatAutoCompleteTextView; Landroidx/appcompat/widget/AppCompatBackgroundHelper; Landroidx/appcompat/widget/AppCompatButton; Landroidx/appcompat/widget/AppCompatCheckBox; @@ -29446,6 +29093,20 @@ Landroidx/appcompat/widget/ResourceManagerInternal$ResourceManagerHooks; Landroidx/appcompat/widget/ResourceManagerInternal; Landroidx/appcompat/widget/ResourcesWrapper; Landroidx/appcompat/widget/RtlSpacingHelper; +Landroidx/appcompat/widget/SearchView$10; +Landroidx/appcompat/widget/SearchView$1; +Landroidx/appcompat/widget/SearchView$2; +Landroidx/appcompat/widget/SearchView$3; +Landroidx/appcompat/widget/SearchView$4; +Landroidx/appcompat/widget/SearchView$5; +Landroidx/appcompat/widget/SearchView$6; +Landroidx/appcompat/widget/SearchView$7; +Landroidx/appcompat/widget/SearchView$8; +Landroidx/appcompat/widget/SearchView$9; +Landroidx/appcompat/widget/SearchView$OnQueryTextListener; +Landroidx/appcompat/widget/SearchView$SearchAutoComplete$1; +Landroidx/appcompat/widget/SearchView$SearchAutoComplete; +Landroidx/appcompat/widget/SearchView; Landroidx/appcompat/widget/ThemeUtils; Landroidx/appcompat/widget/TintContextWrapper; Landroidx/appcompat/widget/TintInfo; @@ -29458,6 +29119,8 @@ Landroidx/appcompat/widget/Toolbar$3; Landroidx/appcompat/widget/Toolbar$ExpandedActionViewMenuPresenter; Landroidx/appcompat/widget/Toolbar$LayoutParams; Landroidx/appcompat/widget/Toolbar$OnMenuItemClickListener; +Landroidx/appcompat/widget/Toolbar$SavedState$1; +Landroidx/appcompat/widget/Toolbar$SavedState; Landroidx/appcompat/widget/Toolbar; Landroidx/appcompat/widget/ToolbarWidgetWrapper$1; Landroidx/appcompat/widget/ToolbarWidgetWrapper; @@ -29600,6 +29263,8 @@ Landroidx/coordinatorlayout/widget/CoordinatorLayout$Behavior; Landroidx/coordinatorlayout/widget/CoordinatorLayout$HierarchyChangeListener; Landroidx/coordinatorlayout/widget/CoordinatorLayout$LayoutParams; Landroidx/coordinatorlayout/widget/CoordinatorLayout$OnPreDrawListener; +Landroidx/coordinatorlayout/widget/CoordinatorLayout$SavedState$1; +Landroidx/coordinatorlayout/widget/CoordinatorLayout$SavedState; Landroidx/coordinatorlayout/widget/CoordinatorLayout$ViewElevationComparator; Landroidx/coordinatorlayout/widget/CoordinatorLayout; Landroidx/coordinatorlayout/widget/DirectedAcyclicGraph; @@ -29637,7 +29302,6 @@ Landroidx/core/content/OnConfigurationChangedProvider; Landroidx/core/content/OnTrimMemoryProvider; Landroidx/core/content/PermissionChecker; Landroidx/core/content/res/ColorStateListInflaterCompat; -Landroidx/core/content/res/FontResourcesParserCompat; Landroidx/core/content/res/GrowingArrayUtils; Landroidx/core/content/res/ResourcesCompat$Api23Impl$$ExternalSyntheticApiModelOutline0; Landroidx/core/content/res/ResourcesCompat$Api23Impl; @@ -29666,14 +29330,12 @@ Landroidx/core/os/BuildCompat$Api30Impl; Landroidx/core/os/BuildCompat; Landroidx/core/os/BundleCompat$Api18Impl; Landroidx/core/os/BundleCompat; +Landroidx/core/os/BundleKt; Landroidx/core/os/CancellationSignal$OnCancelListener; Landroidx/core/os/CancellationSignal; Landroidx/core/os/ConfigurationCompat$Api24Impl$$ExternalSyntheticApiModelOutline0; Landroidx/core/os/ConfigurationCompat$Api24Impl; Landroidx/core/os/ConfigurationCompat; -Landroidx/core/os/HandlerCompat$Api28Impl$$ExternalSyntheticApiModelOutline0; -Landroidx/core/os/HandlerCompat$Api28Impl; -Landroidx/core/os/HandlerCompat; Landroidx/core/os/LocaleListCompat$Api21Impl; Landroidx/core/os/LocaleListCompat$Api24Impl; Landroidx/core/os/LocaleListCompat; @@ -29845,6 +29507,9 @@ Landroidx/core/widget/TintableCompoundButton; Landroidx/core/widget/TintableCompoundDrawablesView; Landroidx/customview/poolingcontainer/PoolingContainer; Landroidx/customview/poolingcontainer/R$id; +Landroidx/customview/view/AbsSavedState$1; +Landroidx/customview/view/AbsSavedState$2; +Landroidx/customview/view/AbsSavedState; Landroidx/customview/widget/ExploreByTouchHelper$1; Landroidx/customview/widget/ExploreByTouchHelper$2; Landroidx/customview/widget/ExploreByTouchHelper; @@ -29908,6 +29573,7 @@ Landroidx/fragment/app/Fragment$6; Landroidx/fragment/app/Fragment$7; Landroidx/fragment/app/Fragment$9; Landroidx/fragment/app/Fragment$AnimationInfo; +Landroidx/fragment/app/Fragment$Api19Impl; Landroidx/fragment/app/Fragment$OnPreAttachedListener; Landroidx/fragment/app/Fragment$SavedState; Landroidx/fragment/app/Fragment; @@ -29922,7 +29588,6 @@ Landroidx/fragment/app/FragmentContainerView; Landroidx/fragment/app/FragmentController; Landroidx/fragment/app/FragmentFactory; Landroidx/fragment/app/FragmentHostCallback; -Landroidx/fragment/app/FragmentLayoutInflaterFactory$1; Landroidx/fragment/app/FragmentLayoutInflaterFactory; Landroidx/fragment/app/FragmentLifecycleCallbacksDispatcher; Landroidx/fragment/app/FragmentManager$$ExternalSyntheticLambda0; @@ -29946,10 +29611,14 @@ Landroidx/fragment/app/FragmentManager$OnBackStackChangedListener; Landroidx/fragment/app/FragmentManager$OpGenerator; Landroidx/fragment/app/FragmentManager; Landroidx/fragment/app/FragmentManagerImpl; +Landroidx/fragment/app/FragmentManagerState$1; +Landroidx/fragment/app/FragmentManagerState; Landroidx/fragment/app/FragmentManagerViewModel$1; Landroidx/fragment/app/FragmentManagerViewModel; Landroidx/fragment/app/FragmentOnAttachListener; Landroidx/fragment/app/FragmentResultListener; +Landroidx/fragment/app/FragmentState$1; +Landroidx/fragment/app/FragmentState; Landroidx/fragment/app/FragmentStateManager$1; Landroidx/fragment/app/FragmentStateManager$2; Landroidx/fragment/app/FragmentStateManager; @@ -29973,7 +29642,6 @@ Landroidx/fragment/app/SpecialEffectsController$WhenMappings; Landroidx/fragment/app/SpecialEffectsController; Landroidx/fragment/app/SpecialEffectsControllerFactory; Landroidx/fragment/app/ViewKt; -Landroidx/fragment/app/strictmode/FragmentStrictMode; Landroidx/interpolator/view/animation/FastOutLinearInInterpolator; Landroidx/interpolator/view/animation/FastOutSlowInInterpolator; Landroidx/interpolator/view/animation/LinearOutSlowInInterpolator; @@ -30597,6 +30265,9 @@ Landroidx/navigation/NavBackStackEntry$Companion; Landroidx/navigation/NavBackStackEntry$defaultFactory$2; Landroidx/navigation/NavBackStackEntry$savedStateHandle$2; Landroidx/navigation/NavBackStackEntry; +Landroidx/navigation/NavBackStackEntryState$Companion$CREATOR$1; +Landroidx/navigation/NavBackStackEntryState$Companion; +Landroidx/navigation/NavBackStackEntryState; Landroidx/navigation/NavController$$ExternalSyntheticLambda0; Landroidx/navigation/NavController$Companion; Landroidx/navigation/NavController$NavControllerNavigatorState; @@ -30718,7 +30389,6 @@ Landroidx/recyclerview/widget/DiffUtil$Diagonal; Landroidx/recyclerview/widget/DiffUtil$DiffResult; Landroidx/recyclerview/widget/DiffUtil$ItemCallback; Landroidx/recyclerview/widget/DiffUtil$Range; -Landroidx/recyclerview/widget/DiffUtil$Snake; Landroidx/recyclerview/widget/DiffUtil; Landroidx/recyclerview/widget/GapWorker$1; Landroidx/recyclerview/widget/GapWorker$LayoutPrefetchRegistryImpl; @@ -30735,6 +30405,8 @@ Landroidx/recyclerview/widget/ItemTouchHelper; Landroidx/recyclerview/widget/LinearLayoutManager$AnchorInfo; Landroidx/recyclerview/widget/LinearLayoutManager$LayoutChunkResult; Landroidx/recyclerview/widget/LinearLayoutManager$LayoutState; +Landroidx/recyclerview/widget/LinearLayoutManager$SavedState$1; +Landroidx/recyclerview/widget/LinearLayoutManager$SavedState; Landroidx/recyclerview/widget/LinearLayoutManager; Landroidx/recyclerview/widget/ListAdapter$1; Landroidx/recyclerview/widget/ListAdapter; @@ -30776,6 +30448,8 @@ Landroidx/recyclerview/widget/RecyclerView$RecycledViewPool$ScrapData; Landroidx/recyclerview/widget/RecyclerView$RecycledViewPool; Landroidx/recyclerview/widget/RecyclerView$Recycler; Landroidx/recyclerview/widget/RecyclerView$RecyclerViewDataObserver; +Landroidx/recyclerview/widget/RecyclerView$SavedState$1; +Landroidx/recyclerview/widget/RecyclerView$SavedState; Landroidx/recyclerview/widget/RecyclerView$SimpleOnItemTouchListener; Landroidx/recyclerview/widget/RecyclerView$SmoothScroller$ScrollVectorProvider; Landroidx/recyclerview/widget/RecyclerView$State; @@ -30838,6 +30512,8 @@ Lcom/airbnb/lottie/LottieAnimationView$$ExternalSyntheticLambda1; Lcom/airbnb/lottie/LottieAnimationView$$ExternalSyntheticLambda2; Lcom/airbnb/lottie/LottieAnimationView$1; Lcom/airbnb/lottie/LottieAnimationView$2; +Lcom/airbnb/lottie/LottieAnimationView$SavedState$1; +Lcom/airbnb/lottie/LottieAnimationView$SavedState; Lcom/airbnb/lottie/LottieAnimationView$UserActionTaken; Lcom/airbnb/lottie/LottieAnimationView; Lcom/airbnb/lottie/LottieComposition; @@ -30885,7 +30561,6 @@ Lcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$KeyframesWrapperImpl Lcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation$SingleKeyframeWrapper; Lcom/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation; Lcom/airbnb/lottie/animation/keyframe/ColorKeyframeAnimation; -Lcom/airbnb/lottie/animation/keyframe/DropShadowKeyframeAnimation; Lcom/airbnb/lottie/animation/keyframe/FloatKeyframeAnimation; Lcom/airbnb/lottie/animation/keyframe/IntegerKeyframeAnimation; Lcom/airbnb/lottie/animation/keyframe/KeyframeAnimation; @@ -30913,7 +30588,6 @@ Lcom/airbnb/lottie/model/animatable/AnimatableSplitDimensionPathValue; Lcom/airbnb/lottie/model/animatable/AnimatableTransform; Lcom/airbnb/lottie/model/animatable/AnimatableValue; Lcom/airbnb/lottie/model/animatable/BaseAnimatableValue; -Lcom/airbnb/lottie/model/content/BlurEffect; Lcom/airbnb/lottie/model/content/CircleShape; Lcom/airbnb/lottie/model/content/ContentModel; Lcom/airbnb/lottie/model/content/Mask$MaskMode; @@ -31460,7 +31134,6 @@ Lcom/fasterxml/jackson/core/exc/InputCoercionException; Lcom/fasterxml/jackson/core/exc/StreamReadException; Lcom/fasterxml/jackson/core/io/CharTypes; Lcom/fasterxml/jackson/core/io/IOContext; -Lcom/fasterxml/jackson/core/io/JsonEOFException; Lcom/fasterxml/jackson/core/io/JsonStringEncoder; Lcom/fasterxml/jackson/core/io/NumberInput; Lcom/fasterxml/jackson/core/io/NumberOutput; @@ -31494,7 +31167,6 @@ Lcom/fasterxml/jackson/core/util/JacksonFeature; Lcom/fasterxml/jackson/core/util/JacksonFeatureSet; Lcom/fasterxml/jackson/core/util/Separators; Lcom/fasterxml/jackson/core/util/TextBuffer; -Lcom/fasterxml/jackson/core/util/ThreadLocalBufferManager; Lcom/fasterxml/jackson/core/util/VersionUtil; Lcom/fasterxml/jackson/databind/AbstractTypeResolver; Lcom/fasterxml/jackson/databind/AnnotationIntrospector$ReferenceProperty$Type; @@ -31561,7 +31233,6 @@ Lcom/fasterxml/jackson/databind/deser/BasicDeserializerFactory$1; Lcom/fasterxml/jackson/databind/deser/BasicDeserializerFactory$ContainerDefaultMappings; Lcom/fasterxml/jackson/databind/deser/BasicDeserializerFactory$CreatorCollectionState; Lcom/fasterxml/jackson/databind/deser/BasicDeserializerFactory; -Lcom/fasterxml/jackson/databind/deser/BeanDeserializer$BeanReferring; Lcom/fasterxml/jackson/databind/deser/BeanDeserializer; Lcom/fasterxml/jackson/databind/deser/BeanDeserializerBase; Lcom/fasterxml/jackson/databind/deser/BeanDeserializerBuilder; @@ -31578,7 +31249,6 @@ Lcom/fasterxml/jackson/databind/deser/Deserializers; Lcom/fasterxml/jackson/databind/deser/KeyDeserializers; Lcom/fasterxml/jackson/databind/deser/NullValueProvider; Lcom/fasterxml/jackson/databind/deser/ResolvableDeserializer; -Lcom/fasterxml/jackson/databind/deser/SettableAnyProperty; Lcom/fasterxml/jackson/databind/deser/SettableBeanProperty$Delegating; Lcom/fasterxml/jackson/databind/deser/SettableBeanProperty; Lcom/fasterxml/jackson/databind/deser/UnresolvedForwardReference; @@ -31589,48 +31259,30 @@ Lcom/fasterxml/jackson/databind/deser/impl/BeanPropertyMap; Lcom/fasterxml/jackson/databind/deser/impl/CreatorCandidate$Param; Lcom/fasterxml/jackson/databind/deser/impl/CreatorCandidate; Lcom/fasterxml/jackson/databind/deser/impl/CreatorCollector; -Lcom/fasterxml/jackson/databind/deser/impl/ExternalTypeHandler; Lcom/fasterxml/jackson/databind/deser/impl/FailingDeserializer; Lcom/fasterxml/jackson/databind/deser/impl/FieldProperty; Lcom/fasterxml/jackson/databind/deser/impl/JDKValueInstantiators$ArrayListInstantiator; -Lcom/fasterxml/jackson/databind/deser/impl/JDKValueInstantiators$LinkedHashMapInstantiator; Lcom/fasterxml/jackson/databind/deser/impl/JDKValueInstantiators; Lcom/fasterxml/jackson/databind/deser/impl/ManagedReferenceProperty; Lcom/fasterxml/jackson/databind/deser/impl/NullsConstantProvider; Lcom/fasterxml/jackson/databind/deser/impl/ObjectIdReader; Lcom/fasterxml/jackson/databind/deser/impl/PropertyBasedCreator; Lcom/fasterxml/jackson/databind/deser/impl/PropertyValueBuffer; -Lcom/fasterxml/jackson/databind/deser/impl/ReadableObjectId$Referring; -Lcom/fasterxml/jackson/databind/deser/impl/UnwrappedPropertyHandler; -Lcom/fasterxml/jackson/databind/deser/impl/ValueInjector; Lcom/fasterxml/jackson/databind/deser/std/BaseNodeDeserializer; Lcom/fasterxml/jackson/databind/deser/std/ContainerDeserializerBase; Lcom/fasterxml/jackson/databind/deser/std/DateDeserializers; Lcom/fasterxml/jackson/databind/deser/std/FromStringDeserializer; Lcom/fasterxml/jackson/databind/deser/std/JdkDeserializers; Lcom/fasterxml/jackson/databind/deser/std/JsonNodeDeserializer; -Lcom/fasterxml/jackson/databind/deser/std/MapDeserializer; -Lcom/fasterxml/jackson/databind/deser/std/NumberDeserializers$BooleanDeserializer; -Lcom/fasterxml/jackson/databind/deser/std/NumberDeserializers$DoubleDeserializer; -Lcom/fasterxml/jackson/databind/deser/std/NumberDeserializers$FloatDeserializer; Lcom/fasterxml/jackson/databind/deser/std/NumberDeserializers$IntegerDeserializer; Lcom/fasterxml/jackson/databind/deser/std/NumberDeserializers$LongDeserializer; Lcom/fasterxml/jackson/databind/deser/std/NumberDeserializers$PrimitiveOrWrapperDeserializer; Lcom/fasterxml/jackson/databind/deser/std/NumberDeserializers; Lcom/fasterxml/jackson/databind/deser/std/ObjectArrayDeserializer; -Lcom/fasterxml/jackson/databind/deser/std/PrimitiveArrayDeserializers$BooleanDeser; -Lcom/fasterxml/jackson/databind/deser/std/PrimitiveArrayDeserializers$DoubleDeser; -Lcom/fasterxml/jackson/databind/deser/std/PrimitiveArrayDeserializers$FloatDeser; -Lcom/fasterxml/jackson/databind/deser/std/PrimitiveArrayDeserializers$IntDeser; -Lcom/fasterxml/jackson/databind/deser/std/PrimitiveArrayDeserializers$LongDeser; -Lcom/fasterxml/jackson/databind/deser/std/PrimitiveArrayDeserializers; Lcom/fasterxml/jackson/databind/deser/std/StdDeserializer; -Lcom/fasterxml/jackson/databind/deser/std/StdKeyDeserializer$StringKD; -Lcom/fasterxml/jackson/databind/deser/std/StdKeyDeserializer; Lcom/fasterxml/jackson/databind/deser/std/StdKeyDeserializers; Lcom/fasterxml/jackson/databind/deser/std/StdScalarDeserializer; Lcom/fasterxml/jackson/databind/deser/std/StdValueInstantiator; -Lcom/fasterxml/jackson/databind/deser/std/StringArrayDeserializer; Lcom/fasterxml/jackson/databind/deser/std/StringCollectionDeserializer; Lcom/fasterxml/jackson/databind/deser/std/StringDeserializer; Lcom/fasterxml/jackson/databind/deser/std/UUIDDeserializer; @@ -31662,7 +31314,6 @@ Lcom/fasterxml/jackson/databind/introspect/AnnotationCollector$NCollector; Lcom/fasterxml/jackson/databind/introspect/AnnotationCollector$NoAnnotations; Lcom/fasterxml/jackson/databind/introspect/AnnotationCollector$OneAnnotation; Lcom/fasterxml/jackson/databind/introspect/AnnotationCollector$OneCollector; -Lcom/fasterxml/jackson/databind/introspect/AnnotationCollector$TwoAnnotations; Lcom/fasterxml/jackson/databind/introspect/AnnotationCollector; Lcom/fasterxml/jackson/databind/introspect/AnnotationIntrospectorPair; Lcom/fasterxml/jackson/databind/introspect/AnnotationMap; @@ -31702,7 +31353,6 @@ Lcom/fasterxml/jackson/databind/jsontype/PolymorphicTypeValidator$Base; Lcom/fasterxml/jackson/databind/jsontype/PolymorphicTypeValidator; Lcom/fasterxml/jackson/databind/jsontype/SubtypeResolver; Lcom/fasterxml/jackson/databind/jsontype/TypeDeserializer; -Lcom/fasterxml/jackson/databind/jsontype/TypeSerializer; Lcom/fasterxml/jackson/databind/jsontype/impl/LaissezFaireSubTypeValidator; Lcom/fasterxml/jackson/databind/jsontype/impl/StdSubtypeResolver; Lcom/fasterxml/jackson/databind/jsontype/impl/SubTypeValidator; @@ -31798,7 +31448,6 @@ Lcom/fasterxml/jackson/databind/type/CollectionType; Lcom/fasterxml/jackson/databind/type/LogicalType; Lcom/fasterxml/jackson/databind/type/MapLikeType; Lcom/fasterxml/jackson/databind/type/MapType; -Lcom/fasterxml/jackson/databind/type/PlaceholderForType; Lcom/fasterxml/jackson/databind/type/ResolvedRecursiveType; Lcom/fasterxml/jackson/databind/type/SimpleType; Lcom/fasterxml/jackson/databind/type/TypeBase; @@ -32099,6 +31748,8 @@ Lcom/google/android/material/shape/ShapePath$PathOperation; Lcom/google/android/material/shape/ShapePath$ShadowCompatOperation; Lcom/google/android/material/shape/ShapePath; Lcom/google/android/material/shape/Shapeable; +Lcom/google/android/material/stateful/ExtendableSavedState$1; +Lcom/google/android/material/stateful/ExtendableSavedState; Lcom/google/android/material/theme/overlay/MaterialThemeOverlay; Lcom/google/common/base/Ascii; Lcom/google/common/base/Charsets; @@ -32350,6 +32001,7 @@ Lcom/google/i18n/phonenumbers/metadata/source/NonGeographicalEntityMetadataSourc Lcom/google/i18n/phonenumbers/metadata/source/PhoneMetadataFileNameProvider; Lcom/google/i18n/phonenumbers/metadata/source/RegionMetadataSource; Lcom/google/i18n/phonenumbers/metadata/source/RegionMetadataSourceImpl; +Lcom/google/protobuf/InvalidProtocolBufferException; Lcom/klinker/android/send_message/MmsFileProvider; Lcom/makeramen/roundedimageview/RoundedDrawable$1; Lcom/makeramen/roundedimageview/RoundedDrawable; @@ -32438,7 +32090,6 @@ Lio/reactivex/rxjava3/disposables/Disposable; Lio/reactivex/rxjava3/disposables/DisposableContainer; Lio/reactivex/rxjava3/disposables/ReferenceDisposable; Lio/reactivex/rxjava3/disposables/RunnableDisposable; -Lio/reactivex/rxjava3/exceptions/Exceptions; Lio/reactivex/rxjava3/exceptions/OnErrorNotImplementedException; Lio/reactivex/rxjava3/flowables/ConnectableFlowable; Lio/reactivex/rxjava3/functions/Action; @@ -32473,7 +32124,6 @@ Lio/reactivex/rxjava3/internal/functions/Functions; Lio/reactivex/rxjava3/internal/functions/ObjectHelper$BiObjectPredicate; Lio/reactivex/rxjava3/internal/functions/ObjectHelper; Lio/reactivex/rxjava3/internal/fuseable/ConditionalSubscriber; -Lio/reactivex/rxjava3/internal/fuseable/FuseToFlowable; Lio/reactivex/rxjava3/internal/fuseable/FuseToObservable; Lio/reactivex/rxjava3/internal/fuseable/QueueDisposable; Lio/reactivex/rxjava3/internal/fuseable/QueueFuseable; @@ -32492,7 +32142,6 @@ Lio/reactivex/rxjava3/internal/observers/QueueDrainObserver; Lio/reactivex/rxjava3/internal/observers/QueueDrainSubscriberPad0; Lio/reactivex/rxjava3/internal/observers/QueueDrainSubscriberPad2; Lio/reactivex/rxjava3/internal/observers/QueueDrainSubscriberWip; -Lio/reactivex/rxjava3/internal/observers/ResumeSingleObserver; Lio/reactivex/rxjava3/internal/operators/flowable/AbstractBackpressureThrottlingSubscriber; Lio/reactivex/rxjava3/internal/operators/flowable/AbstractFlowableWithUpstream; Lio/reactivex/rxjava3/internal/operators/flowable/FlowableCombineLatest$CombineLatestCoordinator; @@ -32508,7 +32157,6 @@ Lio/reactivex/rxjava3/internal/operators/flowable/FlowableDoOnEach$DoOnEachSubsc Lio/reactivex/rxjava3/internal/operators/flowable/FlowableDoOnEach; Lio/reactivex/rxjava3/internal/operators/flowable/FlowableFilter$FilterConditionalSubscriber; Lio/reactivex/rxjava3/internal/operators/flowable/FlowableFilter; -Lio/reactivex/rxjava3/internal/operators/flowable/FlowableFromFuture; Lio/reactivex/rxjava3/internal/operators/flowable/FlowableFromObservable$SubscriberObserver; Lio/reactivex/rxjava3/internal/operators/flowable/FlowableFromObservable; Lio/reactivex/rxjava3/internal/operators/flowable/FlowableInternalHelper$RequestMax; @@ -32537,8 +32185,6 @@ Lio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$ReplaySubscribe Lio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$SizeBoundReplayBuffer; Lio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay; Lio/reactivex/rxjava3/internal/operators/flowable/FlowableScalarXMap; -Lio/reactivex/rxjava3/internal/operators/flowable/FlowableSingleSingle$SingleElementSubscriber; -Lio/reactivex/rxjava3/internal/operators/flowable/FlowableSingleSingle; Lio/reactivex/rxjava3/internal/operators/flowable/FlowableSubscribeOn$SubscribeOnSubscriber$Request; Lio/reactivex/rxjava3/internal/operators/flowable/FlowableSubscribeOn$SubscribeOnSubscriber; Lio/reactivex/rxjava3/internal/operators/flowable/FlowableSubscribeOn; @@ -32642,10 +32288,6 @@ Lio/reactivex/rxjava3/internal/operators/observable/ObservableThrottleFirstTimed Lio/reactivex/rxjava3/internal/operators/observable/ObservableThrottleFirstTimed; Lio/reactivex/rxjava3/internal/operators/single/SingleDoOnSuccess$DoOnSuccess; Lio/reactivex/rxjava3/internal/operators/single/SingleDoOnSuccess; -Lio/reactivex/rxjava3/internal/operators/single/SingleError; -Lio/reactivex/rxjava3/internal/operators/single/SingleFlatMap$SingleFlatMapCallback$FlatMapSingleObserver; -Lio/reactivex/rxjava3/internal/operators/single/SingleFlatMap$SingleFlatMapCallback; -Lio/reactivex/rxjava3/internal/operators/single/SingleFlatMap; Lio/reactivex/rxjava3/internal/operators/single/SingleFromCallable; Lio/reactivex/rxjava3/internal/operators/single/SingleJust; Lio/reactivex/rxjava3/internal/operators/single/SingleMap$MapSingleObserver; @@ -32653,10 +32295,6 @@ Lio/reactivex/rxjava3/internal/operators/single/SingleMap; Lio/reactivex/rxjava3/internal/operators/single/SingleObserveOn$ObserveOnSingleObserver; Lio/reactivex/rxjava3/internal/operators/single/SingleObserveOn; Lio/reactivex/rxjava3/internal/operators/single/SingleOnErrorComplete; -Lio/reactivex/rxjava3/internal/operators/single/SingleOnErrorReturn$OnErrorReturn; -Lio/reactivex/rxjava3/internal/operators/single/SingleOnErrorReturn; -Lio/reactivex/rxjava3/internal/operators/single/SingleResumeNext$ResumeMainSingleObserver; -Lio/reactivex/rxjava3/internal/operators/single/SingleResumeNext; Lio/reactivex/rxjava3/internal/operators/single/SingleSubscribeOn$SubscribeOnObserver; Lio/reactivex/rxjava3/internal/operators/single/SingleSubscribeOn; Lio/reactivex/rxjava3/internal/queue/MpscLinkedQueue$LinkedQueueNode; @@ -32684,7 +32322,6 @@ Lio/reactivex/rxjava3/internal/subscribers/BasicFuseableConditionalSubscriber; Lio/reactivex/rxjava3/internal/subscribers/BasicFuseableSubscriber; Lio/reactivex/rxjava3/internal/subscribers/LambdaSubscriber; Lio/reactivex/rxjava3/internal/subscriptions/BasicIntQueueSubscription; -Lio/reactivex/rxjava3/internal/subscriptions/DeferredScalarSubscription; Lio/reactivex/rxjava3/internal/subscriptions/SubscriptionHelper; Lio/reactivex/rxjava3/internal/util/AppendOnlyLinkedArrayList$NonThrowingPredicate; Lio/reactivex/rxjava3/internal/util/ArrayListSupplier; @@ -32857,6 +32494,7 @@ Lj$/util/function/Function; Lj$/util/function/IntFunction; Lj$/util/function/N; Lj$/util/function/Predicate; +Lj$/util/function/S; Lj$/util/function/Supplier; Lj$/util/function/ToDoubleFunction; Lj$/util/function/ToIntFunction; @@ -32872,58 +32510,59 @@ Lj$/util/function/p0; Lj$/util/function/t; Lj$/util/g; Lj$/util/m; -Lj$/util/stream/A0; -Lj$/util/stream/A2; +Lj$/util/stream/A1; Lj$/util/stream/B0; Lj$/util/stream/B2; Lj$/util/stream/C0; +Lj$/util/stream/C2; Lj$/util/stream/Collector$Characteristics; Lj$/util/stream/Collector; Lj$/util/stream/Collectors; Lj$/util/stream/D0; -Lj$/util/stream/D1; Lj$/util/stream/E0; Lj$/util/stream/E1; -Lj$/util/stream/E3; +Lj$/util/stream/F0; Lj$/util/stream/F1; Lj$/util/stream/F3; -Lj$/util/stream/G; -Lj$/util/stream/K0; -Lj$/util/stream/K; -Lj$/util/stream/L; -Lj$/util/stream/O1; +Lj$/util/stream/G1; +Lj$/util/stream/G3; +Lj$/util/stream/H; +Lj$/util/stream/I; +Lj$/util/stream/L0; +Lj$/util/stream/M; +Lj$/util/stream/N; Lj$/util/stream/P1; -Lj$/util/stream/Q; -Lj$/util/stream/R1; -Lj$/util/stream/R2; +Lj$/util/stream/Q1; +Lj$/util/stream/S1; Lj$/util/stream/S2; -Lj$/util/stream/S; Lj$/util/stream/Stream; -Lj$/util/stream/T1; Lj$/util/stream/T2; -Lj$/util/stream/U0; +Lj$/util/stream/T; Lj$/util/stream/U1; Lj$/util/stream/U2; +Lj$/util/stream/U; Lj$/util/stream/V0; Lj$/util/stream/V1; +Lj$/util/stream/V2; Lj$/util/stream/W0; Lj$/util/stream/W1; Lj$/util/stream/X0; Lj$/util/stream/X1; Lj$/util/stream/Y0; -Lj$/util/stream/b2; +Lj$/util/stream/Y1; +Lj$/util/stream/Z0; Lj$/util/stream/b; +Lj$/util/stream/c2; Lj$/util/stream/c; -Lj$/util/stream/f2; Lj$/util/stream/g2; Lj$/util/stream/h2; +Lj$/util/stream/i2; Lj$/util/stream/i; Lj$/util/stream/l; -Lj$/util/stream/m; -Lj$/util/stream/t2; -Lj$/util/stream/v0; -Lj$/util/stream/v; -Lj$/util/stream/z1; +Lj$/util/stream/n; +Lj$/util/stream/u2; +Lj$/util/stream/w0; +Lj$/util/stream/w; Ljavax/inject/Provider; Lkotlin/Function; Lkotlin/InitializedLazyImpl; @@ -33768,6 +33407,7 @@ Lkotlin/reflect/jvm/internal/impl/protobuf/GeneratedMessageLite; Lkotlin/reflect/jvm/internal/impl/protobuf/Internal$EnumLite; Lkotlin/reflect/jvm/internal/impl/protobuf/Internal$EnumLiteMap; Lkotlin/reflect/jvm/internal/impl/protobuf/InvalidProtocolBufferException; +Lkotlin/reflect/jvm/internal/impl/protobuf/LazyField$LazyIterator; Lkotlin/reflect/jvm/internal/impl/protobuf/LazyStringArrayList; Lkotlin/reflect/jvm/internal/impl/protobuf/LazyStringList; Lkotlin/reflect/jvm/internal/impl/protobuf/LiteralByteString; @@ -33785,7 +33425,6 @@ Lkotlin/reflect/jvm/internal/impl/protobuf/SmallSortedMap$Entry; Lkotlin/reflect/jvm/internal/impl/protobuf/SmallSortedMap$EntryIterator; Lkotlin/reflect/jvm/internal/impl/protobuf/SmallSortedMap$EntrySet; Lkotlin/reflect/jvm/internal/impl/protobuf/SmallSortedMap; -Lkotlin/reflect/jvm/internal/impl/protobuf/UninitializedMessageException; Lkotlin/reflect/jvm/internal/impl/protobuf/UnmodifiableLazyStringList; Lkotlin/reflect/jvm/internal/impl/protobuf/Utf8; Lkotlin/reflect/jvm/internal/impl/protobuf/WireFormat$FieldType$1; @@ -33854,13 +33493,6 @@ Lkotlin/reflect/jvm/internal/impl/resolve/ResolutionAnchorProviderKt; Lkotlin/reflect/jvm/internal/impl/resolve/calls/inference/CapturedTypeConstructor; Lkotlin/reflect/jvm/internal/impl/resolve/calls/inference/CapturedTypeConstructorImpl; Lkotlin/reflect/jvm/internal/impl/resolve/calls/inference/CapturedTypeConstructorKt; -Lkotlin/reflect/jvm/internal/impl/resolve/constants/BooleanValue; -Lkotlin/reflect/jvm/internal/impl/resolve/constants/ConstantValue; -Lkotlin/reflect/jvm/internal/impl/resolve/constants/ConstantValueFactory; -Lkotlin/reflect/jvm/internal/impl/resolve/constants/EnumValue; -Lkotlin/reflect/jvm/internal/impl/resolve/constants/IntValue; -Lkotlin/reflect/jvm/internal/impl/resolve/constants/IntegerValueConstant; -Lkotlin/reflect/jvm/internal/impl/resolve/constants/StringValue; Lkotlin/reflect/jvm/internal/impl/resolve/descriptorUtil/DescriptorUtilsKt$$Lambda$0; Lkotlin/reflect/jvm/internal/impl/resolve/descriptorUtil/DescriptorUtilsKt$declaresOrInheritsDefaultValue$2; Lkotlin/reflect/jvm/internal/impl/resolve/descriptorUtil/DescriptorUtilsKt; @@ -34257,24 +33889,6 @@ Lkotlinx/coroutines/flow/internal/AbstractSharedFlowSlot; Lkotlinx/coroutines/flow/internal/FusibleFlow; Lkotlinx/coroutines/flow/internal/NullSurrogateKt; Lkotlinx/coroutines/internal/Symbol; -Lme/leolin/shortcutbadger/Badger; -Lme/leolin/shortcutbadger/ShortcutBadgeException; -Lme/leolin/shortcutbadger/ShortcutBadger; -Lme/leolin/shortcutbadger/impl/AdwHomeBadger; -Lme/leolin/shortcutbadger/impl/ApexHomeBadger; -Lme/leolin/shortcutbadger/impl/AsusHomeBadger; -Lme/leolin/shortcutbadger/impl/DefaultBadger; -Lme/leolin/shortcutbadger/impl/EverythingMeHomeBadger; -Lme/leolin/shortcutbadger/impl/HuaweiHomeBadger; -Lme/leolin/shortcutbadger/impl/NewHtcHomeBadger; -Lme/leolin/shortcutbadger/impl/NovaHomeBadger; -Lme/leolin/shortcutbadger/impl/OPPOHomeBader; -Lme/leolin/shortcutbadger/impl/SamsungHomeBadger; -Lme/leolin/shortcutbadger/impl/SonyHomeBadger; -Lme/leolin/shortcutbadger/impl/VivoHomeBadger; -Lme/leolin/shortcutbadger/impl/ZTEHomeBadger; -Lme/leolin/shortcutbadger/impl/ZukHomeBadger; -Lme/leolin/shortcutbadger/util/BroadcastHelper; Lnet/zetetic/database/DatabaseErrorHandler; Lnet/zetetic/database/DatabaseUtils; Lnet/zetetic/database/sqlcipher/CloseGuard$DefaultReporter; @@ -34467,9 +34081,6 @@ Lorg/conscrypt/AbstractSessionContext$1; Lorg/conscrypt/AbstractSessionContext; Lorg/conscrypt/ActiveSession; Lorg/conscrypt/AddressUtils; -Lorg/conscrypt/AllocatedBuffer; -Lorg/conscrypt/ArrayUtils; -Lorg/conscrypt/BufferAllocator; Lorg/conscrypt/BufferUtils; Lorg/conscrypt/ByteArray; Lorg/conscrypt/ClientSessionContext$HostAndPort; @@ -34492,7 +34103,6 @@ Lorg/conscrypt/EvpMdRef$SHA1; Lorg/conscrypt/EvpMdRef$SHA256; Lorg/conscrypt/ExternalSession$Provider; Lorg/conscrypt/ExternalSession; -Lorg/conscrypt/GCMParameters; Lorg/conscrypt/HandshakeListener; Lorg/conscrypt/Java7ExtendedSSLSession; Lorg/conscrypt/Java8EngineSocket; @@ -34517,9 +34127,6 @@ Lorg/conscrypt/NativeSsl; Lorg/conscrypt/NativeSslSession$Impl; Lorg/conscrypt/NativeSslSession; Lorg/conscrypt/OidData; -Lorg/conscrypt/OpenSSLAeadCipher; -Lorg/conscrypt/OpenSSLAeadCipherAES$GCM; -Lorg/conscrypt/OpenSSLAeadCipherAES; Lorg/conscrypt/OpenSSLBIOInputStream; Lorg/conscrypt/OpenSSLCipher$Mode; Lorg/conscrypt/OpenSSLCipher$Padding; @@ -34570,7 +34177,6 @@ Lorg/conscrypt/SSLParametersImpl$PSKCallbacks; Lorg/conscrypt/SSLParametersImpl; Lorg/conscrypt/SSLUtils; Lorg/conscrypt/ServerSessionContext; -Lorg/conscrypt/ShortBufferWithoutStackTraceException; Lorg/greenrobot/eventbus/AsyncPoster; Lorg/greenrobot/eventbus/BackgroundPoster; Lorg/greenrobot/eventbus/EventBus$1; @@ -34609,6 +34215,7 @@ Lorg/signal/core/util/CharacterIterable$CharacterIterator; Lorg/signal/core/util/CharacterIterable; Lorg/signal/core/util/CollectionsExtensionsKt; Lorg/signal/core/util/Conversions; +Lorg/signal/core/util/CryptoUtil; Lorg/signal/core/util/CursorExtensionsKt; Lorg/signal/core/util/CursorUtil; Lorg/signal/core/util/DatabaseId; @@ -34655,7 +34262,6 @@ Lorg/signal/core/util/SqlUtil; Lorg/signal/core/util/Stopwatch$Split; Lorg/signal/core/util/Stopwatch$stopAndGetLogString$splitString$1; Lorg/signal/core/util/Stopwatch; -Lorg/signal/core/util/StreamUtil; Lorg/signal/core/util/StringExtensionsKt; Lorg/signal/core/util/StringSerializer; Lorg/signal/core/util/StringUtil; @@ -34700,16 +34306,20 @@ Lorg/signal/core/util/logging/Log$InternalCheck; Lorg/signal/core/util/logging/Log$Logger; Lorg/signal/core/util/logging/Log; Lorg/signal/core/util/logging/NoopLogger; +Lorg/signal/core/util/logging/Scrubber$identifierHmacKeyProvider$1; Lorg/signal/core/util/logging/Scrubber$scrubCallLinkKeys$1; Lorg/signal/core/util/logging/Scrubber$scrubDomains$1; Lorg/signal/core/util/logging/Scrubber$scrubE164$1; +Lorg/signal/core/util/logging/Scrubber$scrubE164Zero$1; Lorg/signal/core/util/logging/Scrubber$scrubEmail$1; Lorg/signal/core/util/logging/Scrubber$scrubGroupsV1$1; Lorg/signal/core/util/logging/Scrubber$scrubGroupsV2$1; Lorg/signal/core/util/logging/Scrubber$scrubIpv4$1; Lorg/signal/core/util/logging/Scrubber$scrubIpv6$1; +Lorg/signal/core/util/logging/Scrubber$scrubPnis$1; Lorg/signal/core/util/logging/Scrubber$scrubUuids$1; Lorg/signal/core/util/logging/Scrubber; +Lorg/signal/core/util/stream/TruncatingInputStream$$ExternalSyntheticBackport0; Lorg/signal/core/util/tracing/DebugAnnotation$Builder; Lorg/signal/core/util/tracing/DebugAnnotation$Companion$ADAPTER$1; Lorg/signal/core/util/tracing/DebugAnnotation$Companion; @@ -34777,7 +34387,6 @@ Lorg/signal/libsignal/protocol/state/SessionStore; Lorg/signal/libsignal/protocol/state/SignalProtocolStore; Lorg/signal/libsignal/protocol/state/SignedPreKeyRecord; Lorg/signal/libsignal/protocol/state/SignedPreKeyStore; -Lorg/signal/libsignal/protocol/util/ByteUtil; Lorg/signal/libsignal/protocol/util/KeyHelper; Lorg/signal/libsignal/protocol/util/Medium; Lorg/signal/libsignal/protocol/util/Pair; @@ -34791,16 +34400,13 @@ Lorg/signal/libsignal/zkgroup/internal/ByteArray; Lorg/signal/libsignal/zkgroup/profiles/ClientZkProfileOperations; Lorg/signal/libsignal/zkgroup/profiles/ExpiringProfileKeyCredential; Lorg/signal/libsignal/zkgroup/profiles/ProfileKey; -Lorg/signal/libsignal/zkgroup/profiles/ProfileKeyVersion; Lorg/signal/libsignal/zkgroup/receipts/ClientZkReceiptOperations; Lorg/signal/paging/BufferedPagingController$$ExternalSyntheticLambda1; -Lorg/signal/paging/BufferedPagingController$$ExternalSyntheticLambda2; Lorg/signal/paging/BufferedPagingController$$ExternalSyntheticLambda3; Lorg/signal/paging/BufferedPagingController; Lorg/signal/paging/CompressedList; Lorg/signal/paging/DataStatus; Lorg/signal/paging/DataStream; -Lorg/signal/paging/FixedSizePagingController$$ExternalSyntheticLambda0; Lorg/signal/paging/FixedSizePagingController$$ExternalSyntheticLambda2; Lorg/signal/paging/FixedSizePagingController$$ExternalSyntheticLambda3; Lorg/signal/paging/FixedSizePagingController; @@ -34822,7 +34428,6 @@ Lorg/signal/ringrtc/CallManager; Lorg/signal/ringrtc/Log$Logger; Lorg/signal/ringrtc/Log; Lorg/signal/ringrtc/WebRtcLogger; -Lorg/thoughtcrime/securesms/AppCapabilities; Lorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda0; Lorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda10; Lorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda11; @@ -34883,6 +34488,9 @@ Lorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda60; Lorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda61; Lorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda62; Lorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda63; +Lorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda64; +Lorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda65; +Lorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda66; Lorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda6; Lorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda7; Lorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda8; @@ -34909,12 +34517,14 @@ Lorg/thoughtcrime/securesms/PassphraseRequiredActivity; Lorg/thoughtcrime/securesms/R$styleable; Lorg/thoughtcrime/securesms/Unbindable; Lorg/thoughtcrime/securesms/animation/AnimationCompleteListener; +Lorg/thoughtcrime/securesms/attachments/Attachment$Companion; Lorg/thoughtcrime/securesms/attachments/Attachment; Lorg/thoughtcrime/securesms/attachments/AttachmentCreator; -Lorg/thoughtcrime/securesms/attachments/AttachmentId$1; +Lorg/thoughtcrime/securesms/attachments/AttachmentId$Creator; Lorg/thoughtcrime/securesms/attachments/AttachmentId; Lorg/thoughtcrime/securesms/attachments/DatabaseAttachment$DisplayOrderComparator; Lorg/thoughtcrime/securesms/attachments/DatabaseAttachment; +Lorg/thoughtcrime/securesms/attachments/PointerAttachment$Companion; Lorg/thoughtcrime/securesms/attachments/PointerAttachment; Lorg/thoughtcrime/securesms/audio/AudioFileInfo; Lorg/thoughtcrime/securesms/audio/AudioHash; @@ -34927,12 +34537,9 @@ Lorg/thoughtcrime/securesms/audio/AudioRecordingHandler; Lorg/thoughtcrime/securesms/avatar/Avatar$Companion; Lorg/thoughtcrime/securesms/avatar/Avatar$DatabaseId$DoNotPersist; Lorg/thoughtcrime/securesms/avatar/Avatar$DatabaseId; -Lorg/thoughtcrime/securesms/avatar/Avatar$Resource; Lorg/thoughtcrime/securesms/avatar/Avatar$Text; Lorg/thoughtcrime/securesms/avatar/Avatar; Lorg/thoughtcrime/securesms/avatar/AvatarPickerStorage; -Lorg/thoughtcrime/securesms/avatar/AvatarRenderer$$ExternalSyntheticLambda0; -Lorg/thoughtcrime/securesms/avatar/AvatarRenderer$renderResource$1; Lorg/thoughtcrime/securesms/avatar/AvatarRenderer; Lorg/thoughtcrime/securesms/avatar/Avatars$ColorPair; Lorg/thoughtcrime/securesms/avatar/Avatars$DefaultAvatar; @@ -34960,6 +34567,7 @@ Lorg/thoughtcrime/securesms/color/MaterialColor$UnknownColorException; Lorg/thoughtcrime/securesms/components/AlbumThumbnailView$$ExternalSyntheticLambda0; Lorg/thoughtcrime/securesms/components/AlbumThumbnailView$$ExternalSyntheticLambda1; Lorg/thoughtcrime/securesms/components/AlbumThumbnailView$$ExternalSyntheticLambda2; +Lorg/thoughtcrime/securesms/components/AlbumThumbnailView$$ExternalSyntheticLambda3; Lorg/thoughtcrime/securesms/components/AlbumThumbnailView; Lorg/thoughtcrime/securesms/components/AlertView; Lorg/thoughtcrime/securesms/components/AnimatingToggle; @@ -35062,6 +34670,7 @@ Lorg/thoughtcrime/securesms/components/ScrollToPositionDelegate$ScrollStrategy; Lorg/thoughtcrime/securesms/components/ScrollToPositionDelegate$ScrollToPositionRequest; Lorg/thoughtcrime/securesms/components/ScrollToPositionDelegate$scrollPositionRequests$1; Lorg/thoughtcrime/securesms/components/ScrollToPositionDelegate; +Lorg/thoughtcrime/securesms/components/SearchView; Lorg/thoughtcrime/securesms/components/SendButton$ScheduledSendListener; Lorg/thoughtcrime/securesms/components/SendButton; Lorg/thoughtcrime/securesms/components/SharedContactView$EventListener; @@ -35164,13 +34773,12 @@ Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Comp Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Mode; Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Progress$Companion; Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Progress; -Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$WhenMappings; Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setCancelClickListener$1; Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setClickable$1; -Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setDownloadClickListener$1; Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setFocusable$1; Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setShowSecondaryText$1; Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setSlides$2; +Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setTransferClickListener$1; Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$setVisible$1; Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView; Lorg/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState; @@ -35288,8 +34896,6 @@ Lorg/thoughtcrime/securesms/contacts/paged/ContactSearchViewModel$controller$1; Lorg/thoughtcrime/securesms/contacts/paged/ContactSearchViewModel$data$1; Lorg/thoughtcrime/securesms/contacts/paged/ContactSearchViewModel$safetyNumberRepository$2; Lorg/thoughtcrime/securesms/contacts/paged/ContactSearchViewModel; -Lorg/thoughtcrime/securesms/contacts/paged/collections/ContactSearchIterator; -Lorg/thoughtcrime/securesms/contacts/sync/ContactDiscovery; Lorg/thoughtcrime/securesms/contactshare/Contact; Lorg/thoughtcrime/securesms/contactshare/ContactUtil; Lorg/thoughtcrime/securesms/conversation/BodyBubbleLayoutTransition; @@ -35308,7 +34914,6 @@ Lorg/thoughtcrime/securesms/conversation/ConversationIntents; Lorg/thoughtcrime/securesms/conversation/ConversationItem$$ExternalSyntheticLambda6; Lorg/thoughtcrime/securesms/conversation/ConversationItem$$ExternalSyntheticLambda9; Lorg/thoughtcrime/securesms/conversation/ConversationItem$1; -Lorg/thoughtcrime/securesms/conversation/ConversationItem$AttachmentCancelClickListener; Lorg/thoughtcrime/securesms/conversation/ConversationItem$AttachmentDownloadClickListener; Lorg/thoughtcrime/securesms/conversation/ConversationItem$ClickListener; Lorg/thoughtcrime/securesms/conversation/ConversationItem$GiftMessageViewCallback; @@ -35343,9 +34948,9 @@ Lorg/thoughtcrime/securesms/conversation/ConversationMessage$ComputedProperties; Lorg/thoughtcrime/securesms/conversation/ConversationMessage$ConversationMessageFactory; Lorg/thoughtcrime/securesms/conversation/ConversationMessage; Lorg/thoughtcrime/securesms/conversation/ConversationOptionsMenu$Callback; +Lorg/thoughtcrime/securesms/conversation/ConversationOptionsMenu$Provider$onCreateMenu$1; Lorg/thoughtcrime/securesms/conversation/ConversationOptionsMenu$Provider; Lorg/thoughtcrime/securesms/conversation/ConversationOptionsMenu$Snapshot; -Lorg/thoughtcrime/securesms/conversation/ConversationOptionsMenu; Lorg/thoughtcrime/securesms/conversation/ConversationReactionDelegate; Lorg/thoughtcrime/securesms/conversation/ConversationReactionOverlay$OnActionSelectedListener; Lorg/thoughtcrime/securesms/conversation/ConversationReactionOverlay$OnHideListener; @@ -35396,6 +35001,8 @@ Lorg/thoughtcrime/securesms/conversation/VoiceNoteDraftView$$ExternalSyntheticLa Lorg/thoughtcrime/securesms/conversation/VoiceNoteDraftView$Listener; Lorg/thoughtcrime/securesms/conversation/VoiceNoteDraftView; Lorg/thoughtcrime/securesms/conversation/VoiceRecorderWakeLock; +Lorg/thoughtcrime/securesms/conversation/clicklisteners/AttachmentCancelClickListener$Companion; +Lorg/thoughtcrime/securesms/conversation/clicklisteners/AttachmentCancelClickListener; Lorg/thoughtcrime/securesms/conversation/colors/AvatarColor; Lorg/thoughtcrime/securesms/conversation/colors/AvatarColorHash$$ExternalSyntheticBackport0; Lorg/thoughtcrime/securesms/conversation/colors/AvatarColorHash; @@ -35408,6 +35015,7 @@ Lorg/thoughtcrime/securesms/conversation/colors/ChatColors$Id$Auto; Lorg/thoughtcrime/securesms/conversation/colors/ChatColors$Id$BuiltIn; Lorg/thoughtcrime/securesms/conversation/colors/ChatColors$Id$Companion$CREATOR$1; Lorg/thoughtcrime/securesms/conversation/colors/ChatColors$Id$Companion; +Lorg/thoughtcrime/securesms/conversation/colors/ChatColors$Id$NotSet; Lorg/thoughtcrime/securesms/conversation/colors/ChatColors$Id; Lorg/thoughtcrime/securesms/conversation/colors/ChatColors$LinearGradient$Creator; Lorg/thoughtcrime/securesms/conversation/colors/ChatColors$LinearGradient; @@ -35421,7 +35029,6 @@ Lorg/thoughtcrime/securesms/conversation/colors/RecyclerViewColorizer$edgeEffect Lorg/thoughtcrime/securesms/conversation/colors/RecyclerViewColorizer$itemDecoration$1; Lorg/thoughtcrime/securesms/conversation/colors/RecyclerViewColorizer$scrollListener$1; Lorg/thoughtcrime/securesms/conversation/colors/RecyclerViewColorizer; -Lorg/thoughtcrime/securesms/conversation/drafts/DraftRepository$$ExternalSyntheticLambda0; Lorg/thoughtcrime/securesms/conversation/drafts/DraftRepository$Companion; Lorg/thoughtcrime/securesms/conversation/drafts/DraftRepository$DatabaseDraft; Lorg/thoughtcrime/securesms/conversation/drafts/DraftRepository$ShareOrDraftData; @@ -35451,7 +35058,6 @@ Lorg/thoughtcrime/securesms/conversation/mutiselect/Multiselectable; Lorg/thoughtcrime/securesms/conversation/mutiselect/forward/MultiselectForwardBottomSheet$Callback; Lorg/thoughtcrime/securesms/conversation/mutiselect/forward/MultiselectForwardFragmentArgs; Lorg/thoughtcrime/securesms/conversation/ui/error/EnableCallNotificationSettingsDialog$Callback; -Lorg/thoughtcrime/securesms/conversation/ui/error/SafetyNumberChangeDialog$$ExternalSyntheticLambda5; Lorg/thoughtcrime/securesms/conversation/ui/inlinequery/InlineQueryChangedListener; Lorg/thoughtcrime/securesms/conversation/ui/inlinequery/InlineQueryResultsControllerV2$$ExternalSyntheticLambda1; Lorg/thoughtcrime/securesms/conversation/ui/inlinequery/InlineQueryResultsControllerV2$1; @@ -35504,17 +35110,16 @@ Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$$ExternalSynth Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$$ExternalSyntheticLambda1; Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$$ExternalSyntheticLambda4; Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$$ExternalSyntheticLambda5; -Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$$ExternalSyntheticLambda8; -Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$$ExternalSyntheticLambda9; +Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$$ExternalSyntheticLambda6; +Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$$ExternalSyntheticLambda7; Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$Companion; Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ConversationViewHolder$$ExternalSyntheticLambda0; Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ConversationViewHolder$$ExternalSyntheticLambda1; Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ConversationViewHolder; Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$IncomingMediaViewHolder; -Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$IncomingTextOnlyViewHolder; Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$OnScrollStateChangedListener; Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ThreadHeaderViewHolder$$ExternalSyntheticLambda0; -Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ThreadHeaderViewHolder$bind$subtitle$1; +Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ThreadHeaderViewHolder$bind$subtitle$2; Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$ThreadHeaderViewHolder; Lorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2; Lorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView$Listener; @@ -35543,6 +35148,8 @@ Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$Companion; Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ComposeTextEventsListener; Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ConversationBannerListener; Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ConversationItemClickListener; +Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ConversationOptionsMenuCallback$onOptionsMenuCreated$1; +Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ConversationOptionsMenuCallback$onOptionsMenuCreated$queryListener$1; Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ConversationOptionsMenuCallback; Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$DataObserver; Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$DisabledInputListener; @@ -35726,7 +35333,6 @@ Lorg/thoughtcrime/securesms/conversation/v2/computed/FormattedDate; Lorg/thoughtcrime/securesms/conversation/v2/data/ConversationDataSource$Companion; Lorg/thoughtcrime/securesms/conversation/v2/data/ConversationDataSource$threadRecipient$2; Lorg/thoughtcrime/securesms/conversation/v2/data/ConversationDataSource; -Lorg/thoughtcrime/securesms/conversation/v2/data/ConversationElementKey$Companion; Lorg/thoughtcrime/securesms/conversation/v2/data/ConversationElementKey; Lorg/thoughtcrime/securesms/conversation/v2/data/ConversationMessageElement; Lorg/thoughtcrime/securesms/conversation/v2/data/ConversationUpdate; @@ -35775,7 +35381,35 @@ Lorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable$Companion; Lorg/thoughtcrime/securesms/conversation/v2/items/ChatColorsDrawable; Lorg/thoughtcrime/securesms/conversation/v2/items/InteractiveConversationElement; Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationContext; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout$OnDispatchTouchEventListener; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout$OnMeasureListener; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemLayout; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape$Companion; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape$MessageShape; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShape; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridge; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyBindingBridgeKt; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda0; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda1; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda2; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda3; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda4; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$$ExternalSyntheticLambda6; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$Companion; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$PassthroughClickListener; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$ReactionMeasureListener; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder$replyDelegate$1; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTheme$getBodyTextColor$1; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTheme$getBodyTextColor$2; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTheme; Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemUtils; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemViewHolder; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2FooterPositionDelegate$DisplayState; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2FooterPositionDelegate; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2OnDispatchTouchEventListener$$ExternalSyntheticLambda1; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2OnDispatchTouchEventListener$Companion; +Lorg/thoughtcrime/securesms/conversation/v2/items/V2OnDispatchTouchEventListener; Lorg/thoughtcrime/securesms/conversation/v2/items/V2Payload; Lorg/thoughtcrime/securesms/conversationlist/ClearFilterViewHolder$OnClearFilterClickListener; Lorg/thoughtcrime/securesms/conversationlist/ConversationFilterBehavior$Callback; @@ -35837,10 +35471,11 @@ Lorg/thoughtcrime/securesms/conversationlist/ConversationListFragment$ArchiveLis Lorg/thoughtcrime/securesms/conversationlist/ConversationListFragment$Callback; Lorg/thoughtcrime/securesms/conversationlist/ConversationListFragment$ContactSearchClickCallbacks; Lorg/thoughtcrime/securesms/conversationlist/ConversationListFragment; -Lorg/thoughtcrime/securesms/conversationlist/ConversationListItem$$ExternalSyntheticLambda3; +Lorg/thoughtcrime/securesms/conversationlist/ConversationListItem$$ExternalSyntheticLambda12; Lorg/thoughtcrime/securesms/conversationlist/ConversationListItem$$ExternalSyntheticLambda4; Lorg/thoughtcrime/securesms/conversationlist/ConversationListItem$$ExternalSyntheticLambda5; -Lorg/thoughtcrime/securesms/conversationlist/ConversationListItem$$ExternalSyntheticLambda9; +Lorg/thoughtcrime/securesms/conversationlist/ConversationListItem$$ExternalSyntheticLambda6; +Lorg/thoughtcrime/securesms/conversationlist/ConversationListItem$$ExternalSyntheticLambda7; Lorg/thoughtcrime/securesms/conversationlist/ConversationListItem; Lorg/thoughtcrime/securesms/conversationlist/ConversationListItemAnimator; Lorg/thoughtcrime/securesms/conversationlist/ConversationListSearchAdapter$$ExternalSyntheticLambda0; @@ -35853,6 +35488,7 @@ Lorg/thoughtcrime/securesms/conversationlist/ConversationListSearchAdapter$BaseC Lorg/thoughtcrime/securesms/conversationlist/ConversationListSearchAdapter$ChatFilterEmptyMappingModel; Lorg/thoughtcrime/securesms/conversationlist/ConversationListSearchAdapter$ChatFilterMappingModel; Lorg/thoughtcrime/securesms/conversationlist/ConversationListSearchAdapter$ChatFilterRepository; +Lorg/thoughtcrime/securesms/conversationlist/ConversationListSearchAdapter$Companion; Lorg/thoughtcrime/securesms/conversationlist/ConversationListSearchAdapter$ConversationListSearchClickCallbacks; Lorg/thoughtcrime/securesms/conversationlist/ConversationListSearchAdapter; Lorg/thoughtcrime/securesms/conversationlist/ConversationListViewModel$$ExternalSyntheticLambda0; @@ -35876,6 +35512,7 @@ Lorg/thoughtcrime/securesms/conversationlist/ConversationListViewModel$pagedData Lorg/thoughtcrime/securesms/conversationlist/ConversationListViewModel$pinnedCount$1; Lorg/thoughtcrime/securesms/conversationlist/ConversationListViewModel$selectedState$1; Lorg/thoughtcrime/securesms/conversationlist/ConversationListViewModel; +Lorg/thoughtcrime/securesms/conversationlist/TimestampPayloadSupport; Lorg/thoughtcrime/securesms/conversationlist/chatfilter/ConversationFilterRequest; Lorg/thoughtcrime/securesms/conversationlist/chatfilter/ConversationFilterSource; Lorg/thoughtcrime/securesms/conversationlist/chatfilter/ConversationListFilterPullView$$ExternalSyntheticLambda1; @@ -35941,9 +35578,12 @@ Lorg/thoughtcrime/securesms/crypto/storage/SignalServiceAccountDataStoreImpl; Lorg/thoughtcrime/securesms/crypto/storage/SignalServiceDataStoreImpl; Lorg/thoughtcrime/securesms/crypto/storage/TextSecurePreKeyStore; Lorg/thoughtcrime/securesms/crypto/storage/TextSecureSessionStore; +Lorg/thoughtcrime/securesms/database/AttachmentTable$Companion; Lorg/thoughtcrime/securesms/database/AttachmentTable$DataInfo; -Lorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties$1; +Lorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties$Companion; +Lorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties$Creator; Lorg/thoughtcrime/securesms/database/AttachmentTable$TransformProperties; +Lorg/thoughtcrime/securesms/database/AttachmentTable$insertAttachment$attachmentId$1; Lorg/thoughtcrime/securesms/database/AttachmentTable; Lorg/thoughtcrime/securesms/database/BodyAdjustment; Lorg/thoughtcrime/securesms/database/BodyRangeUtil; @@ -35963,20 +35603,15 @@ Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda10 Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda11; Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda13; Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda17; -Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda1; Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda20; Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda23; Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda24; Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda28; Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda29; Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda30; -Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda32; -Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda36; Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda37; Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda40; Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda4; -Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda5; -Lorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda6; Lorg/thoughtcrime/securesms/database/DatabaseObserver$MessageObserver; Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer; Lorg/thoughtcrime/securesms/database/DatabaseObserver; @@ -36000,7 +35635,6 @@ Lorg/thoughtcrime/securesms/database/GroupReceiptTable; Lorg/thoughtcrime/securesms/database/GroupTable$Companion; Lorg/thoughtcrime/securesms/database/GroupTable$MembershipTable$Companion; Lorg/thoughtcrime/securesms/database/GroupTable$MembershipTable; -Lorg/thoughtcrime/securesms/database/GroupTable$Reader; Lorg/thoughtcrime/securesms/database/GroupTable$ShowAsStoryState$Companion; Lorg/thoughtcrime/securesms/database/GroupTable$ShowAsStoryState; Lorg/thoughtcrime/securesms/database/GroupTable; @@ -36013,7 +35647,6 @@ Lorg/thoughtcrime/securesms/database/JobDatabase$$ExternalSyntheticLambda0; Lorg/thoughtcrime/securesms/database/JobDatabase$Companion; Lorg/thoughtcrime/securesms/database/JobDatabase$deleteJobs$1; Lorg/thoughtcrime/securesms/database/JobDatabase$insertJobs$2; -Lorg/thoughtcrime/securesms/database/JobDatabase$updateJobs$2; Lorg/thoughtcrime/securesms/database/JobDatabase; Lorg/thoughtcrime/securesms/database/KeyValueDatabase$$ExternalSyntheticLambda0; Lorg/thoughtcrime/securesms/database/KeyValueDatabase$1; @@ -36098,17 +35731,13 @@ Lorg/thoughtcrime/securesms/database/QueryMonitor; Lorg/thoughtcrime/securesms/database/ReactionTable$Companion; Lorg/thoughtcrime/securesms/database/ReactionTable; Lorg/thoughtcrime/securesms/database/RecipientIdDatabaseReference; -Lorg/thoughtcrime/securesms/database/RecipientTable$$ExternalSyntheticLambda0; -Lorg/thoughtcrime/securesms/database/RecipientTable$$ExternalSyntheticLambda11; Lorg/thoughtcrime/securesms/database/RecipientTable$$ExternalSyntheticLambda1; -Lorg/thoughtcrime/securesms/database/RecipientTable$$ExternalSyntheticLambda2; -Lorg/thoughtcrime/securesms/database/RecipientTable$$ExternalSyntheticLambda3; -Lorg/thoughtcrime/securesms/database/RecipientTable$$ExternalSyntheticLambda5; Lorg/thoughtcrime/securesms/database/RecipientTable$Companion; -Lorg/thoughtcrime/securesms/database/RecipientTable$GetOrInsertResult; Lorg/thoughtcrime/securesms/database/RecipientTable$MentionSetting$Companion; Lorg/thoughtcrime/securesms/database/RecipientTable$MentionSetting; Lorg/thoughtcrime/securesms/database/RecipientTable$MissingRecipientException; +Lorg/thoughtcrime/securesms/database/RecipientTable$PhoneNumberSharingState$Companion; +Lorg/thoughtcrime/securesms/database/RecipientTable$PhoneNumberSharingState; Lorg/thoughtcrime/securesms/database/RecipientTable$ProcessPnpTupleResult; Lorg/thoughtcrime/securesms/database/RecipientTable$RecipientReader; Lorg/thoughtcrime/securesms/database/RecipientTable$RecipientType$Companion; @@ -36119,15 +35748,22 @@ Lorg/thoughtcrime/securesms/database/RecipientTable$UnidentifiedAccessMode$Compa Lorg/thoughtcrime/securesms/database/RecipientTable$UnidentifiedAccessMode; Lorg/thoughtcrime/securesms/database/RecipientTable$VibrateState$Companion; Lorg/thoughtcrime/securesms/database/RecipientTable$VibrateState; -Lorg/thoughtcrime/securesms/database/RecipientTable$getRecipientExtras$1; -Lorg/thoughtcrime/securesms/database/RecipientTable$getSyncExtras$forcedUnread$1; -Lorg/thoughtcrime/securesms/database/RecipientTable$getSyncExtras$groupMasterKey$1; -Lorg/thoughtcrime/securesms/database/RecipientTable$getSyncExtras$identityKey$1; -Lorg/thoughtcrime/securesms/database/RecipientTable$getSyncExtras$identityStatus$1; +Lorg/thoughtcrime/securesms/database/RecipientTable$getAndPossiblyMerge$3$$ExternalSyntheticLambda0; +Lorg/thoughtcrime/securesms/database/RecipientTable$getAndPossiblyMerge$3; Lorg/thoughtcrime/securesms/database/RecipientTable; +Lorg/thoughtcrime/securesms/database/RecipientTableCursorUtil$$ExternalSyntheticLambda0; +Lorg/thoughtcrime/securesms/database/RecipientTableCursorUtil$$ExternalSyntheticLambda1; +Lorg/thoughtcrime/securesms/database/RecipientTableCursorUtil$$ExternalSyntheticLambda2; +Lorg/thoughtcrime/securesms/database/RecipientTableCursorUtil$$ExternalSyntheticLambda3; +Lorg/thoughtcrime/securesms/database/RecipientTableCursorUtil$$ExternalSyntheticLambda4; +Lorg/thoughtcrime/securesms/database/RecipientTableCursorUtil$getRecipientExtras$1; +Lorg/thoughtcrime/securesms/database/RecipientTableCursorUtil$getSyncExtras$forcedUnread$1; +Lorg/thoughtcrime/securesms/database/RecipientTableCursorUtil$getSyncExtras$groupMasterKey$1; +Lorg/thoughtcrime/securesms/database/RecipientTableCursorUtil$getSyncExtras$identityKey$1; +Lorg/thoughtcrime/securesms/database/RecipientTableCursorUtil$getSyncExtras$identityStatus$1; +Lorg/thoughtcrime/securesms/database/RecipientTableCursorUtil; Lorg/thoughtcrime/securesms/database/RemappedRecordTables$Companion; Lorg/thoughtcrime/securesms/database/RemappedRecordTables; -Lorg/thoughtcrime/securesms/database/RemappedRecords; Lorg/thoughtcrime/securesms/database/RemoteMegaphoneTable$Companion; Lorg/thoughtcrime/securesms/database/RemoteMegaphoneTable; Lorg/thoughtcrime/securesms/database/RxDatabaseObserver$$ExternalSyntheticLambda0; @@ -36177,7 +35813,6 @@ Lorg/thoughtcrime/securesms/database/SqlCipherDeletingErrorHandler; Lorg/thoughtcrime/securesms/database/SqlCipherErrorHandler$Companion; Lorg/thoughtcrime/securesms/database/SqlCipherErrorHandler; Lorg/thoughtcrime/securesms/database/SqlCipherLibraryLoader; -Lorg/thoughtcrime/securesms/database/StickerTable$StickerPackRecordReader; Lorg/thoughtcrime/securesms/database/StickerTable; Lorg/thoughtcrime/securesms/database/StorySendTable$Companion; Lorg/thoughtcrime/securesms/database/StorySendTable; @@ -36194,7 +35829,6 @@ Lorg/thoughtcrime/securesms/database/ThreadTable$Reader; Lorg/thoughtcrime/securesms/database/ThreadTable$StaticReader; Lorg/thoughtcrime/securesms/database/ThreadTable$WhenMappings; Lorg/thoughtcrime/securesms/database/ThreadTable$getOrCreateThreadIdFor$1; -Lorg/thoughtcrime/securesms/database/ThreadTable$setReadSince$1; Lorg/thoughtcrime/securesms/database/ThreadTable$update$1$isPinned$2; Lorg/thoughtcrime/securesms/database/ThreadTable$update$1$shouldDelete$2; Lorg/thoughtcrime/securesms/database/ThreadTable$update$1; @@ -36229,16 +35863,6 @@ Lorg/thoughtcrime/securesms/database/model/LocalMetricsEvent; Lorg/thoughtcrime/securesms/database/model/LocalMetricsSplit$WhenMappings; Lorg/thoughtcrime/securesms/database/model/LocalMetricsSplit; Lorg/thoughtcrime/securesms/database/model/LogEntry; -Lorg/thoughtcrime/securesms/database/model/MediaMmsMessageRecord$$ExternalSyntheticLambda0; -Lorg/thoughtcrime/securesms/database/model/MediaMmsMessageRecord$$ExternalSyntheticLambda1; -Lorg/thoughtcrime/securesms/database/model/MediaMmsMessageRecord$$ExternalSyntheticLambda2; -Lorg/thoughtcrime/securesms/database/model/MediaMmsMessageRecord$$ExternalSyntheticLambda3; -Lorg/thoughtcrime/securesms/database/model/MediaMmsMessageRecord$$ExternalSyntheticLambda4; -Lorg/thoughtcrime/securesms/database/model/MediaMmsMessageRecord$$ExternalSyntheticLambda5; -Lorg/thoughtcrime/securesms/database/model/MediaMmsMessageRecord$$ExternalSyntheticLambda6; -Lorg/thoughtcrime/securesms/database/model/MediaMmsMessageRecord$$ExternalSyntheticLambda7; -Lorg/thoughtcrime/securesms/database/model/MediaMmsMessageRecord$$ExternalSyntheticLambda8; -Lorg/thoughtcrime/securesms/database/model/MediaMmsMessageRecord; Lorg/thoughtcrime/securesms/database/model/MegaphoneRecord; Lorg/thoughtcrime/securesms/database/model/Mention; Lorg/thoughtcrime/securesms/database/model/MessageId$Companion; @@ -36247,6 +35871,15 @@ Lorg/thoughtcrime/securesms/database/model/MessageId; Lorg/thoughtcrime/securesms/database/model/MessageRecord$InviteAddState; Lorg/thoughtcrime/securesms/database/model/MessageRecord; Lorg/thoughtcrime/securesms/database/model/MessageRecordExtensionsKt; +Lorg/thoughtcrime/securesms/database/model/MmsMessageRecord$$ExternalSyntheticLambda1; +Lorg/thoughtcrime/securesms/database/model/MmsMessageRecord$$ExternalSyntheticLambda2; +Lorg/thoughtcrime/securesms/database/model/MmsMessageRecord$$ExternalSyntheticLambda3; +Lorg/thoughtcrime/securesms/database/model/MmsMessageRecord$$ExternalSyntheticLambda4; +Lorg/thoughtcrime/securesms/database/model/MmsMessageRecord$$ExternalSyntheticLambda5; +Lorg/thoughtcrime/securesms/database/model/MmsMessageRecord$$ExternalSyntheticLambda6; +Lorg/thoughtcrime/securesms/database/model/MmsMessageRecord$$ExternalSyntheticLambda7; +Lorg/thoughtcrime/securesms/database/model/MmsMessageRecord$$ExternalSyntheticLambda8; +Lorg/thoughtcrime/securesms/database/model/MmsMessageRecord$$ExternalSyntheticLambda9; Lorg/thoughtcrime/securesms/database/model/MmsMessageRecord; Lorg/thoughtcrime/securesms/database/model/ParentStoryId$Companion; Lorg/thoughtcrime/securesms/database/model/ParentStoryId; @@ -36281,6 +35914,7 @@ Lorg/thoughtcrime/securesms/database/model/databaseprotos/PendingOneTimeDonation Lorg/thoughtcrime/securesms/database/model/databaseprotos/PendingOneTimeDonation; Lorg/thoughtcrime/securesms/database/model/databaseprotos/RecipientExtras; Lorg/thoughtcrime/securesms/database/model/databaseprotos/Wallpaper; +Lorg/thoughtcrime/securesms/databinding/ConversationHeaderViewBinding; Lorg/thoughtcrime/securesms/databinding/ConversationInputPanelBinding; Lorg/thoughtcrime/securesms/databinding/ConversationListFilterPullViewBinding; Lorg/thoughtcrime/securesms/databinding/ConversationListTabsBinding; @@ -36289,6 +35923,7 @@ Lorg/thoughtcrime/securesms/databinding/ConversationTitleViewBinding; Lorg/thoughtcrime/securesms/databinding/OnboardingMegaphoneCardBinding; Lorg/thoughtcrime/securesms/databinding/TransferControlsViewBinding; Lorg/thoughtcrime/securesms/databinding/V2ConversationFragmentBinding; +Lorg/thoughtcrime/securesms/databinding/V2ConversationItemTextOnlyIncomingBinding; Lorg/thoughtcrime/securesms/dependencies/ApplicationDependencies$$ExternalSyntheticLambda0; Lorg/thoughtcrime/securesms/dependencies/ApplicationDependencies$Provider; Lorg/thoughtcrime/securesms/dependencies/ApplicationDependencies; @@ -36327,7 +35962,6 @@ Lorg/thoughtcrime/securesms/emoji/EmojiJsonParserKt; Lorg/thoughtcrime/securesms/emoji/EmojiJsonRequest; Lorg/thoughtcrime/securesms/emoji/EmojiMetrics; Lorg/thoughtcrime/securesms/emoji/EmojiPage$Asset; -Lorg/thoughtcrime/securesms/emoji/EmojiPage$Disk; Lorg/thoughtcrime/securesms/emoji/EmojiPage; Lorg/thoughtcrime/securesms/emoji/EmojiPageCache$$ExternalSyntheticLambda0; Lorg/thoughtcrime/securesms/emoji/EmojiPageCache$$ExternalSyntheticLambda1; @@ -36341,7 +35975,6 @@ Lorg/thoughtcrime/securesms/emoji/EmojiRemote; Lorg/thoughtcrime/securesms/emoji/EmojiRequest; Lorg/thoughtcrime/securesms/emoji/EmojiSource$Companion$loadAssetBasedEmojis$1$1; Lorg/thoughtcrime/securesms/emoji/EmojiSource$Companion$loadAssetBasedEmojis$1$parsedData$1; -Lorg/thoughtcrime/securesms/emoji/EmojiSource$Companion$loadRemoteBasedEmojis$1$1; Lorg/thoughtcrime/securesms/emoji/EmojiSource$Companion; Lorg/thoughtcrime/securesms/emoji/EmojiSource$canonicalToVariations$2; Lorg/thoughtcrime/securesms/emoji/EmojiSource$emojiTree$2; @@ -36349,7 +35982,6 @@ Lorg/thoughtcrime/securesms/emoji/EmojiSource$maxEmojiLength$2; Lorg/thoughtcrime/securesms/emoji/EmojiSource$variationsToCanonical$2; Lorg/thoughtcrime/securesms/emoji/EmojiSource; Lorg/thoughtcrime/securesms/emoji/EmojiSourceKt; -Lorg/thoughtcrime/securesms/emoji/JumboEmoji$$ExternalSyntheticLambda4; Lorg/thoughtcrime/securesms/emoji/JumboEmoji$$ExternalSyntheticLambda5; Lorg/thoughtcrime/securesms/emoji/JumboEmoji; Lorg/thoughtcrime/securesms/emoji/ObsoleteEmoji; @@ -36433,7 +36065,6 @@ Lorg/thoughtcrime/securesms/jobmanager/ConstraintInstantiator; Lorg/thoughtcrime/securesms/jobmanager/ConstraintObserver$Notifier; Lorg/thoughtcrime/securesms/jobmanager/ConstraintObserver; Lorg/thoughtcrime/securesms/jobmanager/ExecutorFactory; -Lorg/thoughtcrime/securesms/jobmanager/InAppScheduler$$ExternalSyntheticLambda0; Lorg/thoughtcrime/securesms/jobmanager/InAppScheduler; Lorg/thoughtcrime/securesms/jobmanager/Job$1; Lorg/thoughtcrime/securesms/jobmanager/Job$Factory; @@ -36442,11 +36073,6 @@ Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters; Lorg/thoughtcrime/securesms/jobmanager/Job$Result$ResultType; Lorg/thoughtcrime/securesms/jobmanager/Job$Result; Lorg/thoughtcrime/securesms/jobmanager/Job; -Lorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda0; -Lorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda13; -Lorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda14; -Lorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda16; -Lorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda17; Lorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda19; Lorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda1; Lorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda20; @@ -36528,6 +36154,7 @@ Lorg/thoughtcrime/securesms/jobmanager/impl/NotInCallConstraintObserver; Lorg/thoughtcrime/securesms/jobmanager/impl/SqlCipherMigrationConstraint$Factory; Lorg/thoughtcrime/securesms/jobmanager/impl/SqlCipherMigrationConstraintObserver$SqlCipherNeedsMigrationEvent; Lorg/thoughtcrime/securesms/jobmanager/impl/SqlCipherMigrationConstraintObserver; +Lorg/thoughtcrime/securesms/jobmanager/migrations/DonationReceiptRedemptionJobMigration; Lorg/thoughtcrime/securesms/jobmanager/migrations/PushDecryptMessageJobEnvelopeMigration; Lorg/thoughtcrime/securesms/jobmanager/migrations/PushProcessMessageJobMigration$Companion; Lorg/thoughtcrime/securesms/jobmanager/migrations/PushProcessMessageJobMigration; @@ -36550,8 +36177,6 @@ Lorg/thoughtcrime/securesms/jobs/ApkUpdateJob$Factory; Lorg/thoughtcrime/securesms/jobs/AttachmentCompressionJob$Factory; Lorg/thoughtcrime/securesms/jobs/AttachmentCopyJob$Factory; Lorg/thoughtcrime/securesms/jobs/AttachmentDownloadJob$Factory; -Lorg/thoughtcrime/securesms/jobs/AttachmentDownloadJob$InvalidPartException; -Lorg/thoughtcrime/securesms/jobs/AttachmentDownloadJob; Lorg/thoughtcrime/securesms/jobs/AttachmentMarkUploadedJob$Factory; Lorg/thoughtcrime/securesms/jobs/AttachmentUploadJob$Factory; Lorg/thoughtcrime/securesms/jobs/AutomaticSessionResetJob$Factory; @@ -36569,20 +36194,13 @@ Lorg/thoughtcrime/securesms/jobs/CheckServiceReachabilityJob; Lorg/thoughtcrime/securesms/jobs/CleanPreKeysJob$Factory; Lorg/thoughtcrime/securesms/jobs/ConversationShortcutRankingUpdateJob$Factory; Lorg/thoughtcrime/securesms/jobs/ConversationShortcutUpdateJob$Factory; -Lorg/thoughtcrime/securesms/jobs/ConversationShortcutUpdateJob; Lorg/thoughtcrime/securesms/jobs/CreateReleaseChannelJob$Companion; Lorg/thoughtcrime/securesms/jobs/CreateReleaseChannelJob$Factory; -Lorg/thoughtcrime/securesms/jobs/CreateReleaseChannelJob$setAvatar$1; -Lorg/thoughtcrime/securesms/jobs/CreateReleaseChannelJob$setAvatar$2; Lorg/thoughtcrime/securesms/jobs/CreateReleaseChannelJob; Lorg/thoughtcrime/securesms/jobs/DirectoryRefreshJob$Factory; Lorg/thoughtcrime/securesms/jobs/DirectoryRefreshJob; Lorg/thoughtcrime/securesms/jobs/DonationReceiptRedemptionJob$Factory; -Lorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob$$ExternalSyntheticLambda0; Lorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob$$ExternalSyntheticLambda10; -Lorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob$$ExternalSyntheticLambda1; -Lorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob$$ExternalSyntheticLambda2; -Lorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob$$ExternalSyntheticLambda3; Lorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob$$ExternalSyntheticLambda4; Lorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob$$ExternalSyntheticLambda5; Lorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob$$ExternalSyntheticLambda6; @@ -36619,7 +36237,9 @@ Lorg/thoughtcrime/securesms/jobs/GiftSendJob$Factory; Lorg/thoughtcrime/securesms/jobs/GroupCallPeekJob$Factory; Lorg/thoughtcrime/securesms/jobs/GroupCallPeekWorkerJob$Factory; Lorg/thoughtcrime/securesms/jobs/GroupCallUpdateSendJob$Factory; -Lorg/thoughtcrime/securesms/jobs/GroupV1MigrationJob$Factory; +Lorg/thoughtcrime/securesms/jobs/GroupRingCleanupJob$Companion; +Lorg/thoughtcrime/securesms/jobs/GroupRingCleanupJob$Factory; +Lorg/thoughtcrime/securesms/jobs/GroupRingCleanupJob; Lorg/thoughtcrime/securesms/jobs/GroupV2UpdateSelfProfileKeyJob$Factory; Lorg/thoughtcrime/securesms/jobs/GroupV2UpdateSelfProfileKeyJob; Lorg/thoughtcrime/securesms/jobs/IndividualSendJob$Factory; @@ -36654,7 +36274,6 @@ Lorg/thoughtcrime/securesms/jobs/MultiDeviceSubscriptionSyncRequestJob$Factory; Lorg/thoughtcrime/securesms/jobs/MultiDeviceVerifiedUpdateJob$Factory; Lorg/thoughtcrime/securesms/jobs/MultiDeviceViewOnceOpenJob$Factory; Lorg/thoughtcrime/securesms/jobs/MultiDeviceViewedUpdateJob$Factory; -Lorg/thoughtcrime/securesms/jobs/NewRegistrationUsernameSyncJob$Factory; Lorg/thoughtcrime/securesms/jobs/NullMessageSendJob$Factory; Lorg/thoughtcrime/securesms/jobs/OptimizeMessageSearchIndexJob$Factory; Lorg/thoughtcrime/securesms/jobs/PaymentLedgerUpdateJob$Factory; @@ -36679,6 +36298,7 @@ Lorg/thoughtcrime/securesms/jobs/PushProcessMessageErrorJob$Factory; Lorg/thoughtcrime/securesms/jobs/PushProcessMessageJob$Factory; Lorg/thoughtcrime/securesms/jobs/ReactionSendJob$Factory; Lorg/thoughtcrime/securesms/jobs/RebuildMessageSearchIndexJob$Factory; +Lorg/thoughtcrime/securesms/jobs/ReclaimUsernameAndLinkJob$Factory; Lorg/thoughtcrime/securesms/jobs/RefreshAttributesJob$Factory; Lorg/thoughtcrime/securesms/jobs/RefreshAttributesJob; Lorg/thoughtcrime/securesms/jobs/RefreshCallLinkDetailsJob$Factory; @@ -36696,12 +36316,12 @@ Lorg/thoughtcrime/securesms/jobs/ResendMessageJob$Factory; Lorg/thoughtcrime/securesms/jobs/ResetSvrGuessCountJob$Factory; Lorg/thoughtcrime/securesms/jobs/ResumableUploadSpecJob$Factory; Lorg/thoughtcrime/securesms/jobs/RetrieveProfileAvatarJob$Factory; +Lorg/thoughtcrime/securesms/jobs/RetrieveProfileJob$Companion; Lorg/thoughtcrime/securesms/jobs/RetrieveProfileJob$Factory; Lorg/thoughtcrime/securesms/jobs/RetrieveProfileJob; Lorg/thoughtcrime/securesms/jobs/RetrieveRemoteAnnouncementsJob$Companion; Lorg/thoughtcrime/securesms/jobs/RetrieveRemoteAnnouncementsJob$Factory; Lorg/thoughtcrime/securesms/jobs/RetrieveRemoteAnnouncementsJob; -Lorg/thoughtcrime/securesms/jobs/RotateCertificateJob$1; Lorg/thoughtcrime/securesms/jobs/RotateCertificateJob$Factory; Lorg/thoughtcrime/securesms/jobs/RotateCertificateJob; Lorg/thoughtcrime/securesms/jobs/RotateProfileKeyJob$Factory; @@ -36717,7 +36337,6 @@ Lorg/thoughtcrime/securesms/jobs/SmsSendJob$Factory; Lorg/thoughtcrime/securesms/jobs/SmsSentJob$Factory; Lorg/thoughtcrime/securesms/jobs/StickerDownloadJob$Factory; Lorg/thoughtcrime/securesms/jobs/StickerPackDownloadJob$Factory; -Lorg/thoughtcrime/securesms/jobs/StickerPackDownloadJob; Lorg/thoughtcrime/securesms/jobs/StorageAccountRestoreJob$Factory; Lorg/thoughtcrime/securesms/jobs/StorageAccountRestoreJob; Lorg/thoughtcrime/securesms/jobs/StorageForcePushJob$Factory; @@ -36746,14 +36365,13 @@ Lorg/thoughtcrime/securesms/keyboard/emoji/search/EmojiSearchRepository; Lorg/thoughtcrime/securesms/keyboard/gif/GifKeyboardPageFragment$Host; Lorg/thoughtcrime/securesms/keyboard/sticker/StickerKeyboardPageFragment$Callback; Lorg/thoughtcrime/securesms/keyvalue/AccountValues$Companion; -Lorg/thoughtcrime/securesms/keyvalue/AccountValues$UsernameSyncState$Companion; -Lorg/thoughtcrime/securesms/keyvalue/AccountValues$UsernameSyncState; Lorg/thoughtcrime/securesms/keyvalue/AccountValues$aciPreKeys$1; Lorg/thoughtcrime/securesms/keyvalue/AccountValues$pniPreKeys$1; -Lorg/thoughtcrime/securesms/keyvalue/AccountValues$usernameSyncState$2; Lorg/thoughtcrime/securesms/keyvalue/AccountValues; Lorg/thoughtcrime/securesms/keyvalue/ApkUpdateValues$Companion; Lorg/thoughtcrime/securesms/keyvalue/ApkUpdateValues; +Lorg/thoughtcrime/securesms/keyvalue/BackupValues$Companion; +Lorg/thoughtcrime/securesms/keyvalue/BackupValues; Lorg/thoughtcrime/securesms/keyvalue/BlobValue; Lorg/thoughtcrime/securesms/keyvalue/BooleanValue; Lorg/thoughtcrime/securesms/keyvalue/CertificateType; @@ -36775,7 +36393,6 @@ Lorg/thoughtcrime/securesms/keyvalue/IntValue; Lorg/thoughtcrime/securesms/keyvalue/InternalValues; Lorg/thoughtcrime/securesms/keyvalue/KeepMessagesDuration; Lorg/thoughtcrime/securesms/keyvalue/KeyValueDataSet; -Lorg/thoughtcrime/securesms/keyvalue/KeyValueEnumValue; Lorg/thoughtcrime/securesms/keyvalue/KeyValuePersistentStorage; Lorg/thoughtcrime/securesms/keyvalue/KeyValueProtoValue; Lorg/thoughtcrime/securesms/keyvalue/KeyValueReader; @@ -36835,6 +36452,7 @@ Lorg/thoughtcrime/securesms/logging/PersistentLogger$LogRequest; Lorg/thoughtcrime/securesms/logging/PersistentLogger$LogRequests; Lorg/thoughtcrime/securesms/logging/PersistentLogger$WriteThread; Lorg/thoughtcrime/securesms/logging/PersistentLogger; +Lorg/thoughtcrime/securesms/logsubmit/LogSectionNotifications$$ExternalSyntheticApiModelOutline2; Lorg/thoughtcrime/securesms/main/MainActivityListHostFragment$$ExternalSyntheticLambda0; Lorg/thoughtcrime/securesms/main/MainActivityListHostFragment$$ExternalSyntheticLambda1; Lorg/thoughtcrime/securesms/main/MainActivityListHostFragment$$ExternalSyntheticLambda2; @@ -36852,8 +36470,6 @@ Lorg/thoughtcrime/securesms/main/MainActivityListHostFragment$special$$inlined$v Lorg/thoughtcrime/securesms/main/MainActivityListHostFragment; Lorg/thoughtcrime/securesms/main/Material3OnScrollHelperBinder; Lorg/thoughtcrime/securesms/main/SearchBinder; -Lorg/thoughtcrime/securesms/mediasend/Media$1; -Lorg/thoughtcrime/securesms/mediasend/Media; Lorg/thoughtcrime/securesms/megaphone/ForeverSchedule; Lorg/thoughtcrime/securesms/megaphone/Megaphone$Builder; Lorg/thoughtcrime/securesms/megaphone/Megaphone$Style; @@ -36872,15 +36488,15 @@ Lorg/thoughtcrime/securesms/megaphone/MegaphoneRepository; Lorg/thoughtcrime/securesms/megaphone/MegaphoneSchedule; Lorg/thoughtcrime/securesms/megaphone/MegaphoneViewBuilder$1; Lorg/thoughtcrime/securesms/megaphone/MegaphoneViewBuilder; -Lorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda22; -Lorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda23; -Lorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda24; -Lorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda25; +Lorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda18; +Lorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda19; +Lorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda20; +Lorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda21; +Lorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda2; +Lorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda3; Lorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda4; Lorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda5; Lorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda6; -Lorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda7; -Lorg/thoughtcrime/securesms/megaphone/Megaphones$$ExternalSyntheticLambda8; Lorg/thoughtcrime/securesms/megaphone/Megaphones$1; Lorg/thoughtcrime/securesms/megaphone/Megaphones$3; Lorg/thoughtcrime/securesms/megaphone/Megaphones$Event; @@ -36898,6 +36514,7 @@ Lorg/thoughtcrime/securesms/megaphone/PinsForAllSchedule; Lorg/thoughtcrime/securesms/megaphone/RecurringSchedule; Lorg/thoughtcrime/securesms/megaphone/RemoteMegaphoneRepository$Action; Lorg/thoughtcrime/securesms/megaphone/RemoteMegaphoneRepository$donate$1; +Lorg/thoughtcrime/securesms/megaphone/RemoteMegaphoneRepository$donateForFriend$1; Lorg/thoughtcrime/securesms/megaphone/RemoteMegaphoneRepository$finish$1; Lorg/thoughtcrime/securesms/megaphone/RemoteMegaphoneRepository$getRemoteMegaphoneToShow$1; Lorg/thoughtcrime/securesms/megaphone/RemoteMegaphoneRepository$getRemoteMegaphoneToShow$2; @@ -36965,14 +36582,15 @@ Lorg/thoughtcrime/securesms/migrations/ProfileSharingUpdateMigrationJob$Factory; Lorg/thoughtcrime/securesms/migrations/RebuildMessageSearchIndexMigrationJob$Factory; Lorg/thoughtcrime/securesms/migrations/RecheckPaymentsMigrationJob$Factory; Lorg/thoughtcrime/securesms/migrations/RecipientSearchMigrationJob$Factory; +Lorg/thoughtcrime/securesms/migrations/SelfRegisteredStateMigrationJob$Factory; Lorg/thoughtcrime/securesms/migrations/StickerAdditionMigrationJob$Factory; Lorg/thoughtcrime/securesms/migrations/StickerDayByDayMigrationJob$Factory; Lorg/thoughtcrime/securesms/migrations/StickerLaunchMigrationJob$Factory; Lorg/thoughtcrime/securesms/migrations/StickerMyDailyLifeMigrationJob$Factory; Lorg/thoughtcrime/securesms/migrations/StorageCapabilityMigrationJob$Factory; +Lorg/thoughtcrime/securesms/migrations/StorageFixLocalUnknownMigrationJob$Factory; Lorg/thoughtcrime/securesms/migrations/StorageServiceMigrationJob$Factory; Lorg/thoughtcrime/securesms/migrations/StorageServiceSystemNameMigrationJob$Factory; -Lorg/thoughtcrime/securesms/migrations/StoryReadStateMigrationJob$Factory; Lorg/thoughtcrime/securesms/migrations/StoryViewedReceiptsStateMigrationJob$Factory; Lorg/thoughtcrime/securesms/migrations/Svr2MirrorMigrationJob$Factory; Lorg/thoughtcrime/securesms/migrations/SyncDistributionListsMigrationJob$Factory; @@ -37035,11 +36653,7 @@ Lorg/thoughtcrime/securesms/net/StandardUserAgentInterceptor; Lorg/thoughtcrime/securesms/net/StaticDns; Lorg/thoughtcrime/securesms/net/UserAgentInterceptor; Lorg/thoughtcrime/securesms/notifications/Configuration; -Lorg/thoughtcrime/securesms/notifications/MarkReadReceiver; -Lorg/thoughtcrime/securesms/notifications/MessageNotifier$ReminderReceiver; Lorg/thoughtcrime/securesms/notifications/MessageNotifier; -Lorg/thoughtcrime/securesms/notifications/NotificationCancellationHelper$$ExternalSyntheticApiModelOutline0; -Lorg/thoughtcrime/securesms/notifications/NotificationCancellationHelper; Lorg/thoughtcrime/securesms/notifications/NotificationChannels$$ExternalSyntheticApiModelOutline0; Lorg/thoughtcrime/securesms/notifications/NotificationChannels$$ExternalSyntheticApiModelOutline1; Lorg/thoughtcrime/securesms/notifications/NotificationChannels$$ExternalSyntheticApiModelOutline2; @@ -37050,9 +36664,6 @@ Lorg/thoughtcrime/securesms/notifications/NotificationChannels$$ExternalSyntheti Lorg/thoughtcrime/securesms/notifications/NotificationChannels$$ExternalSyntheticLambda11; Lorg/thoughtcrime/securesms/notifications/NotificationChannels$$ExternalSyntheticLambda17; Lorg/thoughtcrime/securesms/notifications/NotificationChannels; -Lorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier$$ExternalSyntheticLambda17; -Lorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier$$ExternalSyntheticLambda3; -Lorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier$$ExternalSyntheticLambda9; Lorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier; Lorg/thoughtcrime/securesms/notifications/SlowNotificationHeuristics; Lorg/thoughtcrime/securesms/notifications/VitalsViewModel$$ExternalSyntheticLambda0; @@ -37066,20 +36677,14 @@ Lorg/thoughtcrime/securesms/notifications/v2/ConversationId$Companion; Lorg/thoughtcrime/securesms/notifications/v2/ConversationId$Creator; Lorg/thoughtcrime/securesms/notifications/v2/ConversationId; Lorg/thoughtcrime/securesms/notifications/v2/DefaultMessageNotifier$Companion; -Lorg/thoughtcrime/securesms/notifications/v2/DefaultMessageNotifier$updateNotification$7; Lorg/thoughtcrime/securesms/notifications/v2/DefaultMessageNotifier; -Lorg/thoughtcrime/securesms/notifications/v2/DefaultMessageNotifierKt; -Lorg/thoughtcrime/securesms/notifications/v2/NotificationPendingIntentHelper; Lorg/thoughtcrime/securesms/notifications/v2/NotificationState$Companion; Lorg/thoughtcrime/securesms/notifications/v2/NotificationState$messageCount$2; Lorg/thoughtcrime/securesms/notifications/v2/NotificationState$notificationIds$2; Lorg/thoughtcrime/securesms/notifications/v2/NotificationState$notificationItems$2; Lorg/thoughtcrime/securesms/notifications/v2/NotificationState; -Lorg/thoughtcrime/securesms/notifications/v2/NotificationStateProvider; Lorg/thoughtcrime/securesms/payments/Payment; Lorg/thoughtcrime/securesms/payments/PaymentsAddressException; -Lorg/thoughtcrime/securesms/permissions/Permissions$$ExternalSyntheticLambda0; -Lorg/thoughtcrime/securesms/permissions/Permissions; Lorg/thoughtcrime/securesms/phonenumbers/PhoneNumberFormatter$PhoneNumber; Lorg/thoughtcrime/securesms/phonenumbers/PhoneNumberFormatter; Lorg/thoughtcrime/securesms/pin/SvrRepository; @@ -37093,15 +36698,8 @@ Lorg/thoughtcrime/securesms/providers/AvatarProvider$Companion; Lorg/thoughtcrime/securesms/providers/AvatarProvider; Lorg/thoughtcrime/securesms/providers/BaseContentProvider; Lorg/thoughtcrime/securesms/providers/BlobContentProvider; -Lorg/thoughtcrime/securesms/providers/BlobProvider$$ExternalSyntheticLambda10; Lorg/thoughtcrime/securesms/providers/BlobProvider$$ExternalSyntheticLambda11; -Lorg/thoughtcrime/securesms/providers/BlobProvider$$ExternalSyntheticLambda1; -Lorg/thoughtcrime/securesms/providers/BlobProvider$$ExternalSyntheticLambda9; Lorg/thoughtcrime/securesms/providers/BlobProvider$1; -Lorg/thoughtcrime/securesms/providers/BlobProvider$2; -Lorg/thoughtcrime/securesms/providers/BlobProvider$BlobBuilder; -Lorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec; -Lorg/thoughtcrime/securesms/providers/BlobProvider$StorageType; Lorg/thoughtcrime/securesms/providers/BlobProvider; Lorg/thoughtcrime/securesms/providers/MmsBodyProvider; Lorg/thoughtcrime/securesms/providers/PartProvider; @@ -37131,7 +36729,6 @@ Lorg/thoughtcrime/securesms/recipients/LiveRecipientCache$$ExternalSyntheticLamb Lorg/thoughtcrime/securesms/recipients/LiveRecipientCache$$ExternalSyntheticLambda4; Lorg/thoughtcrime/securesms/recipients/LiveRecipientCache$$ExternalSyntheticLambda5; Lorg/thoughtcrime/securesms/recipients/LiveRecipientCache; -Lorg/thoughtcrime/securesms/recipients/Recipient$$ExternalSyntheticLambda3; Lorg/thoughtcrime/securesms/recipients/Recipient$Capability; Lorg/thoughtcrime/securesms/recipients/Recipient$Extras; Lorg/thoughtcrime/securesms/recipients/Recipient$FallbackPhotoProvider; @@ -37156,8 +36753,6 @@ Lorg/thoughtcrime/securesms/registration/RegistrationRepository; Lorg/thoughtcrime/securesms/registration/RegistrationUtil; Lorg/thoughtcrime/securesms/registration/VerifyResponse$Companion; Lorg/thoughtcrime/securesms/registration/VerifyResponse; -Lorg/thoughtcrime/securesms/registration/viewmodel/RegistrationViewModel$$ExternalSyntheticLambda3; -Lorg/thoughtcrime/securesms/releasechannel/ReleaseChannel; Lorg/thoughtcrime/securesms/revealable/ViewOnceMessageManager; Lorg/thoughtcrime/securesms/ringrtc/RingRtcLogger; Lorg/thoughtcrime/securesms/s3/S3; @@ -37174,7 +36769,6 @@ Lorg/thoughtcrime/securesms/service/ExpiringMessageManager; Lorg/thoughtcrime/securesms/service/ExpiringStoriesManager$Companion; Lorg/thoughtcrime/securesms/service/ExpiringStoriesManager; Lorg/thoughtcrime/securesms/service/KeyCachingService; -Lorg/thoughtcrime/securesms/service/LocalBackupListener$$ExternalSyntheticBackport0; Lorg/thoughtcrime/securesms/service/LocalBackupListener; Lorg/thoughtcrime/securesms/service/PendingRetryReceiptManager; Lorg/thoughtcrime/securesms/service/PersistentAlarmManagerListener; @@ -37199,9 +36793,6 @@ Lorg/thoughtcrime/securesms/service/webrtc/links/CallLinkRoomId; Lorg/thoughtcrime/securesms/shakereport/ShakeToReport; Lorg/thoughtcrime/securesms/sms/GroupV2UpdateMessageUtil; Lorg/thoughtcrime/securesms/sms/MessageSender$MessageSentEvent; -Lorg/thoughtcrime/securesms/stickers/BlessedPacks$1; -Lorg/thoughtcrime/securesms/stickers/BlessedPacks$Pack; -Lorg/thoughtcrime/securesms/stickers/BlessedPacks; Lorg/thoughtcrime/securesms/stickers/StickerEventListener; Lorg/thoughtcrime/securesms/stickers/StickerLocator; Lorg/thoughtcrime/securesms/stickers/StickerPackInstallEvent; @@ -37262,7 +36853,11 @@ Lorg/thoughtcrime/securesms/util/AppStartup$Task; Lorg/thoughtcrime/securesms/util/AppStartup; Lorg/thoughtcrime/securesms/util/AvatarUtil; Lorg/thoughtcrime/securesms/util/BitmapDecodingException; -Lorg/thoughtcrime/securesms/util/BubbleUtil$BubbleState; +Lorg/thoughtcrime/securesms/util/BubbleUtil$$ExternalSyntheticApiModelOutline0; +Lorg/thoughtcrime/securesms/util/BubbleUtil$$ExternalSyntheticApiModelOutline1; +Lorg/thoughtcrime/securesms/util/BubbleUtil$$ExternalSyntheticApiModelOutline2; +Lorg/thoughtcrime/securesms/util/BubbleUtil$$ExternalSyntheticApiModelOutline3; +Lorg/thoughtcrime/securesms/util/BubbleUtil; Lorg/thoughtcrime/securesms/util/ByteUnit$1; Lorg/thoughtcrime/securesms/util/ByteUnit$2; Lorg/thoughtcrime/securesms/util/ByteUnit$3; @@ -37272,6 +36867,7 @@ Lorg/thoughtcrime/securesms/util/CachedInflater$ViewCache$$ExternalSyntheticLamb Lorg/thoughtcrime/securesms/util/CachedInflater$ViewCache$$ExternalSyntheticLambda1; Lorg/thoughtcrime/securesms/util/CachedInflater$ViewCache; Lorg/thoughtcrime/securesms/util/CachedInflater; +Lorg/thoughtcrime/securesms/util/CenteredImageSpan; Lorg/thoughtcrime/securesms/util/CharacterCalculator; Lorg/thoughtcrime/securesms/util/ConfigurationUtil; Lorg/thoughtcrime/securesms/util/ContextUtil; @@ -37301,12 +36897,10 @@ Lorg/thoughtcrime/securesms/util/FrameRateTracker; Lorg/thoughtcrime/securesms/util/FullscreenHelper$$ExternalSyntheticApiModelOutline0; Lorg/thoughtcrime/securesms/util/FullscreenHelper; Lorg/thoughtcrime/securesms/util/FutureTaskListener; -Lorg/thoughtcrime/securesms/util/IOFunction; Lorg/thoughtcrime/securesms/util/JavaTimeExtensionsKt; Lorg/thoughtcrime/securesms/util/JsonUtils$SaneJSONObject; Lorg/thoughtcrime/securesms/util/JsonUtils; Lorg/thoughtcrime/securesms/util/LRUCache; -Lorg/thoughtcrime/securesms/util/LeakyBucketLimiter$$ExternalSyntheticLambda0; Lorg/thoughtcrime/securesms/util/LeakyBucketLimiter; Lorg/thoughtcrime/securesms/util/ListenableFutureTask$2; Lorg/thoughtcrime/securesms/util/ListenableFutureTask; @@ -37334,11 +36928,7 @@ Lorg/thoughtcrime/securesms/util/NameUtil; Lorg/thoughtcrime/securesms/util/NetworkUtil; Lorg/thoughtcrime/securesms/util/NoCrossfadeChangeDefaultAnimator; Lorg/thoughtcrime/securesms/util/NullableSavedStateHandleDelegate; -Lorg/thoughtcrime/securesms/util/ProfileUtil$$ExternalSyntheticLambda0; Lorg/thoughtcrime/securesms/util/ProfileUtil$$ExternalSyntheticLambda2; -Lorg/thoughtcrime/securesms/util/ProfileUtil$$ExternalSyntheticLambda3; -Lorg/thoughtcrime/securesms/util/ProfileUtil$$ExternalSyntheticLambda4; -Lorg/thoughtcrime/securesms/util/ProfileUtil$$ExternalSyntheticLambda5; Lorg/thoughtcrime/securesms/util/ProfileUtil; Lorg/thoughtcrime/securesms/util/Projection$Corners; Lorg/thoughtcrime/securesms/util/Projection; @@ -37370,6 +36960,11 @@ Lorg/thoughtcrime/securesms/util/SnapToTopDataObserver$ScrollToTop; Lorg/thoughtcrime/securesms/util/SnapToTopDataObserver; Lorg/thoughtcrime/securesms/util/SoftHashMap$SoftValue; Lorg/thoughtcrime/securesms/util/SoftHashMap; +Lorg/thoughtcrime/securesms/util/SpanUtil; +Lorg/thoughtcrime/securesms/util/SplashScreenUtil$$ExternalSyntheticApiModelOutline0; +Lorg/thoughtcrime/securesms/util/SplashScreenUtil$$ExternalSyntheticApiModelOutline1; +Lorg/thoughtcrime/securesms/util/SplashScreenUtil$1; +Lorg/thoughtcrime/securesms/util/SplashScreenUtil; Lorg/thoughtcrime/securesms/util/StorageUtil; Lorg/thoughtcrime/securesms/util/TextSecurePreferences$MediaKeyboardMode; Lorg/thoughtcrime/securesms/util/TextSecurePreferences; @@ -37471,17 +37066,11 @@ Lorg/webrtc/PeerConnectionFactory$InitializationOptions$Builder; Lorg/webrtc/PeerConnectionFactory$InitializationOptions; Lorg/webrtc/PeerConnectionFactory; Lorg/webrtc/WebRtcClassLoader; -Lorg/webrtc/voiceengine/BuildInfo; -Lorg/webrtc/voiceengine/WebRtcAudioManager; -Lorg/webrtc/voiceengine/WebRtcAudioRecord; -Lorg/webrtc/voiceengine/WebRtcAudioTrack; Lorg/whispersystems/signalservice/api/InvalidMessageStructureException; Lorg/whispersystems/signalservice/api/SignalServiceAccountDataStore; Lorg/whispersystems/signalservice/api/SignalServiceAccountManager; Lorg/whispersystems/signalservice/api/SignalServiceDataStore; Lorg/whispersystems/signalservice/api/SignalServiceKyberPreKeyStore; -Lorg/whispersystems/signalservice/api/SignalServiceMessageReceiver$$ExternalSyntheticLambda0; -Lorg/whispersystems/signalservice/api/SignalServiceMessageReceiver; Lorg/whispersystems/signalservice/api/SignalServicePreKeyStore; Lorg/whispersystems/signalservice/api/SignalServiceSenderKeyStore; Lorg/whispersystems/signalservice/api/SignalServiceSessionStore; @@ -37490,11 +37079,8 @@ Lorg/whispersystems/signalservice/api/SignalSessionLock; Lorg/whispersystems/signalservice/api/SignalWebSocket$$ExternalSyntheticLambda0; Lorg/whispersystems/signalservice/api/SignalWebSocket$MessageReceivedCallback; Lorg/whispersystems/signalservice/api/SignalWebSocket; -Lorg/whispersystems/signalservice/api/account/AccountAttributes$Capabilities; -Lorg/whispersystems/signalservice/api/account/AccountAttributes; Lorg/whispersystems/signalservice/api/account/PreKeyCollection; Lorg/whispersystems/signalservice/api/crypto/InvalidCiphertextException; -Lorg/whispersystems/signalservice/api/crypto/UnidentifiedAccess; Lorg/whispersystems/signalservice/api/groupsv2/ClientZkOperations; Lorg/whispersystems/signalservice/api/groupsv2/GroupLinkNotActiveException; Lorg/whispersystems/signalservice/api/groupsv2/GroupsV2Operations; @@ -37509,36 +37095,24 @@ Lorg/whispersystems/signalservice/api/payments/Money$MobileCoin$$ExternalSynthet Lorg/whispersystems/signalservice/api/payments/Money$MobileCoin; Lorg/whispersystems/signalservice/api/payments/Money$ParseException; Lorg/whispersystems/signalservice/api/payments/Money; -Lorg/whispersystems/signalservice/api/profiles/ProfileAndCredential; Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$Capabilities; -Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType; Lorg/whispersystems/signalservice/api/push/ServiceId$ACI$Companion; Lorg/whispersystems/signalservice/api/push/ServiceId$ACI; Lorg/whispersystems/signalservice/api/push/ServiceId$Companion; Lorg/whispersystems/signalservice/api/push/ServiceId$PNI$Companion; Lorg/whispersystems/signalservice/api/push/ServiceId$PNI; Lorg/whispersystems/signalservice/api/push/ServiceId; -Lorg/whispersystems/signalservice/api/push/ServiceIdType; Lorg/whispersystems/signalservice/api/push/SignalServiceAddress; Lorg/whispersystems/signalservice/api/push/TrustStore; Lorg/whispersystems/signalservice/api/push/exceptions/ConflictException; Lorg/whispersystems/signalservice/api/push/exceptions/ContactManifestMismatchException; Lorg/whispersystems/signalservice/api/push/exceptions/MalformedResponseException; Lorg/whispersystems/signalservice/api/push/exceptions/MissingConfigurationException; -Lorg/whispersystems/signalservice/api/push/exceptions/NetworkFailureException; Lorg/whispersystems/signalservice/api/push/exceptions/NoContentException; Lorg/whispersystems/signalservice/api/push/exceptions/NonSuccessfulResponseCodeException; Lorg/whispersystems/signalservice/api/push/exceptions/NotFoundException; Lorg/whispersystems/signalservice/api/push/exceptions/PushNetworkException; -Lorg/whispersystems/signalservice/api/push/exceptions/RangeException; Lorg/whispersystems/signalservice/api/services/DonationsService; -Lorg/whispersystems/signalservice/api/services/MessagingService$$ExternalSyntheticLambda4; -Lorg/whispersystems/signalservice/api/services/ProfileService$$ExternalSyntheticLambda1; -Lorg/whispersystems/signalservice/api/services/ProfileService$$ExternalSyntheticLambda2; -Lorg/whispersystems/signalservice/api/services/ProfileService$$ExternalSyntheticLambda3; -Lorg/whispersystems/signalservice/api/services/ProfileService$ProfileResponseMapper; -Lorg/whispersystems/signalservice/api/services/ProfileService$ProfileResponseProcessor; -Lorg/whispersystems/signalservice/api/services/ProfileService; Lorg/whispersystems/signalservice/api/svr/SecureValueRecovery; Lorg/whispersystems/signalservice/api/svr/SecureValueRecoveryV2$Companion; Lorg/whispersystems/signalservice/api/svr/SecureValueRecoveryV2; @@ -37559,16 +37133,15 @@ Lorg/whispersystems/signalservice/internal/ServiceResponseProcessor; Lorg/whispersystems/signalservice/internal/configuration/SignalCdnUrl; Lorg/whispersystems/signalservice/internal/configuration/SignalCdsiUrl; Lorg/whispersystems/signalservice/internal/configuration/SignalKeyBackupServiceUrl; +Lorg/whispersystems/signalservice/internal/configuration/SignalProxy; Lorg/whispersystems/signalservice/internal/configuration/SignalServiceConfiguration; Lorg/whispersystems/signalservice/internal/configuration/SignalServiceUrl; Lorg/whispersystems/signalservice/internal/configuration/SignalStorageUrl; Lorg/whispersystems/signalservice/internal/configuration/SignalSvr2Url; Lorg/whispersystems/signalservice/internal/configuration/SignalUrl; +Lorg/whispersystems/signalservice/internal/push/PushServiceSocket$$ExternalSyntheticLambda10; Lorg/whispersystems/signalservice/internal/push/PushServiceSocket$$ExternalSyntheticLambda11; Lorg/whispersystems/signalservice/internal/push/PushServiceSocket$$ExternalSyntheticLambda12; -Lorg/whispersystems/signalservice/internal/push/PushServiceSocket$$ExternalSyntheticLambda13; -Lorg/whispersystems/signalservice/internal/push/PushServiceSocket$$ExternalSyntheticLambda15; -Lorg/whispersystems/signalservice/internal/push/PushServiceSocket$1; Lorg/whispersystems/signalservice/internal/push/PushServiceSocket$2; Lorg/whispersystems/signalservice/internal/push/PushServiceSocket$ConnectionHolder; Lorg/whispersystems/signalservice/internal/push/PushServiceSocket$EmptyResponseCodeHandler; @@ -37577,29 +37150,12 @@ Lorg/whispersystems/signalservice/internal/push/PushServiceSocket$ServiceConnect Lorg/whispersystems/signalservice/internal/push/PushServiceSocket; Lorg/whispersystems/signalservice/internal/push/VerifyAccountResponse; Lorg/whispersystems/signalservice/internal/push/exceptions/GroupPatchNotAcceptedException; -Lorg/whispersystems/signalservice/internal/push/http/AcceptLanguagesUtil; Lorg/whispersystems/signalservice/internal/util/BlacklistingTrustManager$1; Lorg/whispersystems/signalservice/internal/util/BlacklistingTrustManager; -Lorg/whispersystems/signalservice/internal/util/Hex; Lorg/whispersystems/signalservice/internal/util/JsonUtil; Lorg/whispersystems/signalservice/internal/util/Util; -Lorg/whispersystems/signalservice/internal/util/concurrent/FutureMapTransformer; -Lorg/whispersystems/signalservice/internal/util/concurrent/FutureTransformers$Transformer; -Lorg/whispersystems/signalservice/internal/util/concurrent/FutureTransformers; -Lorg/whispersystems/signalservice/internal/util/concurrent/ListenableFuture; -Lorg/whispersystems/signalservice/internal/util/concurrent/SettableFuture; -Lorg/whispersystems/signalservice/internal/websocket/DefaultErrorMapper$Builder; -Lorg/whispersystems/signalservice/internal/websocket/DefaultErrorMapper; -Lorg/whispersystems/signalservice/internal/websocket/DefaultResponseMapper$Builder; -Lorg/whispersystems/signalservice/internal/websocket/DefaultResponseMapper$CustomResponseMapper; -Lorg/whispersystems/signalservice/internal/websocket/DefaultResponseMapper; -Lorg/whispersystems/signalservice/internal/websocket/ErrorMapper; -Lorg/whispersystems/signalservice/internal/websocket/ResponseMapper; Lorg/whispersystems/signalservice/internal/websocket/WebSocketConnection; -Lorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage$Builder; -Lorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage$Companion$ADAPTER$1; -Lorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage$Companion; -Lorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage; +Lorg/whispersystems/util/StringUtil; Lrxdogtag2/DogTagMaybeObserver$$ExternalSyntheticLambda0; Lrxdogtag2/DogTagMaybeObserver$$ExternalSyntheticLambda1; Lrxdogtag2/DogTagMaybeObserver$$ExternalSyntheticLambda4; @@ -37636,107 +37192,17 @@ Lrxdogtag2/RxDogTag$Configuration; Lrxdogtag2/RxDogTag$NonCheckingConsumer; Lrxdogtag2/RxDogTag; Lrxdogtag2/RxDogTagErrorReceiver; -PLandroidx/activity/ComponentActivity$$ExternalSyntheticLambda2;->saveState()Landroid/os/Bundle; -PLandroidx/activity/ComponentActivity;->$r8$lambda$OnwlVMZzrLePIRy-6IUDTtLLUV0(Landroidx/activity/ComponentActivity;)Landroid/os/Bundle; -PLandroidx/activity/ComponentActivity;->lambda$new$1()Landroid/os/Bundle; -PLandroidx/activity/ComponentActivity;->onSaveInstanceState(Landroid/os/Bundle;)V PLandroidx/activity/result/ActivityResultRegistry$LifecycleContainer;->clearObservers()V -PLandroidx/activity/result/ActivityResultRegistry;->onSaveInstanceState(Landroid/os/Bundle;)V -PLandroidx/appcompat/app/AppCompatActivity$1;->saveState()Landroid/os/Bundle; -PLandroidx/appcompat/app/AppCompatDelegateImpl;->onSaveInstanceState(Landroid/os/Bundle;)V PLandroidx/appcompat/app/ToolbarActionBar;->onDestroy()V -PLandroidx/appcompat/view/SupportMenuInflater$MenuState;->addSubMenuItem()Landroid/view/SubMenu; -PLandroidx/appcompat/view/SupportMenuInflater$MenuState;->newInstance(Ljava/lang/String;[Ljava/lang/Class;[Ljava/lang/Object;)Ljava/lang/Object; -PLandroidx/appcompat/view/menu/ActionMenuItemView$PopupCallback;->()V -PLandroidx/appcompat/view/menu/ActionMenuItemView;->(Landroid/content/Context;Landroid/util/AttributeSet;)V PLandroidx/appcompat/view/menu/ActionMenuItemView;->getAccessibilityClassName()Ljava/lang/CharSequence; -PLandroidx/appcompat/view/menu/ActionMenuItemView;->getItemData()Landroidx/appcompat/view/menu/MenuItemImpl; -PLandroidx/appcompat/view/menu/ActionMenuItemView;->hasText()Z -PLandroidx/appcompat/view/menu/ActionMenuItemView;->prefersCondensedTitle()Z -PLandroidx/appcompat/view/menu/ActionMenuItemView;->setItemInvoker(Landroidx/appcompat/view/menu/MenuBuilder$ItemInvoker;)V -PLandroidx/appcompat/view/menu/BaseMenuPresenter;->addItemView(Landroid/view/View;I)V -PLandroidx/appcompat/view/menu/BaseMenuPresenter;->filterLeftoverView(Landroid/view/ViewGroup;I)Z -PLandroidx/appcompat/view/menu/MenuBuilder;->addSubMenu(IIILjava/lang/CharSequence;)Landroid/view/SubMenu; -PLandroidx/appcompat/view/menu/MenuBuilder;->clearHeader()V -PLandroidx/appcompat/view/menu/MenuBuilder;->findItemIndex(I)I -PLandroidx/appcompat/view/menu/MenuBuilder;->getContext()Landroid/content/Context; -PLandroidx/appcompat/view/menu/MenuBuilder;->getItem(I)Landroid/view/MenuItem; -PLandroidx/appcompat/view/menu/MenuBuilder;->getResources()Landroid/content/res/Resources; -PLandroidx/appcompat/view/menu/MenuBuilder;->onItemActionRequestChanged(Landroidx/appcompat/view/menu/MenuItemImpl;)V -PLandroidx/appcompat/view/menu/MenuBuilder;->removeItem(I)V -PLandroidx/appcompat/view/menu/MenuBuilder;->removeItemAtInt(IZ)V -PLandroidx/appcompat/view/menu/MenuBuilder;->setHeaderInternal(ILjava/lang/CharSequence;ILandroid/graphics/drawable/Drawable;Landroid/view/View;)V -PLandroidx/appcompat/view/menu/MenuBuilder;->setHeaderTitleInt(Ljava/lang/CharSequence;)Landroidx/appcompat/view/menu/MenuBuilder; -PLandroidx/appcompat/view/menu/MenuItemImpl;->getGroupId()I -PLandroidx/appcompat/view/menu/MenuItemImpl;->getSubMenu()Landroid/view/SubMenu; -PLandroidx/appcompat/view/menu/MenuItemImpl;->getTitleCondensed()Ljava/lang/CharSequence; -PLandroidx/appcompat/view/menu/MenuItemImpl;->setActionView(Landroid/view/View;)Landroid/view/MenuItem; -PLandroidx/appcompat/view/menu/MenuItemImpl;->setActionView(Landroid/view/View;)Landroidx/core/internal/view/SupportMenuItem; -PLandroidx/appcompat/view/menu/MenuItemImpl;->setIconTintList(Landroid/content/res/ColorStateList;)Landroid/view/MenuItem; -PLandroidx/appcompat/view/menu/MenuItemImpl;->setOnActionExpandListener(Landroid/view/MenuItem$OnActionExpandListener;)Landroid/view/MenuItem; -PLandroidx/appcompat/view/menu/MenuItemImpl;->setShowAsAction(I)V -PLandroidx/appcompat/view/menu/MenuItemImpl;->setSubMenu(Landroidx/appcompat/view/menu/SubMenuBuilder;)V -PLandroidx/appcompat/view/menu/MenuItemImpl;->setTitle(Ljava/lang/CharSequence;)Landroid/view/MenuItem; -PLandroidx/appcompat/view/menu/SubMenuBuilder;->(Landroid/content/Context;Landroidx/appcompat/view/menu/MenuBuilder;Landroidx/appcompat/view/menu/MenuItemImpl;)V -PLandroidx/appcompat/view/menu/SubMenuBuilder;->getItem()Landroid/view/MenuItem; -PLandroidx/appcompat/view/menu/SubMenuBuilder;->setHeaderTitle(Ljava/lang/CharSequence;)Landroid/view/SubMenu; -PLandroidx/appcompat/widget/ActionMenuPresenter$ActionMenuPopupCallback;->(Landroidx/appcompat/widget/ActionMenuPresenter;)V PLandroidx/appcompat/widget/ActionMenuPresenter;->dismissPopupMenus()Z -PLandroidx/appcompat/widget/ActionMenuPresenter;->filterLeftoverView(Landroid/view/ViewGroup;I)Z PLandroidx/appcompat/widget/ActionMenuPresenter;->hideOverflowMenu()Z PLandroidx/appcompat/widget/ActionMenuPresenter;->hideSubMenus()Z -PLandroidx/appcompat/widget/ActionMenuPresenter;->isOverflowMenuShowing()Z -PLandroidx/appcompat/widget/ActionMenuView$LayoutParams;->(Landroid/content/Context;Landroid/util/AttributeSet;)V PLandroidx/appcompat/widget/ActionMenuView;->dismissPopupMenus()V -PLandroidx/appcompat/widget/ActionMenuView;->generateLayoutParams(Landroid/util/AttributeSet;)Landroid/view/ViewGroup$LayoutParams; -PLandroidx/appcompat/widget/ActionMenuView;->isOverflowMenuShowing()Z PLandroidx/appcompat/widget/ActionMenuView;->onDetachedFromWindow()V -PLandroidx/appcompat/widget/AppCompatAutoCompleteTextView;->()V -PLandroidx/appcompat/widget/AppCompatAutoCompleteTextView;->(Landroid/content/Context;Landroid/util/AttributeSet;I)V -PLandroidx/appcompat/widget/AppCompatAutoCompleteTextView;->initEmojiKeyListener(Landroidx/appcompat/widget/AppCompatEmojiEditTextHelper;)V -PLandroidx/appcompat/widget/AppCompatAutoCompleteTextView;->setCompoundDrawables(Landroid/graphics/drawable/Drawable;Landroid/graphics/drawable/Drawable;Landroid/graphics/drawable/Drawable;Landroid/graphics/drawable/Drawable;)V -PLandroidx/appcompat/widget/AppCompatDrawableManager$1;->getTintModeForDrawableRes(I)Landroid/graphics/PorterDuff$Mode; -PLandroidx/appcompat/widget/ForwardingListener;->onViewDetachedFromWindow(Landroid/view/View;)V -PLandroidx/appcompat/widget/ResourceManagerInternal;->addTintListToCache(Landroid/content/Context;ILandroid/content/res/ColorStateList;)V -PLandroidx/appcompat/widget/ResourceManagerInternal;->getTintMode(I)Landroid/graphics/PorterDuff$Mode; -PLandroidx/appcompat/widget/SearchView$10;->(Landroidx/appcompat/widget/SearchView;)V -PLandroidx/appcompat/widget/SearchView$1;->(Landroidx/appcompat/widget/SearchView;)V -PLandroidx/appcompat/widget/SearchView$2;->(Landroidx/appcompat/widget/SearchView;)V -PLandroidx/appcompat/widget/SearchView$3;->(Landroidx/appcompat/widget/SearchView;)V -PLandroidx/appcompat/widget/SearchView$4;->(Landroidx/appcompat/widget/SearchView;)V -PLandroidx/appcompat/widget/SearchView$5;->(Landroidx/appcompat/widget/SearchView;)V -PLandroidx/appcompat/widget/SearchView$6;->(Landroidx/appcompat/widget/SearchView;)V -PLandroidx/appcompat/widget/SearchView$7;->(Landroidx/appcompat/widget/SearchView;)V -PLandroidx/appcompat/widget/SearchView$8;->(Landroidx/appcompat/widget/SearchView;)V -PLandroidx/appcompat/widget/SearchView$9;->(Landroidx/appcompat/widget/SearchView;)V -PLandroidx/appcompat/widget/SearchView$SearchAutoComplete$1;->(Landroidx/appcompat/widget/SearchView$SearchAutoComplete;)V -PLandroidx/appcompat/widget/SearchView$SearchAutoComplete;->(Landroid/content/Context;Landroid/util/AttributeSet;)V -PLandroidx/appcompat/widget/SearchView$SearchAutoComplete;->(Landroid/content/Context;Landroid/util/AttributeSet;I)V -PLandroidx/appcompat/widget/SearchView$SearchAutoComplete;->enoughToFilter()Z -PLandroidx/appcompat/widget/SearchView$SearchAutoComplete;->getSearchViewTextMinWidthDp()I -PLandroidx/appcompat/widget/SearchView$SearchAutoComplete;->onFinishInflate()V -PLandroidx/appcompat/widget/SearchView$SearchAutoComplete;->setSearchView(Landroidx/appcompat/widget/SearchView;)V -PLandroidx/appcompat/widget/SearchView;->()V -PLandroidx/appcompat/widget/SearchView;->getDecoratedHint(Ljava/lang/CharSequence;)Ljava/lang/CharSequence; -PLandroidx/appcompat/widget/SearchView;->getQueryHint()Ljava/lang/CharSequence; -PLandroidx/appcompat/widget/SearchView;->isSubmitAreaEnabled()Z -PLandroidx/appcompat/widget/SearchView;->setIconifiedByDefault(Z)V -PLandroidx/appcompat/widget/SearchView;->setMaxWidth(I)V -PLandroidx/appcompat/widget/SearchView;->updateCloseButton()V -PLandroidx/appcompat/widget/SearchView;->updateQueryHint()V -PLandroidx/appcompat/widget/SearchView;->updateSubmitArea()V -PLandroidx/appcompat/widget/SearchView;->updateSubmitButton(Z)V -PLandroidx/appcompat/widget/SearchView;->updateViewsVisibility(Z)V -PLandroidx/appcompat/widget/SearchView;->updateVoiceButton(Z)V -PLandroidx/appcompat/widget/Toolbar$SavedState$1;->()V -PLandroidx/appcompat/widget/Toolbar$SavedState;->()V -PLandroidx/appcompat/widget/Toolbar$SavedState;->(Landroid/os/Parcelable;)V PLandroidx/appcompat/widget/Toolbar$SavedState;->writeToParcel(Landroid/os/Parcel;I)V -PLandroidx/appcompat/widget/Toolbar;->isOverflowMenuShowing()Z PLandroidx/appcompat/widget/Toolbar;->onDetachedFromWindow()V -PLandroidx/appcompat/widget/Toolbar;->onSaveInstanceState()Landroid/os/Parcelable; PLandroidx/collection/SimpleArrayMap;->equals(Ljava/lang/Object;)Z -PLandroidx/collection/SparseArrayCompat;->append(ILjava/lang/Object;)V PLandroidx/collection/SparseArrayCompat;->clear()V PLandroidx/concurrent/futures/AbstractResolvableFuture$AtomicHelper;->()V PLandroidx/concurrent/futures/AbstractResolvableFuture$AtomicHelper;->(Landroidx/concurrent/futures/AbstractResolvableFuture$1;)V @@ -37759,19 +37225,16 @@ PLandroidx/concurrent/futures/ResolvableFuture;->()V PLandroidx/concurrent/futures/ResolvableFuture;->create()Landroidx/concurrent/futures/ResolvableFuture; PLandroidx/concurrent/futures/ResolvableFuture;->set(Ljava/lang/Object;)Z PLandroidx/constraintlayout/widget/ConstraintHelper;->setTag(ILjava/lang/Object;)V -PLandroidx/coordinatorlayout/widget/CoordinatorLayout$SavedState$1;->()V -PLandroidx/coordinatorlayout/widget/CoordinatorLayout$SavedState;->()V -PLandroidx/coordinatorlayout/widget/CoordinatorLayout$SavedState;->(Landroid/os/Parcelable;)V PLandroidx/coordinatorlayout/widget/CoordinatorLayout$SavedState;->writeToParcel(Landroid/os/Parcel;I)V PLandroidx/coordinatorlayout/widget/CoordinatorLayout;->onDetachedFromWindow()V -PLandroidx/coordinatorlayout/widget/CoordinatorLayout;->onSaveInstanceState()Landroid/os/Parcelable; -PLandroidx/core/app/ComponentActivity;->onSaveInstanceState(Landroid/os/Bundle;)V -PLandroidx/core/os/BundleKt;->bundleOf([Lkotlin/Pair;)Landroid/os/Bundle; -PLandroidx/core/view/MenuItemCompat;->setIconTintList(Landroid/view/MenuItem;Landroid/content/res/ColorStateList;)V +PLandroidx/core/os/HandlerCompat$Api28Impl$$ExternalSyntheticApiModelOutline0;->m(Landroid/os/Handler;Ljava/lang/Runnable;Ljava/lang/Object;J)Z +PLandroidx/core/os/HandlerCompat$Api28Impl;->postDelayed(Landroid/os/Handler;Ljava/lang/Runnable;Ljava/lang/Object;J)Z +PLandroidx/core/os/HandlerCompat;->postDelayed(Landroid/os/Handler;Ljava/lang/Runnable;Ljava/lang/Object;J)Z PLandroidx/core/view/NestedScrollingChildHelper;->stopNestedScroll()V PLandroidx/core/view/ViewCompat$Api16Impl;->hasTransientState(Landroid/view/View;)Z PLandroidx/core/view/ViewCompat;->hasTransientState(Landroid/view/View;)Z PLandroidx/core/view/ViewGroupKt$descendants$1;->(Landroid/view/ViewGroup;Lkotlin/coroutines/Continuation;)V +PLandroidx/core/view/ViewGroupKt$descendants$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation; PLandroidx/core/view/ViewKt$allViews$1;->(Landroid/view/View;Lkotlin/coroutines/Continuation;)V PLandroidx/core/view/ViewKt$allViews$1;->create(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation; PLandroidx/core/view/ViewKt$allViews$1;->invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object; @@ -37780,85 +37243,35 @@ PLandroidx/core/view/accessibility/AccessibilityEventCompat$Api19Impl;->getConte PLandroidx/core/view/accessibility/AccessibilityEventCompat$Api19Impl;->setContentChangeTypes(Landroid/view/accessibility/AccessibilityEvent;I)V PLandroidx/core/view/accessibility/AccessibilityEventCompat;->getContentChangeTypes(Landroid/view/accessibility/AccessibilityEvent;)I PLandroidx/core/view/accessibility/AccessibilityEventCompat;->setContentChangeTypes(Landroid/view/accessibility/AccessibilityEvent;I)V -PLandroidx/customview/poolingcontainer/PoolingContainer;->callPoolingContainerOnRelease(Landroid/view/View;)V PLandroidx/customview/poolingcontainer/PoolingContainer;->callPoolingContainerOnReleaseForChildren(Landroid/view/ViewGroup;)V -PLandroidx/customview/poolingcontainer/PoolingContainer;->getPoolingContainerListenerHolder(Landroid/view/View;)Landroidx/customview/poolingcontainer/PoolingContainerListenerHolder; -PLandroidx/customview/poolingcontainer/PoolingContainerListenerHolder;->onRelease()V -PLandroidx/customview/view/AbsSavedState$1;->()V -PLandroidx/customview/view/AbsSavedState$2;->()V -PLandroidx/customview/view/AbsSavedState;->()V -PLandroidx/customview/view/AbsSavedState;->()V -PLandroidx/customview/view/AbsSavedState;->(Landroid/os/Parcelable;)V -PLandroidx/customview/view/AbsSavedState;->(Landroidx/customview/view/AbsSavedState$1;)V PLandroidx/customview/view/AbsSavedState;->writeToParcel(Landroid/os/Parcel;I)V -PLandroidx/emoji2/text/SpannableBuilder;->getSpanEnd(Ljava/lang/Object;)I -PLandroidx/fragment/app/Fragment$Api19Impl;->cancelPendingInputEvents(Landroid/view/View;)V -PLandroidx/fragment/app/Fragment;->getHost()Ljava/lang/Object; PLandroidx/fragment/app/Fragment;->initState()V PLandroidx/fragment/app/Fragment;->onDestroy()V PLandroidx/fragment/app/Fragment;->onDestroyView()V PLandroidx/fragment/app/Fragment;->onDetach()V -PLandroidx/fragment/app/Fragment;->onSaveInstanceState(Landroid/os/Bundle;)V -PLandroidx/fragment/app/Fragment;->onStop()V PLandroidx/fragment/app/Fragment;->performDestroy()V PLandroidx/fragment/app/Fragment;->performDestroyView()V PLandroidx/fragment/app/Fragment;->performDetach()V -PLandroidx/fragment/app/Fragment;->performSaveInstanceState(Landroid/os/Bundle;)V -PLandroidx/fragment/app/Fragment;->performStop()V -PLandroidx/fragment/app/FragmentActivity$$ExternalSyntheticLambda0;->saveState()Landroid/os/Bundle; -PLandroidx/fragment/app/FragmentActivity$HostCallbacks;->onGetHost()Landroidx/fragment/app/FragmentActivity; -PLandroidx/fragment/app/FragmentActivity$HostCallbacks;->onGetHost()Ljava/lang/Object; -PLandroidx/fragment/app/FragmentActivity;->$r8$lambda$t3WwJ1XbNlapyNW0l552nMkkXdo(Landroidx/fragment/app/FragmentActivity;)Landroid/os/Bundle; -PLandroidx/fragment/app/FragmentActivity;->lambda$init$0()Landroid/os/Bundle; PLandroidx/fragment/app/FragmentContainerView;->addDisappearingFragmentView(Landroid/view/View;)V PLandroidx/fragment/app/FragmentContainerView;->removeView(Landroid/view/View;)V PLandroidx/fragment/app/FragmentLifecycleCallbacksDispatcher;->dispatchOnFragmentDestroyed(Landroidx/fragment/app/Fragment;Z)V PLandroidx/fragment/app/FragmentLifecycleCallbacksDispatcher;->dispatchOnFragmentDetached(Landroidx/fragment/app/Fragment;Z)V -PLandroidx/fragment/app/FragmentLifecycleCallbacksDispatcher;->dispatchOnFragmentSaveInstanceState(Landroidx/fragment/app/Fragment;Landroid/os/Bundle;Z)V -PLandroidx/fragment/app/FragmentLifecycleCallbacksDispatcher;->dispatchOnFragmentStopped(Landroidx/fragment/app/Fragment;Z)V PLandroidx/fragment/app/FragmentLifecycleCallbacksDispatcher;->dispatchOnFragmentViewDestroyed(Landroidx/fragment/app/Fragment;Z)V -PLandroidx/fragment/app/FragmentManager$$ExternalSyntheticLambda4;->saveState()Landroid/os/Bundle; -PLandroidx/fragment/app/FragmentManager;->$r8$lambda$sido8p6zuWx0PQxIkv4qM-BRiGM(Landroidx/fragment/app/FragmentManager;)Landroid/os/Bundle; PLandroidx/fragment/app/FragmentManager;->access$100(Landroidx/fragment/app/FragmentManager;)Ljava/util/Map; PLandroidx/fragment/app/FragmentManager;->dispatchDestroyView()V -PLandroidx/fragment/app/FragmentManager;->forcePostponedTransactions()V PLandroidx/fragment/app/FragmentManager;->isDestroyed()Z -PLandroidx/fragment/app/FragmentManager;->lambda$attachController$4()Landroid/os/Bundle; -PLandroidx/fragment/app/FragmentManagerState$1;->()V -PLandroidx/fragment/app/FragmentManagerState;->()V -PLandroidx/fragment/app/FragmentManagerState;->()V PLandroidx/fragment/app/FragmentManagerState;->writeToParcel(Landroid/os/Parcel;I)V PLandroidx/fragment/app/FragmentManagerViewModel;->clearNonConfigState(Landroidx/fragment/app/Fragment;)V PLandroidx/fragment/app/FragmentManagerViewModel;->clearNonConfigStateInternal(Ljava/lang/String;)V PLandroidx/fragment/app/FragmentManagerViewModel;->shouldDestroy(Landroidx/fragment/app/Fragment;)Z -PLandroidx/fragment/app/FragmentState$1;->()V -PLandroidx/fragment/app/FragmentState;->()V -PLandroidx/fragment/app/FragmentState;->(Landroidx/fragment/app/Fragment;)V PLandroidx/fragment/app/FragmentState;->writeToParcel(Landroid/os/Parcel;I)V PLandroidx/fragment/app/FragmentStateManager;->destroy()V PLandroidx/fragment/app/FragmentStateManager;->destroyFragmentView()V PLandroidx/fragment/app/FragmentStateManager;->detach()V -PLandroidx/fragment/app/FragmentStateManager;->saveViewState()V -PLandroidx/fragment/app/FragmentStateManager;->stop()V -PLandroidx/fragment/app/FragmentStore;->getAllSavedState()Ljava/util/HashMap; PLandroidx/fragment/app/FragmentStore;->makeInactive(Landroidx/fragment/app/FragmentStateManager;)V -PLandroidx/fragment/app/FragmentStore;->saveActiveFragments()Ljava/util/ArrayList; -PLandroidx/fragment/app/FragmentStore;->saveAddedFragments()Ljava/util/ArrayList; -PLandroidx/fragment/app/FragmentViewLifecycleOwner;->performSave(Landroid/os/Bundle;)V -PLandroidx/fragment/app/FragmentViewLifecycleOwner;->setCurrentState(Landroidx/lifecycle/Lifecycle$State;)V PLandroidx/fragment/app/SpecialEffectsController;->enqueueRemove(Landroidx/fragment/app/FragmentStateManager;)V -PLandroidx/fragment/app/SpecialEffectsController;->forcePostponedExecutePendingOperations()V PLandroidx/lifecycle/DefaultLifecycleObserver$-CC;->$default$onDestroy(Landroidx/lifecycle/DefaultLifecycleObserver;Landroidx/lifecycle/LifecycleOwner;)V -PLandroidx/lifecycle/EmptyActivityLifecycleCallbacks;->onActivitySaveInstanceState(Landroid/app/Activity;Landroid/os/Bundle;)V -PLandroidx/lifecycle/LifecycleRegistry;->markState(Landroidx/lifecycle/Lifecycle$State;)V PLandroidx/lifecycle/LiveData$LifecycleBoundObserver;->detachObserver()V -PLandroidx/lifecycle/MediatorLiveData;->onInactive()V -PLandroidx/lifecycle/ReportFragment$LifecycleCallbacks;->onActivitySaveInstanceState(Landroid/app/Activity;Landroid/os/Bundle;)V -PLandroidx/lifecycle/SavedStateHandle$Companion;->validateValue(Ljava/lang/Object;)Z -PLandroidx/lifecycle/SavedStateHandle;->access$getACCEPTABLE_CLASSES$cp()[Ljava/lang/Class; -PLandroidx/lifecycle/SavedStateHandle;->set(Ljava/lang/String;Ljava/lang/Object;)V -PLandroidx/lifecycle/SavedStateHandlesProvider;->saveState()Landroid/os/Bundle; -PLandroidx/lifecycle/SavedStateHandlesVM;->getHandles()Ljava/util/Map; PLandroidx/lifecycle/ViewModel;->closeWithRuntimeException(Ljava/lang/Object;)V PLandroidx/loader/app/LoaderManager;->()V PLandroidx/loader/app/LoaderManager;->getInstance(Landroidx/lifecycle/LifecycleOwner;)Landroidx/loader/app/LoaderManager; @@ -37878,29 +37291,13 @@ PLandroidx/media3/session/MediaControllerImplBase$$ExternalSyntheticLambda81;->r PLandroidx/media3/session/MediaControllerImplBase;->$r8$lambda$tIEgcrLv3SECRV9I_ggVsbUKXeY(Landroidx/media3/session/MediaControllerImplBase;)V PLandroidx/media3/session/MediaControllerImplBase;->lambda$release$4()V PLandroidx/media3/session/MediaControllerStub;->destroy()V -PLandroidx/navigation/NavBackStackEntry;->saveState(Landroid/os/Bundle;)V -PLandroidx/navigation/NavBackStackEntryState$Companion$CREATOR$1;->()V -PLandroidx/navigation/NavBackStackEntryState$Companion;->()V -PLandroidx/navigation/NavBackStackEntryState$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V -PLandroidx/navigation/NavBackStackEntryState;->()V -PLandroidx/navigation/NavBackStackEntryState;->(Landroidx/navigation/NavBackStackEntry;)V PLandroidx/navigation/NavBackStackEntryState;->writeToParcel(Landroid/os/Parcel;I)V -PLandroidx/navigation/NavController;->saveState()Landroid/os/Bundle; PLandroidx/navigation/NavControllerViewModel;->onCleared()V -PLandroidx/navigation/Navigator;->onSaveState()Landroid/os/Bundle; PLandroidx/navigation/fragment/FragmentNavigator$ClearEntryStateViewModel;->getCompleteTransition()Ljava/lang/ref/WeakReference; PLandroidx/navigation/fragment/FragmentNavigator$ClearEntryStateViewModel;->onCleared()V PLandroidx/navigation/fragment/FragmentNavigator$sam$androidx_lifecycle_Observer$0;->equals(Ljava/lang/Object;)Z PLandroidx/navigation/fragment/FragmentNavigator$sam$androidx_lifecycle_Observer$0;->getFunctionDelegate()Lkotlin/Function; -PLandroidx/navigation/fragment/FragmentNavigator;->onSaveState()Landroid/os/Bundle; -PLandroidx/navigation/fragment/NavHostFragment$navHostController$2$$ExternalSyntheticLambda0;->saveState()Landroid/os/Bundle; -PLandroidx/navigation/fragment/NavHostFragment$navHostController$2$$ExternalSyntheticLambda1;->saveState()Landroid/os/Bundle; -PLandroidx/navigation/fragment/NavHostFragment$navHostController$2;->$r8$lambda$S8rYz5PdxQ_qmpQw5Wg04g8YyYI(Landroidx/navigation/fragment/NavHostFragment;)Landroid/os/Bundle; -PLandroidx/navigation/fragment/NavHostFragment$navHostController$2;->$r8$lambda$yvpdz-7lzmuHOSkQDGqC7TUxHHI(Landroidx/navigation/NavHostController;)Landroid/os/Bundle; -PLandroidx/navigation/fragment/NavHostFragment$navHostController$2;->invoke$lambda$5$lambda$2(Landroidx/navigation/NavHostController;)Landroid/os/Bundle; -PLandroidx/navigation/fragment/NavHostFragment$navHostController$2;->invoke$lambda$5$lambda$4(Landroidx/navigation/fragment/NavHostFragment;)Landroid/os/Bundle; PLandroidx/navigation/fragment/NavHostFragment;->onDestroyView()V -PLandroidx/navigation/fragment/NavHostFragment;->onSaveInstanceState(Landroid/os/Bundle;)V PLandroidx/profileinstaller/ProfileInstallReceiver$$ExternalSyntheticLambda0;->()V PLandroidx/profileinstaller/ProfileInstaller$1;->()V PLandroidx/profileinstaller/ProfileInstaller$1;->onResultReceived(ILjava/lang/Object;)V @@ -37926,6 +37323,9 @@ PLandroidx/profileinstaller/ProfileVerifier;->getPackageLastUpdateTime(Landroid/ PLandroidx/profileinstaller/ProfileVerifier;->setCompilationStatus(IZZ)Landroidx/profileinstaller/ProfileVerifier$CompilationStatus; PLandroidx/profileinstaller/ProfileVerifier;->writeProfileVerification(Landroid/content/Context;Z)Landroidx/profileinstaller/ProfileVerifier$CompilationStatus; PLandroidx/recyclerview/widget/AdapterListUpdateCallback;->onChanged(IILjava/lang/Object;)V +PLandroidx/recyclerview/widget/AsyncDifferConfig;->getDiffCallback()Landroidx/recyclerview/widget/DiffUtil$ItemCallback; +PLandroidx/recyclerview/widget/AsyncListDiffer$1$1;->areContentsTheSame(II)Z +PLandroidx/recyclerview/widget/AsyncListDiffer$1$1;->areItemsTheSame(II)Z PLandroidx/recyclerview/widget/AsyncListDiffer$1$1;->getChangePayload(II)Ljava/lang/Object; PLandroidx/recyclerview/widget/BatchingListUpdateCallback;->onChanged(IILjava/lang/Object;)V PLandroidx/recyclerview/widget/ChildHelper;->removeViewAt(I)V @@ -37935,21 +37335,25 @@ PLandroidx/recyclerview/widget/ConcatAdapter;->onViewRecycled(Landroidx/recycler PLandroidx/recyclerview/widget/ConcatAdapterController;->onDetachedFromRecyclerView(Landroidx/recyclerview/widget/RecyclerView;)V PLandroidx/recyclerview/widget/ConcatAdapterController;->onViewDetachedFromWindow(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;)V PLandroidx/recyclerview/widget/ConcatAdapterController;->onViewRecycled(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;)V +PLandroidx/recyclerview/widget/DiffUtil$CenteredArray;->get(I)I +PLandroidx/recyclerview/widget/DiffUtil$CenteredArray;->set(II)V +PLandroidx/recyclerview/widget/DiffUtil$Range;->()V +PLandroidx/recyclerview/widget/DiffUtil$Range;->newSize()I +PLandroidx/recyclerview/widget/DiffUtil$Snake;->()V +PLandroidx/recyclerview/widget/DiffUtil$Snake;->diagonalSize()I +PLandroidx/recyclerview/widget/DiffUtil$Snake;->hasAdditionOrRemoval()Z +PLandroidx/recyclerview/widget/DiffUtil$Snake;->toDiagonal()Landroidx/recyclerview/widget/DiffUtil$Diagonal; +PLandroidx/recyclerview/widget/DiffUtil;->backward(Landroidx/recyclerview/widget/DiffUtil$Range;Landroidx/recyclerview/widget/DiffUtil$Callback;Landroidx/recyclerview/widget/DiffUtil$CenteredArray;Landroidx/recyclerview/widget/DiffUtil$CenteredArray;I)Landroidx/recyclerview/widget/DiffUtil$Snake; +PLandroidx/recyclerview/widget/DiffUtil;->forward(Landroidx/recyclerview/widget/DiffUtil$Range;Landroidx/recyclerview/widget/DiffUtil$Callback;Landroidx/recyclerview/widget/DiffUtil$CenteredArray;Landroidx/recyclerview/widget/DiffUtil$CenteredArray;I)Landroidx/recyclerview/widget/DiffUtil$Snake; PLandroidx/recyclerview/widget/GapWorker$LayoutPrefetchRegistryImpl;->lastPrefetchIncludedPosition(I)Z PLandroidx/recyclerview/widget/GapWorker;->remove(Landroidx/recyclerview/widget/RecyclerView;)V PLandroidx/recyclerview/widget/ItemTouchHelper;->endRecoverAnimation(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;Z)V PLandroidx/recyclerview/widget/ItemTouchHelper;->onChildViewDetachedFromWindow(Landroid/view/View;)V PLandroidx/recyclerview/widget/ItemTouchHelper;->removeChildDrawingOrderCallbackIfNecessary(Landroid/view/View;)V -PLandroidx/recyclerview/widget/LinearLayoutManager$SavedState$1;->()V -PLandroidx/recyclerview/widget/LinearLayoutManager$SavedState;->()V -PLandroidx/recyclerview/widget/LinearLayoutManager$SavedState;->()V PLandroidx/recyclerview/widget/LinearLayoutManager$SavedState;->invalidateAnchor()V PLandroidx/recyclerview/widget/LinearLayoutManager$SavedState;->writeToParcel(Landroid/os/Parcel;I)V PLandroidx/recyclerview/widget/LinearLayoutManager;->getChildClosestToEnd()Landroid/view/View; -PLandroidx/recyclerview/widget/LinearLayoutManager;->getChildClosestToStart()Landroid/view/View; PLandroidx/recyclerview/widget/LinearLayoutManager;->onDetachedFromWindow(Landroidx/recyclerview/widget/RecyclerView;Landroidx/recyclerview/widget/RecyclerView$Recycler;)V -PLandroidx/recyclerview/widget/LinearLayoutManager;->onSaveInstanceState()Landroid/os/Parcelable; -PLandroidx/recyclerview/widget/OrientationHelper$1;->getDecoratedStart(Landroid/view/View;)I PLandroidx/recyclerview/widget/RecyclerView$5;->removeViewAt(I)V PLandroidx/recyclerview/widget/RecyclerView$6;->markViewHoldersUpdated(IILjava/lang/Object;)V PLandroidx/recyclerview/widget/RecyclerView$Adapter;->onDetachedFromRecyclerView(Landroidx/recyclerview/widget/RecyclerView;)V @@ -37959,8 +37363,6 @@ PLandroidx/recyclerview/widget/RecyclerView$ItemAnimator;->canReuseUpdatedViewHo PLandroidx/recyclerview/widget/RecyclerView$ItemAnimator;->canReuseUpdatedViewHolder(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;Ljava/util/List;)Z PLandroidx/recyclerview/widget/RecyclerView$ItemAnimator;->recordPreLayoutInformation(Landroidx/recyclerview/widget/RecyclerView$State;Landroidx/recyclerview/widget/RecyclerView$ViewHolder;ILjava/util/List;)Landroidx/recyclerview/widget/RecyclerView$ItemAnimator$ItemHolderInfo; PLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->dispatchDetachedFromWindow(Landroidx/recyclerview/widget/RecyclerView;Landroidx/recyclerview/widget/RecyclerView$Recycler;)V -PLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getDecoratedLeft(Landroid/view/View;)I -PLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->getLeftDecorationWidth(Landroid/view/View;)I PLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->onDetachedFromWindow(Landroidx/recyclerview/widget/RecyclerView;)V PLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->onDetachedFromWindow(Landroidx/recyclerview/widget/RecyclerView;Landroidx/recyclerview/widget/RecyclerView$Recycler;)V PLandroidx/recyclerview/widget/RecyclerView$LayoutManager;->onItemsUpdated(Landroidx/recyclerview/widget/RecyclerView;II)V @@ -37977,9 +37379,6 @@ PLandroidx/recyclerview/widget/RecyclerView$Recycler;->recycleCachedViewAt(I)V PLandroidx/recyclerview/widget/RecyclerView$Recycler;->recycleView(Landroid/view/View;)V PLandroidx/recyclerview/widget/RecyclerView$Recycler;->recycleViewHolderInternal(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;)V PLandroidx/recyclerview/widget/RecyclerView$Recycler;->viewRangeUpdate(II)V -PLandroidx/recyclerview/widget/RecyclerView$SavedState$1;->()V -PLandroidx/recyclerview/widget/RecyclerView$SavedState;->()V -PLandroidx/recyclerview/widget/RecyclerView$SavedState;->(Landroid/os/Parcelable;)V PLandroidx/recyclerview/widget/RecyclerView$SavedState;->writeToParcel(Landroid/os/Parcel;I)V PLandroidx/recyclerview/widget/RecyclerView$ViewHolder;->addChangePayload(Ljava/lang/Object;)V PLandroidx/recyclerview/widget/RecyclerView$ViewHolder;->doesTransientStatePreventRecycling()Z @@ -37992,10 +37391,8 @@ PLandroidx/recyclerview/widget/RecyclerView;->animateChange(Landroidx/recyclervi PLandroidx/recyclerview/widget/RecyclerView;->canReuseUpdatedViewHolder(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;)Z PLandroidx/recyclerview/widget/RecyclerView;->clearNestedRecyclerViewIfNotNested(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;)V PLandroidx/recyclerview/widget/RecyclerView;->dispatchChildDetached(Landroid/view/View;)V -PLandroidx/recyclerview/widget/RecyclerView;->dispatchSaveInstanceState(Landroid/util/SparseArray;)V PLandroidx/recyclerview/widget/RecyclerView;->onChildDetachedFromWindow(Landroid/view/View;)V PLandroidx/recyclerview/widget/RecyclerView;->onDetachedFromWindow()V -PLandroidx/recyclerview/widget/RecyclerView;->onSaveInstanceState()Landroid/os/Parcelable; PLandroidx/recyclerview/widget/RecyclerView;->removeOnScrollListener(Landroidx/recyclerview/widget/RecyclerView$OnScrollListener;)V PLandroidx/recyclerview/widget/RecyclerView;->stopNestedScroll()V PLandroidx/recyclerview/widget/RecyclerView;->viewRangeUpdate(IILjava/lang/Object;)V @@ -38007,17 +37404,7 @@ PLandroidx/recyclerview/widget/ViewInfoStore;->onDetach()V PLandroidx/recyclerview/widget/ViewInfoStore;->popFromPostLayout(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;)Landroidx/recyclerview/widget/RecyclerView$ItemAnimator$ItemHolderInfo; PLandroidx/recyclerview/widget/ViewInfoStore;->popFromPreLayout(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;)Landroidx/recyclerview/widget/RecyclerView$ItemAnimator$ItemHolderInfo; PLandroidx/recyclerview/widget/ViewInfoStore;->removeViewHolder(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;)V -PLandroidx/savedstate/SavedStateRegistryController;->performSave(Landroid/os/Bundle;)V -PLcom/airbnb/lottie/LottieAnimationView$SavedState$1;->()V -PLcom/airbnb/lottie/LottieAnimationView$SavedState;->()V -PLcom/airbnb/lottie/LottieAnimationView$SavedState;->(Landroid/os/Parcelable;)V PLcom/airbnb/lottie/LottieAnimationView$SavedState;->writeToParcel(Landroid/os/Parcel;I)V -PLcom/airbnb/lottie/LottieAnimationView;->onSaveInstanceState()Landroid/os/Parcelable; -PLcom/airbnb/lottie/LottieDrawable;->getImageAssetsFolder()Ljava/lang/String; -PLcom/airbnb/lottie/LottieDrawable;->getProgress()F -PLcom/airbnb/lottie/LottieDrawable;->getRepeatCount()I -PLcom/airbnb/lottie/LottieDrawable;->getRepeatMode()I -PLcom/airbnb/lottie/LottieDrawable;->isAnimatingOrWillAnimateOnVisible()Z PLcom/bumptech/glide/Glide;->unregisterRequestManager(Lcom/bumptech/glide/RequestManager;)V PLcom/bumptech/glide/RequestManager;->onDestroy()V PLcom/bumptech/glide/load/Options;->equals(Ljava/lang/Object;)Z @@ -38050,59 +37437,190 @@ PLcom/bumptech/glide/request/target/ViewTarget;->removeCallback(Lcom/bumptech/gl PLcom/bumptech/glide/util/MultiClassKey;->equals(Ljava/lang/Object;)Z PLcom/bumptech/glide/util/Util;->bothNullOrEqual(Ljava/lang/Object;Ljava/lang/Object;)Z PLcom/bumptech/glide/util/Util;->removeCallbacksOnUiThread(Ljava/lang/Runnable;)V -PLcom/google/android/gms/common/api/internal/BackgroundDetector;->onActivitySaveInstanceState(Landroid/app/Activity;Landroid/os/Bundle;)V +PLcom/fasterxml/jackson/annotation/JsonIgnoreProperties$Value;->_asSet([Ljava/lang/String;)Ljava/util/Set; +PLcom/fasterxml/jackson/annotation/JsonIgnoreProperties$Value;->_empty(Ljava/util/Set;ZZZZ)Z +PLcom/fasterxml/jackson/annotation/JsonIgnoreProperties$Value;->construct(Ljava/util/Set;ZZZZ)Lcom/fasterxml/jackson/annotation/JsonIgnoreProperties$Value; +PLcom/fasterxml/jackson/annotation/JsonIgnoreProperties$Value;->from(Lcom/fasterxml/jackson/annotation/JsonIgnoreProperties;)Lcom/fasterxml/jackson/annotation/JsonIgnoreProperties$Value; +PLcom/fasterxml/jackson/core/Base64Variant;->encodeBase64Chunk(I[CI)I +PLcom/fasterxml/jackson/core/Base64Variant;->encodeBase64Partial(II[CI)I +PLcom/fasterxml/jackson/core/Base64Variant;->getMaxLineLength()I +PLcom/fasterxml/jackson/core/Base64Variant;->usesPadding()Z +PLcom/fasterxml/jackson/core/JacksonException;->(Ljava/lang/String;Ljava/lang/Throwable;)V +PLcom/fasterxml/jackson/core/JsonParseException;->(Lcom/fasterxml/jackson/core/JsonParser;Ljava/lang/String;)V +PLcom/fasterxml/jackson/core/JsonParseException;->withRequestPayload(Lcom/fasterxml/jackson/core/util/RequestPayload;)Lcom/fasterxml/jackson/core/JsonParseException; +PLcom/fasterxml/jackson/core/JsonParser;->_constructError(Ljava/lang/String;)Lcom/fasterxml/jackson/core/JsonParseException; +PLcom/fasterxml/jackson/core/JsonProcessingException;->(Ljava/lang/String;Lcom/fasterxml/jackson/core/JsonLocation;)V +PLcom/fasterxml/jackson/core/JsonProcessingException;->(Ljava/lang/String;Lcom/fasterxml/jackson/core/JsonLocation;Ljava/lang/Throwable;)V +PLcom/fasterxml/jackson/core/base/ParserBase;->_getSourceReference()Ljava/lang/Object; +PLcom/fasterxml/jackson/core/base/ParserBase;->_validJsonValueList()Ljava/lang/String; +PLcom/fasterxml/jackson/core/base/ParserMinimalBase;->_getCharDesc(I)Ljava/lang/String; +PLcom/fasterxml/jackson/core/base/ParserMinimalBase;->_reportError(Ljava/lang/String;)V +PLcom/fasterxml/jackson/core/base/ParserMinimalBase;->_reportUnexpectedChar(ILjava/lang/String;)V +PLcom/fasterxml/jackson/core/exc/StreamReadException;->(Lcom/fasterxml/jackson/core/JsonParser;Ljava/lang/String;)V +PLcom/fasterxml/jackson/core/io/IOContext;->getSourceReference()Ljava/lang/Object; +PLcom/fasterxml/jackson/core/io/NumberOutput;->_full3(I[CI)I +PLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_matchFalse()V +PLcom/fasterxml/jackson/core/json/ReaderBasedJsonParser;->_matchTrue()V +PLcom/fasterxml/jackson/core/json/UTF8JsonGenerator;->writeFieldName(Ljava/lang/String;)V +PLcom/fasterxml/jackson/core/json/UTF8JsonGenerator;->writeNumber(I)V +PLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->_handleUnexpectedValue(I)Lcom/fasterxml/jackson/core/JsonToken; +PLcom/fasterxml/jackson/core/json/UTF8StreamJsonParser;->getCurrentLocation()Lcom/fasterxml/jackson/core/JsonLocation; +PLcom/fasterxml/jackson/core/json/WriterBasedJsonGenerator;->_writeBinary(Lcom/fasterxml/jackson/core/Base64Variant;[BII)V +PLcom/fasterxml/jackson/core/json/WriterBasedJsonGenerator;->writeBinary(Lcom/fasterxml/jackson/core/Base64Variant;[BII)V +PLcom/fasterxml/jackson/core/json/WriterBasedJsonGenerator;->writeNumber(I)V +PLcom/fasterxml/jackson/databind/AnnotationIntrospector;->findKeyDeserializer(Lcom/fasterxml/jackson/databind/introspect/Annotated;)Ljava/lang/Object; +PLcom/fasterxml/jackson/databind/DeserializationContext;->constructType(Ljava/lang/Class;)Lcom/fasterxml/jackson/databind/JavaType; +PLcom/fasterxml/jackson/databind/DeserializationContext;->findKeyDeserializer(Lcom/fasterxml/jackson/databind/JavaType;Lcom/fasterxml/jackson/databind/BeanProperty;)Lcom/fasterxml/jackson/databind/KeyDeserializer; +PLcom/fasterxml/jackson/databind/KeyDeserializer;->()V +PLcom/fasterxml/jackson/databind/cfg/BaseSettings;->getBase64Variant()Lcom/fasterxml/jackson/core/Base64Variant; +PLcom/fasterxml/jackson/databind/cfg/DeserializerFactoryConfig;->hasKeyDeserializers()Z +PLcom/fasterxml/jackson/databind/cfg/DeserializerFactoryConfig;->keyDeserializers()Ljava/lang/Iterable; +PLcom/fasterxml/jackson/databind/cfg/MapperConfig;->getBase64Variant()Lcom/fasterxml/jackson/core/Base64Variant; +PLcom/fasterxml/jackson/databind/deser/BasicDeserializerFactory$ContainerDefaultMappings;->findMapFallback(Lcom/fasterxml/jackson/databind/JavaType;)Ljava/lang/Class; +PLcom/fasterxml/jackson/databind/deser/BasicDeserializerFactory;->_findCustomMapDeserializer(Lcom/fasterxml/jackson/databind/type/MapType;Lcom/fasterxml/jackson/databind/DeserializationConfig;Lcom/fasterxml/jackson/databind/BeanDescription;Lcom/fasterxml/jackson/databind/KeyDeserializer;Lcom/fasterxml/jackson/databind/jsontype/TypeDeserializer;Lcom/fasterxml/jackson/databind/JsonDeserializer;)Lcom/fasterxml/jackson/databind/JsonDeserializer; +PLcom/fasterxml/jackson/databind/deser/BasicDeserializerFactory;->_mapAbstractMapType(Lcom/fasterxml/jackson/databind/JavaType;Lcom/fasterxml/jackson/databind/DeserializationConfig;)Lcom/fasterxml/jackson/databind/type/MapType; +PLcom/fasterxml/jackson/databind/deser/BasicDeserializerFactory;->createKeyDeserializer(Lcom/fasterxml/jackson/databind/DeserializationContext;Lcom/fasterxml/jackson/databind/JavaType;)Lcom/fasterxml/jackson/databind/KeyDeserializer; +PLcom/fasterxml/jackson/databind/deser/BeanDeserializerFactory;->_isSetterlessType(Ljava/lang/Class;)Z +PLcom/fasterxml/jackson/databind/deser/DefaultDeserializationContext;->keyDeserializerInstance(Lcom/fasterxml/jackson/databind/introspect/Annotated;Ljava/lang/Object;)Lcom/fasterxml/jackson/databind/KeyDeserializer; +PLcom/fasterxml/jackson/databind/deser/DeserializerCache;->findKeyDeserializer(Lcom/fasterxml/jackson/databind/DeserializationContext;Lcom/fasterxml/jackson/databind/deser/DeserializerFactory;Lcom/fasterxml/jackson/databind/JavaType;)Lcom/fasterxml/jackson/databind/KeyDeserializer; +PLcom/fasterxml/jackson/databind/deser/Deserializers$Base;->findMapDeserializer(Lcom/fasterxml/jackson/databind/type/MapType;Lcom/fasterxml/jackson/databind/DeserializationConfig;Lcom/fasterxml/jackson/databind/BeanDescription;Lcom/fasterxml/jackson/databind/KeyDeserializer;Lcom/fasterxml/jackson/databind/jsontype/TypeDeserializer;Lcom/fasterxml/jackson/databind/JsonDeserializer;)Lcom/fasterxml/jackson/databind/JsonDeserializer; +PLcom/fasterxml/jackson/databind/deser/ValueInstantiator;->canCreateFromObjectWith()Z +PLcom/fasterxml/jackson/databind/deser/ValueInstantiator;->canCreateUsingArrayDelegate()Z +PLcom/fasterxml/jackson/databind/deser/ValueInstantiator;->canCreateUsingDelegate()Z +PLcom/fasterxml/jackson/databind/deser/ValueInstantiator;->createFromObjectWith(Lcom/fasterxml/jackson/databind/DeserializationContext;[Lcom/fasterxml/jackson/databind/deser/SettableBeanProperty;Lcom/fasterxml/jackson/databind/deser/impl/PropertyValueBuffer;)Ljava/lang/Object; +PLcom/fasterxml/jackson/databind/deser/impl/JDKValueInstantiators$LinkedHashMapInstantiator;->()V +PLcom/fasterxml/jackson/databind/deser/impl/JDKValueInstantiators$LinkedHashMapInstantiator;->()V +PLcom/fasterxml/jackson/databind/deser/impl/JDKValueInstantiators$LinkedHashMapInstantiator;->canCreateUsingDefault()Z +PLcom/fasterxml/jackson/databind/deser/impl/JDKValueInstantiators$LinkedHashMapInstantiator;->createUsingDefault(Lcom/fasterxml/jackson/databind/DeserializationContext;)Ljava/lang/Object; +PLcom/fasterxml/jackson/databind/deser/impl/PropertyValueBuffer;->getParameters([Lcom/fasterxml/jackson/databind/deser/SettableBeanProperty;)[Ljava/lang/Object; +PLcom/fasterxml/jackson/databind/deser/std/MapDeserializer;->(Lcom/fasterxml/jackson/databind/JavaType;Lcom/fasterxml/jackson/databind/deser/ValueInstantiator;Lcom/fasterxml/jackson/databind/KeyDeserializer;Lcom/fasterxml/jackson/databind/JsonDeserializer;Lcom/fasterxml/jackson/databind/jsontype/TypeDeserializer;)V +PLcom/fasterxml/jackson/databind/deser/std/MapDeserializer;->(Lcom/fasterxml/jackson/databind/deser/std/MapDeserializer;Lcom/fasterxml/jackson/databind/KeyDeserializer;Lcom/fasterxml/jackson/databind/JsonDeserializer;Lcom/fasterxml/jackson/databind/jsontype/TypeDeserializer;Lcom/fasterxml/jackson/databind/deser/NullValueProvider;Ljava/util/Set;Ljava/util/Set;)V +PLcom/fasterxml/jackson/databind/deser/std/MapDeserializer;->_isStdKeyDeser(Lcom/fasterxml/jackson/databind/JavaType;Lcom/fasterxml/jackson/databind/KeyDeserializer;)Z +PLcom/fasterxml/jackson/databind/deser/std/MapDeserializer;->_readAndBindStringKeyMap(Lcom/fasterxml/jackson/core/JsonParser;Lcom/fasterxml/jackson/databind/DeserializationContext;Ljava/util/Map;)V +PLcom/fasterxml/jackson/databind/deser/std/MapDeserializer;->createContextual(Lcom/fasterxml/jackson/databind/DeserializationContext;Lcom/fasterxml/jackson/databind/BeanProperty;)Lcom/fasterxml/jackson/databind/JsonDeserializer; +PLcom/fasterxml/jackson/databind/deser/std/MapDeserializer;->deserialize(Lcom/fasterxml/jackson/core/JsonParser;Lcom/fasterxml/jackson/databind/DeserializationContext;)Ljava/lang/Object; +PLcom/fasterxml/jackson/databind/deser/std/MapDeserializer;->isCachable()Z +PLcom/fasterxml/jackson/databind/deser/std/MapDeserializer;->resolve(Lcom/fasterxml/jackson/databind/DeserializationContext;)V +PLcom/fasterxml/jackson/databind/deser/std/MapDeserializer;->setIgnorableProperties(Ljava/util/Set;)V +PLcom/fasterxml/jackson/databind/deser/std/MapDeserializer;->setIncludableProperties(Ljava/util/Set;)V +PLcom/fasterxml/jackson/databind/deser/std/MapDeserializer;->withResolved(Lcom/fasterxml/jackson/databind/KeyDeserializer;Lcom/fasterxml/jackson/databind/jsontype/TypeDeserializer;Lcom/fasterxml/jackson/databind/JsonDeserializer;Lcom/fasterxml/jackson/databind/deser/NullValueProvider;Ljava/util/Set;Ljava/util/Set;)Lcom/fasterxml/jackson/databind/deser/std/MapDeserializer; +PLcom/fasterxml/jackson/databind/deser/std/NumberDeserializers$BooleanDeserializer;->()V +PLcom/fasterxml/jackson/databind/deser/std/NumberDeserializers$BooleanDeserializer;->(Ljava/lang/Class;Ljava/lang/Boolean;)V +PLcom/fasterxml/jackson/databind/deser/std/NumberDeserializers$BooleanDeserializer;->deserialize(Lcom/fasterxml/jackson/core/JsonParser;Lcom/fasterxml/jackson/databind/DeserializationContext;)Ljava/lang/Boolean; +PLcom/fasterxml/jackson/databind/deser/std/NumberDeserializers$BooleanDeserializer;->deserialize(Lcom/fasterxml/jackson/core/JsonParser;Lcom/fasterxml/jackson/databind/DeserializationContext;)Ljava/lang/Object; +PLcom/fasterxml/jackson/databind/deser/std/NumberDeserializers$DoubleDeserializer;->()V +PLcom/fasterxml/jackson/databind/deser/std/NumberDeserializers$DoubleDeserializer;->(Ljava/lang/Class;Ljava/lang/Double;)V +PLcom/fasterxml/jackson/databind/deser/std/NumberDeserializers$FloatDeserializer;->()V +PLcom/fasterxml/jackson/databind/deser/std/NumberDeserializers$FloatDeserializer;->(Ljava/lang/Class;Ljava/lang/Float;)V +PLcom/fasterxml/jackson/databind/deser/std/PrimitiveArrayDeserializers$BooleanDeser;->()V +PLcom/fasterxml/jackson/databind/deser/std/PrimitiveArrayDeserializers$DoubleDeser;->()V +PLcom/fasterxml/jackson/databind/deser/std/PrimitiveArrayDeserializers$FloatDeser;->()V +PLcom/fasterxml/jackson/databind/deser/std/PrimitiveArrayDeserializers$IntDeser;->()V +PLcom/fasterxml/jackson/databind/deser/std/PrimitiveArrayDeserializers$IntDeser;->()V +PLcom/fasterxml/jackson/databind/deser/std/PrimitiveArrayDeserializers$LongDeser;->()V +PLcom/fasterxml/jackson/databind/deser/std/PrimitiveArrayDeserializers$LongDeser;->()V +PLcom/fasterxml/jackson/databind/deser/std/PrimitiveArrayDeserializers;->(Ljava/lang/Class;)V +PLcom/fasterxml/jackson/databind/deser/std/PrimitiveArrayDeserializers;->createContextual(Lcom/fasterxml/jackson/databind/DeserializationContext;Lcom/fasterxml/jackson/databind/BeanProperty;)Lcom/fasterxml/jackson/databind/JsonDeserializer; +PLcom/fasterxml/jackson/databind/deser/std/PrimitiveArrayDeserializers;->forType(Ljava/lang/Class;)Lcom/fasterxml/jackson/databind/JsonDeserializer; +PLcom/fasterxml/jackson/databind/deser/std/StdDeserializer;->isDefaultKeyDeserializer(Lcom/fasterxml/jackson/databind/KeyDeserializer;)Z +PLcom/fasterxml/jackson/databind/deser/std/StdKeyDeserializer$StringKD;->()V +PLcom/fasterxml/jackson/databind/deser/std/StdKeyDeserializer$StringKD;->(Ljava/lang/Class;)V +PLcom/fasterxml/jackson/databind/deser/std/StdKeyDeserializer$StringKD;->forType(Ljava/lang/Class;)Lcom/fasterxml/jackson/databind/deser/std/StdKeyDeserializer$StringKD; +PLcom/fasterxml/jackson/databind/deser/std/StdKeyDeserializer;->(ILjava/lang/Class;)V +PLcom/fasterxml/jackson/databind/deser/std/StdKeyDeserializer;->(ILjava/lang/Class;Lcom/fasterxml/jackson/databind/deser/std/FromStringDeserializer;)V +PLcom/fasterxml/jackson/databind/deser/std/StdKeyDeserializer;->forType(Ljava/lang/Class;)Lcom/fasterxml/jackson/databind/deser/std/StdKeyDeserializer; +PLcom/fasterxml/jackson/databind/deser/std/StdKeyDeserializers;->findKeyDeserializer(Lcom/fasterxml/jackson/databind/JavaType;Lcom/fasterxml/jackson/databind/DeserializationConfig;Lcom/fasterxml/jackson/databind/BeanDescription;)Lcom/fasterxml/jackson/databind/KeyDeserializer; +PLcom/fasterxml/jackson/databind/deser/std/StringArrayDeserializer;->()V +PLcom/fasterxml/jackson/databind/deser/std/StringArrayDeserializer;->()V +PLcom/fasterxml/jackson/databind/deser/std/StringArrayDeserializer;->(Lcom/fasterxml/jackson/databind/JsonDeserializer;Lcom/fasterxml/jackson/databind/deser/NullValueProvider;Ljava/lang/Boolean;)V +PLcom/fasterxml/jackson/databind/deser/std/StringArrayDeserializer;->createContextual(Lcom/fasterxml/jackson/databind/DeserializationContext;Lcom/fasterxml/jackson/databind/BeanProperty;)Lcom/fasterxml/jackson/databind/JsonDeserializer; +PLcom/fasterxml/jackson/databind/introspect/AnnotationCollector$NCollector;->asAnnotations()Lcom/fasterxml/jackson/databind/util/Annotations; +PLcom/fasterxml/jackson/databind/introspect/AnnotationCollector$OneCollector;->isPresent(Ljava/lang/annotation/Annotation;)Z +PLcom/fasterxml/jackson/databind/introspect/AnnotationCollector$TwoAnnotations;->(Ljava/lang/Class;Ljava/lang/annotation/Annotation;Ljava/lang/Class;Ljava/lang/annotation/Annotation;)V +PLcom/fasterxml/jackson/databind/introspect/AnnotationCollector$TwoAnnotations;->get(Ljava/lang/Class;)Ljava/lang/annotation/Annotation; +PLcom/fasterxml/jackson/databind/introspect/AnnotationIntrospectorPair;->findKeyDeserializer(Lcom/fasterxml/jackson/databind/introspect/Annotated;)Ljava/lang/Object; +PLcom/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector;->findKeyDeserializer(Lcom/fasterxml/jackson/databind/introspect/Annotated;)Ljava/lang/Object; +PLcom/fasterxml/jackson/databind/ser/impl/PropertySerializerMap$Double;->(Lcom/fasterxml/jackson/databind/ser/impl/PropertySerializerMap;Ljava/lang/Class;Lcom/fasterxml/jackson/databind/JsonSerializer;Ljava/lang/Class;Lcom/fasterxml/jackson/databind/JsonSerializer;)V +PLcom/fasterxml/jackson/databind/ser/impl/PropertySerializerMap$Double;->serializerFor(Ljava/lang/Class;)Lcom/fasterxml/jackson/databind/JsonSerializer; +PLcom/fasterxml/jackson/databind/ser/impl/PropertySerializerMap$Single;->newWith(Ljava/lang/Class;Lcom/fasterxml/jackson/databind/JsonSerializer;)Lcom/fasterxml/jackson/databind/ser/impl/PropertySerializerMap; +PLcom/fasterxml/jackson/databind/ser/std/ByteArraySerializer;->serialize(Ljava/lang/Object;Lcom/fasterxml/jackson/core/JsonGenerator;Lcom/fasterxml/jackson/databind/SerializerProvider;)V +PLcom/fasterxml/jackson/databind/ser/std/ByteArraySerializer;->serialize([BLcom/fasterxml/jackson/core/JsonGenerator;Lcom/fasterxml/jackson/databind/SerializerProvider;)V +PLcom/fasterxml/jackson/databind/ser/std/NumberSerializers$IntegerSerializer;->serialize(Ljava/lang/Object;Lcom/fasterxml/jackson/core/JsonGenerator;Lcom/fasterxml/jackson/databind/SerializerProvider;)V +PLcom/fasterxml/jackson/databind/type/MapLikeType;->equals(Ljava/lang/Object;)Z +PLcom/fasterxml/jackson/databind/type/PlaceholderForType;->(I)V +PLcom/fasterxml/jackson/databind/type/PlaceholderForType;->actualType()Lcom/fasterxml/jackson/databind/JavaType; +PLcom/fasterxml/jackson/databind/type/PlaceholderForType;->actualType(Lcom/fasterxml/jackson/databind/JavaType;)V +PLcom/fasterxml/jackson/databind/type/PlaceholderForType;->equals(Ljava/lang/Object;)Z +PLcom/fasterxml/jackson/databind/type/TypeBase;->findSuperType(Ljava/lang/Class;)Lcom/fasterxml/jackson/databind/JavaType; +PLcom/fasterxml/jackson/databind/type/TypeFactory;->_bindingsForSubtype(Lcom/fasterxml/jackson/databind/JavaType;ILjava/lang/Class;Z)Lcom/fasterxml/jackson/databind/type/TypeBindings; +PLcom/fasterxml/jackson/databind/type/TypeFactory;->_resolveTypePlaceholders(Lcom/fasterxml/jackson/databind/JavaType;Lcom/fasterxml/jackson/databind/JavaType;)Ljava/lang/String; +PLcom/fasterxml/jackson/databind/type/TypeFactory;->_verifyAndResolvePlaceholders(Lcom/fasterxml/jackson/databind/JavaType;Lcom/fasterxml/jackson/databind/JavaType;)Z PLcom/google/android/material/appbar/AppBarLayout;->clearLiftOnScrollTargetView()V PLcom/google/android/material/appbar/AppBarLayout;->onDetachedFromWindow()V PLcom/google/android/material/appbar/AppBarLayout;->removeOnOffsetChangedListener(Lcom/google/android/material/appbar/AppBarLayout$BaseOnOffsetChangedListener;)V PLcom/google/android/material/appbar/AppBarLayout;->removeOnOffsetChangedListener(Lcom/google/android/material/appbar/AppBarLayout$OnOffsetChangedListener;)V -PLcom/google/android/material/appbar/AppBarLayout;->verifyDrawable(Landroid/graphics/drawable/Drawable;)Z PLcom/google/android/material/appbar/CollapsingToolbarLayout;->onDetachedFromWindow()V PLcom/google/android/material/button/MaterialButton$SavedState$1;->()V PLcom/google/android/material/button/MaterialButton$SavedState;->()V PLcom/google/android/material/button/MaterialButton$SavedState;->(Landroid/os/Parcelable;)V PLcom/google/android/material/button/MaterialButton;->onSaveInstanceState()Landroid/os/Parcelable; -PLcom/google/android/material/expandable/ExpandableWidgetHelper;->onSaveInstanceState()Landroid/os/Bundle; PLcom/google/android/material/floatingactionbutton/FloatingActionButton;->onDetachedFromWindow()V -PLcom/google/android/material/floatingactionbutton/FloatingActionButton;->onSaveInstanceState()Landroid/os/Parcelable; PLcom/google/android/material/floatingactionbutton/FloatingActionButtonImpl;->onDetachedFromWindow()V -PLcom/google/android/material/stateful/ExtendableSavedState$1;->()V -PLcom/google/android/material/stateful/ExtendableSavedState;->()V -PLcom/google/android/material/stateful/ExtendableSavedState;->(Landroid/os/Parcelable;)V PLcom/google/android/material/stateful/ExtendableSavedState;->writeToParcel(Landroid/os/Parcel;I)V -PLcom/google/firebase/messaging/FcmLifecycleCallbacks;->onActivitySaveInstanceState(Landroid/app/Activity;Landroid/os/Bundle;)V PLcom/pnikosis/materialishprogress/ProgressWheel$WheelSavedState$1;->()V PLcom/pnikosis/materialishprogress/ProgressWheel$WheelSavedState;->()V PLcom/pnikosis/materialishprogress/ProgressWheel$WheelSavedState;->(Landroid/os/Parcelable;)V PLcom/pnikosis/materialishprogress/ProgressWheel;->onSaveInstanceState()Landroid/os/Parcelable; +PLio/reactivex/rxjava3/core/Flowable;->fromFuture(Ljava/util/concurrent/Future;JLjava/util/concurrent/TimeUnit;)Lio/reactivex/rxjava3/core/Flowable; PLio/reactivex/rxjava3/core/Scheduler$PeriodicDirectTask;->dispose()V +PLio/reactivex/rxjava3/core/Single;->error(Lio/reactivex/rxjava3/functions/Supplier;)Lio/reactivex/rxjava3/core/Single; +PLio/reactivex/rxjava3/core/Single;->error(Ljava/lang/Throwable;)Lio/reactivex/rxjava3/core/Single; +PLio/reactivex/rxjava3/core/Single;->flatMap(Lio/reactivex/rxjava3/functions/Function;)Lio/reactivex/rxjava3/core/Single; +PLio/reactivex/rxjava3/core/Single;->fromFuture(Ljava/util/concurrent/Future;JLjava/util/concurrent/TimeUnit;)Lio/reactivex/rxjava3/core/Single; +PLio/reactivex/rxjava3/core/Single;->onErrorResumeNext(Lio/reactivex/rxjava3/functions/Function;)Lio/reactivex/rxjava3/core/Single; +PLio/reactivex/rxjava3/core/Single;->onErrorReturn(Lio/reactivex/rxjava3/functions/Function;)Lio/reactivex/rxjava3/core/Single; +PLio/reactivex/rxjava3/core/Single;->toSingle(Lio/reactivex/rxjava3/core/Flowable;)Lio/reactivex/rxjava3/core/Single; PLio/reactivex/rxjava3/disposables/CompositeDisposable;->clear()V +PLio/reactivex/rxjava3/exceptions/Exceptions;->throwIfFatal(Ljava/lang/Throwable;)V +PLio/reactivex/rxjava3/internal/disposables/CancellableDisposable;->dispose()V PLio/reactivex/rxjava3/internal/disposables/EmptyDisposable;->dispose()V +PLio/reactivex/rxjava3/internal/disposables/EmptyDisposable;->error(Ljava/lang/Throwable;Lio/reactivex/rxjava3/core/SingleObserver;)V PLio/reactivex/rxjava3/internal/disposables/SequentialDisposable;->dispose()V PLio/reactivex/rxjava3/internal/observers/ConsumerSingleObserver;->dispose()V PLio/reactivex/rxjava3/internal/observers/QueueDrainObserver;->cancelled()Z PLio/reactivex/rxjava3/internal/observers/QueueDrainObserver;->done()Z PLio/reactivex/rxjava3/internal/observers/QueueDrainObserver;->enter()Z PLio/reactivex/rxjava3/internal/observers/QueueDrainObserver;->error()Ljava/lang/Throwable; +PLio/reactivex/rxjava3/internal/observers/ResumeSingleObserver;->(Ljava/util/concurrent/atomic/AtomicReference;Lio/reactivex/rxjava3/core/SingleObserver;)V +PLio/reactivex/rxjava3/internal/observers/ResumeSingleObserver;->onError(Ljava/lang/Throwable;)V +PLio/reactivex/rxjava3/internal/observers/ResumeSingleObserver;->onSubscribe(Lio/reactivex/rxjava3/disposables/Disposable;)V PLio/reactivex/rxjava3/internal/operators/flowable/AbstractBackpressureThrottlingSubscriber;->cancel()V PLio/reactivex/rxjava3/internal/operators/flowable/FlowableCombineLatest$CombineLatestCoordinator;->cancel()V PLio/reactivex/rxjava3/internal/operators/flowable/FlowableCombineLatest$CombineLatestCoordinator;->cancelAll()V PLio/reactivex/rxjava3/internal/operators/flowable/FlowableCombineLatest$CombineLatestInnerSubscriber;->cancel()V PLio/reactivex/rxjava3/internal/operators/flowable/FlowableCreate$BaseEmitter;->cancel()V PLio/reactivex/rxjava3/internal/operators/flowable/FlowableCreate$LatestAsyncEmitter;->onUnsubscribed()V +PLio/reactivex/rxjava3/internal/operators/flowable/FlowableFromFuture;->(Ljava/util/concurrent/Future;JLjava/util/concurrent/TimeUnit;)V +PLio/reactivex/rxjava3/internal/operators/flowable/FlowableFromFuture;->subscribeActual(Lorg/reactivestreams/Subscriber;)V PLio/reactivex/rxjava3/internal/operators/flowable/FlowableFromObservable$SubscriberObserver;->cancel()V PLio/reactivex/rxjava3/internal/operators/flowable/FlowableInterval$IntervalSubscriber;->cancel()V PLio/reactivex/rxjava3/internal/operators/flowable/FlowableObserveOn$BaseObserveOnSubscriber;->cancel()V PLio/reactivex/rxjava3/internal/operators/flowable/FlowableObserveOn$BaseObserveOnSubscriber;->clear()V -PLio/reactivex/rxjava3/internal/operators/flowable/FlowableObserveOn$ObserveOnSubscriber;->poll()Ljava/lang/Object; -PLio/reactivex/rxjava3/internal/operators/flowable/FlowableObserveOn$ObserveOnSubscriber;->runBackfused()V PLio/reactivex/rxjava3/internal/operators/flowable/FlowableRefCount$RefCountSubscriber;->cancel()V PLio/reactivex/rxjava3/internal/operators/flowable/FlowableRefCount;->cancel(Lio/reactivex/rxjava3/internal/operators/flowable/FlowableRefCount$RefConnection;)V PLio/reactivex/rxjava3/internal/operators/flowable/FlowableRefCount;->timeout(Lio/reactivex/rxjava3/internal/operators/flowable/FlowableRefCount$RefConnection;)V +PLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$BoundedReplayBuffer;->removeFirst()V +PLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$BoundedReplayBuffer;->setFirst(Lio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$Node;)V PLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$InnerSubscription;->cancel()V PLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$InnerSubscription;->dispose()V PLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$ReplaySubscriber;->dispose()V PLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$ReplaySubscriber;->remove(Lio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay$InnerSubscription;)V PLio/reactivex/rxjava3/internal/operators/flowable/FlowableReplay;->reset()V +PLio/reactivex/rxjava3/internal/operators/flowable/FlowableSingleSingle$SingleElementSubscriber;->(Lio/reactivex/rxjava3/core/SingleObserver;Ljava/lang/Object;)V +PLio/reactivex/rxjava3/internal/operators/flowable/FlowableSingleSingle$SingleElementSubscriber;->onError(Ljava/lang/Throwable;)V +PLio/reactivex/rxjava3/internal/operators/flowable/FlowableSingleSingle$SingleElementSubscriber;->onSubscribe(Lorg/reactivestreams/Subscription;)V +PLio/reactivex/rxjava3/internal/operators/flowable/FlowableSingleSingle;->(Lio/reactivex/rxjava3/core/Flowable;Ljava/lang/Object;)V +PLio/reactivex/rxjava3/internal/operators/flowable/FlowableSingleSingle;->subscribeActual(Lio/reactivex/rxjava3/core/SingleObserver;)V PLio/reactivex/rxjava3/internal/operators/flowable/FlowableSubscribeOn$SubscribeOnSubscriber;->cancel()V PLio/reactivex/rxjava3/internal/operators/flowable/FlowableSwitchMap$SwitchMapSubscriber;->cancel()V PLio/reactivex/rxjava3/internal/operators/flowable/FlowableSwitchMap$SwitchMapSubscriber;->disposeInner()V @@ -38124,15 +37642,16 @@ PLio/reactivex/rxjava3/internal/operators/observable/ObservableCombineLatest$Lat PLio/reactivex/rxjava3/internal/operators/observable/ObservableCombineLatest$LatestCoordinator;->dispose()V PLio/reactivex/rxjava3/internal/operators/observable/ObservableConcatMap$ConcatMapDelayErrorObserver$DelayErrorInnerObserver;->dispose()V PLio/reactivex/rxjava3/internal/operators/observable/ObservableConcatMap$ConcatMapDelayErrorObserver;->dispose()V +PLio/reactivex/rxjava3/internal/operators/observable/ObservableCreate$CreateEmitter;->dispose()V PLio/reactivex/rxjava3/internal/operators/observable/ObservableDoOnEach$DoOnEachObserver;->dispose()V PLio/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapMaybe$FlatMapMaybeObserver;->dispose()V PLio/reactivex/rxjava3/internal/operators/observable/ObservableFlatMapSingle$FlatMapSingleObserver;->dispose()V PLio/reactivex/rxjava3/internal/operators/observable/ObservableFromArray$FromArrayDisposable;->dispose()V PLio/reactivex/rxjava3/internal/operators/observable/ObservableFromPublisher$PublisherSubscriber;->dispose()V -PLio/reactivex/rxjava3/internal/operators/observable/ObservableMap$MapObserver;->requestFusion(I)I PLio/reactivex/rxjava3/internal/operators/observable/ObservableObserveOn$ObserveOnObserver;->clear()V -PLio/reactivex/rxjava3/internal/operators/observable/ObservableObserveOn$ObserveOnObserver;->isEmpty()Z PLio/reactivex/rxjava3/internal/operators/observable/ObservableRefCount;->timeout(Lio/reactivex/rxjava3/internal/operators/observable/ObservableRefCount$RefConnection;)V +PLio/reactivex/rxjava3/internal/operators/observable/ObservableReplay$BoundedReplayBuffer;->removeFirst()V +PLio/reactivex/rxjava3/internal/operators/observable/ObservableReplay$BoundedReplayBuffer;->setFirst(Lio/reactivex/rxjava3/internal/operators/observable/ObservableReplay$Node;)V PLio/reactivex/rxjava3/internal/operators/observable/ObservableReplay$ReplayObserver;->dispose()V PLio/reactivex/rxjava3/internal/operators/observable/ObservableReplay;->reset()V PLio/reactivex/rxjava3/internal/operators/observable/ObservableSampleTimed$SampleTimedNoLast;->complete()V @@ -38146,21 +37665,49 @@ PLio/reactivex/rxjava3/internal/operators/observable/ObservableSkip$SkipObserver PLio/reactivex/rxjava3/internal/operators/observable/ObservableSwitchMap$SwitchMapObserver;->dispose()V PLio/reactivex/rxjava3/internal/operators/observable/ObservableSwitchMap$SwitchMapObserver;->disposeInner()V PLio/reactivex/rxjava3/internal/operators/observable/ObservableThrottleFirstTimed$DebounceTimedObserver;->dispose()V +PLio/reactivex/rxjava3/internal/operators/single/SingleError;->(Lio/reactivex/rxjava3/functions/Supplier;)V +PLio/reactivex/rxjava3/internal/operators/single/SingleError;->subscribeActual(Lio/reactivex/rxjava3/core/SingleObserver;)V +PLio/reactivex/rxjava3/internal/operators/single/SingleFlatMap$SingleFlatMapCallback$FlatMapSingleObserver;->(Ljava/util/concurrent/atomic/AtomicReference;Lio/reactivex/rxjava3/core/SingleObserver;)V +PLio/reactivex/rxjava3/internal/operators/single/SingleFlatMap$SingleFlatMapCallback$FlatMapSingleObserver;->onSubscribe(Lio/reactivex/rxjava3/disposables/Disposable;)V +PLio/reactivex/rxjava3/internal/operators/single/SingleFlatMap$SingleFlatMapCallback$FlatMapSingleObserver;->onSuccess(Ljava/lang/Object;)V +PLio/reactivex/rxjava3/internal/operators/single/SingleFlatMap$SingleFlatMapCallback;->(Lio/reactivex/rxjava3/core/SingleObserver;Lio/reactivex/rxjava3/functions/Function;)V +PLio/reactivex/rxjava3/internal/operators/single/SingleFlatMap$SingleFlatMapCallback;->isDisposed()Z +PLio/reactivex/rxjava3/internal/operators/single/SingleFlatMap$SingleFlatMapCallback;->onSubscribe(Lio/reactivex/rxjava3/disposables/Disposable;)V +PLio/reactivex/rxjava3/internal/operators/single/SingleFlatMap$SingleFlatMapCallback;->onSuccess(Ljava/lang/Object;)V +PLio/reactivex/rxjava3/internal/operators/single/SingleFlatMap;->(Lio/reactivex/rxjava3/core/SingleSource;Lio/reactivex/rxjava3/functions/Function;)V +PLio/reactivex/rxjava3/internal/operators/single/SingleFlatMap;->subscribeActual(Lio/reactivex/rxjava3/core/SingleObserver;)V +PLio/reactivex/rxjava3/internal/operators/single/SingleMap$MapSingleObserver;->onError(Ljava/lang/Throwable;)V +PLio/reactivex/rxjava3/internal/operators/single/SingleOnErrorReturn$OnErrorReturn;->(Lio/reactivex/rxjava3/internal/operators/single/SingleOnErrorReturn;Lio/reactivex/rxjava3/core/SingleObserver;)V +PLio/reactivex/rxjava3/internal/operators/single/SingleOnErrorReturn$OnErrorReturn;->onError(Ljava/lang/Throwable;)V +PLio/reactivex/rxjava3/internal/operators/single/SingleOnErrorReturn$OnErrorReturn;->onSubscribe(Lio/reactivex/rxjava3/disposables/Disposable;)V +PLio/reactivex/rxjava3/internal/operators/single/SingleOnErrorReturn$OnErrorReturn;->onSuccess(Ljava/lang/Object;)V +PLio/reactivex/rxjava3/internal/operators/single/SingleOnErrorReturn;->(Lio/reactivex/rxjava3/core/SingleSource;Lio/reactivex/rxjava3/functions/Function;Ljava/lang/Object;)V +PLio/reactivex/rxjava3/internal/operators/single/SingleOnErrorReturn;->subscribeActual(Lio/reactivex/rxjava3/core/SingleObserver;)V +PLio/reactivex/rxjava3/internal/operators/single/SingleResumeNext$ResumeMainSingleObserver;->(Lio/reactivex/rxjava3/core/SingleObserver;Lio/reactivex/rxjava3/functions/Function;)V +PLio/reactivex/rxjava3/internal/operators/single/SingleResumeNext$ResumeMainSingleObserver;->onError(Ljava/lang/Throwable;)V +PLio/reactivex/rxjava3/internal/operators/single/SingleResumeNext$ResumeMainSingleObserver;->onSubscribe(Lio/reactivex/rxjava3/disposables/Disposable;)V +PLio/reactivex/rxjava3/internal/operators/single/SingleResumeNext;->(Lio/reactivex/rxjava3/core/SingleSource;Lio/reactivex/rxjava3/functions/Function;)V +PLio/reactivex/rxjava3/internal/operators/single/SingleResumeNext;->subscribeActual(Lio/reactivex/rxjava3/core/SingleObserver;)V +PLio/reactivex/rxjava3/internal/queue/MpscLinkedQueue;->isEmpty()Z +PLio/reactivex/rxjava3/internal/queue/MpscLinkedQueue;->lvConsumerNode()Lio/reactivex/rxjava3/internal/queue/MpscLinkedQueue$LinkedQueueNode; PLio/reactivex/rxjava3/internal/queue/SpscArrayQueue;->clear()V PLio/reactivex/rxjava3/internal/queue/SpscArrayQueue;->isEmpty()Z +PLio/reactivex/rxjava3/internal/schedulers/AbstractDirectTask;->cancelFuture(Ljava/util/concurrent/Future;)V PLio/reactivex/rxjava3/internal/schedulers/DisposeOnCancel;->cancel(Z)Z PLio/reactivex/rxjava3/internal/schedulers/ScheduledRunnable;->dispose()V PLio/reactivex/rxjava3/internal/subscribers/BasicFuseableConditionalSubscriber;->cancel()V PLio/reactivex/rxjava3/internal/subscribers/BasicFuseableSubscriber;->cancel()V PLio/reactivex/rxjava3/internal/subscribers/LambdaSubscriber;->cancel()V PLio/reactivex/rxjava3/internal/subscribers/LambdaSubscriber;->dispose()V +PLio/reactivex/rxjava3/internal/subscriptions/DeferredScalarSubscription;->(Lorg/reactivestreams/Subscriber;)V +PLio/reactivex/rxjava3/internal/subscriptions/DeferredScalarSubscription;->isCancelled()Z +PLio/reactivex/rxjava3/internal/subscriptions/DeferredScalarSubscription;->request(J)V PLio/reactivex/rxjava3/internal/util/AppendOnlyLinkedArrayList;->(I)V PLio/reactivex/rxjava3/internal/util/AppendOnlyLinkedArrayList;->add(Ljava/lang/Object;)V PLio/reactivex/rxjava3/internal/util/AppendOnlyLinkedArrayList;->forEachWhile(Lio/reactivex/rxjava3/internal/util/AppendOnlyLinkedArrayList$NonThrowingPredicate;)V PLio/reactivex/rxjava3/internal/util/AtomicThrowable;->terminate()Ljava/lang/Throwable; PLio/reactivex/rxjava3/internal/util/AtomicThrowable;->tryTerminateAndReport()V PLio/reactivex/rxjava3/internal/util/ExceptionHelper;->terminate(Ljava/util/concurrent/atomic/AtomicReference;)Ljava/lang/Throwable; -PLio/reactivex/rxjava3/internal/util/NotificationLite;->acceptFull(Ljava/lang/Object;Lio/reactivex/rxjava3/core/Observer;)Z PLio/reactivex/rxjava3/internal/util/NotificationLite;->complete()Ljava/lang/Object; PLio/reactivex/rxjava3/internal/util/QueueDrainHelper;->checkTerminated(ZZLio/reactivex/rxjava3/core/Observer;ZLio/reactivex/rxjava3/internal/fuseable/SimpleQueue;Lio/reactivex/rxjava3/disposables/Disposable;Lio/reactivex/rxjava3/internal/util/ObservableQueueDrain;)Z PLio/reactivex/rxjava3/internal/util/QueueDrainHelper;->drainLoop(Lio/reactivex/rxjava3/internal/fuseable/SimplePlainQueue;Lio/reactivex/rxjava3/core/Observer;ZLio/reactivex/rxjava3/disposables/Disposable;Lio/reactivex/rxjava3/internal/util/ObservableQueueDrain;)V @@ -38175,14 +37722,110 @@ PLio/reactivex/rxjava3/subjects/BehaviorSubject;->onComplete()V PLio/reactivex/rxjava3/subjects/BehaviorSubject;->terminate(Ljava/lang/Object;)[Lio/reactivex/rxjava3/subjects/BehaviorSubject$BehaviorDisposable; PLio/reactivex/rxjava3/subjects/PublishSubject$PublishDisposable;->dispose()V PLio/reactivex/rxjava3/subjects/PublishSubject;->remove(Lio/reactivex/rxjava3/subjects/PublishSubject$PublishDisposable;)V -PLio/reactivex/rxjava3/subjects/SerializedSubject;->test(Ljava/lang/Object;)Z -PLj$/util/DesugarCollections;->a()Ljava/lang/reflect/Constructor; -PLj$/util/d;->a(Ljava/util/Set;Ljava/lang/Object;)Ljava/util/Set; -PLj$/util/d;->keySet()Ljava/util/Set; -PLkotlin/sequences/SequenceScope;->yieldAll(Lkotlin/sequences/Sequence;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +PLj$/util/d;->remove(Ljava/lang/Object;)Ljava/lang/Object; +PLkotlin/Result;->(Ljava/lang/Object;)V +PLkotlin/Result;->box-impl(Ljava/lang/Object;)Lkotlin/Result; +PLkotlin/Result;->unbox-impl()Ljava/lang/Object; +PLkotlin/collections/CollectionsKt;->retainAll(Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;)Z +PLkotlin/collections/CollectionsKt__MutableCollectionsKt;->filterInPlace$CollectionsKt__MutableCollectionsKt(Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;Z)Z +PLkotlin/collections/CollectionsKt__MutableCollectionsKt;->retainAll(Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;)Z +PLkotlin/collections/MapsKt;->mapOf(Lkotlin/Pair;)Ljava/util/Map; +PLkotlin/collections/MapsKt;->plus(Ljava/util/Map;Lkotlin/Pair;)Ljava/util/Map; +PLkotlin/collections/MapsKt__MapsJVMKt;->mapOf(Lkotlin/Pair;)Ljava/util/Map; +PLkotlin/collections/MapsKt__MapsKt;->plus(Ljava/util/Map;Lkotlin/Pair;)Ljava/util/Map; +PLkotlin/reflect/jvm/internal/UtilKt;->createArrayType(Ljava/lang/Class;)Ljava/lang/Class; +PLkotlin/reflect/jvm/internal/impl/descriptors/runtime/components/SignatureSerializer;->fieldDesc(Ljava/lang/reflect/Field;)Ljava/lang/String; +PLkotlin/reflect/jvm/internal/impl/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader$loadAnnotationsAndInitializers$1$AnnotationVisitorForMethod;->visitParameterAnnotation(ILkotlin/reflect/jvm/internal/impl/name/ClassId;Lkotlin/reflect/jvm/internal/impl/descriptors/SourceElement;)Lkotlin/reflect/jvm/internal/impl/load/kotlin/KotlinJvmBinaryClass$AnnotationArgumentVisitor; +PLkotlin/reflect/jvm/internal/impl/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader$loadAnnotationsAndInitializers$1$MemberAnnotationVisitor;->getSignature()Lkotlin/reflect/jvm/internal/impl/load/kotlin/MemberSignature; +PLkotlin/reflect/jvm/internal/impl/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader$loadAnnotationsAndInitializers$1;->visitField(Lkotlin/reflect/jvm/internal/impl/name/Name;Ljava/lang/String;Ljava/lang/Object;)Lkotlin/reflect/jvm/internal/impl/load/kotlin/KotlinJvmBinaryClass$AnnotationVisitor; +PLkotlin/reflect/jvm/internal/impl/load/kotlin/BinaryClassAnnotationAndConstantLoaderImpl$AbstractAnnotationArgumentVisitor;->visit(Lkotlin/reflect/jvm/internal/impl/name/Name;Ljava/lang/Object;)V +PLkotlin/reflect/jvm/internal/impl/load/kotlin/BinaryClassAnnotationAndConstantLoaderImpl$AbstractAnnotationArgumentVisitor;->visitEnum(Lkotlin/reflect/jvm/internal/impl/name/Name;Lkotlin/reflect/jvm/internal/impl/name/ClassId;Lkotlin/reflect/jvm/internal/impl/name/Name;)V +PLkotlin/reflect/jvm/internal/impl/load/kotlin/BinaryClassAnnotationAndConstantLoaderImpl$loadAnnotation$1;->visitConstantValue(Lkotlin/reflect/jvm/internal/impl/name/Name;Lkotlin/reflect/jvm/internal/impl/resolve/constants/ConstantValue;)V +PLkotlin/reflect/jvm/internal/impl/load/kotlin/BinaryClassAnnotationAndConstantLoaderImpl;->access$createConstant(Lkotlin/reflect/jvm/internal/impl/load/kotlin/BinaryClassAnnotationAndConstantLoaderImpl;Lkotlin/reflect/jvm/internal/impl/name/Name;Ljava/lang/Object;)Lkotlin/reflect/jvm/internal/impl/resolve/constants/ConstantValue; +PLkotlin/reflect/jvm/internal/impl/load/kotlin/BinaryClassAnnotationAndConstantLoaderImpl;->createConstant(Lkotlin/reflect/jvm/internal/impl/name/Name;Ljava/lang/Object;)Lkotlin/reflect/jvm/internal/impl/resolve/constants/ConstantValue; +PLkotlin/reflect/jvm/internal/impl/load/kotlin/MemberSignature$Companion;->fromFieldNameAndDesc(Ljava/lang/String;Ljava/lang/String;)Lkotlin/reflect/jvm/internal/impl/load/kotlin/MemberSignature; +PLkotlin/reflect/jvm/internal/impl/load/kotlin/MemberSignature$Companion;->fromMethodSignatureAndParameterIndex(Lkotlin/reflect/jvm/internal/impl/load/kotlin/MemberSignature;I)Lkotlin/reflect/jvm/internal/impl/load/kotlin/MemberSignature; +PLkotlin/reflect/jvm/internal/impl/load/kotlin/MemberSignature;->getSignature()Ljava/lang/String; +PLkotlin/reflect/jvm/internal/impl/resolve/constants/BooleanValue;->(Z)V +PLkotlin/reflect/jvm/internal/impl/resolve/constants/ConstantValue;->(Ljava/lang/Object;)V +PLkotlin/reflect/jvm/internal/impl/resolve/constants/ConstantValueFactory;->()V +PLkotlin/reflect/jvm/internal/impl/resolve/constants/ConstantValueFactory;->()V +PLkotlin/reflect/jvm/internal/impl/resolve/constants/ConstantValueFactory;->createConstantValue(Ljava/lang/Object;)Lkotlin/reflect/jvm/internal/impl/resolve/constants/ConstantValue; +PLkotlin/reflect/jvm/internal/impl/resolve/constants/EnumValue;->(Lkotlin/reflect/jvm/internal/impl/name/ClassId;Lkotlin/reflect/jvm/internal/impl/name/Name;)V +PLkotlin/reflect/jvm/internal/impl/resolve/constants/IntValue;->(I)V +PLkotlin/reflect/jvm/internal/impl/resolve/constants/IntegerValueConstant;->(Ljava/lang/Object;)V +PLkotlin/reflect/jvm/internal/impl/resolve/constants/StringValue;->(Ljava/lang/String;)V PLkotlin/sequences/SequencesKt;->sequence(Lkotlin/jvm/functions/Function2;)Lkotlin/sequences/Sequence; PLkotlin/sequences/SequencesKt__SequenceBuilderKt$sequence$$inlined$Sequence$1;->(Lkotlin/jvm/functions/Function2;)V PLkotlin/sequences/SequencesKt__SequenceBuilderKt$sequence$$inlined$Sequence$1;->iterator()Ljava/util/Iterator; +PLkotlin/sequences/SequencesKt__SequenceBuilderKt;->sequence(Lkotlin/jvm/functions/Function2;)Lkotlin/sequences/Sequence; +PLme/leolin/shortcutbadger/ShortcutBadgeException;->(Ljava/lang/String;)V +PLme/leolin/shortcutbadger/ShortcutBadgeException;->(Ljava/lang/String;Ljava/lang/Exception;)V +PLme/leolin/shortcutbadger/ShortcutBadger;->()V +PLme/leolin/shortcutbadger/ShortcutBadger;->applyCount(Landroid/content/Context;I)Z +PLme/leolin/shortcutbadger/ShortcutBadger;->applyCountOrThrow(Landroid/content/Context;I)V +PLme/leolin/shortcutbadger/ShortcutBadger;->initBadger(Landroid/content/Context;)Z +PLme/leolin/shortcutbadger/ShortcutBadger;->removeCount(Landroid/content/Context;)Z +PLme/leolin/shortcutbadger/impl/AdwHomeBadger;->()V +PLme/leolin/shortcutbadger/impl/AdwHomeBadger;->getSupportLaunchers()Ljava/util/List; +PLme/leolin/shortcutbadger/impl/ApexHomeBadger;->()V +PLme/leolin/shortcutbadger/impl/ApexHomeBadger;->getSupportLaunchers()Ljava/util/List; +PLme/leolin/shortcutbadger/impl/AsusHomeBadger;->()V +PLme/leolin/shortcutbadger/impl/AsusHomeBadger;->getSupportLaunchers()Ljava/util/List; +PLme/leolin/shortcutbadger/impl/DefaultBadger;->()V +PLme/leolin/shortcutbadger/impl/DefaultBadger;->executeBadge(Landroid/content/Context;Landroid/content/ComponentName;I)V +PLme/leolin/shortcutbadger/impl/DefaultBadger;->getSupportLaunchers()Ljava/util/List; +PLme/leolin/shortcutbadger/impl/EverythingMeHomeBadger;->()V +PLme/leolin/shortcutbadger/impl/EverythingMeHomeBadger;->getSupportLaunchers()Ljava/util/List; +PLme/leolin/shortcutbadger/impl/HuaweiHomeBadger;->()V +PLme/leolin/shortcutbadger/impl/HuaweiHomeBadger;->getSupportLaunchers()Ljava/util/List; +PLme/leolin/shortcutbadger/impl/NewHtcHomeBadger;->()V +PLme/leolin/shortcutbadger/impl/NewHtcHomeBadger;->getSupportLaunchers()Ljava/util/List; +PLme/leolin/shortcutbadger/impl/NovaHomeBadger;->()V +PLme/leolin/shortcutbadger/impl/NovaHomeBadger;->getSupportLaunchers()Ljava/util/List; +PLme/leolin/shortcutbadger/impl/OPPOHomeBader;->()V +PLme/leolin/shortcutbadger/impl/OPPOHomeBader;->getSupportLaunchers()Ljava/util/List; +PLme/leolin/shortcutbadger/impl/SamsungHomeBadger;->()V +PLme/leolin/shortcutbadger/impl/SamsungHomeBadger;->()V +PLme/leolin/shortcutbadger/impl/SamsungHomeBadger;->getSupportLaunchers()Ljava/util/List; +PLme/leolin/shortcutbadger/impl/SonyHomeBadger;->()V +PLme/leolin/shortcutbadger/impl/SonyHomeBadger;->getSupportLaunchers()Ljava/util/List; +PLme/leolin/shortcutbadger/impl/VivoHomeBadger;->()V +PLme/leolin/shortcutbadger/impl/VivoHomeBadger;->getSupportLaunchers()Ljava/util/List; +PLme/leolin/shortcutbadger/impl/ZTEHomeBadger;->()V +PLme/leolin/shortcutbadger/impl/ZTEHomeBadger;->getSupportLaunchers()Ljava/util/List; +PLme/leolin/shortcutbadger/impl/ZukHomeBadger;->()V +PLme/leolin/shortcutbadger/impl/ZukHomeBadger;->getSupportLaunchers()Ljava/util/List; +PLme/leolin/shortcutbadger/util/BroadcastHelper;->resolveBroadcast(Landroid/content/Context;Landroid/content/Intent;)Ljava/util/List; +PLme/leolin/shortcutbadger/util/BroadcastHelper;->sendDefaultIntentExplicitly(Landroid/content/Context;Landroid/content/Intent;)V +PLme/leolin/shortcutbadger/util/BroadcastHelper;->sendIntentExplicitly(Landroid/content/Context;Landroid/content/Intent;)V +PLokhttp3/MediaType;->charset()Ljava/nio/charset/Charset; +PLokhttp3/MediaType;->toString()Ljava/lang/String; +PLokhttp3/OkHttpClient$Builder;->retryOnConnectionFailure(Z)Lokhttp3/OkHttpClient$Builder; +PLokhttp3/RequestBody;->create(Lokhttp3/MediaType;Ljava/lang/String;)Lokhttp3/RequestBody; +PLokio/AsyncTimeout;->access$getIDLE_TIMEOUT_NANOS$cp()J +PLorg/conscrypt/ArrayUtils;->checkOffsetAndCount(III)V +PLorg/conscrypt/GCMParameters;->(I[B)V +PLorg/conscrypt/GCMParameters;->getIV()[B +PLorg/conscrypt/GCMParameters;->getTLen()I +PLorg/conscrypt/NativeRef$SSL_SESSION;->doFree(J)V +PLorg/conscrypt/OpenSSLAeadCipher;->()V +PLorg/conscrypt/OpenSSLAeadCipher;->(Lorg/conscrypt/OpenSSLCipher$Mode;)V +PLorg/conscrypt/OpenSSLAeadCipher;->allowsNonceReuse()Z +PLorg/conscrypt/OpenSSLAeadCipher;->checkInitialization()V +PLorg/conscrypt/OpenSSLAeadCipher;->checkSupportedTagLength(I)V +PLorg/conscrypt/OpenSSLAeadCipher;->doFinalInternal([BII)I +PLorg/conscrypt/OpenSSLAeadCipher;->engineInitInternal([BLjava/security/spec/AlgorithmParameterSpec;Ljava/security/SecureRandom;)V +PLorg/conscrypt/OpenSSLAeadCipher;->expand(I)V +PLorg/conscrypt/OpenSSLAeadCipher;->reset()V +PLorg/conscrypt/OpenSSLAeadCipher;->updateInternal([BII[BII)I +PLorg/conscrypt/OpenSSLAeadCipherAES$GCM;->()V +PLorg/conscrypt/OpenSSLAeadCipherAES$GCM;->getEVP_AEAD(I)J +PLorg/conscrypt/OpenSSLAeadCipherAES;->(Lorg/conscrypt/OpenSSLCipher$Mode;)V +PLorg/conscrypt/OpenSSLAeadCipherAES;->checkSupportedKeySize(I)V +PLorg/conscrypt/OpenSSLAeadCipherAES;->getCipherBlockSize()I +PLorg/conscrypt/OpenSSLAeadCipherAES;->getOutputSizeForFinal(I)I +PLorg/conscrypt/Platform;->fromGCMParameterSpec(Ljava/security/spec/AlgorithmParameterSpec;)Lorg/conscrypt/GCMParameters; PLorg/signal/core/util/ByteSize;->getInKibiBytes()F PLorg/signal/core/util/ByteSize;->getInMebiBytes()F PLorg/signal/core/util/FloatExtensionsKt;->roundedString(FI)Ljava/lang/String; @@ -38191,6 +37834,10 @@ PLorg/signal/core/util/MemoryTracker$AppHeapUsage;->getCurrentTotalBytes()J PLorg/signal/core/util/MemoryTracker$AppHeapUsage;->getUsedBytes()J PLorg/signal/core/util/MemoryTracker;->byteDisplay(J)Ljava/lang/String; PLorg/signal/core/util/MemoryTracker;->poll()V +PLorg/signal/core/util/PendingIntentFlags;->cancelCurrent()I +PLorg/signal/core/util/StreamUtil;->()V +PLorg/signal/core/util/StreamUtil;->close(Ljava/io/Closeable;)V +PLorg/signal/core/util/StreamUtil;->copy(Ljava/io/InputStream;Ljava/io/OutputStream;)J PLorg/signal/core/util/concurrent/DeadlockDetector$$ExternalSyntheticLambda0;->run()V PLorg/signal/core/util/concurrent/DeadlockDetector$Companion;->access$isExecutorFull(Lorg/signal/core/util/concurrent/DeadlockDetector$Companion;Ljava/util/concurrent/ExecutorService;)Z PLorg/signal/core/util/concurrent/DeadlockDetector$Companion;->isExecutorFull(Ljava/util/concurrent/ExecutorService;)Z @@ -38200,27 +37847,60 @@ PLorg/signal/core/util/concurrent/DeadlockDetector;->hasPotentialLock([Ljava/lan PLorg/signal/core/util/concurrent/DeadlockDetector;->isWaiting(Ljava/lang/Thread$State;)Z PLorg/signal/core/util/concurrent/DeadlockDetector;->poll()V PLorg/signal/core/util/concurrent/LifecycleDisposable;->onDestroy(Landroidx/lifecycle/LifecycleOwner;)V +PLorg/signal/core/util/concurrent/SignalExecutors$$ExternalSyntheticLambda0;->rejectedExecution(Ljava/lang/Runnable;Ljava/util/concurrent/ThreadPoolExecutor;)V +PLorg/signal/core/util/concurrent/SignalExecutors;->$r8$lambda$0Q0afsv1raKIrq3aP-SuMcT2Ad0(Ljava/lang/Runnable;Ljava/util/concurrent/ThreadPoolExecutor;)V +PLorg/signal/core/util/logging/Log$Logger;->i(Ljava/lang/String;Ljava/lang/String;)V +PLorg/signal/core/util/logging/Log$Logger;->i(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)V +PLorg/signal/core/util/logging/Log;->internal()Lorg/signal/core/util/logging/Log$Logger; PLorg/signal/libsignal/protocol/IdentityKey;->equals(Ljava/lang/Object;)Z PLorg/signal/libsignal/protocol/IdentityKey;->getPublicKey()Lorg/signal/libsignal/protocol/ecc/ECPublicKey; -PLorg/signal/paging/FixedSizePagingController;->buildDataNeededLog(ILjava/lang/String;)Ljava/lang/String; +PLorg/signal/libsignal/protocol/ServiceId;->toServiceIdFixedWidthBinary()[B +PLorg/signal/libsignal/protocol/ecc/ECPublicKey;->equals(Ljava/lang/Object;)Z +PLorg/signal/libsignal/protocol/util/ByteUtil;->trim([BI)[B +PLorg/signal/libsignal/zkgroup/profiles/ProfileKey;->getProfileKeyVersion(Lorg/signal/libsignal/protocol/ServiceId$Aci;)Lorg/signal/libsignal/zkgroup/profiles/ProfileKeyVersion; +PLorg/signal/libsignal/zkgroup/profiles/ProfileKeyVersion;->([B)V +PLorg/signal/libsignal/zkgroup/profiles/ProfileKeyVersion;->serialize()Ljava/lang/String; +PLorg/signal/paging/BufferedPagingController$$ExternalSyntheticLambda2;->(Lorg/signal/paging/BufferedPagingController;Ljava/lang/Object;)V +PLorg/signal/paging/BufferedPagingController$$ExternalSyntheticLambda2;->run()V +PLorg/signal/paging/BufferedPagingController;->$r8$lambda$GxlLAxjfERBgyqmyvxteAPWaQkA(Lorg/signal/paging/BufferedPagingController;Ljava/lang/Object;)V +PLorg/signal/paging/BufferedPagingController;->lambda$onDataItemChanged$2(Ljava/lang/Object;)V +PLorg/signal/paging/BufferedPagingController;->onDataItemChanged(Ljava/lang/Object;)V +PLorg/signal/paging/DataStatus;->mark(I)V +PLorg/signal/paging/FixedSizePagingController$$ExternalSyntheticLambda0;->(Lorg/signal/paging/FixedSizePagingController;Ljava/lang/Object;)V +PLorg/signal/paging/FixedSizePagingController$$ExternalSyntheticLambda0;->run()V +PLorg/signal/paging/FixedSizePagingController;->$r8$lambda$2jZFFAhs3dG0IThMmzJQSvWvcd0(Lorg/signal/paging/FixedSizePagingController;Ljava/lang/Object;)V +PLorg/signal/paging/FixedSizePagingController;->lambda$onDataItemChanged$2(Ljava/lang/Object;)V +PLorg/signal/paging/FixedSizePagingController;->onDataItemChanged(Ljava/lang/Object;)V +PLorg/signal/paging/ProxyPagingController;->onDataItemChanged(Ljava/lang/Object;)V +PLorg/thoughtcrime/securesms/AppCapabilities;->()V +PLorg/thoughtcrime/securesms/AppCapabilities;->()V +PLorg/thoughtcrime/securesms/AppCapabilities;->getCapabilities(Z)Lorg/whispersystems/signalservice/api/account/AccountAttributes$Capabilities; +PLorg/thoughtcrime/securesms/ApplicationContext$$ExternalSyntheticLambda59;->isInternal()Z PLorg/thoughtcrime/securesms/LoggingFragment;->onDestroy()V -PLorg/thoughtcrime/securesms/LoggingFragment;->onStop()V -PLorg/thoughtcrime/securesms/MainActivity;->onStop()V PLorg/thoughtcrime/securesms/PassphraseRequiredActivity;->onDestroy()V PLorg/thoughtcrime/securesms/PassphraseRequiredActivity;->removeClearKeyReceiver(Landroid/content/Context;)V PLorg/thoughtcrime/securesms/animation/AnimationStartListener;->()V PLorg/thoughtcrime/securesms/animation/AnimationStartListener;->()V PLorg/thoughtcrime/securesms/animation/AnimationStartListener;->onAnimationEnd(Landroid/animation/Animator;)V -PLorg/thoughtcrime/securesms/components/AnimatingToggle;->display(Landroid/view/View;)V +PLorg/thoughtcrime/securesms/avatar/Avatar$Resource;->()V +PLorg/thoughtcrime/securesms/avatar/Avatar$Resource;->(ILorg/thoughtcrime/securesms/avatar/Avatars$ColorPair;)V +PLorg/thoughtcrime/securesms/avatar/Avatar$Resource;->getColor()Lorg/thoughtcrime/securesms/avatar/Avatars$ColorPair; +PLorg/thoughtcrime/securesms/avatar/Avatar$Resource;->getResourceId()I +PLorg/thoughtcrime/securesms/avatar/AvatarRenderer$$ExternalSyntheticLambda0;->(Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Landroid/content/Context;Lkotlin/jvm/functions/Function1;)V +PLorg/thoughtcrime/securesms/avatar/AvatarRenderer$$ExternalSyntheticLambda0;->run()V +PLorg/thoughtcrime/securesms/avatar/AvatarRenderer$renderResource$1;->(Landroid/content/Context;Lorg/thoughtcrime/securesms/avatar/Avatar$Resource;)V +PLorg/thoughtcrime/securesms/avatar/AvatarRenderer$renderResource$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; +PLorg/thoughtcrime/securesms/avatar/AvatarRenderer$renderResource$1;->invoke-IoAF18A(Landroid/graphics/Canvas;)Ljava/lang/Object; +PLorg/thoughtcrime/securesms/avatar/AvatarRenderer;->$r8$lambda$LMRb5EH7u5JsF-ZatOYl7HXPvGU(Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Landroid/content/Context;Lkotlin/jvm/functions/Function1;)V +PLorg/thoughtcrime/securesms/avatar/AvatarRenderer;->createMedia(Landroid/net/Uri;J)Lorg/thoughtcrime/securesms/mediasend/Media; +PLorg/thoughtcrime/securesms/avatar/AvatarRenderer;->getDIMENSIONS()I +PLorg/thoughtcrime/securesms/avatar/AvatarRenderer;->renderAvatar(Landroid/content/Context;Lorg/thoughtcrime/securesms/avatar/Avatar;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V +PLorg/thoughtcrime/securesms/avatar/AvatarRenderer;->renderInBackground$lambda$1(Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Landroid/content/Context;Lkotlin/jvm/functions/Function1;)V +PLorg/thoughtcrime/securesms/avatar/AvatarRenderer;->renderInBackground(Landroid/content/Context;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V +PLorg/thoughtcrime/securesms/avatar/AvatarRenderer;->renderResource(Landroid/content/Context;Lorg/thoughtcrime/securesms/avatar/Avatar$Resource;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V +PLorg/thoughtcrime/securesms/avatar/Avatars$ColorPair;->getBackgroundColor()I PLorg/thoughtcrime/securesms/components/AudioView;->onDetachedFromWindow()V PLorg/thoughtcrime/securesms/components/ConversationItemFooter;->onDetachedFromWindow()V -PLorg/thoughtcrime/securesms/components/ConversationScrollToView;->formatUnreadCount(I)Ljava/lang/CharSequence; -PLorg/thoughtcrime/securesms/components/ConversationScrollToView;->setShown(Z)V -PLorg/thoughtcrime/securesms/components/ConversationScrollToView;->setUnreadCount(I)V -PLorg/thoughtcrime/securesms/components/DeliveryStatusView;->isPending()Z -PLorg/thoughtcrime/securesms/components/ExpirationTimerView;->stopAnimation()V -PLorg/thoughtcrime/securesms/components/HidingLinearLayout;->hide()V -PLorg/thoughtcrime/securesms/components/HidingLinearLayout;->show()V PLorg/thoughtcrime/securesms/components/InputPanel$3;->(Lorg/thoughtcrime/securesms/components/InputPanel;Landroid/view/View;)V PLorg/thoughtcrime/securesms/components/InputPanel$3;->onAnimationStart(Landroid/animation/Animator;)V PLorg/thoughtcrime/securesms/components/InputPanel;->fadeIn(Landroid/view/View;)V @@ -38233,22 +37913,13 @@ PLorg/thoughtcrime/securesms/components/InputPanel;->setLinkPreview(Lorg/thought PLorg/thoughtcrime/securesms/components/InputPanel;->setVoiceNoteDraft(Lorg/thoughtcrime/securesms/database/DraftTable$Draft;)V PLorg/thoughtcrime/securesms/components/LinkPreviewView;->onSaveInstanceState()Landroid/os/Parcelable; PLorg/thoughtcrime/securesms/components/LinkPreviewView;->setCorners(II)V -PLorg/thoughtcrime/securesms/components/LinkPreviewViewThumbnailState;->applyState(Lorg/thoughtcrime/securesms/util/views/Stub;)V -PLorg/thoughtcrime/securesms/components/LinkPreviewViewThumbnailState;->copy(IIIILorg/thoughtcrime/securesms/mms/SlidesClickedListener;)Lorg/thoughtcrime/securesms/components/LinkPreviewViewThumbnailState; -PLorg/thoughtcrime/securesms/components/LinkPreviewViewThumbnailState;->getDownloadListener()Lorg/thoughtcrime/securesms/mms/SlidesClickedListener; PLorg/thoughtcrime/securesms/components/MicrophoneRecorderView;->cancelAction(Z)V -PLorg/thoughtcrime/securesms/components/MicrophoneRecorderView;->isRecordingLocked()Z PLorg/thoughtcrime/securesms/components/QuoteView;->onDetachedFromWindow()V PLorg/thoughtcrime/securesms/components/RecyclerViewParentTransitionController;->onViewDetachedFromWindow(Landroid/view/View;)V -PLorg/thoughtcrime/securesms/components/SearchView;->(Landroid/content/Context;)V -PLorg/thoughtcrime/securesms/components/SearchView;->(Landroid/content/Context;Landroid/util/AttributeSet;)V -PLorg/thoughtcrime/securesms/components/SearchView;->(Landroid/content/Context;Landroid/util/AttributeSet;I)V -PLorg/thoughtcrime/securesms/components/SearchView;->appendEmojiFilter(Landroid/widget/TextView;)[Landroid/text/InputFilter; -PLorg/thoughtcrime/securesms/components/SearchView;->initEmojiFilter()V +PLorg/thoughtcrime/securesms/components/ScrollToPositionDelegate;->isListCommitted()Z PLorg/thoughtcrime/securesms/components/ViewBinderDelegate$1;->invoke(Landroidx/viewbinding/ViewBinding;)V PLorg/thoughtcrime/securesms/components/ViewBinderDelegate$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; PLorg/thoughtcrime/securesms/components/ViewBinderDelegate;->onDestroy(Landroidx/lifecycle/LifecycleOwner;)V -PLorg/thoughtcrime/securesms/components/ViewBinderDelegate;->onStop(Landroidx/lifecycle/LifecycleOwner;)V PLorg/thoughtcrime/securesms/components/emoji/EmojiEditText;->removeOnFocusChangeListener(Landroid/view/View$OnFocusChangeListener;)V PLorg/thoughtcrime/securesms/components/emoji/EmojiProvider$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/components/emoji/EmojiProvider$EmojiDrawable;Lorg/thoughtcrime/securesms/emoji/EmojiPageCache$LoadResult;)V PLorg/thoughtcrime/securesms/components/emoji/EmojiProvider$$ExternalSyntheticLambda0;->run()V @@ -38259,9 +37930,7 @@ PLorg/thoughtcrime/securesms/components/registration/PulsingFloatingActionButton PLorg/thoughtcrime/securesms/components/registration/PulsingFloatingActionButton$1$1;->lambda$onAnimationEnd$0(J)V PLorg/thoughtcrime/securesms/components/registration/PulsingFloatingActionButton;->-$$Nest$mpulse(Lorg/thoughtcrime/securesms/components/registration/PulsingFloatingActionButton;J)V PLorg/thoughtcrime/securesms/components/settings/app/subscription/completed/TerminalDonationDelegate;->onDestroy(Landroidx/lifecycle/LifecycleOwner;)V -PLorg/thoughtcrime/securesms/components/settings/app/subscription/completed/TerminalDonationDelegate;->onStop(Landroidx/lifecycle/LifecycleOwner;)V PLorg/thoughtcrime/securesms/components/spoiler/SpoilerRendererDelegate$1$onViewAttachedToWindow$1;->onDestroy(Landroidx/lifecycle/LifecycleOwner;)V -PLorg/thoughtcrime/securesms/components/spoiler/SpoilerRendererDelegate$1$onViewAttachedToWindow$1;->onStop(Landroidx/lifecycle/LifecycleOwner;)V PLorg/thoughtcrime/securesms/components/spoiler/SpoilerRendererDelegate$1;->onViewDetachedFromWindow(Landroid/view/View;)V PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView$Progress;->equals(Ljava/lang/Object;)Z PLorg/thoughtcrime/securesms/components/transfercontrols/TransferControlView;->onDetachedFromWindow()V @@ -38273,6 +37942,10 @@ PLorg/thoughtcrime/securesms/contacts/avatars/ProfileContactPhoto;->equals(Ljava PLorg/thoughtcrime/securesms/contacts/paged/ContactSearchMediator$sam$androidx_lifecycle_Observer$0;->equals(Ljava/lang/Object;)Z PLorg/thoughtcrime/securesms/contacts/paged/ContactSearchMediator$sam$androidx_lifecycle_Observer$0;->getFunctionDelegate()Lkotlin/Function; PLorg/thoughtcrime/securesms/contacts/paged/ContactSearchViewModel;->onCleared()V +PLorg/thoughtcrime/securesms/contacts/sync/ContactDiscovery;->()V +PLorg/thoughtcrime/securesms/contacts/sync/ContactDiscovery;->()V +PLorg/thoughtcrime/securesms/contacts/sync/ContactDiscovery;->hasContactsPermissions(Landroid/content/Context;)Z +PLorg/thoughtcrime/securesms/contacts/sync/ContactDiscovery;->refreshAll(Landroid/content/Context;Z)V PLorg/thoughtcrime/securesms/conversation/ConversationItem;->getBadgeImageView()Landroid/view/View; PLorg/thoughtcrime/securesms/conversation/ConversationItem;->getBubbleViews()Ljava/util/List; PLorg/thoughtcrime/securesms/conversation/ConversationItem;->getContactPhotoHolderView()Landroid/view/View; @@ -38281,12 +37954,8 @@ PLorg/thoughtcrime/securesms/conversation/ConversationItem;->getReactionsView()L PLorg/thoughtcrime/securesms/conversation/ConversationItem;->getReactionsView()Lorg/thoughtcrime/securesms/reactions/ReactionsConversationView; PLorg/thoughtcrime/securesms/conversation/ConversationItem;->getReplyView()Landroid/view/View; PLorg/thoughtcrime/securesms/conversation/ConversationItem;->onDetachedFromWindow()V -PLorg/thoughtcrime/securesms/conversation/ConversationItem;->onRecipientChanged(Lorg/thoughtcrime/securesms/recipients/Recipient;)V -PLorg/thoughtcrime/securesms/conversation/ConversationOptionsMenu$Provider$onCreateMenu$1;->(Landroid/view/Menu;Z)V PLorg/thoughtcrime/securesms/conversation/ConversationOptionsMenu$Provider$onCreateMenu$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; PLorg/thoughtcrime/securesms/conversation/ConversationOptionsMenu$Provider$onCreateMenu$1;->invoke(Z)V -PLorg/thoughtcrime/securesms/conversation/ConversationOptionsMenu$Provider;->applyTitleSpan(Landroid/view/MenuItem;Ljava/lang/Object;)V -PLorg/thoughtcrime/securesms/conversation/ConversationOptionsMenu$Provider;->hideMenuItem(Landroid/view/Menu;I)V PLorg/thoughtcrime/securesms/conversation/ConversationSwipeAnimationHelper$BubblePositionInterpolator;->getInterpolation(F)F PLorg/thoughtcrime/securesms/conversation/ConversationSwipeAnimationHelper$ClampingLinearInterpolator;->getInterpolation(F)F PLorg/thoughtcrime/securesms/conversation/ConversationSwipeAnimationHelper;->update(Lorg/thoughtcrime/securesms/conversation/v2/items/InteractiveConversationElement;FF)V @@ -38296,25 +37965,35 @@ PLorg/thoughtcrime/securesms/conversation/ConversationSwipeAnimationHelper;->upd PLorg/thoughtcrime/securesms/conversation/ConversationSwipeAnimationHelper;->updateReactionsTransition(Landroid/view/View;FF)V PLorg/thoughtcrime/securesms/conversation/ConversationSwipeAnimationHelper;->updateReplyIconTransition(Landroid/view/View;FFF)V PLorg/thoughtcrime/securesms/conversation/ConversationTitleView;->onSaveInstanceState()Landroid/os/Parcelable; -PLorg/thoughtcrime/securesms/conversation/ConversationTitleView;->setVerified(Z)V PLorg/thoughtcrime/securesms/conversation/ConversationUpdateTick;->onDestroy(Landroidx/lifecycle/LifecycleOwner;)V PLorg/thoughtcrime/securesms/conversation/ConversationUpdateTick;->onPause(Landroidx/lifecycle/LifecycleOwner;)V PLorg/thoughtcrime/securesms/conversation/ConversationUpdateTick;->onStop(Landroidx/lifecycle/LifecycleOwner;)V -PLorg/thoughtcrime/securesms/conversation/MessageStyler;->boldStyle()Landroid/text/style/CharacterStyle; -PLorg/thoughtcrime/securesms/conversation/MessageStyler;->italicStyle()Landroid/text/style/CharacterStyle; -PLorg/thoughtcrime/securesms/conversation/MessageStyler;->monoStyle()Landroid/text/style/CharacterStyle; -PLorg/thoughtcrime/securesms/conversation/MessageStyler;->strikethroughStyle()Landroid/text/style/CharacterStyle; +PLorg/thoughtcrime/securesms/conversation/MarkReadHelper$$ExternalSyntheticLambda2;->run()V +PLorg/thoughtcrime/securesms/conversation/MarkReadHelper;->$r8$lambda$h27hRrs_Rwv2sGlsmjqcW0dGIZI(Lorg/thoughtcrime/securesms/conversation/MarkReadHelper;J)V +PLorg/thoughtcrime/securesms/conversation/MarkReadHelper;->lambda$onViewsRevealed$0(J)V PLorg/thoughtcrime/securesms/conversation/ScheduledMessagesRepository$$ExternalSyntheticLambda4;->cancel()V PLorg/thoughtcrime/securesms/conversation/ScheduledMessagesRepository;->$r8$lambda$ej8HJIkF2PUVMbFRXSAuhVdCWz4(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V PLorg/thoughtcrime/securesms/conversation/ScheduledMessagesRepository;->getScheduledMessageCount$lambda$6$lambda$5(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V -PLorg/thoughtcrime/securesms/conversation/VoiceNoteDraftView;->clearDraft()V PLorg/thoughtcrime/securesms/conversation/VoiceRecorderWakeLock;->onDestroy(Landroidx/lifecycle/LifecycleOwner;)V PLorg/thoughtcrime/securesms/conversation/VoiceRecorderWakeLock;->onPause(Landroidx/lifecycle/LifecycleOwner;)V PLorg/thoughtcrime/securesms/conversation/VoiceRecorderWakeLock;->onStop(Landroidx/lifecycle/LifecycleOwner;)V PLorg/thoughtcrime/securesms/conversation/VoiceRecorderWakeLock;->release()V -PLorg/thoughtcrime/securesms/conversation/drafts/DraftState;->getVoiceNoteDraft()Lorg/thoughtcrime/securesms/database/DraftTable$Draft; -PLorg/thoughtcrime/securesms/conversation/drafts/DraftViewModel;->getVoiceNoteDraft()Lorg/thoughtcrime/securesms/database/DraftTable$Draft; +PLorg/thoughtcrime/securesms/conversation/colors/AvatarColor;->random()Lorg/thoughtcrime/securesms/conversation/colors/AvatarColor; +PLorg/thoughtcrime/securesms/conversation/drafts/DraftRepository$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/database/DraftTable$Drafts;Lorg/thoughtcrime/securesms/conversation/drafts/DraftRepository;J)V +PLorg/thoughtcrime/securesms/conversation/drafts/DraftRepository$$ExternalSyntheticLambda0;->run()V +PLorg/thoughtcrime/securesms/conversation/drafts/DraftRepository;->$r8$lambda$SLYPkhFM2MVtyCpgHajSG6aOkdY(Lorg/thoughtcrime/securesms/database/DraftTable$Drafts;Lorg/thoughtcrime/securesms/conversation/drafts/DraftRepository;J)V +PLorg/thoughtcrime/securesms/conversation/drafts/DraftRepository;->saveDrafts$lambda$9(Lorg/thoughtcrime/securesms/database/DraftTable$Drafts;Lorg/thoughtcrime/securesms/conversation/drafts/DraftRepository;J)V +PLorg/thoughtcrime/securesms/conversation/drafts/DraftRepository;->saveDrafts(JLorg/thoughtcrime/securesms/database/DraftTable$Drafts;)V +PLorg/thoughtcrime/securesms/conversation/drafts/DraftState;->copy(JLorg/thoughtcrime/securesms/database/DraftTable$Draft;Lorg/thoughtcrime/securesms/database/DraftTable$Draft;Lorg/thoughtcrime/securesms/database/DraftTable$Draft;Lorg/thoughtcrime/securesms/database/DraftTable$Draft;Lorg/thoughtcrime/securesms/database/DraftTable$Draft;Lorg/thoughtcrime/securesms/database/DraftTable$Draft;)Lorg/thoughtcrime/securesms/conversation/drafts/DraftState; +PLorg/thoughtcrime/securesms/conversation/drafts/DraftState;->copyAndSetDrafts$default(Lorg/thoughtcrime/securesms/conversation/drafts/DraftState;JLorg/thoughtcrime/securesms/database/DraftTable$Drafts;ILjava/lang/Object;)Lorg/thoughtcrime/securesms/conversation/drafts/DraftState; +PLorg/thoughtcrime/securesms/conversation/drafts/DraftState;->copyAndSetDrafts(JLorg/thoughtcrime/securesms/database/DraftTable$Drafts;)Lorg/thoughtcrime/securesms/conversation/drafts/DraftState; +PLorg/thoughtcrime/securesms/conversation/drafts/DraftState;->getThreadId()J +PLorg/thoughtcrime/securesms/conversation/drafts/DraftState;->toDrafts()Lorg/thoughtcrime/securesms/database/DraftTable$Drafts; +PLorg/thoughtcrime/securesms/conversation/drafts/DraftViewModel$loadShareOrDraftData$1$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; +PLorg/thoughtcrime/securesms/conversation/drafts/DraftViewModel$loadShareOrDraftData$1$1;->invoke(Lorg/thoughtcrime/securesms/conversation/drafts/DraftState;)Lorg/thoughtcrime/securesms/conversation/drafts/DraftState; +PLorg/thoughtcrime/securesms/conversation/drafts/DraftViewModel;->access$saveDrafts(Lorg/thoughtcrime/securesms/conversation/drafts/DraftViewModel;Lorg/thoughtcrime/securesms/conversation/drafts/DraftState;)Lorg/thoughtcrime/securesms/conversation/drafts/DraftState; PLorg/thoughtcrime/securesms/conversation/drafts/DraftViewModel;->onCleared()V +PLorg/thoughtcrime/securesms/conversation/drafts/DraftViewModel;->saveDrafts(Lorg/thoughtcrime/securesms/conversation/drafts/DraftState;)Lorg/thoughtcrime/securesms/conversation/drafts/DraftState; PLorg/thoughtcrime/securesms/conversation/mutiselect/ConversationItemAnimator;->endAnimations()V PLorg/thoughtcrime/securesms/conversation/mutiselect/ConversationItemAnimator;->endSlideAnimations()V PLorg/thoughtcrime/securesms/conversation/mutiselect/MultiselectItemDecoration;->onDestroy(Landroidx/lifecycle/LifecycleOwner;)V @@ -38338,80 +38017,27 @@ PLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2$onDetachedFro PLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;->onDetachedFromRecyclerView(Landroidx/recyclerview/widget/RecyclerView;)V PLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;->onViewRecycled(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;)V PLorg/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2;->onViewRecycled(Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingViewHolder;)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView$reminderStub$2;->invoke()Ljava/lang/Object; -PLorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView$reminderStub$2;->invoke()Lorg/thoughtcrime/securesms/util/views/Stub; -PLorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView$reviewBannerStub$2;->invoke()Ljava/lang/Object; -PLorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView$reviewBannerStub$2;->invoke()Lorg/thoughtcrime/securesms/util/views/Stub; -PLorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView$unverifiedBannerStub$2;->invoke()Ljava/lang/Object; -PLorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView$unverifiedBannerStub$2;->invoke()Lorg/thoughtcrime/securesms/util/views/Stub; -PLorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView;->clearReminder()V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView;->clearRequestReview()V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView;->clearUnverifiedBanner()V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView;->getReminderStub()Lorg/thoughtcrime/securesms/util/views/Stub; -PLorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView;->getReviewBannerStub()Lorg/thoughtcrime/securesms/util/views/Stub; -PLorg/thoughtcrime/securesms/conversation/v2/ConversationBannerView;->getUnverifiedBannerStub()Lorg/thoughtcrime/securesms/util/views/Stub; PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ComposeTextEventsListener;->onFocusChange(Landroid/view/View;Z)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ConversationOptionsMenuCallback$onOptionsMenuCreated$1;->(Landroidx/appcompat/widget/SearchView;Landroidx/appcompat/widget/SearchView$OnQueryTextListener;Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;Landroid/view/Menu;)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ConversationOptionsMenuCallback$onOptionsMenuCreated$queryListener$1;->(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ConversationOptionsMenuCallback;->clearExpiring()V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ConversationOptionsMenuCallback;->onOptionsMenuCreated(Landroid/view/Menu;)V PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$DataObserver;->onItemRangeChanged(II)V PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$LastScrolledPositionUpdater;->onDestroy(Landroidx/lifecycle/LifecycleOwner;)V PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$LastScrolledPositionUpdater;->onPause(Landroidx/lifecycle/LifecycleOwner;)V PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$LastScrolledPositionUpdater;->onStop(Landroidx/lifecycle/LifecycleOwner;)V PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$binding$3;->invoke(Ljava/lang/Object;)Ljava/lang/Object; PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$binding$3;->invoke(Lorg/thoughtcrime/securesms/databinding/V2ConversationFragmentBinding;)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$10;->invoke(Lj$/util/Optional;)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$10;->invoke(Ljava/lang/Object;)Ljava/lang/Object; -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$11;->invoke(Ljava/lang/Object;)Ljava/lang/Object; -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$11;->invoke(Lorg/thoughtcrime/securesms/conversation/v2/IdentityRecordsState;)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$12;->invoke(Ljava/lang/Object;)Ljava/lang/Object; -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$12;->invoke(Lorg/thoughtcrime/securesms/conversation/v2/RequestReviewState;)V PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$15;->test(Ljava/lang/Object;Ljava/lang/Object;)Z PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$15;->test(Lorg/thoughtcrime/securesms/conversation/drafts/DraftState;Lorg/thoughtcrime/securesms/conversation/drafts/DraftState;)Z -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$16;->accept(Ljava/lang/Object;)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$16;->accept(Lorg/thoughtcrime/securesms/conversation/drafts/DraftState;)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$17;->invoke(I)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$17;->invoke(Ljava/lang/Object;)Ljava/lang/Object; PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$1;->test(Ljava/lang/Object;Ljava/lang/Object;)Z PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$1;->test(Lorg/thoughtcrime/securesms/recipients/Recipient;Lorg/thoughtcrime/securesms/recipients/Recipient;)Z -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object; -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$2;->invoke(Lorg/thoughtcrime/securesms/recipients/Recipient;)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$3;->invoke(Ljava/lang/Object;)Ljava/lang/Object; -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$doAfterFirstRender$3;->invoke(Lorg/thoughtcrime/securesms/conversation/v2/ConversationScrollButtonState;)V PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$initializeConversationThreadUi$6;->invoke()Ljava/lang/Object; -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$initializeLinkPreviews$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$initializeLinkPreviews$1;->invoke(Lorg/thoughtcrime/securesms/linkpreview/LinkPreviewState;)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$presentGroupCallJoinButton$2;->invoke(Ljava/lang/Object;)Ljava/lang/Object; -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$presentGroupCallJoinButton$2;->invoke(Lorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallState;)V PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$sam$androidx_lifecycle_Observer$0;->equals(Ljava/lang/Object;)Z PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$sam$androidx_lifecycle_Observer$0;->getFunctionDelegate()Lkotlin/Function; -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$scheduledMessagesStub$2;->invoke()Ljava/lang/Object; -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$scheduledMessagesStub$2;->invoke()Lorg/thoughtcrime/securesms/util/views/Stub; PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->access$getComposeTextEventsListener$p(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ComposeTextEventsListener; PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->access$getDataObserver$p(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$DataObserver; PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->access$getScrollListener$p(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ScrollListener; -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->access$getSearchMenuItem$p(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)Landroid/view/MenuItem; PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->access$getTextDraftSaveDebouncer$p(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)Lorg/thoughtcrime/securesms/util/Debouncer; -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->access$handleScheduledMessagesCountChange(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;I)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->access$invalidateOptionsMenu(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->access$isSearchRequested$p(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)Z -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->access$onRecipientChanged(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;Lorg/thoughtcrime/securesms/recipients/Recipient;)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->access$presentIdentityRecordsState(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;Lorg/thoughtcrime/securesms/conversation/v2/IdentityRecordsState;)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->access$presentRequestReviewState(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;Lorg/thoughtcrime/securesms/conversation/v2/RequestReviewState;)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->access$presentScrollButtons(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;Lorg/thoughtcrime/securesms/conversation/v2/ConversationScrollButtonState;)V PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->access$setScrollListener$p(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment$ScrollListener;)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->access$setSearchMenuItem$p(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;Landroid/view/MenuItem;)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->access$updateToggleButtonState(Lorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->getScheduledMessagesStub()Lorg/thoughtcrime/securesms/util/views/Stub; -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->handleScheduledMessagesCountChange(I)V PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->onDestroyView()V PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->onPause()V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->onRecipientChanged(Lorg/thoughtcrime/securesms/recipients/Recipient;)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->presentIdentityRecordsState(Lorg/thoughtcrime/securesms/conversation/v2/IdentityRecordsState;)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->presentRequestReviewState(Lorg/thoughtcrime/securesms/conversation/v2/RequestReviewState;)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->presentScrollButtons(Lorg/thoughtcrime/securesms/conversation/v2/ConversationScrollButtonState;)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationFragment;->updateToggleButtonState()V PLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository$$ExternalSyntheticLambda12;->(JJ)V PLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository$$ExternalSyntheticLambda12;->run()V PLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository$$ExternalSyntheticLambda9;->(J)V @@ -38424,117 +38050,582 @@ PLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository;->setLastVis PLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository;->setLastVisibleMessageTimestamp(JJ)V PLorg/thoughtcrime/securesms/conversation/v2/ConversationRepository;->startExpirationTimeout(Ljava/util/List;)V PLorg/thoughtcrime/securesms/conversation/v2/ConversationScrollButtonState;->equals(Ljava/lang/Object;)Z -PLorg/thoughtcrime/securesms/conversation/v2/ConversationScrollButtonState;->getHasMentions()Z -PLorg/thoughtcrime/securesms/conversation/v2/ConversationScrollButtonState;->getShowScrollButtons()Z -PLorg/thoughtcrime/securesms/conversation/v2/ConversationScrollButtonState;->getUnreadCount()I -PLorg/thoughtcrime/securesms/conversation/v2/ConversationScrollButtonState;->toString()Ljava/lang/String; PLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$16;->onComplete()V PLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$16;->onNext(Ljava/lang/Object;)V PLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$16;->onNext(Ljava/util/List;)V PLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$3$$ExternalSyntheticLambda3;->cancel()V PLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$3;->$r8$lambda$V11cxEHo-PxLzOR7edRE3YltJBk(Lorg/thoughtcrime/securesms/database/DatabaseObserver$MessageObserver;Lorg/thoughtcrime/securesms/database/DatabaseObserver$MessageObserver;Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V PLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$3;->apply$lambda$4$lambda$3(Lorg/thoughtcrime/securesms/database/DatabaseObserver$MessageObserver;Lorg/thoughtcrime/securesms/database/DatabaseObserver$MessageObserver;Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V -PLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$canShowAsBubble$1;->apply(Ljava/lang/Object;)Ljava/lang/Object; +PLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$6;->invoke(Ljava/lang/Object;)Ljava/lang/Object; +PLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel$6;->invoke(Lorg/thoughtcrime/securesms/recipients/Recipient;)V PLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel;->markLastSeen()V PLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel;->onCleared()V PLorg/thoughtcrime/securesms/conversation/v2/ConversationViewModel;->setLastScrolled(J)V -PLorg/thoughtcrime/securesms/conversation/v2/IdentityRecordsState;->isGroup()Z -PLorg/thoughtcrime/securesms/conversation/v2/IdentityRecordsState;->isUnverified()Z -PLorg/thoughtcrime/securesms/conversation/v2/IdentityRecordsState;->isVerified()Z PLorg/thoughtcrime/securesms/conversation/v2/RequestReviewState;->equals(Ljava/lang/Object;)Z -PLorg/thoughtcrime/securesms/conversation/v2/RequestReviewState;->shouldShowReviewBanner()Z +PLorg/thoughtcrime/securesms/conversation/v2/data/ConversationDataSource;->load(Ljava/lang/Object;)Ljava/lang/Object; +PLorg/thoughtcrime/securesms/conversation/v2/data/ConversationDataSource;->load(Lorg/thoughtcrime/securesms/conversation/v2/data/ConversationElementKey;)Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingModel; +PLorg/thoughtcrime/securesms/conversation/v2/data/ConversationElementKey$Companion;->()V +PLorg/thoughtcrime/securesms/conversation/v2/data/ConversationElementKey$Companion;->()V +PLorg/thoughtcrime/securesms/conversation/v2/data/ConversationElementKey$Companion;->getThreadHeader()Lorg/thoughtcrime/securesms/conversation/v2/data/ConversationElementKey; +PLorg/thoughtcrime/securesms/conversation/v2/data/ConversationElementKey;->()V +PLorg/thoughtcrime/securesms/conversation/v2/data/IncomingMedia;->areContentsTheSame(Ljava/lang/Object;)Z +PLorg/thoughtcrime/securesms/conversation/v2/data/IncomingMedia;->areContentsTheSame(Lorg/thoughtcrime/securesms/conversation/v2/data/IncomingMedia;)Z +PLorg/thoughtcrime/securesms/conversation/v2/data/IncomingMedia;->areItemsTheSame(Ljava/lang/Object;)Z +PLorg/thoughtcrime/securesms/conversation/v2/data/IncomingMedia;->areItemsTheSame(Lorg/thoughtcrime/securesms/conversation/v2/data/IncomingMedia;)Z PLorg/thoughtcrime/securesms/conversation/v2/data/IncomingMedia;->getChangePayload(Ljava/lang/Object;)Ljava/lang/Object; +PLorg/thoughtcrime/securesms/conversation/v2/data/IncomingTextOnly;->areContentsTheSame(Ljava/lang/Object;)Z +PLorg/thoughtcrime/securesms/conversation/v2/data/IncomingTextOnly;->areContentsTheSame(Lorg/thoughtcrime/securesms/conversation/v2/data/IncomingTextOnly;)Z +PLorg/thoughtcrime/securesms/conversation/v2/data/IncomingTextOnly;->areItemsTheSame(Ljava/lang/Object;)Z +PLorg/thoughtcrime/securesms/conversation/v2/data/IncomingTextOnly;->areItemsTheSame(Lorg/thoughtcrime/securesms/conversation/v2/data/IncomingTextOnly;)Z PLorg/thoughtcrime/securesms/conversation/v2/data/IncomingTextOnly;->getChangePayload(Ljava/lang/Object;)Ljava/lang/Object; +PLorg/thoughtcrime/securesms/conversation/v2/data/ThreadHeader;->areContentsTheSame(Ljava/lang/Object;)Z +PLorg/thoughtcrime/securesms/conversation/v2/data/ThreadHeader;->areContentsTheSame(Lorg/thoughtcrime/securesms/conversation/v2/data/ThreadHeader;)Z +PLorg/thoughtcrime/securesms/conversation/v2/data/ThreadHeader;->areItemsTheSame(Ljava/lang/Object;)Z +PLorg/thoughtcrime/securesms/conversation/v2/data/ThreadHeader;->areItemsTheSame(Lorg/thoughtcrime/securesms/conversation/v2/data/ThreadHeader;)Z PLorg/thoughtcrime/securesms/conversation/v2/data/ThreadHeader;->getChangePayload(Ljava/lang/Object;)Ljava/lang/Object; PLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallState;->equals(Ljava/lang/Object;)Z -PLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallState;->getHasCapacity()Z PLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupCallViewModel;->onCleared()V PLorg/thoughtcrime/securesms/conversation/v2/groups/ConversationGroupViewModel;->onCleared()V PLorg/thoughtcrime/securesms/conversationlist/ConversationListFragment;->onDestroyView()V -PLorg/thoughtcrime/securesms/conversationlist/ConversationListFragment;->onStop()V PLorg/thoughtcrime/securesms/conversationlist/ConversationListViewModel;->onCleared()V -PLorg/thoughtcrime/securesms/conversationlist/chatfilter/ConversationListFilterPullView;->onSaveInstanceState()Landroid/os/Parcelable; +PLorg/thoughtcrime/securesms/conversationlist/model/Conversation;->equals(Ljava/lang/Object;)Z +PLorg/thoughtcrime/securesms/crypto/ProfileKeyUtil;->getSelfProfileKey()Lorg/signal/libsignal/zkgroup/profiles/ProfileKey; +PLorg/thoughtcrime/securesms/crypto/ProfileKeyUtil;->profileKeyOptional([B)Lj$/util/Optional; +PLorg/thoughtcrime/securesms/crypto/ProfileKeyUtil;->profileKeyOrNull([B)Lorg/signal/libsignal/zkgroup/profiles/ProfileKey; +PLorg/thoughtcrime/securesms/database/AttachmentTable;->getAttachment(Lorg/thoughtcrime/securesms/attachments/AttachmentId;)Lorg/thoughtcrime/securesms/attachments/DatabaseAttachment; +PLorg/thoughtcrime/securesms/database/CallTable;->getLatestRingingCalls()Ljava/util/List; +PLorg/thoughtcrime/securesms/database/CallTable;->markRingingCallsAsMissed()V +PLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda13;->run()V +PLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda1;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V +PLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda1;->run()V PLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda26;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/database/DatabaseObserver$MessageObserver;)V PLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda26;->run()V +PLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda5;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/recipients/RecipientId;)V +PLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda5;->run()V +PLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda6;->(Lorg/thoughtcrime/securesms/database/DatabaseObserver;J)V +PLorg/thoughtcrime/securesms/database/DatabaseObserver$$ExternalSyntheticLambda6;->run()V PLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$2v1Sb-6JCd9xjyyzfbH5tKTRWy8(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/database/DatabaseObserver$MessageObserver;)V +PLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$6H_TtixOHSa7Tr30medlqcHry2c(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V +PLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$8PzBCQMLi_6Y7FOR98cRbpXw-Xk(Lorg/thoughtcrime/securesms/database/DatabaseObserver;JLorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V +PLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$XcpL0fyOGdTr1sc4d0z4i8eoe14(Lorg/thoughtcrime/securesms/database/DatabaseObserver;Lorg/thoughtcrime/securesms/recipients/RecipientId;)V +PLorg/thoughtcrime/securesms/database/DatabaseObserver;->$r8$lambda$ZjxWKgbWA1SSTmnWoVneQana_Lk(Lorg/thoughtcrime/securesms/database/DatabaseObserver;J)V +PLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$notifyStoryObservers$35(Lorg/thoughtcrime/securesms/recipients/RecipientId;)V +PLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$notifyVerboseConversationListeners$20(J)V +PLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$registerScheduledMessageObserver$14(JLorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V PLorg/thoughtcrime/securesms/database/DatabaseObserver;->lambda$unregisterObserver$18(Lorg/thoughtcrime/securesms/database/DatabaseObserver$MessageObserver;)V +PLorg/thoughtcrime/securesms/database/DatabaseObserver;->notifyStoryObservers(Lorg/thoughtcrime/securesms/recipients/RecipientId;)V +PLorg/thoughtcrime/securesms/database/DatabaseObserver;->notifyVerboseConversationListeners(Ljava/util/Set;)V +PLorg/thoughtcrime/securesms/database/DatabaseObserver;->unregisterMapped(Ljava/util/Map;Ljava/lang/Object;)V PLorg/thoughtcrime/securesms/database/DatabaseObserver;->unregisterObserver(Lorg/thoughtcrime/securesms/database/DatabaseObserver$MessageObserver;)V +PLorg/thoughtcrime/securesms/database/DatabaseObserver;->unregisterObserver(Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V +PLorg/thoughtcrime/securesms/database/DatabaseTable;->notifyVerboseConversationListeners(Ljava/util/Set;)V +PLorg/thoughtcrime/securesms/database/DraftTable$Drafts;->(Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V +PLorg/thoughtcrime/securesms/database/DraftTable$Drafts;->addIfNotNull(Lorg/thoughtcrime/securesms/database/DraftTable$Draft;)V +PLorg/thoughtcrime/securesms/database/DraftTable;->clearDrafts(J)V +PLorg/thoughtcrime/securesms/database/JobDatabase;->updateJobAfterRetry(Ljava/lang/String;JIJ[B)V PLorg/thoughtcrime/securesms/database/MessageTable$markExpireStarted$1;->(Ljava/util/Collection;)V PLorg/thoughtcrime/securesms/database/MessageTable$markExpireStarted$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; PLorg/thoughtcrime/securesms/database/MessageTable$markExpireStarted$1;->invoke(Lorg/thoughtcrime/securesms/database/SQLiteDatabase;)V +PLorg/thoughtcrime/securesms/database/MessageTable;->getAllStoriesFor(Lorg/thoughtcrime/securesms/recipients/RecipientId;I)Lorg/thoughtcrime/securesms/database/MessageTable$Reader; PLorg/thoughtcrime/securesms/database/MessageTable;->getMessagePositionOnOrAfterTimestamp(JJ)I +PLorg/thoughtcrime/securesms/database/MessageTable;->getMessagesForNotificationState(Ljava/util/Collection;)Landroid/database/Cursor; PLorg/thoughtcrime/securesms/database/MessageTable;->markExpireStarted(Ljava/util/Collection;)V +PLorg/thoughtcrime/securesms/database/MessageTable;->setMessagesReadSince(JJ)Ljava/util/List; +PLorg/thoughtcrime/securesms/database/MessageTable;->setReactionsSeen(JJ)V +PLorg/thoughtcrime/securesms/database/RecipientTable$GetOrInsertResult;->(Lorg/thoughtcrime/securesms/recipients/RecipientId;Z)V +PLorg/thoughtcrime/securesms/database/RecipientTable$GetOrInsertResult;->getRecipientId()Lorg/thoughtcrime/securesms/recipients/RecipientId; +PLorg/thoughtcrime/securesms/database/RecipientTable;->insertReleaseChannelRecipient()Lorg/thoughtcrime/securesms/recipients/RecipientId; +PLorg/thoughtcrime/securesms/database/RecipientTable;->setMuted(Lorg/thoughtcrime/securesms/recipients/RecipientId;J)V +PLorg/thoughtcrime/securesms/database/RecipientTable;->setProfileAvatar(Lorg/thoughtcrime/securesms/recipients/RecipientId;Ljava/lang/String;)V PLorg/thoughtcrime/securesms/database/RxDatabaseObserver$$ExternalSyntheticLambda1;->cancel()V +PLorg/thoughtcrime/securesms/database/RxDatabaseObserver$RxObserver;->onChanged()V PLorg/thoughtcrime/securesms/database/RxDatabaseObserver;->$r8$lambda$6u1bbd117Cl1h38MfeI7BgZPo1A(Lorg/thoughtcrime/securesms/database/RxDatabaseObserver$RxObserver;)V PLorg/thoughtcrime/securesms/database/RxDatabaseObserver;->databaseFlowable$lambda$1$lambda$0(Lorg/thoughtcrime/securesms/database/RxDatabaseObserver$RxObserver;)V +PLorg/thoughtcrime/securesms/database/ThreadTable$setReadSince$1;->(Ljava/util/Map;Lorg/thoughtcrime/securesms/database/ThreadTable;Ljava/util/List;ZLkotlin/jvm/internal/Ref$BooleanRef;)V +PLorg/thoughtcrime/securesms/database/ThreadTable$setReadSince$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; +PLorg/thoughtcrime/securesms/database/ThreadTable$setReadSince$1;->invoke(Lorg/thoughtcrime/securesms/database/SQLiteDatabase;)V +PLorg/thoughtcrime/securesms/database/ThreadTable;->createQuery(Ljava/lang/String;J)Ljava/lang/String; +PLorg/thoughtcrime/securesms/database/ThreadTable;->getThreadRecord(Ljava/lang/Long;)Lorg/thoughtcrime/securesms/database/model/ThreadRecord; PLorg/thoughtcrime/securesms/database/ThreadTable;->setLastSeen(J)V +PLorg/thoughtcrime/securesms/database/ThreadTable;->setReadSince(JZJ)Ljava/util/List; +PLorg/thoughtcrime/securesms/database/ThreadTable;->setReadSince(Ljava/util/Map;Z)Ljava/util/List; +PLorg/thoughtcrime/securesms/database/ThreadTable;->setReadSince(Lorg/thoughtcrime/securesms/notifications/v2/ConversationId;ZJ)Ljava/util/List; +PLorg/thoughtcrime/securesms/database/ThreadTable;->update(JZZ)Z PLorg/thoughtcrime/securesms/database/identity/IdentityRecordList;->equals(Ljava/lang/Object;)Z PLorg/thoughtcrime/securesms/database/model/ProfileAvatarFileDetails;->equals(Ljava/lang/Object;)Z +PLorg/thoughtcrime/securesms/database/model/StoryViewState$Companion$$ExternalSyntheticLambda3;->cancel()V +PLorg/thoughtcrime/securesms/database/model/StoryViewState$Companion;->$r8$lambda$_YM1i9V93JIKhbRirbAeb_98VJw(Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V +PLorg/thoughtcrime/securesms/database/model/StoryViewState$Companion;->getState$lambda$3$lambda$2(Lorg/thoughtcrime/securesms/database/DatabaseObserver$Observer;)V +PLorg/thoughtcrime/securesms/database/model/ThreadRecord;->isForcedUnread()Z +PLorg/thoughtcrime/securesms/dependencies/ApplicationDependencies;->getProfileService()Lorg/whispersystems/signalservice/api/services/ProfileService; +PLorg/thoughtcrime/securesms/dependencies/ApplicationDependencies;->getSignalServiceMessageReceiver()Lorg/whispersystems/signalservice/api/SignalServiceMessageReceiver; +PLorg/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider;->provideProfileService(Lorg/signal/libsignal/zkgroup/profiles/ClientZkProfileOperations;Lorg/whispersystems/signalservice/api/SignalServiceMessageReceiver;Lorg/whispersystems/signalservice/api/SignalWebSocket;)Lorg/whispersystems/signalservice/api/services/ProfileService; +PLorg/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider;->provideSignalServiceMessageReceiver(Lorg/whispersystems/signalservice/internal/configuration/SignalServiceConfiguration;)Lorg/whispersystems/signalservice/api/SignalServiceMessageReceiver; +PLorg/thoughtcrime/securesms/emoji/EmojiFiles$Version$Companion;->writeVersion(Landroid/content/Context;Lorg/thoughtcrime/securesms/emoji/EmojiFiles$Version;)V +PLorg/thoughtcrime/securesms/emoji/EmojiFiles$Version;->access$getObjectMapper$cp()Lcom/fasterxml/jackson/databind/ObjectMapper; +PLorg/thoughtcrime/securesms/emoji/EmojiFiles$Version;->writeVersion(Landroid/content/Context;Lorg/thoughtcrime/securesms/emoji/EmojiFiles$Version;)V +PLorg/thoughtcrime/securesms/emoji/EmojiFiles;->getBaseDirectory(Landroid/content/Context;)Ljava/io/File; +PLorg/thoughtcrime/securesms/emoji/EmojiFiles;->openForReading(Landroid/content/Context;Ljava/lang/String;)Ljava/io/InputStream; +PLorg/thoughtcrime/securesms/emoji/EmojiPage$Disk;->()V +PLorg/thoughtcrime/securesms/emoji/EmojiPage$Disk;->(Landroid/net/Uri;)V +PLorg/thoughtcrime/securesms/emoji/EmojiPage$Disk;->equals(Ljava/lang/Object;)Z +PLorg/thoughtcrime/securesms/emoji/EmojiPage$Disk;->getUri()Landroid/net/Uri; +PLorg/thoughtcrime/securesms/emoji/EmojiPage$Disk;->hashCode()I +PLorg/thoughtcrime/securesms/emoji/EmojiPage$Disk;->toString()Ljava/lang/String; PLorg/thoughtcrime/securesms/emoji/EmojiPageCache$LoadResult$Immediate;->()V PLorg/thoughtcrime/securesms/emoji/EmojiPageCache$LoadResult$Immediate;->(Landroid/graphics/Bitmap;)V PLorg/thoughtcrime/securesms/emoji/EmojiPageCache$LoadResult$Immediate;->getBitmap()Landroid/graphics/Bitmap; +PLorg/thoughtcrime/securesms/emoji/EmojiPageCache;->clear()V +PLorg/thoughtcrime/securesms/emoji/EmojiSource$Companion$loadRemoteBasedEmojis$1$1;->()V +PLorg/thoughtcrime/securesms/emoji/EmojiSource$Companion$loadRemoteBasedEmojis$1$1;->()V +PLorg/thoughtcrime/securesms/emoji/EmojiSource$Companion$loadRemoteBasedEmojis$1$1;->invoke(Landroid/net/Uri;)Lorg/thoughtcrime/securesms/emoji/EmojiPage; +PLorg/thoughtcrime/securesms/emoji/EmojiSource$Companion$loadRemoteBasedEmojis$1$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; +PLorg/thoughtcrime/securesms/emoji/JumboEmoji$$ExternalSyntheticLambda4;->(Lorg/thoughtcrime/securesms/emoji/EmojiFiles$Version;)V +PLorg/thoughtcrime/securesms/emoji/JumboEmoji$$ExternalSyntheticLambda4;->run()V +PLorg/thoughtcrime/securesms/emoji/JumboEmoji;->$r8$lambda$1MZfAT8L5tWrN2t8lIV1c9aeWgA(Lorg/thoughtcrime/securesms/emoji/EmojiFiles$Version;)V +PLorg/thoughtcrime/securesms/emoji/JumboEmoji;->updateCurrentVersion$lambda$1$lambda$0(Lorg/thoughtcrime/securesms/emoji/EmojiFiles$Version;)V +PLorg/thoughtcrime/securesms/fonts/FontFileMap$Companion;->put(Landroid/content/Context;Lorg/thoughtcrime/securesms/fonts/FontVersion;Ljava/lang/String;Ljava/lang/String;)V +PLorg/thoughtcrime/securesms/fonts/FontFileMap$Companion;->setMap(Landroid/content/Context;Lorg/thoughtcrime/securesms/fonts/FontVersion;Lorg/thoughtcrime/securesms/fonts/FontFileMap;)V +PLorg/thoughtcrime/securesms/fonts/FontFileMap;->(Ljava/util/Map;)V +PLorg/thoughtcrime/securesms/fonts/FontFileMap;->access$getObjectMapper$cp()Lcom/fasterxml/jackson/databind/ObjectMapper; +PLorg/thoughtcrime/securesms/fonts/FontFileMap;->copy(Ljava/util/Map;)Lorg/thoughtcrime/securesms/fonts/FontFileMap; +PLorg/thoughtcrime/securesms/fonts/FontFileMap;->getMap()Ljava/util/Map; +PLorg/thoughtcrime/securesms/fonts/Fonts;->loadFontIntoTypeface(Landroid/content/Context;Lorg/thoughtcrime/securesms/fonts/FontVersion;Ljava/lang/String;)Landroid/graphics/Typeface; PLorg/thoughtcrime/securesms/giph/mp4/GiphyMp4ProjectionPlayerHolder;->onDestroy(Landroidx/lifecycle/LifecycleOwner;)V PLorg/thoughtcrime/securesms/giph/mp4/GiphyMp4ProjectionPlayerHolder;->onPause(Landroidx/lifecycle/LifecycleOwner;)V PLorg/thoughtcrime/securesms/giph/mp4/GiphyMp4ProjectionPlayerHolder;->onStop(Landroidx/lifecycle/LifecycleOwner;)V PLorg/thoughtcrime/securesms/giph/mp4/GiphyMp4VideoPlayer;->getExoPlayer()Landroidx/media3/exoplayer/ExoPlayer; -PLorg/thoughtcrime/securesms/keyvalue/SettingsValues$Theme;->values()[Lorg/thoughtcrime/securesms/keyvalue/SettingsValues$Theme; -PLorg/thoughtcrime/securesms/linkpreview/LinkPreviewState;->hasLinks()Z -PLorg/thoughtcrime/securesms/linkpreview/LinkPreviewViewModelV2$savedStateDisposable$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; -PLorg/thoughtcrime/securesms/linkpreview/LinkPreviewViewModelV2$savedStateDisposable$1;->invoke(Lorg/thoughtcrime/securesms/linkpreview/LinkPreviewState;)V -PLorg/thoughtcrime/securesms/linkpreview/LinkPreviewViewModelV2;->access$setSavedLinkPreviewState(Lorg/thoughtcrime/securesms/linkpreview/LinkPreviewViewModelV2;Lorg/thoughtcrime/securesms/linkpreview/LinkPreviewState;)V +PLorg/thoughtcrime/securesms/jobmanager/InAppScheduler$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/jobmanager/InAppScheduler;)V +PLorg/thoughtcrime/securesms/jobmanager/InAppScheduler$$ExternalSyntheticLambda0;->run()V +PLorg/thoughtcrime/securesms/jobmanager/InAppScheduler;->$r8$lambda$hBEn_48T9NK-BhOmjEIwF4k281g(Lorg/thoughtcrime/securesms/jobmanager/InAppScheduler;)V +PLorg/thoughtcrime/securesms/jobmanager/InAppScheduler;->lambda$schedule$0()V +PLorg/thoughtcrime/securesms/jobmanager/Job$Parameters$Builder;->setPriority(I)Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters$Builder; +PLorg/thoughtcrime/securesms/jobmanager/Job$Result;->failure()Lorg/thoughtcrime/securesms/jobmanager/Job$Result; +PLorg/thoughtcrime/securesms/jobmanager/Job$Result;->getBackoffInterval()J +PLorg/thoughtcrime/securesms/jobmanager/Job$Result;->retry(J)Lorg/thoughtcrime/securesms/jobmanager/Job$Result; +PLorg/thoughtcrime/securesms/jobmanager/Job;->onRetry()V +PLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda0;->()V +PLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda0;->accept(Ljava/lang/Object;)V +PLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda13;->()V +PLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda13;->apply(Ljava/lang/Object;)Ljava/lang/Object; +PLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda14;->(Lorg/thoughtcrime/securesms/jobmanager/persistence/JobStorage;)V +PLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda14;->apply(Ljava/lang/Object;)Ljava/lang/Object; +PLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda16;->(Lorg/thoughtcrime/securesms/jobmanager/JobController;)V +PLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda16;->apply(Ljava/lang/Object;)Ljava/lang/Object; +PLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda17;->(Lorg/thoughtcrime/securesms/jobmanager/JobController;)V +PLorg/thoughtcrime/securesms/jobmanager/JobController$$ExternalSyntheticLambda17;->accept(Ljava/lang/Object;)V +PLorg/thoughtcrime/securesms/jobmanager/JobController;->$r8$lambda$AFTJhIYz2IJLxa5cdEvspxyDnpk(Lorg/thoughtcrime/securesms/jobmanager/JobController;Lorg/thoughtcrime/securesms/jobmanager/persistence/JobSpec;)Lorg/thoughtcrime/securesms/jobmanager/Job; +PLorg/thoughtcrime/securesms/jobmanager/JobController;->$r8$lambda$_WG2KEwBlGL1eFDhBUpj1kHB45I(Lorg/thoughtcrime/securesms/jobmanager/JobController;Lorg/thoughtcrime/securesms/jobmanager/Job;)V +PLorg/thoughtcrime/securesms/jobmanager/JobController;->lambda$onFailure$3(Lorg/thoughtcrime/securesms/jobmanager/persistence/JobSpec;)Lorg/thoughtcrime/securesms/jobmanager/Job; +PLorg/thoughtcrime/securesms/jobmanager/JobController;->lambda$onFailure$4(Lorg/thoughtcrime/securesms/jobmanager/Job;)V +PLorg/thoughtcrime/securesms/jobmanager/JobController;->onFailure(Lorg/thoughtcrime/securesms/jobmanager/Job;)Ljava/util/List; +PLorg/thoughtcrime/securesms/jobmanager/JobController;->onRetry(Lorg/thoughtcrime/securesms/jobmanager/Job;J)V +PLorg/thoughtcrime/securesms/jobmanager/JsonJobData;->getBoolean(Ljava/lang/String;)Z +PLorg/thoughtcrime/securesms/jobmanager/JsonJobData;->getBooleanOrDefault(Ljava/lang/String;Z)Z +PLorg/thoughtcrime/securesms/jobmanager/JsonJobData;->getLong(Ljava/lang/String;)J +PLorg/thoughtcrime/securesms/jobmanager/JsonJobData;->getString(Ljava/lang/String;)Ljava/lang/String; +PLorg/thoughtcrime/securesms/jobmanager/JsonJobData;->hasBoolean(Ljava/lang/String;)Z +PLorg/thoughtcrime/securesms/jobmanager/JsonJobData;->hasString(Ljava/lang/String;)Z +PLorg/thoughtcrime/securesms/jobmanager/JsonJobData;->throwIfAbsent(Ljava/util/Map;Ljava/lang/String;)V +PLorg/thoughtcrime/securesms/jobs/AccountConsistencyWorkerJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobmanager/Job; +PLorg/thoughtcrime/securesms/jobs/AccountConsistencyWorkerJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobs/AccountConsistencyWorkerJob; +PLorg/thoughtcrime/securesms/jobs/AccountConsistencyWorkerJob;->(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;Lkotlin/jvm/internal/DefaultConstructorMarker;)V +PLorg/thoughtcrime/securesms/jobs/AccountConsistencyWorkerJob;->onRun()V +PLorg/thoughtcrime/securesms/jobs/AccountConsistencyWorkerJob;->onShouldRetry(Ljava/lang/Exception;)Z +PLorg/thoughtcrime/securesms/jobs/AttachmentDownloadJob;->()V +PLorg/thoughtcrime/securesms/jobs/AttachmentDownloadJob;->(JLorg/thoughtcrime/securesms/attachments/AttachmentId;Z)V +PLorg/thoughtcrime/securesms/jobs/AttachmentDownloadJob;->(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;JLorg/thoughtcrime/securesms/attachments/AttachmentId;Z)V +PLorg/thoughtcrime/securesms/jobs/AttachmentDownloadJob;->constructQueueString(Lorg/thoughtcrime/securesms/attachments/AttachmentId;)Ljava/lang/String; +PLorg/thoughtcrime/securesms/jobs/AttachmentDownloadJob;->getFactoryKey()Ljava/lang/String; +PLorg/thoughtcrime/securesms/jobs/AttachmentDownloadJob;->onAdded()V +PLorg/thoughtcrime/securesms/jobs/AttachmentDownloadJob;->serialize()[B +PLorg/thoughtcrime/securesms/jobs/BaseJob;->getNextRunAttemptBackoff(ILjava/lang/Exception;)J +PLorg/thoughtcrime/securesms/jobs/ConversationShortcutUpdateJob;->()V +PLorg/thoughtcrime/securesms/jobs/ConversationShortcutUpdateJob;->()V +PLorg/thoughtcrime/securesms/jobs/ConversationShortcutUpdateJob;->(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;)V +PLorg/thoughtcrime/securesms/jobs/ConversationShortcutUpdateJob;->enqueue()V +PLorg/thoughtcrime/securesms/jobs/ConversationShortcutUpdateJob;->getFactoryKey()Ljava/lang/String; +PLorg/thoughtcrime/securesms/jobs/ConversationShortcutUpdateJob;->serialize()[B +PLorg/thoughtcrime/securesms/jobs/CreateReleaseChannelJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobmanager/Job; +PLorg/thoughtcrime/securesms/jobs/CreateReleaseChannelJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobs/CreateReleaseChannelJob; +PLorg/thoughtcrime/securesms/jobs/CreateReleaseChannelJob$setAvatar$1;->(Lorg/thoughtcrime/securesms/jobs/CreateReleaseChannelJob;Lorg/thoughtcrime/securesms/recipients/RecipientId;Ljava/util/concurrent/CountDownLatch;)V +PLorg/thoughtcrime/securesms/jobs/CreateReleaseChannelJob$setAvatar$1;->invoke(Ljava/lang/Object;)Ljava/lang/Object; +PLorg/thoughtcrime/securesms/jobs/CreateReleaseChannelJob$setAvatar$1;->invoke(Lorg/thoughtcrime/securesms/mediasend/Media;)V +PLorg/thoughtcrime/securesms/jobs/CreateReleaseChannelJob$setAvatar$2;->(Ljava/util/concurrent/CountDownLatch;)V +PLorg/thoughtcrime/securesms/jobs/CreateReleaseChannelJob;->access$getContext$p$s1046862181(Lorg/thoughtcrime/securesms/jobs/CreateReleaseChannelJob;)Landroid/content/Context; +PLorg/thoughtcrime/securesms/jobs/CreateReleaseChannelJob;->onRun()V +PLorg/thoughtcrime/securesms/jobs/CreateReleaseChannelJob;->setAvatar(Lorg/thoughtcrime/securesms/recipients/RecipientId;)V +PLorg/thoughtcrime/securesms/jobs/DirectoryRefreshJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobmanager/Job; +PLorg/thoughtcrime/securesms/jobs/DirectoryRefreshJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobs/DirectoryRefreshJob; +PLorg/thoughtcrime/securesms/jobs/DirectoryRefreshJob;->(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;Lorg/thoughtcrime/securesms/recipients/Recipient;ZLorg/thoughtcrime/securesms/jobs/DirectoryRefreshJob-IA;)V +PLorg/thoughtcrime/securesms/jobs/DirectoryRefreshJob;->onRun()V +PLorg/thoughtcrime/securesms/jobs/DirectoryRefreshJob;->shouldTrace()Z +PLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob$$ExternalSyntheticLambda0;->()V +PLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob$$ExternalSyntheticLambda0;->test(Ljava/lang/Object;)Z +PLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob$$ExternalSyntheticLambda1;->(Ljava/lang/String;)V +PLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob$$ExternalSyntheticLambda1;->test(Ljava/lang/Object;)Z +PLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob$$ExternalSyntheticLambda2;->(Ljava/lang/String;)V +PLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob$$ExternalSyntheticLambda2;->test(Ljava/lang/Object;)Z +PLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob$$ExternalSyntheticLambda3;->()V +PLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob;->$r8$lambda$QoH7dcHnoANVuwa796Z9UVq8P10(Ljava/lang/String;Ljava/io/File;)Z +PLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob;->$r8$lambda$svSYKFZ54wbzF4p08Dr861zOX4Y(Ljava/lang/String;Ljava/io/File;)Z +PLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob;->clearOldEmojiData(Landroid/content/Context;Lorg/thoughtcrime/securesms/emoji/EmojiFiles$Version;)V +PLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob;->lambda$clearOldEmojiData$2(Ljava/lang/String;Ljava/io/File;)Z +PLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob;->lambda$clearOldEmojiData$3(Ljava/lang/String;Ljava/io/File;)Z +PLorg/thoughtcrime/securesms/jobs/DownloadLatestEmojiDataJob;->markComplete(Lorg/thoughtcrime/securesms/emoji/EmojiFiles$Version;)V +PLorg/thoughtcrime/securesms/jobs/FastJobStorage;->getDependencySpecsThatDependOnJob(Ljava/lang/String;)Ljava/util/List; +PLorg/thoughtcrime/securesms/jobs/FastJobStorage;->getSingleLayerOfDependencySpecsThatDependOnJob(Ljava/lang/String;)Ljava/util/List; +PLorg/thoughtcrime/securesms/jobs/FastJobStorage;->updateJobAfterRetry(Ljava/lang/String;JIJ[B)V +PLorg/thoughtcrime/securesms/jobs/FontDownloaderJob$onRun$listener$1;->onSuccess(Landroid/graphics/Typeface;)V +PLorg/thoughtcrime/securesms/jobs/FontDownloaderJob$onRun$listener$1;->onSuccess(Ljava/lang/Object;)V +PLorg/thoughtcrime/securesms/jobs/GroupRingCleanupJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobmanager/Job; +PLorg/thoughtcrime/securesms/jobs/GroupRingCleanupJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobs/GroupRingCleanupJob; +PLorg/thoughtcrime/securesms/jobs/GroupRingCleanupJob;->onRun()V +PLorg/thoughtcrime/securesms/jobs/MultiDeviceProfileKeyUpdateJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobmanager/Job; +PLorg/thoughtcrime/securesms/jobs/MultiDeviceProfileKeyUpdateJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobs/MultiDeviceProfileKeyUpdateJob; +PLorg/thoughtcrime/securesms/jobs/MultiDeviceProfileKeyUpdateJob;->(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;Lorg/thoughtcrime/securesms/jobs/MultiDeviceProfileKeyUpdateJob-IA;)V +PLorg/thoughtcrime/securesms/jobs/MultiDeviceProfileKeyUpdateJob;->onFailure()V +PLorg/thoughtcrime/securesms/jobs/PreKeysSyncJob;->onFailure()V +PLorg/thoughtcrime/securesms/jobs/PreKeysSyncJob;->onShouldRetry(Ljava/lang/Exception;)Z +PLorg/thoughtcrime/securesms/jobs/PreKeysSyncJob;->syncPreKeys(Lorg/whispersystems/signalservice/api/push/ServiceIdType;Lorg/whispersystems/signalservice/api/push/ServiceId;Lorg/whispersystems/signalservice/api/SignalServiceAccountDataStore;Lorg/thoughtcrime/securesms/crypto/storage/PreKeyMetadataStore;)V +PLorg/thoughtcrime/securesms/jobs/ProfileUploadJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobmanager/Job; +PLorg/thoughtcrime/securesms/jobs/ProfileUploadJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobs/ProfileUploadJob; +PLorg/thoughtcrime/securesms/jobs/ProfileUploadJob;->(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;Lorg/thoughtcrime/securesms/jobs/ProfileUploadJob-IA;)V +PLorg/thoughtcrime/securesms/jobs/ProfileUploadJob;->onFailure()V +PLorg/thoughtcrime/securesms/jobs/RefreshAttributesJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobmanager/Job; +PLorg/thoughtcrime/securesms/jobs/RefreshAttributesJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobs/RefreshAttributesJob; +PLorg/thoughtcrime/securesms/jobs/RefreshAttributesJob;->(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;ZLorg/thoughtcrime/securesms/jobs/RefreshAttributesJob-IA;)V +PLorg/thoughtcrime/securesms/jobs/RefreshAttributesJob;->onFailure()V +PLorg/thoughtcrime/securesms/jobs/RefreshAttributesJob;->onRun()V +PLorg/thoughtcrime/securesms/jobs/RefreshAttributesJob;->onShouldRetry(Ljava/lang/Exception;)Z +PLorg/thoughtcrime/securesms/jobs/RotateCertificateJob$1;->()V +PLorg/thoughtcrime/securesms/jobs/RotateCertificateJob;->onFailure()V +PLorg/thoughtcrime/securesms/jobs/RotateCertificateJob;->onShouldRetry(Ljava/lang/Exception;)Z +PLorg/thoughtcrime/securesms/jobs/StoryOnboardingDownloadJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobmanager/Job; +PLorg/thoughtcrime/securesms/jobs/StoryOnboardingDownloadJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobs/StoryOnboardingDownloadJob; +PLorg/thoughtcrime/securesms/jobs/StoryOnboardingDownloadJob;->getLocaleCodes()Ljava/util/List; +PLorg/thoughtcrime/securesms/jobs/StoryOnboardingDownloadJob;->onRun()V +PLorg/thoughtcrime/securesms/jobs/ThreadUpdateJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobmanager/Job; +PLorg/thoughtcrime/securesms/jobs/ThreadUpdateJob$Factory;->create(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;[B)Lorg/thoughtcrime/securesms/jobs/ThreadUpdateJob; +PLorg/thoughtcrime/securesms/jobs/ThreadUpdateJob;->(Lorg/thoughtcrime/securesms/jobmanager/Job$Parameters;JLorg/thoughtcrime/securesms/jobs/ThreadUpdateJob-IA;)V +PLorg/thoughtcrime/securesms/jobs/ThreadUpdateJob;->onRun()V +PLorg/thoughtcrime/securesms/keyvalue/AccountValues;->getDeviceName()Ljava/lang/String; +PLorg/thoughtcrime/securesms/keyvalue/CertificateType;->values()[Lorg/thoughtcrime/securesms/keyvalue/CertificateType; +PLorg/thoughtcrime/securesms/keyvalue/EmojiValues;->getJumboEmojiSheets(I)Ljava/util/HashSet; +PLorg/thoughtcrime/securesms/keyvalue/PhoneNumberPrivacyValues$PhoneNumberListingMode;->isDiscoverable()Z +PLorg/thoughtcrime/securesms/keyvalue/PhoneNumberPrivacyValues;->getAllCertificateTypes()Ljava/util/Collection; +PLorg/thoughtcrime/securesms/keyvalue/ReleaseChannelValues;->setReleaseChannelRecipientId(Lorg/thoughtcrime/securesms/recipients/RecipientId;)V +PLorg/thoughtcrime/securesms/keyvalue/StoryValues;->setHasDownloadedOnboardingStory(Z)V +PLorg/thoughtcrime/securesms/keyvalue/SvrValues;->getMasterKey()Lorg/whispersystems/signalservice/api/kbs/MasterKey; +PLorg/thoughtcrime/securesms/keyvalue/SvrValues;->getRecoveryPassword()Ljava/lang/String; +PLorg/thoughtcrime/securesms/keyvalue/SvrValues;->isRegistrationLockEnabled()Z PLorg/thoughtcrime/securesms/linkpreview/LinkPreviewViewModelV2;->onCleared()V -PLorg/thoughtcrime/securesms/linkpreview/LinkPreviewViewModelV2;->setSavedLinkPreviewState(Lorg/thoughtcrime/securesms/linkpreview/LinkPreviewState;)V -PLorg/thoughtcrime/securesms/logsubmit/LogSectionNotifications$$ExternalSyntheticApiModelOutline2;->m(Landroid/app/NotificationChannel;)Z PLorg/thoughtcrime/securesms/main/MainActivityListHostFragment;->onDestroyView()V -PLorg/thoughtcrime/securesms/mms/AttachmentManager;->isAttachmentPresent()Z +PLorg/thoughtcrime/securesms/mediasend/Media$1;->()V +PLorg/thoughtcrime/securesms/mediasend/Media;->()V +PLorg/thoughtcrime/securesms/mediasend/Media;->(Landroid/net/Uri;Ljava/lang/String;JIIJJZZLj$/util/Optional;Lj$/util/Optional;Lj$/util/Optional;)V +PLorg/thoughtcrime/securesms/mediasend/Media;->getUri()Landroid/net/Uri; +PLorg/thoughtcrime/securesms/messages/IncomingMessageObserver;->getDecryptionDrained()Z +PLorg/thoughtcrime/securesms/mms/PartAuthority;->getEmojiFilename(Landroid/net/Uri;)Ljava/lang/String; +PLorg/thoughtcrime/securesms/notifications/MarkReadReceiver;->()V +PLorg/thoughtcrime/securesms/notifications/MarkReadReceiver;->process(Ljava/util/List;)V +PLorg/thoughtcrime/securesms/notifications/NotificationCancellationHelper$$ExternalSyntheticApiModelOutline0;->m(Landroid/app/NotificationManager;)[Landroid/service/notification/StatusBarNotification; +PLorg/thoughtcrime/securesms/notifications/NotificationCancellationHelper;->()V +PLorg/thoughtcrime/securesms/notifications/NotificationCancellationHelper;->cancelAllMessageNotifications(Landroid/content/Context;Ljava/util/Set;)V +PLorg/thoughtcrime/securesms/notifications/NotificationCancellationHelper;->cancelLegacy(Landroid/content/Context;I)V +PLorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier$$ExternalSyntheticLambda11;->(Ljava/lang/Runnable;Ljava/lang/Throwable;)V +PLorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier$$ExternalSyntheticLambda11;->run()V +PLorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier$$ExternalSyntheticLambda6;->(Lorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier;Landroid/content/Context;)V +PLorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier$$ExternalSyntheticLambda6;->run()V +PLorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier$$ExternalSyntheticLambda7;->(Lorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier;Landroid/content/Context;)V +PLorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier$$ExternalSyntheticLambda7;->run()V +PLorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier;->$r8$lambda$0MHafWJB8CwezqWVbncoEq_a3Xg(Ljava/lang/Runnable;Ljava/lang/Throwable;)V +PLorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier;->$r8$lambda$dClU2s1olLC6PkCR84cT7C2dqfA(Lorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier;Landroid/content/Context;)V +PLorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier;->$r8$lambda$mDf5wVelzwBtc0Vgo342P1gCo0c(Lorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier;Landroid/content/Context;)V PLorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier;->clearVisibleThread()V +PLorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier;->lambda$runOnLimiter$11(Ljava/lang/Runnable;Ljava/lang/Throwable;)V +PLorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier;->lambda$updateNotification$3(Landroid/content/Context;)V +PLorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier;->lambda$updateNotification$4(Landroid/content/Context;)V +PLorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier;->runOnLimiter(Ljava/lang/Runnable;)V +PLorg/thoughtcrime/securesms/notifications/OptimizedMessageNotifier;->updateNotification(Landroid/content/Context;)V +PLorg/thoughtcrime/securesms/notifications/v2/ConversationId;->getGroupStoryId()Ljava/lang/Long; +PLorg/thoughtcrime/securesms/notifications/v2/ConversationId;->getThreadId()J +PLorg/thoughtcrime/securesms/notifications/v2/DefaultMessageNotifier$Companion;->access$updateBadge(Lorg/thoughtcrime/securesms/notifications/v2/DefaultMessageNotifier$Companion;Landroid/content/Context;I)V +PLorg/thoughtcrime/securesms/notifications/v2/DefaultMessageNotifier$Companion;->updateBadge(Landroid/content/Context;I)V +PLorg/thoughtcrime/securesms/notifications/v2/DefaultMessageNotifier$updateNotification$7;->(Ljava/util/Set;)V +PLorg/thoughtcrime/securesms/notifications/v2/DefaultMessageNotifier;->clearReminderInternal(Landroid/content/Context;)V PLorg/thoughtcrime/securesms/notifications/v2/DefaultMessageNotifier;->clearVisibleThread()V -PLorg/thoughtcrime/securesms/preferences/widgets/NotificationPrivacyPreference;->isDisplayContact()Z +PLorg/thoughtcrime/securesms/notifications/v2/DefaultMessageNotifier;->updateNotification(Landroid/content/Context;)V +PLorg/thoughtcrime/securesms/notifications/v2/DefaultMessageNotifier;->updateNotification(Landroid/content/Context;Lorg/thoughtcrime/securesms/notifications/v2/ConversationId;Lorg/thoughtcrime/securesms/util/BubbleUtil$BubbleState;)V +PLorg/thoughtcrime/securesms/notifications/v2/DefaultMessageNotifierKt;->access$getDisplayedNotificationIds(Landroid/app/NotificationManager;)Ljava/lang/Object; +PLorg/thoughtcrime/securesms/notifications/v2/DefaultMessageNotifierKt;->getDisplayedNotificationIds(Landroid/app/NotificationManager;)Ljava/lang/Object; +PLorg/thoughtcrime/securesms/notifications/v2/NotificationPendingIntentHelper;->()V +PLorg/thoughtcrime/securesms/notifications/v2/NotificationPendingIntentHelper;->()V +PLorg/thoughtcrime/securesms/notifications/v2/NotificationPendingIntentHelper;->getBroadcast(Landroid/content/Context;ILandroid/content/Intent;I)Landroid/app/PendingIntent; +PLorg/thoughtcrime/securesms/notifications/v2/NotificationState;->getConversations()Ljava/util/List; +PLorg/thoughtcrime/securesms/notifications/v2/NotificationState;->getMuteFilteredMessages()Ljava/util/List; +PLorg/thoughtcrime/securesms/notifications/v2/NotificationState;->getProfileFilteredMessages()Ljava/util/List; +PLorg/thoughtcrime/securesms/notifications/v2/NotificationState;->getThreadsWithMostRecentNotificationFromSelf()Ljava/util/Set; +PLorg/thoughtcrime/securesms/notifications/v2/NotificationState;->isEmpty()Z +PLorg/thoughtcrime/securesms/notifications/v2/NotificationState;->toString()Ljava/lang/String; +PLorg/thoughtcrime/securesms/notifications/v2/NotificationStateProvider;->()V +PLorg/thoughtcrime/securesms/notifications/v2/NotificationStateProvider;->()V +PLorg/thoughtcrime/securesms/notifications/v2/NotificationStateProvider;->constructNotificationState(Ljava/util/Map;Lorg/thoughtcrime/securesms/notifications/profiles/NotificationProfile;)Lorg/thoughtcrime/securesms/notifications/v2/NotificationState; +PLorg/thoughtcrime/securesms/permissions/Permissions$$ExternalSyntheticLambda0;->(Landroid/content/Context;)V +PLorg/thoughtcrime/securesms/permissions/Permissions$$ExternalSyntheticLambda0;->test(Ljava/lang/Object;)Z +PLorg/thoughtcrime/securesms/permissions/Permissions;->$r8$lambda$Q0AcdMcPXUgr1QQ_HDTcoSx0sHo(Landroid/content/Context;Ljava/lang/String;)Z +PLorg/thoughtcrime/securesms/permissions/Permissions;->()V +PLorg/thoughtcrime/securesms/permissions/Permissions;->hasAll(Landroid/content/Context;[Ljava/lang/String;)Z +PLorg/thoughtcrime/securesms/permissions/Permissions;->isRuntimePermissionsRequired()Z +PLorg/thoughtcrime/securesms/permissions/Permissions;->lambda$hasAll$2(Landroid/content/Context;Ljava/lang/String;)Z +PLorg/thoughtcrime/securesms/preferences/widgets/NotificationPrivacyPreference;->equals(Ljava/lang/Object;)Z +PLorg/thoughtcrime/securesms/profiles/AvatarHelper;->getOutputStream(Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/RecipientId;Z)Ljava/io/OutputStream; +PLorg/thoughtcrime/securesms/profiles/AvatarHelper;->setAvatar(Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/RecipientId;Ljava/io/InputStream;)V +PLorg/thoughtcrime/securesms/profiles/ProfileName;->asGiven(Ljava/lang/String;)Lorg/thoughtcrime/securesms/profiles/ProfileName; +PLorg/thoughtcrime/securesms/providers/BlobProvider$$ExternalSyntheticLambda10;->(Lorg/thoughtcrime/securesms/providers/BlobProvider;Landroid/content/Context;J)V +PLorg/thoughtcrime/securesms/providers/BlobProvider$$ExternalSyntheticLambda10;->apply(Ljava/lang/Object;)Ljava/lang/Object; +PLorg/thoughtcrime/securesms/providers/BlobProvider$$ExternalSyntheticLambda1;->(Lorg/thoughtcrime/securesms/providers/BlobProvider;Lorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;Ljava/io/OutputStream;Landroid/net/Uri;Landroid/content/Context;)V +PLorg/thoughtcrime/securesms/providers/BlobProvider$$ExternalSyntheticLambda1;->call()Ljava/lang/Object; +PLorg/thoughtcrime/securesms/providers/BlobProvider$$ExternalSyntheticLambda9;->(JLandroid/net/Uri;)V +PLorg/thoughtcrime/securesms/providers/BlobProvider$2;->()V +PLorg/thoughtcrime/securesms/providers/BlobProvider$BlobBuilder;->(Lorg/thoughtcrime/securesms/providers/BlobProvider;Ljava/io/InputStream;J)V +PLorg/thoughtcrime/securesms/providers/BlobProvider$BlobBuilder;->(Lorg/thoughtcrime/securesms/providers/BlobProvider;Ljava/io/InputStream;JLorg/thoughtcrime/securesms/providers/BlobProvider$BlobBuilder-IA;)V +PLorg/thoughtcrime/securesms/providers/BlobProvider$BlobBuilder;->buildBlobSpec(Lorg/thoughtcrime/securesms/providers/BlobProvider$StorageType;)Lorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec; +PLorg/thoughtcrime/securesms/providers/BlobProvider$BlobBuilder;->createForSingleSessionOnDisk(Landroid/content/Context;)Landroid/net/Uri; +PLorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;->-$$Nest$fgetid(Lorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;)Ljava/lang/String; +PLorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;->-$$Nest$mgetData(Lorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;)Ljava/io/InputStream; +PLorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;->-$$Nest$mgetFileName(Lorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;)Ljava/lang/String; +PLorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;->-$$Nest$mgetFileSize(Lorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;)J +PLorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;->-$$Nest$mgetId(Lorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;)Ljava/lang/String; +PLorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;->-$$Nest$mgetMimeType(Lorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;)Ljava/lang/String; +PLorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;->-$$Nest$mgetStorageType(Lorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;)Lorg/thoughtcrime/securesms/providers/BlobProvider$StorageType; +PLorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;->(Ljava/io/InputStream;Ljava/lang/String;Lorg/thoughtcrime/securesms/providers/BlobProvider$StorageType;Ljava/lang/String;Ljava/lang/String;J)V +PLorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;->(Ljava/io/InputStream;Ljava/lang/String;Lorg/thoughtcrime/securesms/providers/BlobProvider$StorageType;Ljava/lang/String;Ljava/lang/String;JLorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec-IA;)V +PLorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;->getData()Ljava/io/InputStream; +PLorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;->getFileName()Ljava/lang/String; +PLorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;->getFileSize()J +PLorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;->getId()Ljava/lang/String; +PLorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;->getMimeType()Ljava/lang/String; +PLorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;->getStorageType()Lorg/thoughtcrime/securesms/providers/BlobProvider$StorageType; +PLorg/thoughtcrime/securesms/providers/BlobProvider$StorageType;->$values()[Lorg/thoughtcrime/securesms/providers/BlobProvider$StorageType; +PLorg/thoughtcrime/securesms/providers/BlobProvider$StorageType;->-$$Nest$mencode(Lorg/thoughtcrime/securesms/providers/BlobProvider$StorageType;)Ljava/lang/String; +PLorg/thoughtcrime/securesms/providers/BlobProvider$StorageType;->-$$Nest$misMemory(Lorg/thoughtcrime/securesms/providers/BlobProvider$StorageType;)Z +PLorg/thoughtcrime/securesms/providers/BlobProvider$StorageType;->-$$Nest$smdecode(Ljava/lang/String;)Lorg/thoughtcrime/securesms/providers/BlobProvider$StorageType; +PLorg/thoughtcrime/securesms/providers/BlobProvider$StorageType;->()V +PLorg/thoughtcrime/securesms/providers/BlobProvider$StorageType;->(Ljava/lang/String;ILjava/lang/String;Z)V +PLorg/thoughtcrime/securesms/providers/BlobProvider$StorageType;->decode(Ljava/lang/String;)Lorg/thoughtcrime/securesms/providers/BlobProvider$StorageType; +PLorg/thoughtcrime/securesms/providers/BlobProvider$StorageType;->encode()Ljava/lang/String; +PLorg/thoughtcrime/securesms/providers/BlobProvider$StorageType;->isMemory()Z +PLorg/thoughtcrime/securesms/providers/BlobProvider$StorageType;->values()[Lorg/thoughtcrime/securesms/providers/BlobProvider$StorageType; +PLorg/thoughtcrime/securesms/providers/BlobProvider;->$r8$lambda$hszeKLE-J5rLAuyYe0jTaWcOLg4(Lorg/thoughtcrime/securesms/providers/BlobProvider;Landroid/content/Context;JLjava/io/File;)Ljava/io/InputStream; +PLorg/thoughtcrime/securesms/providers/BlobProvider;->$r8$lambda$vEFatZFK1evY8Xp8zObuRkcU_8Q(Lorg/thoughtcrime/securesms/providers/BlobProvider;Lorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;Ljava/io/OutputStream;Landroid/net/Uri;Landroid/content/Context;)Landroid/net/Uri; +PLorg/thoughtcrime/securesms/providers/BlobProvider;->-$$Nest$mwriteBlobSpecToDisk(Lorg/thoughtcrime/securesms/providers/BlobProvider;Landroid/content/Context;Lorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;)Landroid/net/Uri; +PLorg/thoughtcrime/securesms/providers/BlobProvider;->buildFileName(Ljava/lang/String;)Ljava/lang/String; +PLorg/thoughtcrime/securesms/providers/BlobProvider;->buildUri(Lorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;)Landroid/net/Uri; +PLorg/thoughtcrime/securesms/providers/BlobProvider;->forData(Ljava/io/InputStream;J)Lorg/thoughtcrime/securesms/providers/BlobProvider$BlobBuilder; +PLorg/thoughtcrime/securesms/providers/BlobProvider;->getAttachmentSecret(Landroid/content/Context;)Lorg/thoughtcrime/securesms/crypto/AttachmentSecret; +PLorg/thoughtcrime/securesms/providers/BlobProvider;->getBlobRepresentation(Landroid/content/Context;Landroid/net/Uri;Lorg/thoughtcrime/securesms/util/IOFunction;Lorg/thoughtcrime/securesms/util/IOFunction;)Ljava/lang/Object; +PLorg/thoughtcrime/securesms/providers/BlobProvider;->getDirectory(Lorg/thoughtcrime/securesms/providers/BlobProvider$StorageType;)Ljava/lang/String; +PLorg/thoughtcrime/securesms/providers/BlobProvider;->getStream(Landroid/content/Context;Landroid/net/Uri;)Ljava/io/InputStream; +PLorg/thoughtcrime/securesms/providers/BlobProvider;->getStream(Landroid/content/Context;Landroid/net/Uri;J)Ljava/io/InputStream; +PLorg/thoughtcrime/securesms/providers/BlobProvider;->isAuthority(Landroid/net/Uri;)Z +PLorg/thoughtcrime/securesms/providers/BlobProvider;->lambda$getStream$1(Landroid/content/Context;JLjava/io/File;)Ljava/io/InputStream; +PLorg/thoughtcrime/securesms/providers/BlobProvider;->lambda$writeBlobSpecToDiskAsync$4(Lorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;Ljava/io/OutputStream;Landroid/net/Uri;Landroid/content/Context;)Landroid/net/Uri; +PLorg/thoughtcrime/securesms/providers/BlobProvider;->waitUntilInitialized()V +PLorg/thoughtcrime/securesms/providers/BlobProvider;->writeBlobSpecToDisk(Landroid/content/Context;Lorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;)Landroid/net/Uri; +PLorg/thoughtcrime/securesms/providers/BlobProvider;->writeBlobSpecToDiskAsync(Landroid/content/Context;Lorg/thoughtcrime/securesms/providers/BlobProvider$BlobSpec;)Ljava/util/concurrent/Future; +PLorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda4;->contentsMatch(Ljava/lang/Object;Ljava/lang/Object;)Z PLorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda7;->(Lorg/thoughtcrime/securesms/recipients/LiveRecipient;Lorg/thoughtcrime/securesms/recipients/RecipientForeverObserver;)V PLorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda7;->run()V -PLorg/thoughtcrime/securesms/recipients/LiveRecipient$$ExternalSyntheticLambda9;->run()V -PLorg/thoughtcrime/securesms/recipients/LiveRecipient;->$r8$lambda$CYcq6dHxZW6RfEGMe0s6kvofKaE(Lorg/thoughtcrime/securesms/recipients/LiveRecipient;Lorg/thoughtcrime/securesms/recipients/Recipient;)V PLorg/thoughtcrime/securesms/recipients/LiveRecipient;->$r8$lambda$snuajGLvOLM6I1QDJohSseUuY1E(Lorg/thoughtcrime/securesms/recipients/LiveRecipient;Lorg/thoughtcrime/securesms/recipients/RecipientForeverObserver;)V -PLorg/thoughtcrime/securesms/recipients/LiveRecipient;->lambda$new$0(Lorg/thoughtcrime/securesms/recipients/Recipient;)V PLorg/thoughtcrime/securesms/recipients/LiveRecipient;->lambda$removeForeverObserver$7(Lorg/thoughtcrime/securesms/recipients/RecipientForeverObserver;)V PLorg/thoughtcrime/securesms/recipients/LiveRecipient;->removeForeverObserver(Lorg/thoughtcrime/securesms/recipients/RecipientForeverObserver;)V -PLorg/thoughtcrime/securesms/recipients/Recipient;->getContactUri()Landroid/net/Uri; -PLorg/thoughtcrime/securesms/recipients/Recipient;->getNotificationChannel()Ljava/lang/String; -PLorg/thoughtcrime/securesms/recipients/Recipient;->isActiveGroup()Z +PLorg/thoughtcrime/securesms/recipients/Recipient$$ExternalSyntheticLambda3;->()V +PLorg/thoughtcrime/securesms/recipients/Recipient;->getProfileKey()[B +PLorg/thoughtcrime/securesms/recipients/Recipient;->shouldHideStory()Z +PLorg/thoughtcrime/securesms/recipients/RecipientUtil;->toSignalServiceAddress(Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/Recipient;)Lorg/whispersystems/signalservice/api/push/SignalServiceAddress; +PLorg/thoughtcrime/securesms/registration/viewmodel/RegistrationViewModel$$ExternalSyntheticLambda3;->()V +PLorg/thoughtcrime/securesms/registration/viewmodel/RegistrationViewModel$$ExternalSyntheticLambda3;->apply(Ljava/lang/Object;)Ljava/lang/Object; +PLorg/thoughtcrime/securesms/releasechannel/ReleaseChannel;->()V +PLorg/thoughtcrime/securesms/releasechannel/ReleaseChannel;->()V +PLorg/thoughtcrime/securesms/releasechannel/ReleaseChannel;->insertReleaseChannelMessage$default(Lorg/thoughtcrime/securesms/releasechannel/ReleaseChannel;Lorg/thoughtcrime/securesms/recipients/RecipientId;Ljava/lang/String;JLjava/lang/String;IILjava/lang/String;Ljava/lang/String;Lorg/thoughtcrime/securesms/database/model/databaseprotos/BodyRangeList;Lorg/thoughtcrime/securesms/database/model/StoryType;ILjava/lang/Object;)Lorg/thoughtcrime/securesms/database/MessageTable$InsertResult; +PLorg/thoughtcrime/securesms/releasechannel/ReleaseChannel;->insertReleaseChannelMessage(Lorg/thoughtcrime/securesms/recipients/RecipientId;Ljava/lang/String;JLjava/lang/String;IILjava/lang/String;Ljava/lang/String;Lorg/thoughtcrime/securesms/database/model/databaseprotos/BodyRangeList;Lorg/thoughtcrime/securesms/database/model/StoryType;)Lorg/thoughtcrime/securesms/database/MessageTable$InsertResult; PLorg/thoughtcrime/securesms/service/ExpiringMessageManager$$ExternalSyntheticLambda0;->()V PLorg/thoughtcrime/securesms/service/ExpiringMessageManager;->scheduleDeletion(Ljava/util/List;)V PLorg/thoughtcrime/securesms/stories/tabs/ConversationListTabsViewModel;->onCleared()V -PLorg/thoughtcrime/securesms/util/AppForegroundObserver;->removeListener(Lorg/thoughtcrime/securesms/util/AppForegroundObserver$Listener;)V -PLorg/thoughtcrime/securesms/util/BubbleUtil$$ExternalSyntheticApiModelOutline0;->m(Landroid/app/NotificationManager;Ljava/lang/String;Ljava/lang/String;)Landroid/app/NotificationChannel; -PLorg/thoughtcrime/securesms/util/BubbleUtil$$ExternalSyntheticApiModelOutline1;->m(Landroid/app/NotificationManager;)Z -PLorg/thoughtcrime/securesms/util/BubbleUtil$$ExternalSyntheticApiModelOutline2;->m(Landroid/app/NotificationManager;)I -PLorg/thoughtcrime/securesms/util/BubbleUtil$$ExternalSyntheticApiModelOutline3;->m(Landroid/app/NotificationManager;)Z -PLorg/thoughtcrime/securesms/util/BubbleUtil;->()V -PLorg/thoughtcrime/securesms/util/ConversationUtil;->getChannelId(Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/Recipient;)Ljava/lang/String; -PLorg/thoughtcrime/securesms/util/ConversationUtil;->getShortcutId(Lorg/thoughtcrime/securesms/recipients/RecipientId;)Ljava/lang/String; +PLorg/thoughtcrime/securesms/util/BubbleUtil$BubbleState;->$values()[Lorg/thoughtcrime/securesms/util/BubbleUtil$BubbleState; +PLorg/thoughtcrime/securesms/util/BubbleUtil$BubbleState;->()V +PLorg/thoughtcrime/securesms/util/BubbleUtil$BubbleState;->(Ljava/lang/String;I)V +PLorg/thoughtcrime/securesms/util/ConversationUtil;->refreshRecipientShortcuts()V PLorg/thoughtcrime/securesms/util/Debouncer;->clear()V -PLorg/thoughtcrime/securesms/util/DefaultSavedStateHandleDelegate;->setValue(Ljava/lang/Object;Lkotlin/reflect/KProperty;Ljava/lang/Object;)V +PLorg/thoughtcrime/securesms/util/FeatureFlags;->getDefaultMaxBackoff()J +PLorg/thoughtcrime/securesms/util/JsonUtils;->fromJson([BLjava/lang/Class;)Ljava/lang/Object; +PLorg/thoughtcrime/securesms/util/LeakyBucketLimiter$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/util/LeakyBucketLimiter;)V PLorg/thoughtcrime/securesms/util/LeakyBucketLimiter$$ExternalSyntheticLambda0;->run()V PLorg/thoughtcrime/securesms/util/LeakyBucketLimiter;->$r8$lambda$1L8FIPWGmHakh7u9Krsm8K4DSjQ(Lorg/thoughtcrime/securesms/util/LeakyBucketLimiter;)V PLorg/thoughtcrime/securesms/util/LeakyBucketLimiter;->drip()V +PLorg/thoughtcrime/securesms/util/LeakyBucketLimiter;->run(Ljava/lang/Runnable;)V PLorg/thoughtcrime/securesms/util/Material3OnScrollHelper$3;->onDestroy(Landroidx/lifecycle/LifecycleOwner;)V -PLorg/thoughtcrime/securesms/util/Material3OnScrollHelper$3;->onStop(Landroidx/lifecycle/LifecycleOwner;)V PLorg/thoughtcrime/securesms/util/Material3OnScrollHelper;->access$getAnimator$p(Lorg/thoughtcrime/securesms/util/Material3OnScrollHelper;)Landroid/animation/ValueAnimator; PLorg/thoughtcrime/securesms/util/Material3OnScrollHelper;->access$getSetStatusBarColor$p(Lorg/thoughtcrime/securesms/util/Material3OnScrollHelper;)Lkotlin/jvm/functions/Function1; PLorg/thoughtcrime/securesms/util/Material3OnScrollHelper;->getPreviousStatusBarColor()I -PLorg/thoughtcrime/securesms/util/SplashScreenUtil$$ExternalSyntheticApiModelOutline0;->m(Landroid/app/Activity;)Landroid/window/SplashScreen; -PLorg/thoughtcrime/securesms/util/SplashScreenUtil$$ExternalSyntheticApiModelOutline1;->m(Landroid/window/SplashScreen;I)V -PLorg/thoughtcrime/securesms/util/SplashScreenUtil$1;->()V -PLorg/thoughtcrime/securesms/util/SplashScreenUtil;->setSplashScreenThemeIfNecessary(Landroid/app/Activity;Lorg/thoughtcrime/securesms/keyvalue/SettingsValues$Theme;)V +PLorg/thoughtcrime/securesms/util/MediaUtil;->isVideo(Ljava/lang/String;)Z +PLorg/thoughtcrime/securesms/util/ProfileUtil$$ExternalSyntheticLambda0;->(Lorg/thoughtcrime/securesms/recipients/Recipient;)V +PLorg/thoughtcrime/securesms/util/ProfileUtil$$ExternalSyntheticLambda0;->apply(Ljava/lang/Object;)Ljava/lang/Object; +PLorg/thoughtcrime/securesms/util/ProfileUtil$$ExternalSyntheticLambda3;->(Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/Recipient;)V +PLorg/thoughtcrime/securesms/util/ProfileUtil$$ExternalSyntheticLambda3;->call()Ljava/lang/Object; +PLorg/thoughtcrime/securesms/util/ProfileUtil$$ExternalSyntheticLambda4;->(Lorg/whispersystems/signalservice/api/services/ProfileService;Lj$/util/Optional;Lj$/util/Optional;Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;Lorg/thoughtcrime/securesms/recipients/Recipient;)V +PLorg/thoughtcrime/securesms/util/ProfileUtil$$ExternalSyntheticLambda4;->apply(Ljava/lang/Object;)Ljava/lang/Object; +PLorg/thoughtcrime/securesms/util/ProfileUtil$$ExternalSyntheticLambda5;->(Lorg/thoughtcrime/securesms/recipients/Recipient;)V +PLorg/thoughtcrime/securesms/util/ProfileUtil;->$r8$lambda$64DRPwLhDKidiYVBrJ1oGsaeSVY(Lorg/thoughtcrime/securesms/recipients/Recipient;Lorg/whispersystems/signalservice/internal/ServiceResponse;)Lorg/signal/libsignal/protocol/util/Pair; +PLorg/thoughtcrime/securesms/util/ProfileUtil;->$r8$lambda$cqO5Ws54dRBOxkD_sPlVLlSYwIg(Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/Recipient;)Lorg/whispersystems/signalservice/api/push/SignalServiceAddress; +PLorg/thoughtcrime/securesms/util/ProfileUtil;->$r8$lambda$dDuBqdOM1yCYB_18NZWtjJd7BlA(Lorg/whispersystems/signalservice/api/services/ProfileService;Lj$/util/Optional;Lj$/util/Optional;Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;Lorg/thoughtcrime/securesms/recipients/Recipient;Lorg/whispersystems/signalservice/api/push/SignalServiceAddress;)Lio/reactivex/rxjava3/core/SingleSource; +PLorg/thoughtcrime/securesms/util/ProfileUtil;->lambda$retrieveProfile$0(Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/Recipient;)Lorg/whispersystems/signalservice/api/push/SignalServiceAddress; +PLorg/thoughtcrime/securesms/util/ProfileUtil;->lambda$retrieveProfile$1(Lorg/thoughtcrime/securesms/recipients/Recipient;Lorg/whispersystems/signalservice/internal/ServiceResponse;)Lorg/signal/libsignal/protocol/util/Pair; +PLorg/thoughtcrime/securesms/util/ProfileUtil;->lambda$retrieveProfile$2(Lorg/whispersystems/signalservice/api/services/ProfileService;Lj$/util/Optional;Lj$/util/Optional;Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;Lorg/thoughtcrime/securesms/recipients/Recipient;Lorg/whispersystems/signalservice/api/push/SignalServiceAddress;)Lio/reactivex/rxjava3/core/SingleSource; +PLorg/thoughtcrime/securesms/util/ProfileUtil;->retrieveProfile(Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/Recipient;Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;Z)Lio/reactivex/rxjava3/core/Single; +PLorg/thoughtcrime/securesms/util/ProfileUtil;->retrieveProfileSync(Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/Recipient;Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;Z)Lorg/whispersystems/signalservice/api/profiles/ProfileAndCredential; +PLorg/thoughtcrime/securesms/util/ProfileUtil;->toSignalServiceAddress(Landroid/content/Context;Lorg/thoughtcrime/securesms/recipients/Recipient;)Lorg/whispersystems/signalservice/api/push/SignalServiceAddress; +PLorg/thoughtcrime/securesms/util/SoftHashMap;->clear()V +PLorg/thoughtcrime/securesms/util/TextSecurePreferences;->isUniversalUnidentifiedAccess(Landroid/content/Context;)Z PLorg/thoughtcrime/securesms/util/Util;->clamp(FFF)F -PLorg/thoughtcrime/securesms/util/ViewUtil;->animateOut(Landroid/view/View;Landroid/view/animation/Animation;I)Lorg/thoughtcrime/securesms/util/concurrent/ListenableFuture; -PLorg/thoughtcrime/securesms/util/ViewUtil;->fadeOut(Landroid/view/View;I)Lorg/thoughtcrime/securesms/util/concurrent/ListenableFuture; -PLorg/thoughtcrime/securesms/util/ViewUtil;->fadeOut(Landroid/view/View;II)Lorg/thoughtcrime/securesms/util/concurrent/ListenableFuture; -PLorg/thoughtcrime/securesms/util/ViewUtil;->getAlphaAnimation(FFI)Landroid/view/animation/Animation; PLorg/thoughtcrime/securesms/util/adapter/mapping/MappingAdapter;->onViewDetachedFromWindow(Landroidx/recyclerview/widget/RecyclerView$ViewHolder;)V PLorg/thoughtcrime/securesms/util/adapter/mapping/MappingAdapter;->onViewDetachedFromWindow(Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingViewHolder;)V +PLorg/thoughtcrime/securesms/util/adapter/mapping/MappingDiffCallback;->areContentsTheSame(Ljava/lang/Object;Ljava/lang/Object;)Z +PLorg/thoughtcrime/securesms/util/adapter/mapping/MappingDiffCallback;->areContentsTheSame(Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingModel;Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingModel;)Z +PLorg/thoughtcrime/securesms/util/adapter/mapping/MappingDiffCallback;->areItemsTheSame(Ljava/lang/Object;Ljava/lang/Object;)Z +PLorg/thoughtcrime/securesms/util/adapter/mapping/MappingDiffCallback;->areItemsTheSame(Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingModel;Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingModel;)Z PLorg/thoughtcrime/securesms/util/adapter/mapping/MappingDiffCallback;->getChangePayload(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; PLorg/thoughtcrime/securesms/util/adapter/mapping/MappingDiffCallback;->getChangePayload(Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingModel;Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingModel;)Ljava/lang/Object; PLorg/thoughtcrime/securesms/util/adapter/mapping/MappingModel$-CC;->$default$getChangePayload(Lorg/thoughtcrime/securesms/util/adapter/mapping/MappingModel;Ljava/lang/Object;)Ljava/lang/Object; PLorg/thoughtcrime/securesms/util/adapter/mapping/MappingViewHolder;->onDetachedFromWindow()V +PLorg/thoughtcrime/securesms/util/concurrent/SerialMonoLifoExecutor$$ExternalSyntheticLambda0;->run()V +PLorg/thoughtcrime/securesms/util/concurrent/SerialMonoLifoExecutor;->$r8$lambda$axI96jKiGgASw-5DyS1pXfSexxk(Lorg/thoughtcrime/securesms/util/concurrent/SerialMonoLifoExecutor;Ljava/lang/Runnable;)V +PLorg/thoughtcrime/securesms/util/concurrent/SerialMonoLifoExecutor;->lambda$enqueue$0(Ljava/lang/Runnable;)V PLorg/thoughtcrime/securesms/util/rx/RxStore;->dispose()V +PLorg/whispersystems/signalservice/api/SignalServiceAccountManager;->getPreKeyCounts(Lorg/whispersystems/signalservice/api/push/ServiceIdType;)Lorg/whispersystems/signalservice/internal/push/OneTimePreKeyCounts; +PLorg/whispersystems/signalservice/api/SignalServiceAccountManager;->getSenderCertificate()[B +PLorg/whispersystems/signalservice/api/SignalServiceAccountManager;->setAccountAttributes(Lorg/whispersystems/signalservice/api/account/AccountAttributes;)V +PLorg/whispersystems/signalservice/api/SignalServiceMessageReceiver$$ExternalSyntheticLambda0;->()V +PLorg/whispersystems/signalservice/api/SignalServiceMessageReceiver;->(Lorg/whispersystems/signalservice/internal/configuration/SignalServiceConfiguration;Lorg/whispersystems/signalservice/api/util/CredentialsProvider;Ljava/lang/String;Lorg/signal/libsignal/zkgroup/profiles/ClientZkProfileOperations;Z)V +PLorg/whispersystems/signalservice/api/SignalServiceMessageReceiver;->retrieveProfile(Lorg/whispersystems/signalservice/api/push/SignalServiceAddress;Lj$/util/Optional;Lj$/util/Optional;Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;Ljava/util/Locale;)Lorg/whispersystems/signalservice/internal/util/concurrent/ListenableFuture; +PLorg/whispersystems/signalservice/api/SignalWebSocket;->request(Lorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage;)Lio/reactivex/rxjava3/core/Single; +PLorg/whispersystems/signalservice/api/SignalWebSocket;->request(Lorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage;Lj$/util/Optional;)Lio/reactivex/rxjava3/core/Single; +PLorg/whispersystems/signalservice/api/account/AccountAttributes$Capabilities;->(ZZZZZZZZ)V +PLorg/whispersystems/signalservice/api/account/AccountAttributes$Capabilities;->getAnnouncementGroup()Z +PLorg/whispersystems/signalservice/api/account/AccountAttributes$Capabilities;->getChangeNumber()Z +PLorg/whispersystems/signalservice/api/account/AccountAttributes$Capabilities;->getGiftBadges()Z +PLorg/whispersystems/signalservice/api/account/AccountAttributes$Capabilities;->getPaymentActivation()Z +PLorg/whispersystems/signalservice/api/account/AccountAttributes$Capabilities;->getPni()Z +PLorg/whispersystems/signalservice/api/account/AccountAttributes$Capabilities;->getSenderKey()Z +PLorg/whispersystems/signalservice/api/account/AccountAttributes$Capabilities;->getStorage()Z +PLorg/whispersystems/signalservice/api/account/AccountAttributes$Capabilities;->getStories()Z +PLorg/whispersystems/signalservice/api/account/AccountAttributes$Capabilities;->toString()Ljava/lang/String; +PLorg/whispersystems/signalservice/api/account/AccountAttributes;->(Ljava/lang/String;IZLjava/lang/String;[BZLorg/whispersystems/signalservice/api/account/AccountAttributes$Capabilities;ZLjava/lang/String;ILjava/lang/String;)V +PLorg/whispersystems/signalservice/api/account/AccountAttributes;->(Ljava/lang/String;IZZZLjava/lang/String;[BZZLorg/whispersystems/signalservice/api/account/AccountAttributes$Capabilities;Ljava/lang/String;ILjava/lang/String;)V +PLorg/whispersystems/signalservice/api/account/AccountAttributes;->getCapabilities()Lorg/whispersystems/signalservice/api/account/AccountAttributes$Capabilities; +PLorg/whispersystems/signalservice/api/account/AccountAttributes;->getDiscoverableByPhoneNumber()Z +PLorg/whispersystems/signalservice/api/account/AccountAttributes;->getFetchesMessages()Z +PLorg/whispersystems/signalservice/api/account/AccountAttributes;->getName()Ljava/lang/String; +PLorg/whispersystems/signalservice/api/account/AccountAttributes;->getPniRegistrationId()I +PLorg/whispersystems/signalservice/api/account/AccountAttributes;->getRecoveryPassword()Ljava/lang/String; +PLorg/whispersystems/signalservice/api/account/AccountAttributes;->getRegistrationId()I +PLorg/whispersystems/signalservice/api/account/AccountAttributes;->getRegistrationLock()Ljava/lang/String; +PLorg/whispersystems/signalservice/api/account/AccountAttributes;->getSignalingKey()Ljava/lang/String; +PLorg/whispersystems/signalservice/api/account/AccountAttributes;->getUnidentifiedAccessKey()[B +PLorg/whispersystems/signalservice/api/account/AccountAttributes;->getUnrestrictedUnidentifiedAccess()Z +PLorg/whispersystems/signalservice/api/account/AccountAttributes;->getVideo()Z +PLorg/whispersystems/signalservice/api/account/AccountAttributes;->getVoice()Z +PLorg/whispersystems/signalservice/api/crypto/UnidentifiedAccess;->createEmptyByteArray(I)[B +PLorg/whispersystems/signalservice/api/crypto/UnidentifiedAccess;->deriveAccessKeyFrom(Lorg/signal/libsignal/zkgroup/profiles/ProfileKey;)[B +PLorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;->$values()[Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType; +PLorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;->()V +PLorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;->(Ljava/lang/String;I)V +PLorg/whispersystems/signalservice/api/push/ServiceId$ACI;->getLibSignalAci()Lorg/signal/libsignal/protocol/ServiceId$Aci; +PLorg/whispersystems/signalservice/api/push/ServiceIdType;->$values()[Lorg/whispersystems/signalservice/api/push/ServiceIdType; +PLorg/whispersystems/signalservice/api/push/ServiceIdType;->()V +PLorg/whispersystems/signalservice/api/push/ServiceIdType;->(Ljava/lang/String;ILjava/lang/String;)V +PLorg/whispersystems/signalservice/api/push/ServiceIdType;->queryParam()Ljava/lang/String; +PLorg/whispersystems/signalservice/api/push/exceptions/NonSuccessfulResponseCodeException;->(ILjava/lang/String;)V +PLorg/whispersystems/signalservice/api/push/exceptions/NonSuccessfulResponseCodeException;->getCode()I +PLorg/whispersystems/signalservice/api/services/MessagingService$$ExternalSyntheticLambda4;->(Lorg/whispersystems/signalservice/internal/websocket/ResponseMapper;)V +PLorg/whispersystems/signalservice/api/services/ProfileService$$ExternalSyntheticLambda1;->(Lorg/whispersystems/signalservice/api/services/ProfileService;Lorg/whispersystems/signalservice/api/push/SignalServiceAddress;Lj$/util/Optional;Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;Ljava/util/Locale;)V +PLorg/whispersystems/signalservice/api/services/ProfileService$$ExternalSyntheticLambda1;->apply(Ljava/lang/Object;)Ljava/lang/Object; +PLorg/whispersystems/signalservice/api/services/ProfileService$$ExternalSyntheticLambda2;->()V +PLorg/whispersystems/signalservice/api/services/ProfileService$$ExternalSyntheticLambda3;->(Lorg/whispersystems/signalservice/api/services/ProfileService;Lorg/whispersystems/signalservice/api/push/SignalServiceAddress;Lj$/util/Optional;Lj$/util/Optional;Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;Ljava/util/Locale;)V +PLorg/whispersystems/signalservice/api/services/ProfileService$$ExternalSyntheticLambda3;->apply(Ljava/lang/Object;)Ljava/lang/Object; +PLorg/whispersystems/signalservice/api/services/ProfileService$ProfileResponseMapper;->(Lorg/whispersystems/signalservice/api/services/ProfileService;Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;Lorg/signal/libsignal/zkgroup/profiles/ProfileKeyCredentialRequestContext;)V +PLorg/whispersystems/signalservice/api/services/ProfileService$ProfileResponseProcessor;->(Lorg/whispersystems/signalservice/internal/ServiceResponse;)V +PLorg/whispersystems/signalservice/api/services/ProfileService$ProfileResponseProcessor;->getError()Ljava/lang/Throwable; +PLorg/whispersystems/signalservice/api/services/ProfileService;->$r8$lambda$qreHJhpvwxlLuI7XzIfP9eBBI24(Lorg/whispersystems/signalservice/api/services/ProfileService;Lorg/whispersystems/signalservice/api/push/SignalServiceAddress;Lj$/util/Optional;Lj$/util/Optional;Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;Ljava/util/Locale;Ljava/lang/Throwable;)Lio/reactivex/rxjava3/core/SingleSource; +PLorg/whispersystems/signalservice/api/services/ProfileService;->$r8$lambda$vXxdyhUGBLq7AFV9vG9NvMDxbNI(Lorg/whispersystems/signalservice/api/services/ProfileService;Lorg/whispersystems/signalservice/api/push/SignalServiceAddress;Lj$/util/Optional;Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;Ljava/util/Locale;Ljava/lang/Throwable;)Lio/reactivex/rxjava3/core/SingleSource; +PLorg/whispersystems/signalservice/api/services/ProfileService;->()V +PLorg/whispersystems/signalservice/api/services/ProfileService;->(Lorg/signal/libsignal/zkgroup/profiles/ClientZkProfileOperations;Lorg/whispersystems/signalservice/api/SignalServiceMessageReceiver;Lorg/whispersystems/signalservice/api/SignalWebSocket;)V +PLorg/whispersystems/signalservice/api/services/ProfileService;->getProfile(Lorg/whispersystems/signalservice/api/push/SignalServiceAddress;Lj$/util/Optional;Lj$/util/Optional;Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;Ljava/util/Locale;)Lio/reactivex/rxjava3/core/Single; +PLorg/whispersystems/signalservice/api/services/ProfileService;->getProfileRestFallback(Lorg/whispersystems/signalservice/api/push/SignalServiceAddress;Lj$/util/Optional;Lj$/util/Optional;Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;Ljava/util/Locale;)Lio/reactivex/rxjava3/core/Single; +PLorg/whispersystems/signalservice/api/services/ProfileService;->lambda$getProfile$0(Lorg/whispersystems/signalservice/api/push/SignalServiceAddress;Lj$/util/Optional;Lj$/util/Optional;Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;Ljava/util/Locale;Ljava/lang/Throwable;)Lio/reactivex/rxjava3/core/SingleSource; +PLorg/whispersystems/signalservice/api/services/ProfileService;->lambda$getProfileRestFallback$3(Lorg/whispersystems/signalservice/api/push/SignalServiceAddress;Lj$/util/Optional;Lorg/whispersystems/signalservice/api/profiles/SignalServiceProfile$RequestType;Ljava/util/Locale;Ljava/lang/Throwable;)Lio/reactivex/rxjava3/core/SingleSource; +PLorg/whispersystems/signalservice/internal/ServiceResponse;->forApplicationError(Ljava/lang/Throwable;ILjava/lang/String;)Lorg/whispersystems/signalservice/internal/ServiceResponse; +PLorg/whispersystems/signalservice/internal/ServiceResponse;->forUnknownError(Ljava/lang/Throwable;)Lorg/whispersystems/signalservice/internal/ServiceResponse; +PLorg/whispersystems/signalservice/internal/ServiceResponse;->getApplicationError()Lj$/util/Optional; +PLorg/whispersystems/signalservice/internal/ServiceResponse;->getExecutionError()Lj$/util/Optional; +PLorg/whispersystems/signalservice/internal/ServiceResponseProcessor;->getError()Ljava/lang/Throwable; +PLorg/whispersystems/signalservice/internal/push/PushServiceSocket$$ExternalSyntheticLambda14;->()V +PLorg/whispersystems/signalservice/internal/push/PushServiceSocket$1;->(Lorg/whispersystems/signalservice/internal/push/PushServiceSocket;Lorg/whispersystems/signalservice/internal/util/concurrent/SettableFuture;)V +PLorg/whispersystems/signalservice/internal/push/PushServiceSocket$1;->onResponse(Lokhttp3/Call;Lokhttp3/Response;)V +PLorg/whispersystems/signalservice/internal/push/PushServiceSocket$ConnectionHolder;->getClient()Lokhttp3/OkHttpClient; +PLorg/whispersystems/signalservice/internal/push/PushServiceSocket$ConnectionHolder;->getHostHeader()Lj$/util/Optional; +PLorg/whispersystems/signalservice/internal/push/PushServiceSocket$ConnectionHolder;->getUrl()Ljava/lang/String; +PLorg/whispersystems/signalservice/internal/push/PushServiceSocket$EmptyResponseCodeHandler;->handle(ILokhttp3/ResponseBody;)V +PLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->-$$Nest$mvalidateServiceResponse(Lorg/whispersystems/signalservice/internal/push/PushServiceSocket;Lokhttp3/Response;)Lokhttp3/Response; +PLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->buildOkHttpClient(Z)Lokhttp3/OkHttpClient; +PLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->buildServiceRequest(Ljava/lang/String;Ljava/lang/String;Lokhttp3/RequestBody;Ljava/util/Map;Lj$/util/Optional;Z)Lokhttp3/Request; +PLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->getAuthorizationHeader(Lorg/whispersystems/signalservice/api/util/CredentialsProvider;)Ljava/lang/String; +PLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->getAvailablePreKeys(Lorg/whispersystems/signalservice/api/push/ServiceIdType;)Lorg/whispersystems/signalservice/internal/push/OneTimePreKeyCounts; +PLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->getRandom([Lorg/whispersystems/signalservice/internal/push/PushServiceSocket$ConnectionHolder;Ljava/security/SecureRandom;)Lorg/whispersystems/signalservice/internal/push/PushServiceSocket$ConnectionHolder; +PLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->getSenderCertificate()[B +PLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->getServiceConnection(Ljava/lang/String;Ljava/lang/String;Lokhttp3/RequestBody;Ljava/util/Map;Lj$/util/Optional;Z)Lokhttp3/Response; +PLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->jsonRequestBody(Ljava/lang/String;)Lokhttp3/RequestBody; +PLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->makeServiceRequest(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; +PLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->makeServiceRequest(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;Lorg/whispersystems/signalservice/internal/push/PushServiceSocket$ResponseCodeHandler;Lj$/util/Optional;)Ljava/lang/String; +PLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->makeServiceRequest(Ljava/lang/String;Ljava/lang/String;Lokhttp3/RequestBody;Ljava/util/Map;Lorg/whispersystems/signalservice/internal/push/PushServiceSocket$ResponseCodeHandler;Lj$/util/Optional;Z)Lokhttp3/Response; +PLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->retrieveVersionedProfile(Lorg/whispersystems/signalservice/api/push/ServiceId$ACI;Lorg/signal/libsignal/zkgroup/profiles/ProfileKey;Lj$/util/Optional;Ljava/util/Locale;)Lorg/whispersystems/signalservice/internal/util/concurrent/ListenableFuture; +PLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->setAccountAttributes(Lorg/whispersystems/signalservice/api/account/AccountAttributes;)V +PLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->submitServiceRequest(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;Lj$/util/Optional;)Lorg/whispersystems/signalservice/internal/util/concurrent/ListenableFuture; +PLorg/whispersystems/signalservice/internal/push/PushServiceSocket;->validateServiceResponse(Lokhttp3/Response;)Lokhttp3/Response; +PLorg/whispersystems/signalservice/internal/push/http/AcceptLanguagesUtil;->formatLanguages(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; +PLorg/whispersystems/signalservice/internal/push/http/AcceptLanguagesUtil;->getAcceptLanguageHeader(Ljava/util/Locale;)Ljava/lang/String; +PLorg/whispersystems/signalservice/internal/push/http/AcceptLanguagesUtil;->getHeadersWithAcceptLanguage(Ljava/util/Locale;)Ljava/util/Map; +PLorg/whispersystems/signalservice/internal/util/JsonUtil;->toJson(Ljava/lang/Object;)Ljava/lang/String; +PLorg/whispersystems/signalservice/internal/util/concurrent/FutureMapTransformer;->(Lorg/whispersystems/signalservice/internal/util/concurrent/ListenableFuture;Lorg/whispersystems/signalservice/internal/util/concurrent/FutureTransformers$Transformer;)V +PLorg/whispersystems/signalservice/internal/util/concurrent/FutureMapTransformer;->get(JLjava/util/concurrent/TimeUnit;)Ljava/lang/Object; +PLorg/whispersystems/signalservice/internal/util/concurrent/FutureTransformers;->map(Lorg/whispersystems/signalservice/internal/util/concurrent/ListenableFuture;Lorg/whispersystems/signalservice/internal/util/concurrent/FutureTransformers$Transformer;)Lorg/whispersystems/signalservice/internal/util/concurrent/ListenableFuture; +PLorg/whispersystems/signalservice/internal/util/concurrent/SettableFuture;->()V +PLorg/whispersystems/signalservice/internal/util/concurrent/SettableFuture;->get()Ljava/lang/Object; +PLorg/whispersystems/signalservice/internal/util/concurrent/SettableFuture;->get(JLjava/util/concurrent/TimeUnit;)Ljava/lang/Object; +PLorg/whispersystems/signalservice/internal/util/concurrent/SettableFuture;->notifyAllListeners()V +PLorg/whispersystems/signalservice/internal/util/concurrent/SettableFuture;->setException(Ljava/lang/Throwable;)Z +PLorg/whispersystems/signalservice/internal/websocket/DefaultErrorMapper$Builder;->()V +PLorg/whispersystems/signalservice/internal/websocket/DefaultErrorMapper$Builder;->build()Lorg/whispersystems/signalservice/internal/websocket/ErrorMapper; +PLorg/whispersystems/signalservice/internal/websocket/DefaultErrorMapper;->()V +PLorg/whispersystems/signalservice/internal/websocket/DefaultErrorMapper;->()V +PLorg/whispersystems/signalservice/internal/websocket/DefaultErrorMapper;->(Ljava/util/Map;)V +PLorg/whispersystems/signalservice/internal/websocket/DefaultErrorMapper;->(Ljava/util/Map;Lorg/whispersystems/signalservice/internal/websocket/DefaultErrorMapper-IA;)V +PLorg/whispersystems/signalservice/internal/websocket/DefaultErrorMapper;->extend()Lorg/whispersystems/signalservice/internal/websocket/DefaultErrorMapper$Builder; +PLorg/whispersystems/signalservice/internal/websocket/DefaultResponseMapper$Builder;->(Ljava/lang/Class;)V +PLorg/whispersystems/signalservice/internal/websocket/DefaultResponseMapper$Builder;->build()Lorg/whispersystems/signalservice/internal/websocket/ResponseMapper; +PLorg/whispersystems/signalservice/internal/websocket/DefaultResponseMapper$Builder;->withResponseMapper(Lorg/whispersystems/signalservice/internal/websocket/DefaultResponseMapper$CustomResponseMapper;)Lorg/whispersystems/signalservice/internal/websocket/DefaultResponseMapper$Builder; +PLorg/whispersystems/signalservice/internal/websocket/DefaultResponseMapper;->(Ljava/lang/Class;Lorg/whispersystems/signalservice/internal/websocket/DefaultResponseMapper$CustomResponseMapper;Lorg/whispersystems/signalservice/internal/websocket/ErrorMapper;)V +PLorg/whispersystems/signalservice/internal/websocket/DefaultResponseMapper;->(Ljava/lang/Class;Lorg/whispersystems/signalservice/internal/websocket/DefaultResponseMapper$CustomResponseMapper;Lorg/whispersystems/signalservice/internal/websocket/ErrorMapper;Lorg/whispersystems/signalservice/internal/websocket/DefaultResponseMapper-IA;)V +PLorg/whispersystems/signalservice/internal/websocket/DefaultResponseMapper;->extend(Ljava/lang/Class;)Lorg/whispersystems/signalservice/internal/websocket/DefaultResponseMapper$Builder; +PLorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage$Builder;->()V +PLorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage$Builder;->build()Lorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage; +PLorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage$Builder;->headers(Ljava/util/List;)Lorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage$Builder; +PLorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage$Builder;->id(Ljava/lang/Long;)Lorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage$Builder; +PLorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage$Builder;->path(Ljava/lang/String;)Lorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage$Builder; +PLorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage$Builder;->verb(Ljava/lang/String;)Lorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage$Builder; +PLorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage$Companion$ADAPTER$1;->(Lcom/squareup/wire/FieldEncoding;Lkotlin/reflect/KClass;Lcom/squareup/wire/Syntax;)V +PLorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage$Companion;->()V +PLorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage$Companion;->(Lkotlin/jvm/internal/DefaultConstructorMarker;)V +PLorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage;->()V +PLorg/whispersystems/signalservice/internal/websocket/WebSocketRequestMessage;->(Ljava/lang/String;Ljava/lang/String;Lokio/ByteString;Ljava/util/List;Ljava/lang/Long;Lokio/ByteString;)V diff --git a/app/src/main/java/com/google/android/material/bottomsheet/BottomSheetBehaviorHack.kt b/app/src/main/java/com/google/android/material/bottomsheet/BottomSheetBehaviorHack.kt index a9b644e87e..8c2469bad6 100644 --- a/app/src/main/java/com/google/android/material/bottomsheet/BottomSheetBehaviorHack.kt +++ b/app/src/main/java/com/google/android/material/bottomsheet/BottomSheetBehaviorHack.kt @@ -1,14 +1,13 @@ package com.google.android.material.bottomsheet import android.view.View -import android.widget.FrameLayout import java.lang.ref.WeakReference /** * Manually adjust the nested scrolling child for a given [BottomSheetBehavior]. */ object BottomSheetBehaviorHack { - fun setNestedScrollingChild(behavior: BottomSheetBehavior, view: View) { + fun setNestedScrollingChild(behavior: BottomSheetBehavior, view: View) { behavior.nestedScrollingChildRef = WeakReference(view) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java index c66a1e22a6..cb6d08b1f4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java +++ b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java @@ -39,6 +39,7 @@ import org.signal.core.util.concurrent.SignalExecutors; import org.signal.core.util.logging.AndroidLogger; import org.signal.core.util.logging.Log; +import org.signal.core.util.logging.Scrubber; import org.signal.core.util.tracing.Tracer; import org.signal.glide.SignalGlideCodecs; import org.signal.libsignal.protocol.logging.SignalProtocolLoggerProvider; @@ -64,6 +65,7 @@ import org.thoughtcrime.securesms.jobs.EmojiSearchIndexDownloadJob; import org.thoughtcrime.securesms.jobs.FcmRefreshJob; import org.thoughtcrime.securesms.jobs.FontDownloaderJob; +import org.thoughtcrime.securesms.jobs.GroupRingCleanupJob; import org.thoughtcrime.securesms.jobs.GroupV2UpdateSelfProfileKeyJob; import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob; import org.thoughtcrime.securesms.jobs.PnpInitializeDevicesJob; @@ -191,17 +193,18 @@ private void onCreateUnlock() { .addBlocking("crash-handling", this::initializeCrashHandling) .addBlocking("rx-init", this::initializeRx) .addBlocking("app-dependencies", this::initializeAppDependencies) + .addBlocking("scrubber", () -> Scrubber.setIdentifierHmacKeyProvider(() -> SignalStore.svr().getOrCreateMasterKey().deriveLoggingKey())) .addBlocking("network-settings", this::initializeNetworkSettings) .addBlocking("first-launch", this::initializeFirstEverAppLaunch) .addBlocking("gcm-check", this::initializeFcmCheck) .addBlocking("app-migrations", this::initializeApplicationMigrations) - .addBlocking("mark-registration", () -> RegistrationUtil.maybeMarkRegistrationComplete()) .addBlocking("lifecycle-observer", () -> ApplicationDependencies.getAppForegroundObserver().addListener(this)) .addBlocking("message-retriever", this::initializeMessageRetrieval) .addBlocking("blob-provider", this::initializeBlobProvider) .addBlocking("feature-flags", FeatureFlags::init) .addBlocking("ring-rtc", this::initializeRingRtc) .addBlocking("glide", () -> SignalGlideModule.setRegisterGlideComponents(new SignalGlideComponents())) + .addNonBlocking(() -> RegistrationUtil.maybeMarkRegistrationComplete()) .addNonBlocking(() -> GlideApp.get(this)) .addNonBlocking(this::cleanAvatarStorage) .addNonBlocking(this::initializeRevealableMessageManager) @@ -237,6 +240,7 @@ private void onCreateUnlock() { .addPostRender(() -> ApplicationDependencies.getExoPlayerPool().getPoolStats().getMaxUnreserved()) .addPostRender(() -> ApplicationDependencies.getRecipientCache().warmUp()) .addPostRender(AccountConsistencyWorkerJob::enqueueIfNecessary) + .addPostRender(GroupRingCleanupJob::enqueue) .execute(); Log.d(TAG, "onCreateUnlock() took " + (System.currentTimeMillis() - startTime) + " ms"); @@ -263,7 +267,7 @@ private void onStartUnlock() { SignalExecutors.BOUNDED.execute(() -> { FeatureFlags.refreshIfNecessary(); - RetrieveProfileJob.enqueueRoutineFetchIfNecessary(this); + RetrieveProfileJob.enqueueRoutineFetchIfNecessary(); executePendingContactSync(); checkBuildExpiration(); MemoryTracker.start(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/BindableConversationListItem.java b/app/src/main/java/org/thoughtcrime/securesms/BindableConversationListItem.java index 5b11c1fa2f..63f0970803 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/BindableConversationListItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/BindableConversationListItem.java @@ -20,4 +20,5 @@ void bind(@NonNull LifecycleOwner lifecycleOwner, void setSelectedConversations(@NonNull ConversationSet conversations); void updateTypingIndicator(@NonNull Set typingThreads); + void updateTimestamp(); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/BlockUnblockDialog.java b/app/src/main/java/org/thoughtcrime/securesms/BlockUnblockDialog.java index a96296800c..5f186ece37 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/BlockUnblockDialog.java +++ b/app/src/main/java/org/thoughtcrime/securesms/BlockUnblockDialog.java @@ -53,17 +53,6 @@ public static void showUnblockFor(@NonNull Context context, AlertDialog.Builder::show); } - public static void showDeleteFor(@NonNull Context context, - @NonNull Lifecycle lifecycle, - @NonNull Recipient recipient, - @NonNull Runnable onDelete, - @NonNull Runnable onBlockAndDelete) - { - SimpleTask.run(lifecycle, - () -> buildDeleteFor(context, recipient, onDelete, onBlockAndDelete), - AlertDialog.Builder::show); - } - @WorkerThread private static AlertDialog.Builder buildBlockFor(@NonNull Context context, @NonNull Recipient recipient, @@ -148,41 +137,4 @@ private static AlertDialog.Builder buildUnblockFor(@NonNull Context context, return builder; } - - @WorkerThread - private static AlertDialog.Builder buildDeleteFor(@NonNull Context context, - @NonNull Recipient recipient, - @NonNull Runnable onDelete, - @NonNull Runnable onBlockAndDelete) - { - final Recipient resolved = recipient.resolve(); - - AlertDialog.Builder builder = new MaterialAlertDialogBuilder(context); - Resources resources = context.getResources(); - - int messageResId = resolved.isSystemContact() ? - R.string.BlockUnblockDialog_your_chat_history_with_s_will_be_deleted_and_s_will_be_removed_from_your_phone_contacts : - R.string.BlockUnblockDialog_your_chat_history_with_s_will_be_deleted; - - StringBuilder message = new StringBuilder(resources.getString(messageResId, recipient.getDisplayName(context))); - - if (!recipient.isBlocked()) { - message.append("\n\n"); - message.append(resources.getString(R.string.BlockUnblockDialog_deleted_contacts_are_still_able_to_call_you_or_send_you_messages)); - } - - builder.setTitle(resources.getString(R.string.BlockUnblockDialog_delete_this_contact)); - builder.setMessage(message); - - if (!resolved.isBlocked()) { - builder.setNeutralButton(android.R.string.cancel, null); - builder.setNegativeButton(R.string.delete, (d, w) -> onDelete.run()); - builder.setPositiveButton(R.string.BlockUnblockDialog_block_and_delete, (d, w) -> onBlockAndDelete.run()); - } else { - builder.setPositiveButton(R.string.delete, ((dialog, which) -> onDelete.run())); - builder.setNegativeButton(android.R.string.cancel, null); - } - - return builder; - } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/DeviceActivity.java b/app/src/main/java/org/thoughtcrime/securesms/DeviceActivity.java index faa045d9f5..ba214acbc6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/DeviceActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/DeviceActivity.java @@ -197,7 +197,7 @@ protected Integer doInBackground(Void... params) { ProfileKey profileKey = ProfileKeyUtil.getSelfProfileKey(); TextSecurePreferences.setMultiDevice(DeviceActivity.this, true); - accountManager.addDevice(ephemeralId, publicKey, aciIdentityKeyPair, pniIdentityKeyPair, profileKey, verificationCode); + accountManager.addDevice(ephemeralId, publicKey, aciIdentityKeyPair, pniIdentityKeyPair, profileKey, SignalStore.svr().getOrCreateMasterKey(), verificationCode); return SUCCESS; } catch (NotFoundException e) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/MainActivity.java b/app/src/main/java/org/thoughtcrime/securesms/MainActivity.java index aa834557d9..755ffc089b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/MainActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/MainActivity.java @@ -16,6 +16,8 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder; import org.signal.core.util.concurrent.LifecycleDisposable; +import org.signal.core.util.logging.Log; +import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity; import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaController; import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaControllerOwner; import org.thoughtcrime.securesms.conversationlist.RelinkDevicesReminderBottomSheetFragment; @@ -84,9 +86,7 @@ public boolean onPreDraw() { ConversationListTabRepository repository = new ConversationListTabRepository(); ConversationListTabsViewModel.Factory factory = new ConversationListTabsViewModel.Factory(repository); - handleGroupLinkInIntent(getIntent()); - handleSignalMeIntent(getIntent()); - handleCallLinkInIntent(getIntent()); + handleDeeplinkIntent(getIntent()); CachedInflater.from(this).clear(); @@ -104,9 +104,7 @@ public Intent getIntent() { @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); - handleGroupLinkInIntent(intent); - handleSignalMeIntent(intent); - handleCallLinkInIntent(intent); + handleDeeplinkIntent(intent); } @Override @@ -170,6 +168,12 @@ private void updateTabVisibility() { return navigator; } + private void handleDeeplinkIntent(Intent intent) { + handleGroupLinkInIntent(intent); + handleSignalMeIntent(intent); + handleCallLinkInIntent(intent); + } + private void handleGroupLinkInIntent(Intent intent) { Uri data = intent.getData(); if (data != null) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/WebRtcCallActivity.java b/app/src/main/java/org/thoughtcrime/securesms/WebRtcCallActivity.java index c4e62008bb..2550e23a9a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/WebRtcCallActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/WebRtcCallActivity.java @@ -54,12 +54,12 @@ import org.signal.core.util.ThreadUtil; import org.signal.core.util.concurrent.LifecycleDisposable; import org.signal.core.util.concurrent.SignalExecutors; -import org.signal.libsignal.protocol.IdentityKey; import org.signal.core.util.logging.Log; +import org.signal.libsignal.protocol.IdentityKey; import org.thoughtcrime.securesms.components.TooltipPopup; import org.thoughtcrime.securesms.components.sensors.DeviceOrientationMonitor; -import org.thoughtcrime.securesms.components.webrtc.CallLinkInfoSheet; import org.thoughtcrime.securesms.components.webrtc.CallLinkProfileKeySender; +import org.thoughtcrime.securesms.components.webrtc.CallOverflowPopupWindow; import org.thoughtcrime.securesms.components.webrtc.CallParticipantsListUpdatePopupWindow; import org.thoughtcrime.securesms.components.webrtc.CallParticipantsState; import org.thoughtcrime.securesms.components.webrtc.CallStateUpdatePopupWindow; @@ -74,6 +74,8 @@ import org.thoughtcrime.securesms.components.webrtc.WebRtcCallViewModel; import org.thoughtcrime.securesms.components.webrtc.WebRtcControls; import org.thoughtcrime.securesms.components.webrtc.WifiToCellularPopupWindow; +import org.thoughtcrime.securesms.components.webrtc.controls.ControlsAndInfoController; +import org.thoughtcrime.securesms.components.webrtc.controls.ControlsAndInfoViewModel; import org.thoughtcrime.securesms.components.webrtc.participantslist.CallParticipantsListDialog; import org.thoughtcrime.securesms.components.webrtc.requests.CallLinkIncomingRequestSheet; import org.thoughtcrime.securesms.conversation.ui.error.SafetyNumberChangeDialog; @@ -81,7 +83,7 @@ import org.thoughtcrime.securesms.events.WebRtcViewModel; import org.thoughtcrime.securesms.messagerequests.CalleeMustAcceptMessageRequestActivity; import org.thoughtcrime.securesms.permissions.Permissions; -import org.thoughtcrime.securesms.recipients.LiveRecipient; +import org.thoughtcrime.securesms.reactions.any.ReactWithAnyEmojiBottomSheetDialogFragment; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.safety.SafetyNumberBottomSheet; @@ -96,6 +98,7 @@ import org.thoughtcrime.securesms.util.ThrottledDebouncer; import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.VibrateUtil; +import org.thoughtcrime.securesms.util.WindowUtil; import org.thoughtcrime.securesms.util.livedata.LiveDataUtil; import org.thoughtcrime.securesms.webrtc.CallParticipantsViewState; import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager; @@ -113,7 +116,7 @@ import static org.thoughtcrime.securesms.components.sensors.Orientation.PORTRAIT_BOTTOM_EDGE; -public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChangeDialog.Callback { +public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChangeDialog.Callback, ReactWithAnyEmojiBottomSheetDialogFragment.Callback { private static final String TAG = Log.tag(WebRtcCallActivity.class); @@ -139,13 +142,16 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan private CallParticipantsListUpdatePopupWindow participantUpdateWindow; private CallStateUpdatePopupWindow callStateUpdatePopupWindow; + private CallOverflowPopupWindow callOverflowPopupWindow; private WifiToCellularPopupWindow wifiToCellularPopupWindow; private DeviceOrientationMonitor deviceOrientationMonitor; private FullscreenHelper fullscreenHelper; private WebRtcCallView callScreen; private TooltipPopup videoTooltip; + private TooltipPopup switchCameraTooltip; private WebRtcCallViewModel viewModel; + private ControlsAndInfoViewModel controlsAndInfoViewModel; private boolean enableVideoIfAvailable; private boolean hasWarnedAboutBluetooth; private WindowLayoutInfoConsumer windowLayoutInfoConsumer; @@ -154,6 +160,7 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan private PictureInPictureParams.Builder pipBuilderParams; private LifecycleDisposable lifecycleDisposable; private long lastCallLinkDisconnectDialogShowTime; + private ControlsAndInfoController controlsAndInfo; private Disposable ephemeralStateDisposable = Disposable.empty(); @@ -163,7 +170,7 @@ protected void attachBaseContext(@NonNull Context newBase) { super.attachBaseContext(newBase); } - @SuppressLint("SourceLockedOrientationActivity") + @SuppressLint({ "SourceLockedOrientationActivity", "MissingInflatedId" }) @Override public void onCreate(Bundle savedInstanceState) { Log.i(TAG, "onCreate(" + getIntent().getBooleanExtra(EXTRA_STARTED_FROM_FULLSCREEN, false) + ")"); @@ -191,6 +198,16 @@ public void onCreate(Bundle savedInstanceState) { initializeViewModel(isLandscapeEnabled); initializePictureInPictureParams(); + controlsAndInfo = new ControlsAndInfoController(this, callScreen, callOverflowPopupWindow, viewModel, controlsAndInfoViewModel); + controlsAndInfo.addVisibilityListener(new FadeCallback()); + + fullscreenHelper.showAndHideWithSystemUI(getWindow(), + findViewById(R.id.call_screen_header_gradient), + findViewById(R.id.webrtc_call_view_toolbar_text), + findViewById(R.id.webrtc_call_view_toolbar_no_text)); + + lifecycleDisposable.add(controlsAndInfo); + logIntent(getIntent()); if (ANSWER_VIDEO_ACTION.equals(getIntent().getAction())) { @@ -212,6 +229,8 @@ public void onCreate(Bundle savedInstanceState) { requestNewSizesThrottle = new ThrottledDebouncer(TimeUnit.SECONDS.toMillis(1)); initializePendingParticipantFragmentListener(); + + WindowUtil.setNavigationBarColor(this, ContextCompat.getColor(this, R.color.signal_dark_colorSurface)); } @Override @@ -429,6 +448,13 @@ private void initializeResources() { participantUpdateWindow = new CallParticipantsListUpdatePopupWindow(callScreen); callStateUpdatePopupWindow = new CallStateUpdatePopupWindow(callScreen); wifiToCellularPopupWindow = new WifiToCellularPopupWindow(callScreen); + callOverflowPopupWindow = new CallOverflowPopupWindow(this, callScreen, () -> { + CallParticipantsState state = viewModel.getCallParticipantsStateSnapshot(); + if (state == null) { + return false; + } + return state.getLocalParticipant().isHandRaised(); + }); } private void initializeViewModel(boolean isLandscapeEnabled) { @@ -441,7 +467,10 @@ private void initializeViewModel(boolean isLandscapeEnabled) { viewModel.setIsLandscapeEnabled(isLandscapeEnabled); viewModel.setIsInPipMode(isInPipMode()); viewModel.getMicrophoneEnabled().observe(this, callScreen::setMicEnabled); - viewModel.getWebRtcControls().observe(this, callScreen::setWebRtcControls); + viewModel.getWebRtcControls().observe(this, controls -> { + callScreen.setWebRtcControls(controls); + controlsAndInfo.updateControls(controls); + }); viewModel.getEvents().observe(this, this::handleViewModelEvent); lifecycleDisposable.add(viewModel.getInCallstatus().subscribe(this::handleInCallStatus)); @@ -485,6 +514,8 @@ private void initializeViewModel(boolean isLandscapeEnabled) { .subscribe(callScreen::updatePendingParticipantsList); lifecycleDisposable.add(disposable); + + controlsAndInfoViewModel = new ViewModelProvider(this).get(ControlsAndInfoViewModel.class); } private void initializePictureInPictureParams() { @@ -532,7 +563,6 @@ private void handleViewModelEvent(@NonNull WebRtcCallViewModel.Event event) { .setText(R.string.WebRtcCallActivity__tap_here_to_turn_on_your_video) .setOnDismissListener(() -> viewModel.onDismissedVideoTooltip()) .show(TooltipPopup.POSITION_ABOVE); - return; } } else if (event instanceof WebRtcCallViewModel.Event.DismissVideoTooltip) { if (videoTooltip != null) { @@ -541,6 +571,20 @@ private void handleViewModelEvent(@NonNull WebRtcCallViewModel.Event event) { } } else if (event instanceof WebRtcCallViewModel.Event.ShowWifiToCellularPopup) { wifiToCellularPopupWindow.show(); + } else if (event instanceof WebRtcCallViewModel.Event.ShowSwitchCameraTooltip) { + if (switchCameraTooltip == null) { + switchCameraTooltip = TooltipPopup.forTarget(callScreen.getSwitchCameraTooltipTarget()) + .setBackgroundTint(ContextCompat.getColor(this, R.color.core_ultramarine)) + .setTextColor(ContextCompat.getColor(this, R.color.core_white)) + .setText(R.string.WebRtcCallActivity__flip_camera_tooltip) + .setOnDismissListener(() -> viewModel.onDismissedSwitchCameraTooltip()) + .show(TooltipPopup.POSITION_ABOVE); + } + } else if (event instanceof WebRtcCallViewModel.Event.DismissSwitchCameraTooltip) { + if (switchCameraTooltip != null) { + switchCameraTooltip.dismiss(); + switchCameraTooltip = null; + } } else { throw new IllegalArgumentException("Unknown event: " + event); } @@ -849,6 +893,7 @@ public void onEventMainThread(@NonNull WebRtcViewModel event) { viewModel.setRecipient(event.getRecipient()); callScreen.setRecipient(event.getRecipient()); + controlsAndInfoViewModel.setRecipient(event.getRecipient()); switch (event.getState()) { case CALL_PRE_JOIN: @@ -936,7 +981,6 @@ private void displayDeniedRequestToJoinCallLinkDialog() { private void handleCallPreJoin(@NonNull WebRtcViewModel event) { if (event.getGroupState().isNotIdle()) { - callScreen.setStatusFromGroupCallState(event.getGroupState()); callScreen.setRingGroup(event.shouldRingGroup()); if (event.shouldRingGroup() && event.areRemoteDevicesInCall()) { @@ -957,6 +1001,15 @@ private void startCall(boolean isVideoCall) { MessageSender.onMessageSent(); } + @Override + public void onReactWithAnyEmojiDialogDismissed() { /* no-op */ } + + @Override + public void onReactWithAnyEmojiSelected(@NonNull String emoji) { + ApplicationDependencies.getSignalCallManager().react(emoji); + callOverflowPopupWindow.dismiss(); + } + private final class ControlsListener implements WebRtcCallView.ControlsListener { @Override @@ -970,22 +1023,13 @@ public void onCancelStartCall() { } @Override - public void onControlsFadeOut() { - if (videoTooltip != null) { - videoTooltip.dismiss(); + public void toggleControls() { + WebRtcControls controlState = viewModel.getWebRtcControls().getValue(); + if (controlState != null && !controlState.displayIncomingCallButtons()) { + controlsAndInfo.toggleControls(); } } - @Override - public void showSystemUI() { - fullscreenHelper.showSystemUI(); - } - - @Override - public void hideSystemUI() { - fullscreenHelper.hideSystemUI(); - } - @Override public void onAudioOutputChanged(@NonNull WebRtcAudioOutput audioOutput) { maybeDisplaySpeakerphonePopup(audioOutput); @@ -1046,6 +1090,11 @@ public void onAcceptCallWithVoiceOnlyPressed() { handleAnswerWithAudio(); } + @Override + public void onOverflowClicked() { + controlsAndInfo.toggleOverflowPopup(); + } + @Override public void onAcceptCallPressed() { if (viewModel.isAnswerWithVideoAvailable()) { @@ -1063,6 +1112,7 @@ public void onPageChanged(@NonNull CallParticipantsState.SelectedPage page) { @Override public void onLocalPictureInPictureClicked() { viewModel.onLocalPictureInPictureClicked(); + controlsAndInfo.restartHideControlsTimer(); } @Override @@ -1079,13 +1129,7 @@ public void onRingGroupChanged(boolean ringGroup, boolean ringingAllowed) { @Override public void onCallInfoClicked() { - LiveRecipient liveRecipient = viewModel.getRecipient(); - - if (liveRecipient.get().isCallLink()) { - CallLinkInfoSheet.show(getSupportFragmentManager(), liveRecipient.get().requireCallLinkRoomId()); - } else { - CallParticipantsListDialog.show(getSupportFragmentManager()); - } + controlsAndInfo.showCallInfo(); } @Override @@ -1148,4 +1192,20 @@ public void accept(WindowLayoutInfo windowLayoutInfo) { } } } + + private class FadeCallback implements ControlsAndInfoController.BottomSheetVisibilityListener { + + @Override + public void onShown() { + fullscreenHelper.showSystemUI(); + } + + @Override + public void onHidden() { + fullscreenHelper.hideSystemUI(); + if (videoTooltip != null) { + videoTooltip.dismiss(); + } + } + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/animation/ResizeAnimation.java b/app/src/main/java/org/thoughtcrime/securesms/animation/ResizeAnimation.java index 5308484398..f259aa9d3a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/animation/ResizeAnimation.java +++ b/app/src/main/java/org/thoughtcrime/securesms/animation/ResizeAnimation.java @@ -1,5 +1,6 @@ package org.thoughtcrime.securesms.animation; +import android.graphics.Point; import android.view.View; import android.view.ViewGroup; import android.view.animation.Animation; @@ -16,6 +17,10 @@ public class ResizeAnimation extends Animation { private int startWidth; private int startHeight; + public ResizeAnimation(@NonNull View target, @NonNull Point dimension) { + this(target, dimension.x, dimension.y); + } + public ResizeAnimation(@NonNull View target, int targetWidthPx, int targetHeightPx) { this.target = target; this.targetWidthPx = targetWidthPx; diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/Attachment.java b/app/src/main/java/org/thoughtcrime/securesms/attachments/Attachment.java deleted file mode 100644 index 0ee7b7ef18..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/Attachment.java +++ /dev/null @@ -1,319 +0,0 @@ -/* - * Copyright 2023 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.thoughtcrime.securesms.attachments; - -import android.net.Uri; -import android.os.Parcel; -import android.os.Parcelable; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.os.ParcelCompat; - -import org.thoughtcrime.securesms.audio.AudioHash; -import org.thoughtcrime.securesms.blurhash.BlurHash; -import org.thoughtcrime.securesms.database.AttachmentTable; -import org.thoughtcrime.securesms.database.AttachmentTable.TransformProperties; -import org.thoughtcrime.securesms.stickers.StickerLocator; -import org.thoughtcrime.securesms.util.ParcelUtil; - -import java.util.Objects; - -public abstract class Attachment implements Parcelable { - - @NonNull - private final String contentType; - private final int transferState; - private final long size; - - @Nullable - private final String fileName; - - private final int cdnNumber; - - @Nullable - private final String location; - - @Nullable - private final String key; - - @Nullable - private final String relay; - - @Nullable - private final byte[] digest; - - @Nullable - private final byte[] incrementalDigest; - - @Nullable - private final String fastPreflightId; - - private final boolean voiceNote; - private final boolean borderless; - private final boolean videoGif; - private final int width; - private final int height; - private final boolean quote; - private final long uploadTimestamp; - private final int incrementalMacChunkSize; - - @Nullable - private final String caption; - - @Nullable - private final StickerLocator stickerLocator; - - @Nullable - private final BlurHash blurHash; - - @Nullable - private final AudioHash audioHash; - - @NonNull - private final TransformProperties transformProperties; - - public Attachment(@NonNull String contentType, - int transferState, - long size, - @Nullable String fileName, - int cdnNumber, - @Nullable String location, - @Nullable String key, - @Nullable String relay, - @Nullable byte[] digest, - @Nullable byte[] incrementalDigest, - @Nullable String fastPreflightId, - boolean voiceNote, - boolean borderless, - boolean videoGif, - int width, - int height, - int incrementalMacChunkSize, - boolean quote, - long uploadTimestamp, - @Nullable String caption, - @Nullable StickerLocator stickerLocator, - @Nullable BlurHash blurHash, - @Nullable AudioHash audioHash, - @Nullable TransformProperties transformProperties) - { - this.contentType = contentType; - this.transferState = transferState; - this.size = size; - this.fileName = fileName; - this.cdnNumber = cdnNumber; - this.location = location; - this.key = key; - this.relay = relay; - this.digest = digest; - this.incrementalDigest = incrementalDigest; - this.fastPreflightId = fastPreflightId; - this.voiceNote = voiceNote; - this.borderless = borderless; - this.videoGif = videoGif; - this.width = width; - this.height = height; - this.incrementalMacChunkSize = incrementalMacChunkSize; - this.quote = quote; - this.uploadTimestamp = uploadTimestamp; - this.stickerLocator = stickerLocator; - this.caption = caption; - this.blurHash = blurHash; - this.audioHash = audioHash; - this.transformProperties = transformProperties != null ? transformProperties : TransformProperties.empty(); - } - - protected Attachment(Parcel in) { - this.contentType = Objects.requireNonNull(in.readString()); - this.transferState = in.readInt(); - this.size = in.readLong(); - this.fileName = in.readString(); - this.cdnNumber = in.readInt(); - this.location = in.readString(); - this.key = in.readString(); - this.relay = in.readString(); - this.digest = ParcelUtil.readByteArray(in); - this.incrementalDigest = ParcelUtil.readByteArray(in); - this.fastPreflightId = in.readString(); - this.voiceNote = ParcelUtil.readBoolean(in); - this.borderless = ParcelUtil.readBoolean(in); - this.videoGif = ParcelUtil.readBoolean(in); - this.width = in.readInt(); - this.height = in.readInt(); - this.incrementalMacChunkSize = in.readInt(); - this.quote = ParcelUtil.readBoolean(in); - this.uploadTimestamp = in.readLong(); - this.stickerLocator = ParcelCompat.readParcelable(in, StickerLocator.class.getClassLoader(), StickerLocator.class); - this.caption = in.readString(); - this.blurHash = ParcelCompat.readParcelable(in, BlurHash.class.getClassLoader(), BlurHash.class); - this.audioHash = ParcelCompat.readParcelable(in, AudioHash.class.getClassLoader(), AudioHash.class); - this.transformProperties = Objects.requireNonNull(ParcelCompat.readParcelable(in, TransformProperties.class.getClassLoader(), TransformProperties.class)); - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - AttachmentCreator.writeSubclass(dest, this); - dest.writeString(contentType); - dest.writeInt(transferState); - dest.writeLong(size); - dest.writeString(fileName); - dest.writeInt(cdnNumber); - dest.writeString(location); - dest.writeString(key); - dest.writeString(relay); - ParcelUtil.writeByteArray(dest, digest); - ParcelUtil.writeByteArray(dest, incrementalDigest); - dest.writeString(fastPreflightId); - ParcelUtil.writeBoolean(dest, voiceNote); - ParcelUtil.writeBoolean(dest, borderless); - ParcelUtil.writeBoolean(dest, videoGif); - dest.writeInt(width); - dest.writeInt(height); - dest.writeInt(incrementalMacChunkSize); - ParcelUtil.writeBoolean(dest, quote); - dest.writeLong(uploadTimestamp); - dest.writeParcelable(stickerLocator, 0); - dest.writeString(caption); - dest.writeParcelable(blurHash, 0); - dest.writeParcelable(audioHash, 0); - dest.writeParcelable(transformProperties, 0); - } - - @Override - public int describeContents() { - return 0; - } - - public static final Creator CREATOR = AttachmentCreator.INSTANCE; - - @Nullable - public abstract Uri getUri(); - - public abstract @Nullable Uri getPublicUri(); - - public int getTransferState() { - return transferState; - } - - public boolean isInProgress() { - return transferState != AttachmentTable.TRANSFER_PROGRESS_DONE && - transferState != AttachmentTable.TRANSFER_PROGRESS_FAILED && - transferState != AttachmentTable.TRANSFER_PROGRESS_PERMANENT_FAILURE; - } - - public boolean isPermanentlyFailed() { - return transferState == AttachmentTable.TRANSFER_PROGRESS_PERMANENT_FAILURE; - } - - public long getSize() { - return size; - } - - @Nullable - public String getFileName() { - return fileName; - } - - @NonNull - public String getContentType() { - return contentType; - } - - public int getCdnNumber() { - return cdnNumber; - } - - @Nullable - public String getLocation() { - return location; - } - - @Nullable - public String getKey() { - return key; - } - - @Nullable - public String getRelay() { - return relay; - } - - @Nullable - public byte[] getDigest() { - return digest; - } - - @Nullable - public byte[] getIncrementalDigest() { - if (incrementalDigest != null && incrementalDigest.length > 0) { - return incrementalDigest; - } else { - return null; - } - } - - @Nullable - public String getFastPreflightId() { - return fastPreflightId; - } - - public boolean isVoiceNote() { - return voiceNote; - } - - public boolean isBorderless() { - return borderless; - } - - public boolean isVideoGif() { - return videoGif; - } - - public int getWidth() { - return width; - } - - public int getHeight() { - return height; - } - - public int getIncrementalMacChunkSize() { - return incrementalMacChunkSize; - } - - public boolean isQuote() { - return quote; - } - - public long getUploadTimestamp() { - return uploadTimestamp; - } - - public boolean isSticker() { - return stickerLocator != null; - } - - public @Nullable StickerLocator getSticker() { - return stickerLocator; - } - - public @Nullable BlurHash getBlurHash() { - return blurHash; - } - - public @Nullable AudioHash getAudioHash() { - return audioHash; - } - - public @Nullable String getCaption() { - return caption; - } - - public @NonNull TransformProperties getTransformProperties() { - return transformProperties; - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/Attachment.kt b/app/src/main/java/org/thoughtcrime/securesms/attachments/Attachment.kt new file mode 100644 index 0000000000..0330e91e6c --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/Attachment.kt @@ -0,0 +1,152 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ +package org.thoughtcrime.securesms.attachments + +import android.net.Uri +import android.os.Parcel +import android.os.Parcelable +import androidx.core.os.ParcelCompat +import org.thoughtcrime.securesms.audio.AudioHash +import org.thoughtcrime.securesms.blurhash.BlurHash +import org.thoughtcrime.securesms.database.AttachmentTable +import org.thoughtcrime.securesms.database.AttachmentTable.TransformProperties +import org.thoughtcrime.securesms.stickers.StickerLocator +import org.thoughtcrime.securesms.util.ParcelUtil + +/** + * Note: We have to use our own Parcelable implementation because we need to do custom stuff to preserve + * subclass information. + */ +abstract class Attachment( + @JvmField + val contentType: String, + @JvmField + val transferState: Int, + @JvmField + val size: Long, + @JvmField + val fileName: String?, + @JvmField + val cdnNumber: Int, + @JvmField + val remoteLocation: String?, + @JvmField + val remoteKey: String?, + @JvmField + val remoteDigest: ByteArray?, + @JvmField + val incrementalDigest: ByteArray?, + @JvmField + val fastPreflightId: String?, + @JvmField + val voiceNote: Boolean, + @JvmField + val borderless: Boolean, + @JvmField + val videoGif: Boolean, + @JvmField + val width: Int, + @JvmField + val height: Int, + @JvmField + val incrementalMacChunkSize: Int, + @JvmField + val quote: Boolean, + @JvmField + val uploadTimestamp: Long, + @JvmField + val caption: String?, + @JvmField + val stickerLocator: StickerLocator?, + @JvmField + val blurHash: BlurHash?, + @JvmField + val audioHash: AudioHash?, + @JvmField + val transformProperties: TransformProperties? +) : Parcelable { + + abstract val uri: Uri? + abstract val publicUri: Uri? + + protected constructor(parcel: Parcel) : this( + contentType = parcel.readString()!!, + transferState = parcel.readInt(), + size = parcel.readLong(), + fileName = parcel.readString(), + cdnNumber = parcel.readInt(), + remoteLocation = parcel.readString(), + remoteKey = parcel.readString(), + remoteDigest = ParcelUtil.readByteArray(parcel), + incrementalDigest = ParcelUtil.readByteArray(parcel), + fastPreflightId = parcel.readString(), + voiceNote = ParcelUtil.readBoolean(parcel), + borderless = ParcelUtil.readBoolean(parcel), + videoGif = ParcelUtil.readBoolean(parcel), + width = parcel.readInt(), + height = parcel.readInt(), + incrementalMacChunkSize = parcel.readInt(), + quote = ParcelUtil.readBoolean(parcel), + uploadTimestamp = parcel.readLong(), + caption = parcel.readString(), + stickerLocator = ParcelCompat.readParcelable(parcel, StickerLocator::class.java.classLoader, StickerLocator::class.java), + blurHash = ParcelCompat.readParcelable(parcel, BlurHash::class.java.classLoader, BlurHash::class.java), + audioHash = ParcelCompat.readParcelable(parcel, AudioHash::class.java.classLoader, AudioHash::class.java), + transformProperties = ParcelCompat.readParcelable(parcel, TransformProperties::class.java.classLoader, TransformProperties::class.java) + ) + + override fun writeToParcel(dest: Parcel, flags: Int) { + AttachmentCreator.writeSubclass(dest, this) + dest.writeString(contentType) + dest.writeInt(transferState) + dest.writeLong(size) + dest.writeString(fileName) + dest.writeInt(cdnNumber) + dest.writeString(remoteLocation) + dest.writeString(remoteKey) + ParcelUtil.writeByteArray(dest, remoteDigest) + ParcelUtil.writeByteArray(dest, incrementalDigest) + dest.writeString(fastPreflightId) + ParcelUtil.writeBoolean(dest, voiceNote) + ParcelUtil.writeBoolean(dest, borderless) + ParcelUtil.writeBoolean(dest, videoGif) + dest.writeInt(width) + dest.writeInt(height) + dest.writeInt(incrementalMacChunkSize) + ParcelUtil.writeBoolean(dest, quote) + dest.writeLong(uploadTimestamp) + dest.writeString(caption) + dest.writeParcelable(stickerLocator, 0) + dest.writeParcelable(blurHash, 0) + dest.writeParcelable(audioHash, 0) + dest.writeParcelable(transformProperties, 0) + } + + override fun describeContents(): Int { + return 0 + } + + val isInProgress: Boolean + get() = transferState != AttachmentTable.TRANSFER_PROGRESS_DONE && transferState != AttachmentTable.TRANSFER_PROGRESS_FAILED && transferState != AttachmentTable.TRANSFER_PROGRESS_PERMANENT_FAILURE + + val isPermanentlyFailed: Boolean + get() = transferState == AttachmentTable.TRANSFER_PROGRESS_PERMANENT_FAILURE + + val isSticker: Boolean + get() = stickerLocator != null + + fun getIncrementalDigest(): ByteArray? { + return if (incrementalDigest != null && incrementalDigest.size > 0) { + incrementalDigest + } else { + null + } + } + + companion object { + @JvmField + val CREATOR: Parcelable.Creator = AttachmentCreator + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/AttachmentCreator.kt b/app/src/main/java/org/thoughtcrime/securesms/attachments/AttachmentCreator.kt index 99b814a2a0..e70b2b61cf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/AttachmentCreator.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/AttachmentCreator.kt @@ -15,7 +15,6 @@ import android.os.Parcelable object AttachmentCreator : Parcelable.Creator { enum class Subclass(val clazz: Class, val code: String) { DATABASE(DatabaseAttachment::class.java, "database"), - MMS_NOTIFICATION(MmsNotificationAttachment::class.java, "mms_notification"), POINTER(PointerAttachment::class.java, "pointer"), TOMBSTONE(TombstoneAttachment::class.java, "tombstone"), URI(UriAttachment::class.java, "uri") @@ -32,7 +31,6 @@ object AttachmentCreator : Parcelable.Creator { return when (Subclass.values().first { rawCode == it.code }) { Subclass.DATABASE -> DatabaseAttachment(source) - Subclass.MMS_NOTIFICATION -> MmsNotificationAttachment(source) Subclass.POINTER -> PointerAttachment(source) Subclass.TOMBSTONE -> TombstoneAttachment(source) Subclass.URI -> UriAttachment(source) diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/AttachmentId.java b/app/src/main/java/org/thoughtcrime/securesms/attachments/AttachmentId.java deleted file mode 100644 index d8d5e1057e..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/AttachmentId.java +++ /dev/null @@ -1,89 +0,0 @@ -package org.thoughtcrime.securesms.attachments; - -import android.os.Parcel; -import android.os.Parcelable; - -import androidx.annotation.NonNull; - -import com.fasterxml.jackson.annotation.JsonProperty; - -import org.thoughtcrime.securesms.util.Util; - -public class AttachmentId implements Parcelable { - - @JsonProperty - private final long rowId; - - @JsonProperty - private final long uniqueId; - - public AttachmentId(@JsonProperty("rowId") long rowId, @JsonProperty("uniqueId") long uniqueId) { - this.rowId = rowId; - this.uniqueId = uniqueId; - } - - private AttachmentId(Parcel in) { - this.rowId = in.readLong(); - this.uniqueId = in.readLong(); - } - - public long getRowId() { - return rowId; - } - - public long getUniqueId() { - return uniqueId; - } - - public String[] toStrings() { - return new String[] {String.valueOf(rowId), String.valueOf(uniqueId)}; - } - - public @NonNull String toString() { - return "AttachmentId::(" + rowId + ", " + uniqueId + ")"; - } - - public boolean isValid() { - return rowId >= 0 && uniqueId >= 0; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - AttachmentId attachmentId = (AttachmentId)o; - - if (rowId != attachmentId.rowId) return false; - return uniqueId == attachmentId.uniqueId; - } - - @Override - public int hashCode() { - return Util.hashCode(rowId, uniqueId); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeLong(rowId); - dest.writeLong(uniqueId); - } - - public static final Creator CREATOR = new Creator() { - @Override - public AttachmentId createFromParcel(Parcel in) { - return new AttachmentId(in); - } - - @Override - public AttachmentId[] newArray(int size) { - return new AttachmentId[size]; - } - }; - -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/AttachmentId.kt b/app/src/main/java/org/thoughtcrime/securesms/attachments/AttachmentId.kt new file mode 100644 index 0000000000..981ba5d880 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/AttachmentId.kt @@ -0,0 +1,20 @@ +package org.thoughtcrime.securesms.attachments + +import android.os.Parcelable +import com.fasterxml.jackson.annotation.JsonProperty +import kotlinx.parcelize.Parcelize + +@Parcelize +data class AttachmentId( + @JsonProperty("rowId") + @JvmField + val id: Long +) : Parcelable { + + val isValid: Boolean + get() = id >= 0 + + override fun toString(): String { + return "AttachmentId::$id" + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachment.java b/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachment.java deleted file mode 100644 index c63b92098d..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachment.java +++ /dev/null @@ -1,142 +0,0 @@ -package org.thoughtcrime.securesms.attachments; - -import android.net.Uri; -import android.os.Parcel; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.os.ParcelCompat; - -import org.thoughtcrime.securesms.audio.AudioHash; -import org.thoughtcrime.securesms.blurhash.BlurHash; -import org.thoughtcrime.securesms.database.AttachmentTable.TransformProperties; -import org.thoughtcrime.securesms.mms.PartAuthority; -import org.thoughtcrime.securesms.stickers.StickerLocator; -import org.thoughtcrime.securesms.util.FeatureFlags; -import org.thoughtcrime.securesms.util.ParcelUtil; - -import java.util.Comparator; - -public class DatabaseAttachment extends Attachment { - - private final AttachmentId attachmentId; - private final long mmsId; - private final boolean hasData; - private final boolean hasThumbnail; - private final int displayOrder; - - public DatabaseAttachment(AttachmentId attachmentId, - long mmsId, - boolean hasData, - boolean hasThumbnail, - String contentType, - int transferProgress, - long size, - String fileName, - int cdnNumber, - String location, - String key, - String relay, - byte[] digest, - byte[] incrementalDigest, - int incrementalMacChunkSize, - String fastPreflightId, - boolean voiceNote, - boolean borderless, - boolean videoGif, - int width, - int height, - boolean quote, - @Nullable String caption, - @Nullable StickerLocator stickerLocator, - @Nullable BlurHash blurHash, - @Nullable AudioHash audioHash, - @Nullable TransformProperties transformProperties, - int displayOrder, - long uploadTimestamp) - { - super(contentType, transferProgress, size, fileName, cdnNumber, location, key, relay, digest, incrementalDigest, fastPreflightId, voiceNote, borderless, videoGif, width, height, incrementalMacChunkSize, quote, uploadTimestamp, caption, stickerLocator, blurHash, audioHash, transformProperties); - this.attachmentId = attachmentId; - this.hasData = hasData; - this.hasThumbnail = hasThumbnail; - this.mmsId = mmsId; - this.displayOrder = displayOrder; - } - - protected DatabaseAttachment(Parcel in) { - super(in); - this.attachmentId = ParcelCompat.readParcelable(in, AttachmentId.class.getClassLoader(), AttachmentId.class); - this.hasData = ParcelUtil.readBoolean(in); - this.hasThumbnail = ParcelUtil.readBoolean(in); - this.mmsId = in.readLong(); - this.displayOrder = in.readInt(); - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - super.writeToParcel(dest, flags); - dest.writeParcelable(attachmentId, 0); - ParcelUtil.writeBoolean(dest, hasData); - ParcelUtil.writeBoolean(dest, hasThumbnail); - dest.writeLong(mmsId); - dest.writeInt(displayOrder); - } - - @Override - @Nullable - public Uri getUri() { - if (hasData || (FeatureFlags.instantVideoPlayback() && getIncrementalDigest() != null)) { - return PartAuthority.getAttachmentDataUri(attachmentId); - } else { - return null; - } - } - - @Override - public @Nullable Uri getPublicUri() { - if (hasData) { - return PartAuthority.getAttachmentPublicUri(getUri()); - } else { - return null; - } - } - - public AttachmentId getAttachmentId() { - return attachmentId; - } - - public int getDisplayOrder() { - return displayOrder; - } - - @Override - public boolean equals(Object other) { - return other != null && - other instanceof DatabaseAttachment && - ((DatabaseAttachment) other).attachmentId.equals(this.attachmentId); - } - - @Override - public int hashCode() { - return attachmentId.hashCode(); - } - - public long getMmsId() { - return mmsId; - } - - public boolean hasData() { - return hasData; - } - - public boolean hasThumbnail() { - return hasThumbnail; - } - - public static class DisplayOrderComparator implements Comparator { - @Override - public int compare(DatabaseAttachment lhs, DatabaseAttachment rhs) { - return Integer.compare(lhs.getDisplayOrder(), rhs.getDisplayOrder()); - } - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachment.kt b/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachment.kt new file mode 100644 index 0000000000..fa1570a529 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachment.kt @@ -0,0 +1,133 @@ +package org.thoughtcrime.securesms.attachments + +import android.net.Uri +import android.os.Parcel +import androidx.core.os.ParcelCompat +import org.thoughtcrime.securesms.audio.AudioHash +import org.thoughtcrime.securesms.blurhash.BlurHash +import org.thoughtcrime.securesms.database.AttachmentTable.TransformProperties +import org.thoughtcrime.securesms.mms.PartAuthority +import org.thoughtcrime.securesms.stickers.StickerLocator +import org.thoughtcrime.securesms.util.FeatureFlags +import org.thoughtcrime.securesms.util.ParcelUtil + +class DatabaseAttachment : Attachment { + + @JvmField + val attachmentId: AttachmentId + + @JvmField + val mmsId: Long + + @JvmField + val hasData: Boolean + + private val hasThumbnail: Boolean + val displayOrder: Int + + constructor( + attachmentId: AttachmentId, + mmsId: Long, + hasData: Boolean, + hasThumbnail: Boolean, + contentType: String?, + transferProgress: Int, + size: Long, + fileName: String?, + cdnNumber: Int, + location: String?, + key: String?, + digest: ByteArray?, + incrementalDigest: ByteArray?, + incrementalMacChunkSize: Int, + fastPreflightId: String?, + voiceNote: Boolean, + borderless: Boolean, + videoGif: Boolean, + width: Int, + height: Int, + quote: Boolean, + caption: String?, + stickerLocator: StickerLocator?, + blurHash: BlurHash?, + audioHash: AudioHash?, + transformProperties: TransformProperties?, + displayOrder: Int, + uploadTimestamp: Long + ) : super( + contentType = contentType!!, + transferState = transferProgress, + size = size, + fileName = fileName, + cdnNumber = cdnNumber, + remoteLocation = location, + remoteKey = key, + remoteDigest = digest, + incrementalDigest = incrementalDigest, + fastPreflightId = fastPreflightId, + voiceNote = voiceNote, + borderless = borderless, + videoGif = videoGif, width = width, + height = height, + incrementalMacChunkSize = incrementalMacChunkSize, + quote = quote, + uploadTimestamp = uploadTimestamp, + caption = caption, + stickerLocator = stickerLocator, + blurHash = blurHash, + audioHash = audioHash, + transformProperties = transformProperties + ) { + this.attachmentId = attachmentId + this.mmsId = mmsId + this.hasData = hasData + this.hasThumbnail = hasThumbnail + this.displayOrder = displayOrder + } + + constructor(parcel: Parcel) : super(parcel) { + attachmentId = ParcelCompat.readParcelable(parcel, AttachmentId::class.java.classLoader, AttachmentId::class.java)!! + hasData = ParcelUtil.readBoolean(parcel) + hasThumbnail = ParcelUtil.readBoolean(parcel) + mmsId = parcel.readLong() + displayOrder = parcel.readInt() + } + + override fun writeToParcel(dest: Parcel, flags: Int) { + super.writeToParcel(dest, flags) + dest.writeParcelable(attachmentId, 0) + ParcelUtil.writeBoolean(dest, hasData) + ParcelUtil.writeBoolean(dest, hasThumbnail) + dest.writeLong(mmsId) + dest.writeInt(displayOrder) + } + + override val uri: Uri? + get() = if (hasData || FeatureFlags.instantVideoPlayback() && getIncrementalDigest() != null) { + PartAuthority.getAttachmentDataUri(attachmentId) + } else { + null + } + + override val publicUri: Uri? + get() = if (hasData) { + PartAuthority.getAttachmentPublicUri(uri) + } else { + null + } + + override fun equals(other: Any?): Boolean { + return other != null && + other is DatabaseAttachment && other.attachmentId == attachmentId + } + + override fun hashCode(): Int { + return attachmentId.hashCode() + } + + class DisplayOrderComparator : Comparator { + override fun compare(lhs: DatabaseAttachment, rhs: DatabaseAttachment): Int { + return lhs.displayOrder.compareTo(rhs.displayOrder) + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/MmsNotificationAttachment.java b/app/src/main/java/org/thoughtcrime/securesms/attachments/MmsNotificationAttachment.java deleted file mode 100644 index 9bc99d5c40..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/MmsNotificationAttachment.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.thoughtcrime.securesms.attachments; - - -import android.net.Uri; -import android.os.Parcel; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import org.thoughtcrime.securesms.database.AttachmentTable; -import org.thoughtcrime.securesms.database.MessageTable; - -public class MmsNotificationAttachment extends Attachment { - - public MmsNotificationAttachment(int status, long size) { - super("application/mms", getTransferStateFromStatus(status), size, null, 0, null, null, null, null, null, null, false, false, false, 0, 0, 0, false, 0, null, null, null, null, null); - } - - protected MmsNotificationAttachment(Parcel in) { - super(in); - } - - @Nullable - @Override - public Uri getUri() { - return null; - } - - @Override - public @Nullable Uri getPublicUri() { - return null; - } - - private static int getTransferStateFromStatus(int status) { - if (status == MessageTable.MmsStatus.DOWNLOAD_INITIALIZED || - status == MessageTable.MmsStatus.DOWNLOAD_NO_CONNECTIVITY) - { - return AttachmentTable.TRANSFER_PROGRESS_PENDING; - } else if (status == MessageTable.MmsStatus.DOWNLOAD_CONNECTING) { - return AttachmentTable.TRANSFER_PROGRESS_STARTED; - } else { - return AttachmentTable.TRANSFER_PROGRESS_FAILED; - } - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/PointerAttachment.java b/app/src/main/java/org/thoughtcrime/securesms/attachments/PointerAttachment.java deleted file mode 100644 index a4265eed34..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/PointerAttachment.java +++ /dev/null @@ -1,194 +0,0 @@ -package org.thoughtcrime.securesms.attachments; - -import android.net.Uri; -import android.os.Parcel; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import org.thoughtcrime.securesms.blurhash.BlurHash; -import org.thoughtcrime.securesms.database.AttachmentTable; -import org.thoughtcrime.securesms.stickers.StickerLocator; -import org.signal.core.util.Base64; -import org.whispersystems.signalservice.api.InvalidMessageStructureException; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; -import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; -import org.whispersystems.signalservice.api.util.AttachmentPointerUtil; -import org.whispersystems.signalservice.internal.push.DataMessage; - -import java.util.LinkedList; -import java.util.List; -import java.util.Optional; - -public class PointerAttachment extends Attachment { - - private PointerAttachment(@NonNull String contentType, - int transferState, - long size, - @Nullable String fileName, - int cdnNumber, - @NonNull String location, - @Nullable String key, - @Nullable String relay, - @Nullable byte[] digest, - @Nullable byte[] incrementalDigest, - int incrementalMacChunkSize, - @Nullable String fastPreflightId, - boolean voiceNote, - boolean borderless, - boolean videoGif, - int width, - int height, - long uploadTimestamp, - @Nullable String caption, - @Nullable StickerLocator stickerLocator, - @Nullable BlurHash blurHash) - { - super(contentType, transferState, size, fileName, cdnNumber, location, key, relay, digest, incrementalDigest, fastPreflightId, voiceNote, borderless, videoGif, width, height, incrementalMacChunkSize, false, uploadTimestamp, caption, stickerLocator, blurHash, null, null); - } - - protected PointerAttachment(Parcel in) { - super(in); - } - - @Nullable - @Override - public Uri getUri() { - return null; - } - - @Override - public @Nullable Uri getPublicUri() { - return null; - } - - public static List forPointers(Optional> pointers) { - List results = new LinkedList<>(); - - if (pointers.isPresent()) { - for (SignalServiceAttachment pointer : pointers.get()) { - Optional result = forPointer(Optional.of(pointer)); - - if (result.isPresent()) { - results.add(result.get()); - } - } - } - - return results; - } - - public static List forPointers(@Nullable List pointers) { - List results = new LinkedList<>(); - - if (pointers != null) { - for (SignalServiceDataMessage.Quote.QuotedAttachment pointer : pointers) { - Optional result = forPointer(pointer); - - if (result.isPresent()) { - results.add(result.get()); - } - } - } - - return results; - } - - public static Optional forPointer(Optional pointer) { - return forPointer(pointer, null, null); - } - - public static Optional forPointer(Optional pointer, @Nullable StickerLocator stickerLocator) { - return forPointer(pointer, stickerLocator, null); - } - - public static Optional forPointer(Optional pointer, @Nullable StickerLocator stickerLocator, @Nullable String fastPreflightId) { - if (!pointer.isPresent() || !pointer.get().isPointer()) return Optional.empty(); - - String encodedKey = null; - - if (pointer.get().asPointer().getKey() != null) { - encodedKey = Base64.encodeWithPadding(pointer.get().asPointer().getKey()); - } - - return Optional.of(new PointerAttachment(pointer.get().getContentType(), - AttachmentTable.TRANSFER_PROGRESS_PENDING, - pointer.get().asPointer().getSize().orElse(0), - pointer.get().asPointer().getFileName().orElse(null), - pointer.get().asPointer().getCdnNumber(), - pointer.get().asPointer().getRemoteId().toString(), - encodedKey, - null, - pointer.get().asPointer().getDigest().orElse(null), - pointer.get().asPointer().getIncrementalDigest().orElse(null), - pointer.get().asPointer().getIncrementalMacChunkSize(), - fastPreflightId, - pointer.get().asPointer().getVoiceNote(), - pointer.get().asPointer().isBorderless(), - pointer.get().asPointer().isGif(), - pointer.get().asPointer().getWidth(), - pointer.get().asPointer().getHeight(), - pointer.get().asPointer().getUploadTimestamp(), - pointer.get().asPointer().getCaption().orElse(null), - stickerLocator, - BlurHash.parseOrNull(pointer.get().asPointer().getBlurHash().orElse(null)))); - - } - - public static Optional forPointer(SignalServiceDataMessage.Quote.QuotedAttachment pointer) { - SignalServiceAttachment thumbnail = pointer.getThumbnail(); - - return Optional.of(new PointerAttachment(pointer.getContentType(), - AttachmentTable.TRANSFER_PROGRESS_PENDING, - thumbnail != null ? thumbnail.asPointer().getSize().orElse(0) : 0, - pointer.getFileName(), - thumbnail != null ? thumbnail.asPointer().getCdnNumber() : 0, - thumbnail != null ? thumbnail.asPointer().getRemoteId().toString() : "0", - thumbnail != null && thumbnail.asPointer().getKey() != null ? Base64.encodeWithPadding(thumbnail.asPointer().getKey()) : null, - null, - thumbnail != null ? thumbnail.asPointer().getDigest().orElse(null) : null, - thumbnail != null ? thumbnail.asPointer().getIncrementalDigest().orElse(null) : null, - thumbnail != null ? thumbnail.asPointer().getIncrementalMacChunkSize() : 0, - null, - false, - false, - false, - thumbnail != null ? thumbnail.asPointer().getWidth() : 0, - thumbnail != null ? thumbnail.asPointer().getHeight() : 0, - thumbnail != null ? thumbnail.asPointer().getUploadTimestamp() : 0, - thumbnail != null ? thumbnail.asPointer().getCaption().orElse(null) : null, - null, - null)); - } - - public static Optional forPointer(DataMessage.Quote.QuotedAttachment quotedAttachment) { - SignalServiceAttachment thumbnail; - try { - thumbnail = quotedAttachment.thumbnail != null ? AttachmentPointerUtil.createSignalAttachmentPointer(quotedAttachment.thumbnail) : null; - } catch (InvalidMessageStructureException e) { - return Optional.empty(); - } - - return Optional.of(new PointerAttachment(quotedAttachment.contentType, - AttachmentTable.TRANSFER_PROGRESS_PENDING, - thumbnail != null ? thumbnail.asPointer().getSize().orElse(0) : 0, - quotedAttachment.fileName, - thumbnail != null ? thumbnail.asPointer().getCdnNumber() : 0, - thumbnail != null ? thumbnail.asPointer().getRemoteId().toString() : "0", - thumbnail != null && thumbnail.asPointer().getKey() != null ? Base64.encodeWithPadding(thumbnail.asPointer().getKey()) : null, - null, - thumbnail != null ? thumbnail.asPointer().getDigest().orElse(null) : null, - thumbnail != null ? thumbnail.asPointer().getIncrementalDigest().orElse(null) : null, - thumbnail != null ? thumbnail.asPointer().getIncrementalMacChunkSize() : 0, - null, - false, - false, - false, - thumbnail != null ? thumbnail.asPointer().getWidth() : 0, - thumbnail != null ? thumbnail.asPointer().getHeight() : 0, - thumbnail != null ? thumbnail.asPointer().getUploadTimestamp() : 0, - thumbnail != null ? thumbnail.asPointer().getCaption().orElse(null) : null, - null, - null)); - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/PointerAttachment.kt b/app/src/main/java/org/thoughtcrime/securesms/attachments/PointerAttachment.kt new file mode 100644 index 0000000000..92d533c84f --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/PointerAttachment.kt @@ -0,0 +1,187 @@ +package org.thoughtcrime.securesms.attachments + +import android.net.Uri +import android.os.Parcel +import org.signal.core.util.Base64.encodeWithPadding +import org.thoughtcrime.securesms.blurhash.BlurHash +import org.thoughtcrime.securesms.database.AttachmentTable +import org.thoughtcrime.securesms.stickers.StickerLocator +import org.whispersystems.signalservice.api.InvalidMessageStructureException +import org.whispersystems.signalservice.api.messages.SignalServiceAttachment +import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage +import org.whispersystems.signalservice.api.util.AttachmentPointerUtil +import org.whispersystems.signalservice.internal.push.DataMessage +import java.util.Optional + +class PointerAttachment : Attachment { + private constructor( + contentType: String, + transferState: Int, + size: Long, + fileName: String?, + cdnNumber: Int, + location: String, + key: String?, + digest: ByteArray?, + incrementalDigest: ByteArray?, + incrementalMacChunkSize: Int, + fastPreflightId: String?, + voiceNote: Boolean, + borderless: Boolean, + videoGif: Boolean, + width: Int, + height: Int, + uploadTimestamp: Long, + caption: String?, + stickerLocator: StickerLocator?, + blurHash: BlurHash? + ) : super( + contentType = contentType, + transferState = transferState, + size = size, + fileName = fileName, + cdnNumber = cdnNumber, + remoteLocation = location, + remoteKey = key, + remoteDigest = digest, + incrementalDigest = incrementalDigest, + fastPreflightId = fastPreflightId, + voiceNote = voiceNote, + borderless = borderless, + videoGif = videoGif, + width = width, + height = height, + incrementalMacChunkSize = incrementalMacChunkSize, + quote = false, + uploadTimestamp = uploadTimestamp, + caption = caption, + stickerLocator = stickerLocator, + blurHash = blurHash, + audioHash = null, + transformProperties = null + ) + + constructor(parcel: Parcel) : super(parcel) + + override val uri: Uri? = null + override val publicUri: Uri? = null + + companion object { + @JvmStatic + fun forPointers(pointers: Optional>): List { + if (!pointers.isPresent) { + return emptyList() + } + + return pointers.get() + .map { forPointer(Optional.ofNullable(it)) } + .filter { it.isPresent } + .map { it.get() } + } + + @JvmStatic + @JvmOverloads + fun forPointer(pointer: Optional, stickerLocator: StickerLocator? = null, fastPreflightId: String? = null): Optional { + if (!pointer.isPresent || !pointer.get().isPointer) { + return Optional.empty() + } + + val encodedKey: String? = if (pointer.get().asPointer().key != null) { + encodeWithPadding(pointer.get().asPointer().key) + } else { + null + } + + return Optional.of( + PointerAttachment( + contentType = pointer.get().contentType, + transferState = AttachmentTable.TRANSFER_PROGRESS_PENDING, + size = pointer.get().asPointer().size.orElse(0).toLong(), + fileName = pointer.get().asPointer().fileName.orElse(null), + cdnNumber = pointer.get().asPointer().cdnNumber, + location = pointer.get().asPointer().remoteId.toString(), + key = encodedKey, + digest = pointer.get().asPointer().digest.orElse(null), + incrementalDigest = pointer.get().asPointer().incrementalDigest.orElse(null), + incrementalMacChunkSize = pointer.get().asPointer().incrementalMacChunkSize, + fastPreflightId = fastPreflightId, + voiceNote = pointer.get().asPointer().voiceNote, + borderless = pointer.get().asPointer().isBorderless, + videoGif = pointer.get().asPointer().isGif, + width = pointer.get().asPointer().width, + height = pointer.get().asPointer().height, + uploadTimestamp = pointer.get().asPointer().uploadTimestamp, + caption = pointer.get().asPointer().caption.orElse(null), + stickerLocator = stickerLocator, + blurHash = BlurHash.parseOrNull(pointer.get().asPointer().blurHash.orElse(null)) + ) + ) + } + + fun forPointer(pointer: SignalServiceDataMessage.Quote.QuotedAttachment): Optional { + val thumbnail = pointer.thumbnail + + return Optional.of( + PointerAttachment( + contentType = pointer.contentType, + transferState = AttachmentTable.TRANSFER_PROGRESS_PENDING, + size = (if (thumbnail != null) thumbnail.asPointer().size.orElse(0) else 0).toLong(), + fileName = pointer.fileName, + cdnNumber = thumbnail?.asPointer()?.cdnNumber ?: 0, + location = thumbnail?.asPointer()?.remoteId?.toString() ?: "0", + key = if (thumbnail != null && thumbnail.asPointer().key != null) encodeWithPadding(thumbnail.asPointer().key) else null, + digest = thumbnail?.asPointer()?.digest?.orElse(null), + incrementalDigest = thumbnail?.asPointer()?.incrementalDigest?.orElse(null), + incrementalMacChunkSize = thumbnail?.asPointer()?.incrementalMacChunkSize ?: 0, + fastPreflightId = null, + voiceNote = false, + borderless = false, + videoGif = false, + width = thumbnail?.asPointer()?.width ?: 0, + height = thumbnail?.asPointer()?.height ?: 0, + uploadTimestamp = thumbnail?.asPointer()?.uploadTimestamp ?: 0, + caption = thumbnail?.asPointer()?.caption?.orElse(null), + stickerLocator = null, + blurHash = null + ) + ) + } + + fun forPointer(quotedAttachment: DataMessage.Quote.QuotedAttachment): Optional { + val thumbnail: SignalServiceAttachment? = try { + if (quotedAttachment.thumbnail != null) { + AttachmentPointerUtil.createSignalAttachmentPointer(quotedAttachment.thumbnail) + } else { + null + } + } catch (e: InvalidMessageStructureException) { + return Optional.empty() + } + + return Optional.of( + PointerAttachment( + contentType = quotedAttachment.contentType!!, + transferState = AttachmentTable.TRANSFER_PROGRESS_PENDING, + size = (if (thumbnail != null) thumbnail.asPointer().size.orElse(0) else 0).toLong(), + fileName = quotedAttachment.fileName, + cdnNumber = thumbnail?.asPointer()?.cdnNumber ?: 0, + location = thumbnail?.asPointer()?.remoteId?.toString() ?: "0", + key = if (thumbnail != null && thumbnail.asPointer().key != null) encodeWithPadding(thumbnail.asPointer().key) else null, + digest = thumbnail?.asPointer()?.digest?.orElse(null), + incrementalDigest = thumbnail?.asPointer()?.incrementalDigest?.orElse(null), + incrementalMacChunkSize = thumbnail?.asPointer()?.incrementalMacChunkSize ?: 0, + fastPreflightId = null, + voiceNote = false, + borderless = false, + videoGif = false, + width = thumbnail?.asPointer()?.width ?: 0, + height = thumbnail?.asPointer()?.height ?: 0, + uploadTimestamp = thumbnail?.asPointer()?.uploadTimestamp ?: 0, + caption = thumbnail?.asPointer()?.caption?.orElse(null), + stickerLocator = null, + blurHash = null + ) + ) + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/TombstoneAttachment.java b/app/src/main/java/org/thoughtcrime/securesms/attachments/TombstoneAttachment.java deleted file mode 100644 index 43d89ae7cf..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/TombstoneAttachment.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.thoughtcrime.securesms.attachments; - -import android.net.Uri; -import android.os.Parcel; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import org.thoughtcrime.securesms.database.AttachmentTable; - -/** - * An attachment that represents where an attachment used to be. Useful when you need to know that - * a message had an attachment and some metadata about it (like the contentType), even though the - * underlying media no longer exists. An example usecase would be view-once messages, so that we can - * quote them and know their contentType even though the media has been deleted. - */ -public class TombstoneAttachment extends Attachment { - - public TombstoneAttachment(@NonNull String contentType, boolean quote) { - super(contentType, AttachmentTable.TRANSFER_PROGRESS_DONE, 0, null, 0, null, null, null, null, null, null, false, false, false, 0, 0, 0, quote, 0, null, null, null, null, null); - } - - protected TombstoneAttachment(Parcel in) { - super(in); - } - - @Override - public @Nullable Uri getUri() { - return null; - } - - @Override - public @Nullable Uri getPublicUri() { - return null; - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/TombstoneAttachment.kt b/app/src/main/java/org/thoughtcrime/securesms/attachments/TombstoneAttachment.kt new file mode 100644 index 0000000000..ef7a371ff0 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/TombstoneAttachment.kt @@ -0,0 +1,44 @@ +package org.thoughtcrime.securesms.attachments + +import android.net.Uri +import android.os.Parcel +import org.thoughtcrime.securesms.database.AttachmentTable + +/** + * An attachment that represents where an attachment used to be. Useful when you need to know that + * a message had an attachment and some metadata about it (like the contentType), even though the + * underlying media no longer exists. An example usecase would be view-once messages, so that we can + * quote them and know their contentType even though the media has been deleted. + */ +class TombstoneAttachment : Attachment { + constructor(contentType: String, quote: Boolean) : super( + contentType = contentType, + quote = quote, + transferState = AttachmentTable.TRANSFER_PROGRESS_DONE, + size = 0, + fileName = null, + cdnNumber = 0, + remoteLocation = null, + remoteKey = null, + remoteDigest = null, + incrementalDigest = null, + fastPreflightId = null, + voiceNote = false, + borderless = false, + videoGif = false, + width = 0, + height = 0, + incrementalMacChunkSize = 0, + uploadTimestamp = 0, + caption = null, + stickerLocator = null, + blurHash = null, + audioHash = null, + transformProperties = null + ) + + constructor(parcel: Parcel) : super(parcel) + + override val uri: Uri? = null + override val publicUri: Uri? = null +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/UriAttachment.java b/app/src/main/java/org/thoughtcrime/securesms/attachments/UriAttachment.java deleted file mode 100644 index eb0c7bcd26..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/UriAttachment.java +++ /dev/null @@ -1,92 +0,0 @@ -package org.thoughtcrime.securesms.attachments; - -import android.net.Uri; -import android.os.Parcel; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.os.ParcelCompat; - -import org.thoughtcrime.securesms.audio.AudioHash; -import org.thoughtcrime.securesms.blurhash.BlurHash; -import org.thoughtcrime.securesms.database.AttachmentTable.TransformProperties; -import org.thoughtcrime.securesms.stickers.StickerLocator; - -import java.util.Objects; - -public class UriAttachment extends Attachment { - - private final @NonNull Uri dataUri; - - public UriAttachment(@NonNull Uri uri, - @NonNull String contentType, - int transferState, - long size, - @Nullable String fileName, - boolean voiceNote, - boolean borderless, - boolean videoGif, - boolean quote, - @Nullable String caption, - @Nullable StickerLocator stickerLocator, - @Nullable BlurHash blurHash, - @Nullable AudioHash audioHash, - @Nullable TransformProperties transformProperties) - { - this(uri, contentType, transferState, size, 0, 0, fileName, null, voiceNote, borderless, videoGif, quote, caption, stickerLocator, blurHash, audioHash, transformProperties); - } - - public UriAttachment(@NonNull Uri dataUri, - @NonNull String contentType, - int transferState, - long size, - int width, - int height, - @Nullable String fileName, - @Nullable String fastPreflightId, - boolean voiceNote, - boolean borderless, - boolean videoGif, - boolean quote, - @Nullable String caption, - @Nullable StickerLocator stickerLocator, - @Nullable BlurHash blurHash, - @Nullable AudioHash audioHash, - @Nullable TransformProperties transformProperties) - { - super(contentType, transferState, size, fileName, 0, null, null, null, null, null, fastPreflightId, voiceNote, borderless, videoGif, width, height, 0, quote, 0, caption, stickerLocator, blurHash, audioHash, transformProperties); - this.dataUri = Objects.requireNonNull(dataUri); - } - - protected UriAttachment(Parcel in) { - super(in); - this.dataUri = Objects.requireNonNull(ParcelCompat.readParcelable(in, Uri.class.getClassLoader(), Uri.class)); - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - super.writeToParcel(dest, flags); - dest.writeParcelable(dataUri, 0); - } - - @Override - @NonNull - public Uri getUri() { - return dataUri; - } - - @Override - public @Nullable Uri getPublicUri() { - return null; - } - - @Override - public boolean equals(Object other) { - return other != null && other instanceof UriAttachment && ((UriAttachment) other).dataUri.equals(this.dataUri); - } - - @Override - public int hashCode() { - return dataUri.hashCode(); - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/UriAttachment.kt b/app/src/main/java/org/thoughtcrime/securesms/attachments/UriAttachment.kt new file mode 100644 index 0000000000..f158bd367a --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/UriAttachment.kt @@ -0,0 +1,114 @@ +package org.thoughtcrime.securesms.attachments + +import android.net.Uri +import android.os.Parcel +import androidx.core.os.ParcelCompat +import org.thoughtcrime.securesms.audio.AudioHash +import org.thoughtcrime.securesms.blurhash.BlurHash +import org.thoughtcrime.securesms.database.AttachmentTable.TransformProperties +import org.thoughtcrime.securesms.stickers.StickerLocator +import java.util.Objects + +class UriAttachment : Attachment { + + constructor( + uri: Uri, + contentType: String, + transferState: Int, + size: Long, + fileName: String?, + voiceNote: Boolean, + borderless: Boolean, + videoGif: Boolean, + quote: Boolean, + caption: String?, + stickerLocator: StickerLocator?, + blurHash: BlurHash?, + audioHash: AudioHash?, + transformProperties: TransformProperties? + ) : this( + dataUri = uri, + contentType = contentType, + transferState = transferState, + size = size, + width = 0, + height = 0, + fileName = fileName, + fastPreflightId = null, + voiceNote = voiceNote, + borderless = borderless, + videoGif = videoGif, + quote = quote, + caption = caption, + stickerLocator = stickerLocator, + blurHash = blurHash, + audioHash = audioHash, + transformProperties = transformProperties + ) + + constructor( + dataUri: Uri, + contentType: String, + transferState: Int, + size: Long, + width: Int, + height: Int, + fileName: String?, + fastPreflightId: String?, + voiceNote: Boolean, + borderless: Boolean, + videoGif: Boolean, + quote: Boolean, + caption: String?, + stickerLocator: StickerLocator?, + blurHash: BlurHash?, + audioHash: AudioHash?, + transformProperties: TransformProperties? + ) : super( + contentType = contentType, + transferState = transferState, + size = size, + fileName = fileName, + cdnNumber = 0, + remoteLocation = null, + remoteKey = null, + remoteDigest = null, + incrementalDigest = null, + fastPreflightId = fastPreflightId, + voiceNote = voiceNote, + borderless = borderless, + videoGif = videoGif, + width = width, + height = height, + incrementalMacChunkSize = 0, + quote = quote, + uploadTimestamp = 0, + caption = caption, + stickerLocator = stickerLocator, + blurHash = blurHash, + audioHash = audioHash, + transformProperties = transformProperties + ) { + uri = Objects.requireNonNull(dataUri) + } + + constructor(parcel: Parcel) : super(parcel) { + uri = ParcelCompat.readParcelable(parcel, Uri::class.java.classLoader, Uri::class.java)!! + } + + override val uri: Uri + override val publicUri: Uri? = null + + override fun writeToParcel(dest: Parcel, flags: Int) { + super.writeToParcel(dest, flags) + dest.writeParcelable(uri, 0) + } + + override fun equals(other: Any?): Boolean { + return other != null && other is UriAttachment && other.uri == uri + } + + override fun hashCode(): Int { + return uri.hashCode() + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/audio/AudioRecorder.java b/app/src/main/java/org/thoughtcrime/securesms/audio/AudioRecorder.java index 790176c8bf..f5f0f803aa 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/audio/AudioRecorder.java +++ b/app/src/main/java/org/thoughtcrime/securesms/audio/AudioRecorder.java @@ -113,6 +113,23 @@ private void startRecordingInternal(boolean useMediaRecorderWrapper, SingleSubje }); } + public void discardRecording() { + Log.i(TAG, "cancelRecording()"); + executor.execute(() -> { + if (recorder == null) { + Log.e(TAG, "MediaRecorder was never initialized successfully!"); + return; + } + audioFocusManager.abandonAudioFocus(); + recorder.stop(); + recordingUriFuture.cancel(true); + + recordingSubject = null; + recorder = null; + recordingUriFuture = null; + }); + } + public void stopRecording() { Log.i(TAG, "stopRecording()"); diff --git a/app/src/main/java/org/thoughtcrime/securesms/avatar/AvatarImage.kt b/app/src/main/java/org/thoughtcrime/securesms/avatar/AvatarImage.kt new file mode 100644 index 0000000000..6a78a607d4 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/avatar/AvatarImage.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.avatar + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalInspectionMode +import androidx.compose.ui.viewinterop.AndroidView +import org.thoughtcrime.securesms.components.AvatarImageView +import org.thoughtcrime.securesms.recipients.Recipient + +@Composable +fun AvatarImage( + recipient: Recipient, + modifier: Modifier = Modifier +) { + if (LocalInspectionMode.current) { + Spacer( + modifier = modifier + .background(color = Color.Red, shape = CircleShape) + ) + } else { + AndroidView( + factory = ::AvatarImageView, + modifier = modifier.background(color = Color.Transparent, shape = CircleShape) + ) { + it.setAvatarUsingProfile(recipient) + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/BackupCountQueries.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/BackupCountQueries.kt index 8bbba04bab..4ae57007e3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/BackupCountQueries.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/BackupCountQueries.kt @@ -21,7 +21,7 @@ object BackupCountQueries { @get:JvmStatic val attachmentCount: String = """ SELECT COUNT(*) FROM ${AttachmentTable.TABLE_NAME} - INNER JOIN ${MessageTable.TABLE_NAME} ON ${AttachmentTable.TABLE_NAME}.${AttachmentTable.MMS_ID} = ${MessageTable.TABLE_NAME}.${MessageTable.ID} + INNER JOIN ${MessageTable.TABLE_NAME} ON ${AttachmentTable.TABLE_NAME}.${AttachmentTable.MESSAGE_ID} = ${MessageTable.TABLE_NAME}.${MessageTable.ID} WHERE ${MessageTable.TABLE_NAME}.${MessageTable.EXPIRES_IN} <= 0 AND ${MessageTable.TABLE_NAME}.${MessageTable.VIEW_ONCE} <= 0 """ } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/BackupFrameOutputStream.java b/app/src/main/java/org/thoughtcrime/securesms/backup/BackupFrameOutputStream.java index 0da664e6e7..0e943104f5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/BackupFrameOutputStream.java +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/BackupFrameOutputStream.java @@ -119,8 +119,7 @@ public void write(@NonNull AttachmentId attachmentId, @NonNull InputStream in, l try { write(outputStream, new BackupFrame.Builder() .attachment(new Attachment.Builder() - .rowId(attachmentId.getRowId()) - .attachmentId(attachmentId.getUniqueId()) + .rowId(attachmentId.id) .length(Util.toIntExact(size)) .build()) .build()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupExporter.java b/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupExporter.java index ba492fe538..c5c704fbfb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupExporter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupExporter.java @@ -43,7 +43,6 @@ import org.thoughtcrime.securesms.database.SignedPreKeyTable; import org.thoughtcrime.securesms.database.StickerTable; import org.thoughtcrime.securesms.database.model.AvatarPickerDatabase; -import org.thoughtcrime.securesms.database.model.MessageId; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.keyvalue.KeyValueDataSet; import org.thoughtcrime.securesms.keyvalue.SignalStore; @@ -64,6 +63,7 @@ import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import okio.ByteString; @@ -76,6 +76,7 @@ public class FullBackupExporter extends FullBackupBase { private static final long TABLE_RECORD_COUNT_MULTIPLIER = 3L; private static final long IDENTITY_KEY_BACKUP_RECORD_COUNT = 2L; private static final long FINAL_MESSAGE_COUNT = 1L; + private static final long EXPIRATION_BACKUP_THRESHOLD = TimeUnit.DAYS.toMillis(1); /** * Tables in list will still have their *schema* exported (so the tables will be created), @@ -158,15 +159,15 @@ private static BackupEvent internalExport(@NonNull Context context, for (String table : tables) { throwIfCanceled(cancellationSignal); if (table.equals(MessageTable.TABLE_NAME)) { - count = exportTable(table, input, outputStream, FullBackupExporter::isNonExpiringMmsMessage, null, count, estimatedCount, cancellationSignal); + count = exportTable(table, input, outputStream, cursor -> isNonExpiringMessage(cursor), null, count, estimatedCount, cancellationSignal); } else if (table.equals(ReactionTable.TABLE_NAME)) { - count = exportTable(table, input, outputStream, cursor -> isForNonExpiringMessage(input, new MessageId(CursorUtil.requireLong(cursor, ReactionTable.MESSAGE_ID))), null, count, estimatedCount, cancellationSignal); + count = exportTable(table, input, outputStream, cursor -> isForNonExpiringMessage(input, CursorUtil.requireLong(cursor, ReactionTable.MESSAGE_ID)), null, count, estimatedCount, cancellationSignal); } else if (table.equals(MentionTable.TABLE_NAME)) { - count = exportTable(table, input, outputStream, cursor -> isForNonExpiringMmsMessage(input, CursorUtil.requireLong(cursor, MentionTable.MESSAGE_ID)), null, count, estimatedCount, cancellationSignal); + count = exportTable(table, input, outputStream, cursor -> isForNonExpiringMessage(input, CursorUtil.requireLong(cursor, MentionTable.MESSAGE_ID)), null, count, estimatedCount, cancellationSignal); } else if (table.equals(GroupReceiptTable.TABLE_NAME)) { - count = exportTable(table, input, outputStream, cursor -> isForNonExpiringMmsMessage(input, cursor.getLong(cursor.getColumnIndexOrThrow(GroupReceiptTable.MMS_ID))), null, count, estimatedCount, cancellationSignal); + count = exportTable(table, input, outputStream, cursor -> isForNonExpiringMessage(input, cursor.getLong(cursor.getColumnIndexOrThrow(GroupReceiptTable.MMS_ID))), null, count, estimatedCount, cancellationSignal); } else if (table.equals(AttachmentTable.TABLE_NAME)) { - count = exportTable(table, input, outputStream, cursor -> isForNonExpiringMmsMessage(input, cursor.getLong(cursor.getColumnIndexOrThrow(AttachmentTable.MMS_ID))), (cursor, innerCount) -> exportAttachment(attachmentSecret, cursor, outputStream, innerCount, estimatedCount), count, estimatedCount, cancellationSignal); + count = exportTable(table, input, outputStream, cursor -> isForNonExpiringMessage(input, cursor.getLong(cursor.getColumnIndexOrThrow(AttachmentTable.MESSAGE_ID))), (cursor, innerCount) -> exportAttachment(attachmentSecret, cursor, outputStream, innerCount, estimatedCount), count, estimatedCount, cancellationSignal); } else if (table.equals(StickerTable.TABLE_NAME)) { count = exportTable(table, input, outputStream, cursor -> true, (cursor, innerCount) -> exportSticker(attachmentSecret, cursor, outputStream, innerCount, estimatedCount), count, estimatedCount, cancellationSignal); } else if (!TABLE_CONTENT_BLOCKLIST.contains(table)) { @@ -443,11 +444,10 @@ private static int exportAttachment(@NonNull AttachmentSecret attachmentSecret, long estimatedCount) throws IOException { - long rowId = cursor.getLong(cursor.getColumnIndexOrThrow(AttachmentTable.ROW_ID)); - long uniqueId = cursor.getLong(cursor.getColumnIndexOrThrow(AttachmentTable.UNIQUE_ID)); - long size = cursor.getLong(cursor.getColumnIndexOrThrow(AttachmentTable.SIZE)); + long rowId = cursor.getLong(cursor.getColumnIndexOrThrow(AttachmentTable.ID)); + long size = cursor.getLong(cursor.getColumnIndexOrThrow(AttachmentTable.DATA_SIZE)); - String data = cursor.getString(cursor.getColumnIndexOrThrow(AttachmentTable.DATA)); + String data = cursor.getString(cursor.getColumnIndexOrThrow(AttachmentTable.DATA_FILE)); byte[] random = cursor.getBlob(cursor.getColumnIndexOrThrow(AttachmentTable.DATA_RANDOM)); if (!TextUtils.isEmpty(data)) { @@ -456,14 +456,14 @@ private static int exportAttachment(@NonNull AttachmentSecret attachmentSecret, if (size <= 0 || fileLength != dbLength) { size = calculateVeryOldStreamLength(attachmentSecret, random, data); - Log.w(TAG, "Needed size calculation! Manual: " + size + " File: " + fileLength + " DB: " + dbLength + " ID: " + new AttachmentId(rowId, uniqueId)); + Log.w(TAG, "Needed size calculation! Manual: " + size + " File: " + fileLength + " DB: " + dbLength + " ID: " + new AttachmentId(rowId)); } } EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, ++count, estimatedCount)); if (!TextUtils.isEmpty(data) && size > 0) { try (InputStream inputStream = openAttachmentStream(attachmentSecret, random, data)) { - outputStream.write(new AttachmentId(rowId, uniqueId), inputStream, size); + outputStream.write(new AttachmentId(rowId), inputStream, size); } catch (FileNotFoundException e) { Log.w(TAG, "Missing attachment", e); } @@ -574,27 +574,25 @@ private static int exportKeyValues(@NonNull BackupFrameOutputStream outputStream return count; } - private static boolean isNonExpiringMmsMessage(@NonNull Cursor cursor) { - return cursor.getLong(cursor.getColumnIndexOrThrow(MessageTable.EXPIRES_IN)) <= 0 && - cursor.getLong(cursor.getColumnIndexOrThrow(MessageTable.VIEW_ONCE)) <= 0; - } + private static boolean isNonExpiringMessage(@NonNull Cursor cursor) { + long expiresIn = CursorUtil.requireLong(cursor, MessageTable.EXPIRES_IN); + boolean viewOnce = CursorUtil.requireBoolean(cursor, MessageTable.VIEW_ONCE); - private static boolean isNonExpiringSmsMessage(@NonNull Cursor cursor) { - return cursor.getLong(cursor.getColumnIndexOrThrow(MessageTable.EXPIRES_IN)) <= 0; - } + if (expiresIn == 0 && !viewOnce) { + return true; + } - private static boolean isForNonExpiringMessage(@NonNull SQLiteDatabase db, @NonNull MessageId messageId) { - return isForNonExpiringMmsMessage(db, messageId.getId()); + return expiresIn > EXPIRATION_BACKUP_THRESHOLD; } - private static boolean isForNonExpiringMmsMessage(@NonNull SQLiteDatabase db, long mmsId) { + private static boolean isForNonExpiringMessage(@NonNull SQLiteDatabase db, long messageId) { String[] columns = new String[] { MessageTable.EXPIRES_IN, MessageTable.VIEW_ONCE }; String where = MessageTable.ID + " = ?"; - String[] args = new String[] { String.valueOf(mmsId) }; + String[] args = SqlUtil.buildArgs(messageId); try (Cursor mmsCursor = db.query(MessageTable.TABLE_NAME, columns, where, args, null, null, null)) { if (mmsCursor != null && mmsCursor.moveToFirst()) { - return isNonExpiringMmsMessage(mmsCursor); + return isNonExpiringMessage(mmsCursor); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupImporter.java b/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupImporter.java index 6f9ee0874e..e37c176587 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupImporter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupImporter.java @@ -198,26 +198,33 @@ private static void processStatement(@NonNull SQLiteDatabase db, SqlStatement st private static void processAttachment(@NonNull Context context, @NonNull AttachmentSecret attachmentSecret, @NonNull SQLiteDatabase db, @NonNull Attachment attachment, BackupRecordInputStream inputStream) throws IOException { - File dataFile = AttachmentTable.newFile(context); - Pair output = ModernEncryptingPartOutputStream.createFor(attachmentSecret, dataFile, false); + File dataFile = AttachmentTable.newFile(context); + Pair output = ModernEncryptingPartOutputStream.createFor(attachmentSecret, dataFile, false); + boolean isLegacyTable = SqlUtil.tableExists(db, "part"); + + String dataFileColumnName = isLegacyTable ? "_data" : AttachmentTable.DATA_FILE; + String dataRandomColumnName = isLegacyTable ? "data_random" : AttachmentTable.DATA_RANDOM; + String idColumnName = isLegacyTable ? "_id" : AttachmentTable.ID; + String tableName = isLegacyTable ? "part" : AttachmentTable.TABLE_NAME; ContentValues contentValues = new ContentValues(); try { inputStream.readAttachmentTo(output.second, attachment.length); - contentValues.put(AttachmentTable.DATA, dataFile.getAbsolutePath()); - contentValues.put(AttachmentTable.DATA_RANDOM, output.first); + contentValues.put(dataFileColumnName, dataFile.getAbsolutePath()); + contentValues.put(dataRandomColumnName, output.first); } catch (BackupRecordInputStream.BadMacException e) { Log.w(TAG, "Bad MAC for attachment " + attachment.attachmentId + "! Can't restore it.", e); dataFile.delete(); - contentValues.put(AttachmentTable.DATA, (String) null); - contentValues.put(AttachmentTable.DATA_RANDOM, (String) null); + contentValues.put(dataFileColumnName, (String) null); + contentValues.put(dataRandomColumnName, (String) null); } - db.update(AttachmentTable.TABLE_NAME, contentValues, - AttachmentTable.ROW_ID + " = ? AND " + AttachmentTable.UNIQUE_ID + " = ?", - new String[] {String.valueOf(attachment.rowId), String.valueOf(attachment.attachmentId)}); + db.update(tableName, + contentValues, + idColumnName + " = ?", + SqlUtil.buildArgs(attachment.rowId)); } private static void processSticker(@NonNull Context context, @NonNull AttachmentSecret attachmentSecret, @NonNull SQLiteDatabase db, @NonNull Sticker sticker, BackupRecordInputStream inputStream) diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt new file mode 100644 index 0000000000..67fdca80f5 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt @@ -0,0 +1,257 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2 + +import org.signal.core.util.EventTimer +import org.signal.core.util.logging.Log +import org.signal.core.util.withinTransaction +import org.signal.libsignal.zkgroup.profiles.ProfileKey +import org.thoughtcrime.securesms.backup.v2.database.ChatItemImportInserter +import org.thoughtcrime.securesms.backup.v2.database.clearAllDataForBackupRestore +import org.thoughtcrime.securesms.backup.v2.processor.AccountDataProcessor +import org.thoughtcrime.securesms.backup.v2.processor.CallLogBackupProcessor +import org.thoughtcrime.securesms.backup.v2.processor.ChatBackupProcessor +import org.thoughtcrime.securesms.backup.v2.processor.ChatItemBackupProcessor +import org.thoughtcrime.securesms.backup.v2.processor.RecipientBackupProcessor +import org.thoughtcrime.securesms.backup.v2.stream.BackupExportWriter +import org.thoughtcrime.securesms.backup.v2.stream.EncryptedBackupReader +import org.thoughtcrime.securesms.backup.v2.stream.EncryptedBackupWriter +import org.thoughtcrime.securesms.backup.v2.stream.PlainTextBackupReader +import org.thoughtcrime.securesms.backup.v2.stream.PlainTextBackupWriter +import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.dependencies.ApplicationDependencies +import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.recipients.RecipientId +import org.whispersystems.signalservice.api.NetworkResult +import org.whispersystems.signalservice.api.archive.ArchiveGetBackupInfoResponse +import org.whispersystems.signalservice.api.archive.ArchiveServiceCredential +import org.whispersystems.signalservice.api.push.ServiceId.ACI +import org.whispersystems.signalservice.api.push.ServiceId.PNI +import java.io.ByteArrayOutputStream +import java.io.InputStream +import kotlin.time.Duration.Companion.milliseconds + +object BackupRepository { + + private val TAG = Log.tag(BackupRepository::class.java) + + fun export(plaintext: Boolean = false): ByteArray { + val eventTimer = EventTimer() + + val outputStream = ByteArrayOutputStream() + val writer: BackupExportWriter = if (plaintext) { + PlainTextBackupWriter(outputStream) + } else { + EncryptedBackupWriter( + key = SignalStore.svr().getOrCreateMasterKey().deriveBackupKey(), + aci = SignalStore.account().aci!!, + outputStream = outputStream, + append = { mac -> outputStream.write(mac) } + ) + } + + writer.use { + // Note: Without a transaction, we may export inconsistent state. But because we have a transaction, + // writes from other threads are blocked. This is something to think more about. + SignalDatabase.rawDatabase.withinTransaction { + AccountDataProcessor.export { + writer.write(it) + eventTimer.emit("account") + } + + RecipientBackupProcessor.export { + writer.write(it) + eventTimer.emit("recipient") + } + + ChatBackupProcessor.export { frame -> + writer.write(frame) + eventTimer.emit("thread") + } + + CallLogBackupProcessor.export { frame -> + writer.write(frame) + eventTimer.emit("call") + } + + ChatItemBackupProcessor.export { frame -> + writer.write(frame) + eventTimer.emit("message") + } + } + } + + Log.d(TAG, "export() ${eventTimer.stop().summary}") + + return outputStream.toByteArray() + } + + fun import(length: Long, inputStreamFactory: () -> InputStream, selfData: SelfData, plaintext: Boolean = false) { + val eventTimer = EventTimer() + + val frameReader = if (plaintext) { + PlainTextBackupReader(inputStreamFactory()) + } else { + EncryptedBackupReader( + key = SignalStore.svr().getOrCreateMasterKey().deriveBackupKey(), + aci = selfData.aci, + streamLength = length, + dataStream = inputStreamFactory + ) + } + + // Note: Without a transaction, bad imports could lead to lost data. But because we have a transaction, + // writes from other threads are blocked. This is something to think more about. + SignalDatabase.rawDatabase.withinTransaction { + SignalStore.clearAllDataForBackupRestore() + SignalDatabase.recipients.clearAllDataForBackupRestore() + SignalDatabase.distributionLists.clearAllDataForBackupRestore() + SignalDatabase.threads.clearAllDataForBackupRestore() + SignalDatabase.messages.clearAllDataForBackupRestore() + SignalDatabase.attachments.clearAllDataForBackupRestore() + + // Add back self after clearing data + val selfId: RecipientId = SignalDatabase.recipients.getAndPossiblyMerge(selfData.aci, selfData.pni, selfData.e164, pniVerified = true, changeSelf = true) + SignalDatabase.recipients.setProfileKey(selfId, selfData.profileKey) + SignalDatabase.recipients.setProfileSharing(selfId, true) + + val backupState = BackupState() + val chatItemInserter: ChatItemImportInserter = ChatItemBackupProcessor.beginImport(backupState) + + for (frame in frameReader) { + when { + frame.account != null -> { + AccountDataProcessor.import(frame.account, selfId) + eventTimer.emit("account") + } + + frame.recipient != null -> { + RecipientBackupProcessor.import(frame.recipient, backupState) + eventTimer.emit("recipient") + } + + frame.chat != null -> { + ChatBackupProcessor.import(frame.chat, backupState) + eventTimer.emit("chat") + } + + frame.call != null -> { + CallLogBackupProcessor.import(frame.call, backupState) + eventTimer.emit("call") + } + + frame.chatItem != null -> { + chatItemInserter.insert(frame.chatItem) + eventTimer.emit("chatItem") + // TODO if there's stuff in the stream after chatItems, we need to flush the inserter before going to the next phase + } + + else -> Log.w(TAG, "Unrecognized frame") + } + } + + if (chatItemInserter.flush()) { + eventTimer.emit("chatItem") + } + + backupState.chatIdToLocalThreadId.values.forEach { + SignalDatabase.threads.update(it, unarchive = false, allowDeletion = false) + } + } + + Log.d(TAG, "import() ${eventTimer.stop().summary}") + } + + /** + * Returns an object with details about the remote backup state. + */ + fun getRemoteBackupState(): NetworkResult { + val api = ApplicationDependencies.getSignalServiceAccountManager().archiveApi + val backupKey = SignalStore.svr().getOrCreateMasterKey().deriveBackupKey() + + return api + .triggerBackupIdReservation(backupKey) + .then { getAuthCredential() } + .then { credential -> + api.setPublicKey(backupKey, credential) + .also { Log.i(TAG, "PublicKeyResult: $it") } + .map { credential } + } + .then { credential -> + api.getBackupInfo(backupKey, credential) + } + } + + /** + * A simple test method that just hits various network endpoints. Only useful for the playground. + * + * @return True if successful, otherwise false. + */ + fun uploadBackupFile(backupStream: InputStream, backupStreamLength: Long): Boolean { + val api = ApplicationDependencies.getSignalServiceAccountManager().archiveApi + val backupKey = SignalStore.svr().getOrCreateMasterKey().deriveBackupKey() + + return api + .triggerBackupIdReservation(backupKey) + .then { getAuthCredential() } + .then { credential -> + api.setPublicKey(backupKey, credential) + .also { Log.i(TAG, "PublicKeyResult: $it") } + .map { credential } + } + .then { credential -> + api.getMessageBackupUploadForm(backupKey, credential) + .also { Log.i(TAG, "UploadFormResult: $it") } + } + .then { form -> + api.getBackupResumableUploadUrl(form) + .also { Log.i(TAG, "ResumableUploadUrlResult: $it") } + .map { form to it } + } + .then { formAndUploadUrl -> + val (form, resumableUploadUrl) = formAndUploadUrl + api.uploadBackupFile(form, resumableUploadUrl, backupStream, backupStreamLength) + .also { Log.i(TAG, "UploadBackupFileResult: $it") } + } + .also { Log.i(TAG, "OverallResult: $it") } is NetworkResult.Success + } + + /** + * Retrieves an auth credential, preferring a cached value if available. + */ + private fun getAuthCredential(): NetworkResult { + val currentTime = System.currentTimeMillis() + + val credential = SignalStore.backup().credentialsByDay.getForCurrentTime(currentTime.milliseconds) + + if (credential != null) { + return NetworkResult.Success(credential) + } + + Log.w(TAG, "No credentials found for today, need to fetch new ones! This shouldn't happen under normal circumstances. We should ensure the routine fetch is running properly.") + + return ApplicationDependencies.getSignalServiceAccountManager().archiveApi.getServiceCredentials(currentTime).map { result -> + SignalStore.backup().addCredentials(result.credentials.toList()) + SignalStore.backup().clearCredentialsOlderThan(currentTime) + SignalStore.backup().credentialsByDay.getForCurrentTime(currentTime.milliseconds)!! + } + } + + data class SelfData( + val aci: ACI, + val pni: PNI, + val e164: String, + val profileKey: ProfileKey + ) +} + +class BackupState { + val backupToLocalRecipientId = HashMap() + val chatIdToLocalThreadId = HashMap() + val chatIdToLocalRecipientId = HashMap() + val chatIdToBackupRecipientId = HashMap() + val callIdToType = HashMap() +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/AttachmentTableBackupExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/AttachmentTableBackupExtensions.kt new file mode 100644 index 0000000000..08934ef9c5 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/AttachmentTableBackupExtensions.kt @@ -0,0 +1,13 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2.database + +import org.signal.core.util.delete +import org.thoughtcrime.securesms.database.AttachmentTable + +fun AttachmentTable.clearAllDataForBackupRestore() { + writableDatabase.delete(AttachmentTable.TABLE_NAME).run() +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallTableBackupExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallTableBackupExtensions.kt new file mode 100644 index 0000000000..0f48979fa3 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/CallTableBackupExtensions.kt @@ -0,0 +1,124 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2.database + +import android.database.Cursor +import android.database.sqlite.SQLiteDatabase +import androidx.core.content.contentValuesOf +import org.signal.core.util.isNull +import org.signal.core.util.requireInt +import org.signal.core.util.requireLong +import org.signal.core.util.select +import org.thoughtcrime.securesms.backup.v2.BackupState +import org.thoughtcrime.securesms.backup.v2.proto.Call +import org.thoughtcrime.securesms.database.CallTable +import org.thoughtcrime.securesms.database.RecipientTable +import java.io.Closeable + +typealias BackupCall = org.thoughtcrime.securesms.backup.v2.proto.Call + +fun CallTable.getCallsForBackup(): CallLogIterator { + return CallLogIterator( + readableDatabase + .select() + .from(CallTable.TABLE_NAME) + .where("${CallTable.EVENT} != ${CallTable.Event.serialize(CallTable.Event.DELETE)}") + .run() + ) +} + +fun CallTable.restoreCallLogFromBackup(call: BackupCall, backupState: BackupState) { + val type = when (call.type) { + Call.Type.VIDEO_CALL -> CallTable.Type.VIDEO_CALL + Call.Type.AUDIO_CALL -> CallTable.Type.AUDIO_CALL + Call.Type.AD_HOC_CALL -> CallTable.Type.AD_HOC_CALL + Call.Type.GROUP_CALL -> CallTable.Type.GROUP_CALL + Call.Type.UNKNOWN_TYPE -> return + } + + val event = when (call.event) { + Call.Event.DELETE -> CallTable.Event.DELETE + Call.Event.JOINED -> CallTable.Event.JOINED + Call.Event.GENERIC_GROUP_CALL -> CallTable.Event.GENERIC_GROUP_CALL + Call.Event.DECLINED -> CallTable.Event.DECLINED + Call.Event.ACCEPTED -> CallTable.Event.ACCEPTED + Call.Event.MISSED -> CallTable.Event.MISSED + Call.Event.OUTGOING_RING -> CallTable.Event.OUTGOING_RING + Call.Event.OUTGOING -> CallTable.Event.ONGOING + Call.Event.NOT_ACCEPTED -> CallTable.Event.NOT_ACCEPTED + Call.Event.UNKNOWN_EVENT -> return + } + + val direction = if (call.outgoing) CallTable.Direction.OUTGOING else CallTable.Direction.INCOMING + + backupState.callIdToType[call.callId] = CallTable.Call.getMessageType(type, direction, event) + + val values = contentValuesOf( + CallTable.CALL_ID to call.callId, + CallTable.PEER to backupState.backupToLocalRecipientId[call.conversationRecipientId]!!.serialize(), + CallTable.TYPE to CallTable.Type.serialize(type), + CallTable.DIRECTION to CallTable.Direction.serialize(direction), + CallTable.EVENT to CallTable.Event.serialize(event), + CallTable.TIMESTAMP to call.timestamp + ) + + writableDatabase.insert(CallTable.TABLE_NAME, SQLiteDatabase.CONFLICT_IGNORE, values) +} + +/** + * Provides a nice iterable interface over a [RecipientTable] cursor, converting rows to [BackupRecipient]s. + * Important: Because this is backed by a cursor, you must close it. It's recommended to use `.use()` or try-with-resources. + */ +class CallLogIterator(private val cursor: Cursor) : Iterator, Closeable { + override fun hasNext(): Boolean { + return cursor.count > 0 && !cursor.isLast + } + + override fun next(): BackupCall? { + if (!cursor.moveToNext()) { + throw NoSuchElementException() + } + + val callId = cursor.requireLong(CallTable.CALL_ID) + val type = CallTable.Type.deserialize(cursor.requireInt(CallTable.TYPE)) + val direction = CallTable.Direction.deserialize(cursor.requireInt(CallTable.DIRECTION)) + val event = CallTable.Event.deserialize(cursor.requireInt(CallTable.EVENT)) + + return BackupCall( + callId = callId, + conversationRecipientId = cursor.requireLong(CallTable.PEER), + type = when (type) { + CallTable.Type.AUDIO_CALL -> Call.Type.AUDIO_CALL + CallTable.Type.VIDEO_CALL -> Call.Type.VIDEO_CALL + CallTable.Type.AD_HOC_CALL -> Call.Type.AD_HOC_CALL + CallTable.Type.GROUP_CALL -> Call.Type.GROUP_CALL + }, + outgoing = when (direction) { + CallTable.Direction.OUTGOING -> true + else -> false + }, + timestamp = cursor.requireLong(CallTable.TIMESTAMP), + ringerRecipientId = if (cursor.isNull(CallTable.RINGER)) null else cursor.requireLong(CallTable.RINGER), + event = when (event) { + CallTable.Event.ONGOING -> Call.Event.OUTGOING + CallTable.Event.OUTGOING_RING -> Call.Event.OUTGOING_RING + CallTable.Event.ACCEPTED -> Call.Event.ACCEPTED + CallTable.Event.DECLINED -> Call.Event.DECLINED + CallTable.Event.GENERIC_GROUP_CALL -> Call.Event.GENERIC_GROUP_CALL + CallTable.Event.JOINED -> Call.Event.JOINED + CallTable.Event.MISSED, + CallTable.Event.MISSED_NOTIFICATION_PROFILE -> Call.Event.MISSED + CallTable.Event.DELETE -> Call.Event.DELETE + CallTable.Event.RINGING -> Call.Event.UNKNOWN_EVENT + CallTable.Event.NOT_ACCEPTED -> Call.Event.NOT_ACCEPTED + } + ) + } + + override fun close() { + cursor.close() + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemExportIterator.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemExportIterator.kt new file mode 100644 index 0000000000..b1c893ccf0 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemExportIterator.kt @@ -0,0 +1,482 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2.database + +import android.database.Cursor +import com.annimon.stream.Stream +import okio.ByteString.Companion.toByteString +import org.signal.core.util.Base64 +import org.signal.core.util.Base64.decodeOrThrow +import org.signal.core.util.logging.Log +import org.signal.core.util.requireBlob +import org.signal.core.util.requireBoolean +import org.signal.core.util.requireInt +import org.signal.core.util.requireLong +import org.signal.core.util.requireString +import org.thoughtcrime.securesms.backup.v2.proto.CallChatUpdate +import org.thoughtcrime.securesms.backup.v2.proto.ChatItem +import org.thoughtcrime.securesms.backup.v2.proto.ChatUpdateMessage +import org.thoughtcrime.securesms.backup.v2.proto.ExpirationTimerChatUpdate +import org.thoughtcrime.securesms.backup.v2.proto.GroupCallChatUpdate +import org.thoughtcrime.securesms.backup.v2.proto.IndividualCallChatUpdate +import org.thoughtcrime.securesms.backup.v2.proto.ProfileChangeChatUpdate +import org.thoughtcrime.securesms.backup.v2.proto.Quote +import org.thoughtcrime.securesms.backup.v2.proto.Reaction +import org.thoughtcrime.securesms.backup.v2.proto.RemoteDeletedMessage +import org.thoughtcrime.securesms.backup.v2.proto.SendStatus +import org.thoughtcrime.securesms.backup.v2.proto.SessionSwitchoverChatUpdate +import org.thoughtcrime.securesms.backup.v2.proto.SimpleChatUpdate +import org.thoughtcrime.securesms.backup.v2.proto.StandardMessage +import org.thoughtcrime.securesms.backup.v2.proto.Text +import org.thoughtcrime.securesms.backup.v2.proto.ThreadMergeChatUpdate +import org.thoughtcrime.securesms.database.GroupReceiptTable +import org.thoughtcrime.securesms.database.MessageTable +import org.thoughtcrime.securesms.database.MessageTypes +import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.database.SignalDatabase.Companion.calls +import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatchSet +import org.thoughtcrime.securesms.database.documents.NetworkFailureSet +import org.thoughtcrime.securesms.database.model.GroupCallUpdateDetailsUtil +import org.thoughtcrime.securesms.database.model.ReactionRecord +import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList +import org.thoughtcrime.securesms.database.model.databaseprotos.ProfileChangeDetails +import org.thoughtcrime.securesms.database.model.databaseprotos.SessionSwitchoverEvent +import org.thoughtcrime.securesms.database.model.databaseprotos.ThreadMergeEvent +import org.thoughtcrime.securesms.mms.QuoteModel +import org.thoughtcrime.securesms.util.JsonUtils +import org.whispersystems.signalservice.api.push.ServiceId.ACI +import org.whispersystems.signalservice.api.util.UuidUtil +import org.whispersystems.signalservice.api.util.toByteArray +import java.io.Closeable +import java.io.IOException +import java.util.LinkedList +import java.util.Queue +import java.util.UUID +import org.thoughtcrime.securesms.backup.v2.proto.BodyRange as BackupBodyRange + +/** + * An iterator for chat items with a clever performance twist: rather than do the extra queries one at a time (for reactions, + * attachments, etc), this will populate items in batches, doing bulk lookups to improve throughput. We keep these in a buffer + * and only do more queries when the buffer is empty. + * + * All of this complexity is hidden from the user -- they just get a normal iterator interface. + */ +class ChatItemExportIterator(private val cursor: Cursor, private val batchSize: Int) : Iterator, Closeable { + + companion object { + private val TAG = Log.tag(ChatItemExportIterator::class.java) + + const val COLUMN_BASE_TYPE = "base_type" + } + + /** + * A queue of already-parsed ChatItems. Processing in batches means that we read ahead in the cursor and put + * the pending items here. + */ + private val buffer: Queue = LinkedList() + + override fun hasNext(): Boolean { + return buffer.isNotEmpty() || (cursor.count > 0 && !cursor.isLast && !cursor.isAfterLast) + } + + override fun next(): ChatItem { + if (buffer.isNotEmpty()) { + return buffer.remove() + } + + val records: LinkedHashMap = linkedMapOf() + + for (i in 0 until batchSize) { + if (cursor.moveToNext()) { + val record = cursor.toBackupMessageRecord() + records[record.id] = record + } else { + break + } + } + + val reactionsById: Map> = SignalDatabase.reactions.getReactionsForMessages(records.keys) + val groupReceiptsById: Map> = SignalDatabase.groupReceipts.getGroupReceiptInfoForMessages(records.keys) + + for ((id, record) in records) { + val builder = record.toBasicChatItemBuilder(groupReceiptsById[id]) + + when { + record.remoteDeleted -> builder.remoteDeletedMessage = RemoteDeletedMessage() + MessageTypes.isJoinedType(record.type) -> builder.updateMessage = ChatUpdateMessage(simpleUpdate = SimpleChatUpdate(type = SimpleChatUpdate.Type.JOINED_SIGNAL)) + MessageTypes.isIdentityUpdate(record.type) -> builder.updateMessage = ChatUpdateMessage(simpleUpdate = SimpleChatUpdate(type = SimpleChatUpdate.Type.IDENTITY_UPDATE)) + MessageTypes.isIdentityVerified(record.type) -> builder.updateMessage = ChatUpdateMessage(simpleUpdate = SimpleChatUpdate(type = SimpleChatUpdate.Type.IDENTITY_VERIFIED)) + MessageTypes.isIdentityDefault(record.type) -> builder.updateMessage = ChatUpdateMessage(simpleUpdate = SimpleChatUpdate(type = SimpleChatUpdate.Type.IDENTITY_DEFAULT)) + MessageTypes.isChangeNumber(record.type) -> builder.updateMessage = ChatUpdateMessage(simpleUpdate = SimpleChatUpdate(type = SimpleChatUpdate.Type.CHANGE_NUMBER)) + MessageTypes.isBoostRequest(record.type) -> builder.updateMessage = ChatUpdateMessage(simpleUpdate = SimpleChatUpdate(type = SimpleChatUpdate.Type.BOOST_REQUEST)) + MessageTypes.isEndSessionType(record.type) -> builder.updateMessage = ChatUpdateMessage(simpleUpdate = SimpleChatUpdate(type = SimpleChatUpdate.Type.END_SESSION)) + MessageTypes.isChatSessionRefresh(record.type) -> builder.updateMessage = ChatUpdateMessage(simpleUpdate = SimpleChatUpdate(type = SimpleChatUpdate.Type.CHAT_SESSION_REFRESH)) + MessageTypes.isBadDecryptType(record.type) -> builder.updateMessage = ChatUpdateMessage(simpleUpdate = SimpleChatUpdate(type = SimpleChatUpdate.Type.BAD_DECRYPT)) + MessageTypes.isPaymentsActivated(record.type) -> builder.updateMessage = ChatUpdateMessage(simpleUpdate = SimpleChatUpdate(type = SimpleChatUpdate.Type.PAYMENTS_ACTIVATED)) + MessageTypes.isPaymentsRequestToActivate(record.type) -> builder.updateMessage = ChatUpdateMessage(simpleUpdate = SimpleChatUpdate(type = SimpleChatUpdate.Type.PAYMENT_ACTIVATION_REQUEST)) + MessageTypes.isExpirationTimerUpdate(record.type) -> builder.updateMessage = ChatUpdateMessage(expirationTimerChange = ExpirationTimerChatUpdate((record.expiresIn / 1000).toInt())) + MessageTypes.isProfileChange(record.type) -> { + builder.updateMessage = ChatUpdateMessage( + profileChange = try { + val decoded: ByteArray = Base64.decode(record.body!!) + val profileChangeDetails = ProfileChangeDetails.ADAPTER.decode(decoded) + if (profileChangeDetails.profileNameChange != null) { + ProfileChangeChatUpdate(previousName = profileChangeDetails.profileNameChange.previous, newName = profileChangeDetails.profileNameChange.newValue) + } else { + ProfileChangeChatUpdate() + } + } catch (e: IOException) { + Log.w(TAG, "Profile name change details could not be read", e) + ProfileChangeChatUpdate() + } + ) + } + MessageTypes.isSessionSwitchoverType(record.type) -> { + builder.updateMessage = ChatUpdateMessage( + sessionSwitchover = try { + val event = SessionSwitchoverEvent.ADAPTER.decode(decodeOrThrow(record.body!!)) + SessionSwitchoverChatUpdate(event.e164.e164ToLong()!!) + } catch (e: Exception) { + SessionSwitchoverChatUpdate() + } + ) + } + MessageTypes.isThreadMergeType(record.type) -> { + builder.updateMessage = ChatUpdateMessage( + threadMerge = try { + val event = ThreadMergeEvent.ADAPTER.decode(decodeOrThrow(record.body!!)) + ThreadMergeChatUpdate(event.previousE164.e164ToLong()!!) + } catch (e: Exception) { + ThreadMergeChatUpdate() + } + ) + } + MessageTypes.isCallLog(record.type) -> { + val call = calls.getCallByMessageId(record.id) + if (call != null) { + builder.updateMessage = ChatUpdateMessage(callingMessage = CallChatUpdate(callId = call.callId)) + } else { + when { + MessageTypes.isMissedAudioCall(record.type) -> { + builder.updateMessage = ChatUpdateMessage(callingMessage = CallChatUpdate(callMessage = IndividualCallChatUpdate(type = IndividualCallChatUpdate.Type.MISSED_AUDIO_CALL))) + } + MessageTypes.isMissedVideoCall(record.type) -> { + builder.updateMessage = ChatUpdateMessage(callingMessage = CallChatUpdate(callMessage = IndividualCallChatUpdate(type = IndividualCallChatUpdate.Type.MISSED_VIDEO_CALL))) + } + MessageTypes.isIncomingAudioCall(record.type) -> { + builder.updateMessage = ChatUpdateMessage(callingMessage = CallChatUpdate(callMessage = IndividualCallChatUpdate(type = IndividualCallChatUpdate.Type.INCOMING_AUDIO_CALL))) + } + MessageTypes.isIncomingVideoCall(record.type) -> { + builder.updateMessage = ChatUpdateMessage(callingMessage = CallChatUpdate(callMessage = IndividualCallChatUpdate(type = IndividualCallChatUpdate.Type.INCOMING_VIDEO_CALL))) + } + MessageTypes.isOutgoingAudioCall(record.type) -> { + builder.updateMessage = ChatUpdateMessage(callingMessage = CallChatUpdate(callMessage = IndividualCallChatUpdate(type = IndividualCallChatUpdate.Type.OUTGOING_AUDIO_CALL))) + } + MessageTypes.isOutgoingVideoCall(record.type) -> { + builder.updateMessage = ChatUpdateMessage(callingMessage = CallChatUpdate(callMessage = IndividualCallChatUpdate(type = IndividualCallChatUpdate.Type.OUTGOING_VIDEO_CALL))) + } + MessageTypes.isGroupCall(record.type) -> { + try { + val groupCallUpdateDetails = GroupCallUpdateDetailsUtil.parse(record.body) + + val joinedMembers = Stream.of(groupCallUpdateDetails.inCallUuids) + .map { uuid: String? -> UuidUtil.parseOrNull(uuid) } + .withoutNulls() + .map { obj: UUID? -> ACI.from(obj!!).toByteString() } + .toList() + builder.updateMessage = ChatUpdateMessage( + callingMessage = CallChatUpdate( + groupCall = GroupCallChatUpdate( + startedCallAci = ACI.from(UuidUtil.parseOrThrow(groupCallUpdateDetails.startedCallUuid)).toByteString(), + startedCallTimestamp = groupCallUpdateDetails.startedCallTimestamp, + inCallAcis = joinedMembers + ) + ) + ) + } catch (exception: java.lang.Exception) { + continue + } + } + } + } + } + record.body == null -> { + Log.w(TAG, "Record missing a body, skipping") + continue + } + else -> builder.standardMessage = record.toTextMessage(reactionsById[id]) + } + + buffer += builder.build() + } + + return if (buffer.isNotEmpty()) { + buffer.remove() + } else { + throw NoSuchElementException() + } + } + + override fun close() { + cursor.close() + } + + private fun String.e164ToLong(): Long? { + val fixed = if (this.startsWith("+")) { + this.substring(1) + } else { + this + } + + return fixed.toLongOrNull() + } + + private fun BackupMessageRecord.toBasicChatItemBuilder(groupReceipts: List?): ChatItem.Builder { + val record = this + + return ChatItem.Builder().apply { + chatId = record.threadId + authorId = record.fromRecipientId + dateSent = record.dateSent + sealedSender = record.sealedSender + expireStartDate = if (record.expireStarted > 0) record.expireStarted else null + expiresInMs = if (record.expiresIn > 0) record.expiresIn else null + revisions = emptyList() + sms = !MessageTypes.isSecureType(record.type) + + if (MessageTypes.isOutgoingMessageType(record.type)) { + outgoing = ChatItem.OutgoingMessageDetails( + sendStatus = record.toBackupSendStatus(groupReceipts) + ) + } else { + incoming = ChatItem.IncomingMessageDetails( + dateServerSent = record.dateServer, + dateReceived = record.dateReceived, + read = record.read + ) + } + } + } + + private fun BackupMessageRecord.toTextMessage(reactionRecords: List?): StandardMessage { + return StandardMessage( + quote = this.toQuote(), + text = Text( + body = this.body!!, + bodyRanges = this.bodyRanges?.toBackupBodyRanges() ?: emptyList() + ), + // TODO Link previews! + linkPreview = emptyList(), + longText = null, + reactions = reactionRecords.toBackupReactions() + ) + } + + private fun BackupMessageRecord.toQuote(): Quote? { + return if (this.quoteTargetSentTimestamp != MessageTable.QUOTE_NOT_PRESENT_ID && this.quoteAuthor > 0) { + // TODO Attachments! + val type = QuoteModel.Type.fromCode(this.quoteType) + Quote( + targetSentTimestamp = this.quoteTargetSentTimestamp.takeIf { !this.quoteMissing && it != MessageTable.QUOTE_TARGET_MISSING_ID }, + authorId = this.quoteAuthor, + text = this.quoteBody, + bodyRanges = this.quoteBodyRanges?.toBackupBodyRanges() ?: emptyList(), + type = when (type) { + QuoteModel.Type.NORMAL -> Quote.Type.NORMAL + QuoteModel.Type.GIFT_BADGE -> Quote.Type.GIFTBADGE + } + ) + } else { + null + } + } + + private fun ByteArray.toBackupBodyRanges(): List { + val decoded: BodyRangeList = try { + BodyRangeList.ADAPTER.decode(this) + } catch (e: IOException) { + Log.w(TAG, "Failed to decode BodyRangeList!") + return emptyList() + } + + return decoded.ranges.map { + BackupBodyRange( + start = it.start, + length = it.length, + mentionAci = it.mentionUuid?.let { UuidUtil.parseOrThrow(it) }?.toByteArray()?.toByteString(), + style = it.style?.toBackupBodyRangeStyle() + ) + } + } + + private fun BodyRangeList.BodyRange.Style.toBackupBodyRangeStyle(): BackupBodyRange.Style { + return when (this) { + BodyRangeList.BodyRange.Style.BOLD -> BackupBodyRange.Style.BOLD + BodyRangeList.BodyRange.Style.ITALIC -> BackupBodyRange.Style.ITALIC + BodyRangeList.BodyRange.Style.STRIKETHROUGH -> BackupBodyRange.Style.STRIKETHROUGH + BodyRangeList.BodyRange.Style.MONOSPACE -> BackupBodyRange.Style.MONOSPACE + BodyRangeList.BodyRange.Style.SPOILER -> BackupBodyRange.Style.SPOILER + } + } + + private fun List?.toBackupReactions(): List { + return this + ?.map { + Reaction( + emoji = it.emoji, + authorId = it.author.toLong(), + sentTimestamp = it.dateSent, + receivedTimestamp = it.dateReceived + ) + } ?: emptyList() + } + + private fun BackupMessageRecord.toBackupSendStatus(groupReceipts: List?): List { + if (!MessageTypes.isOutgoingMessageType(this.type)) { + return emptyList() + } + + if (!groupReceipts.isNullOrEmpty()) { + return groupReceipts.toBackupSendStatus(this.networkFailureRecipientIds, this.identityMismatchRecipientIds) + } + + val status: SendStatus.Status = when { + this.viewed -> SendStatus.Status.VIEWED + this.hasReadReceipt -> SendStatus.Status.READ + this.hasDeliveryReceipt -> SendStatus.Status.DELIVERED + this.baseType == MessageTypes.BASE_SENT_TYPE -> SendStatus.Status.SENT + MessageTypes.isFailedMessageType(this.type) -> SendStatus.Status.FAILED + else -> SendStatus.Status.PENDING + } + + return listOf( + SendStatus( + recipientId = this.toRecipientId, + deliveryStatus = status, + lastStatusUpdateTimestamp = this.receiptTimestamp, + sealedSender = this.sealedSender, + networkFailure = this.networkFailureRecipientIds.contains(this.toRecipientId), + identityKeyMismatch = this.identityMismatchRecipientIds.contains(this.toRecipientId) + ) + ) + } + + private fun List.toBackupSendStatus(networkFailureRecipientIds: Set, identityMismatchRecipientIds: Set): List { + return this.map { + SendStatus( + recipientId = it.recipientId.toLong(), + deliveryStatus = it.status.toBackupDeliveryStatus(), + sealedSender = it.isUnidentified, + lastStatusUpdateTimestamp = it.timestamp, + networkFailure = networkFailureRecipientIds.contains(it.recipientId.toLong()), + identityKeyMismatch = identityMismatchRecipientIds.contains(it.recipientId.toLong()) + ) + } + } + + private fun Int.toBackupDeliveryStatus(): SendStatus.Status { + return when (this) { + GroupReceiptTable.STATUS_UNDELIVERED -> SendStatus.Status.PENDING + GroupReceiptTable.STATUS_DELIVERED -> SendStatus.Status.DELIVERED + GroupReceiptTable.STATUS_READ -> SendStatus.Status.READ + GroupReceiptTable.STATUS_VIEWED -> SendStatus.Status.VIEWED + GroupReceiptTable.STATUS_SKIPPED -> SendStatus.Status.SKIPPED + else -> SendStatus.Status.SKIPPED + } + } + + private fun String?.parseNetworkFailures(): Set { + if (this.isNullOrBlank()) { + return emptySet() + } + + return try { + JsonUtils.fromJson(this, NetworkFailureSet::class.java).items.map { it.recipientId.toLong() }.toSet() + } catch (e: IOException) { + emptySet() + } + } + + private fun String?.parseIdentityMismatches(): Set { + if (this.isNullOrBlank()) { + return emptySet() + } + + return try { + JsonUtils.fromJson(this, IdentityKeyMismatchSet::class.java).items.map { it.recipientId.toLong() }.toSet() + } catch (e: IOException) { + emptySet() + } + } + + private fun Cursor.toBackupMessageRecord(): BackupMessageRecord { + return BackupMessageRecord( + id = this.requireLong(MessageTable.ID), + dateSent = this.requireLong(MessageTable.DATE_SENT), + dateReceived = this.requireLong(MessageTable.DATE_RECEIVED), + dateServer = this.requireLong(MessageTable.DATE_SERVER), + type = this.requireLong(MessageTable.TYPE), + threadId = this.requireLong(MessageTable.THREAD_ID), + body = this.requireString(MessageTable.BODY), + bodyRanges = this.requireBlob(MessageTable.MESSAGE_RANGES), + fromRecipientId = this.requireLong(MessageTable.FROM_RECIPIENT_ID), + toRecipientId = this.requireLong(MessageTable.TO_RECIPIENT_ID), + expiresIn = this.requireLong(MessageTable.EXPIRES_IN), + expireStarted = this.requireLong(MessageTable.EXPIRE_STARTED), + remoteDeleted = this.requireBoolean(MessageTable.REMOTE_DELETED), + sealedSender = this.requireBoolean(MessageTable.UNIDENTIFIED), + quoteTargetSentTimestamp = this.requireLong(MessageTable.QUOTE_ID), + quoteAuthor = this.requireLong(MessageTable.QUOTE_AUTHOR), + quoteBody = this.requireString(MessageTable.QUOTE_BODY), + quoteMissing = this.requireBoolean(MessageTable.QUOTE_MISSING), + quoteBodyRanges = this.requireBlob(MessageTable.QUOTE_BODY_RANGES), + quoteType = this.requireInt(MessageTable.QUOTE_TYPE), + originalMessageId = this.requireLong(MessageTable.ORIGINAL_MESSAGE_ID), + latestRevisionId = this.requireLong(MessageTable.LATEST_REVISION_ID), + hasDeliveryReceipt = this.requireBoolean(MessageTable.HAS_DELIVERY_RECEIPT), + viewed = this.requireBoolean(MessageTable.VIEWED_COLUMN), + hasReadReceipt = this.requireBoolean(MessageTable.HAS_READ_RECEIPT), + read = this.requireBoolean(MessageTable.READ), + receiptTimestamp = this.requireLong(MessageTable.RECEIPT_TIMESTAMP), + networkFailureRecipientIds = this.requireString(MessageTable.NETWORK_FAILURES).parseNetworkFailures(), + identityMismatchRecipientIds = this.requireString(MessageTable.MISMATCHED_IDENTITIES).parseIdentityMismatches(), + baseType = this.requireLong(COLUMN_BASE_TYPE) + ) + } + + private class BackupMessageRecord( + val id: Long, + val dateSent: Long, + val dateReceived: Long, + val dateServer: Long, + val type: Long, + val threadId: Long, + val body: String?, + val bodyRanges: ByteArray?, + val fromRecipientId: Long, + val toRecipientId: Long, + val expiresIn: Long, + val expireStarted: Long, + val remoteDeleted: Boolean, + val sealedSender: Boolean, + val quoteTargetSentTimestamp: Long, + val quoteAuthor: Long, + val quoteBody: String?, + val quoteMissing: Boolean, + val quoteBodyRanges: ByteArray?, + val quoteType: Int, + val originalMessageId: Long, + val latestRevisionId: Long, + val hasDeliveryReceipt: Boolean, + val hasReadReceipt: Boolean, + val viewed: Boolean, + val receiptTimestamp: Long, + val read: Boolean, + val networkFailureRecipientIds: Set, + val identityMismatchRecipientIds: Set, + val baseType: Long + ) +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt new file mode 100644 index 0000000000..5c049409c9 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt @@ -0,0 +1,516 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2.database + +import android.content.ContentValues +import androidx.core.content.contentValuesOf +import org.signal.core.util.Base64 +import org.signal.core.util.SqlUtil +import org.signal.core.util.logging.Log +import org.signal.core.util.requireLong +import org.signal.core.util.toInt +import org.thoughtcrime.securesms.backup.v2.BackupState +import org.thoughtcrime.securesms.backup.v2.proto.BodyRange +import org.thoughtcrime.securesms.backup.v2.proto.ChatItem +import org.thoughtcrime.securesms.backup.v2.proto.ChatUpdateMessage +import org.thoughtcrime.securesms.backup.v2.proto.IndividualCallChatUpdate +import org.thoughtcrime.securesms.backup.v2.proto.Quote +import org.thoughtcrime.securesms.backup.v2.proto.Reaction +import org.thoughtcrime.securesms.backup.v2.proto.SendStatus +import org.thoughtcrime.securesms.backup.v2.proto.SimpleChatUpdate +import org.thoughtcrime.securesms.backup.v2.proto.StandardMessage +import org.thoughtcrime.securesms.database.CallTable +import org.thoughtcrime.securesms.database.GroupReceiptTable +import org.thoughtcrime.securesms.database.MessageTable +import org.thoughtcrime.securesms.database.MessageTypes +import org.thoughtcrime.securesms.database.ReactionTable +import org.thoughtcrime.securesms.database.SQLiteDatabase +import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch +import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatchSet +import org.thoughtcrime.securesms.database.documents.NetworkFailure +import org.thoughtcrime.securesms.database.documents.NetworkFailureSet +import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList +import org.thoughtcrime.securesms.database.model.databaseprotos.ProfileChangeDetails +import org.thoughtcrime.securesms.database.model.databaseprotos.SessionSwitchoverEvent +import org.thoughtcrime.securesms.database.model.databaseprotos.ThreadMergeEvent +import org.thoughtcrime.securesms.mms.QuoteModel +import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.recipients.RecipientId +import org.thoughtcrime.securesms.util.JsonUtils +import org.whispersystems.signalservice.api.util.UuidUtil + +/** + * An object that will ingest all fo the [ChatItem]s you want to write, buffer them until hitting a specified batch size, and then batch insert them + * for fast throughput. + */ +class ChatItemImportInserter( + private val db: SQLiteDatabase, + private val backupState: BackupState, + private val batchSize: Int +) { + companion object { + private val TAG = Log.tag(ChatItemImportInserter::class.java) + + private val MESSAGE_COLUMNS = arrayOf( + MessageTable.DATE_SENT, + MessageTable.DATE_RECEIVED, + MessageTable.DATE_SERVER, + MessageTable.TYPE, + MessageTable.THREAD_ID, + MessageTable.READ, + MessageTable.BODY, + MessageTable.FROM_RECIPIENT_ID, + MessageTable.TO_RECIPIENT_ID, + MessageTable.HAS_DELIVERY_RECEIPT, + MessageTable.HAS_READ_RECEIPT, + MessageTable.VIEWED_COLUMN, + MessageTable.MISMATCHED_IDENTITIES, + MessageTable.EXPIRES_IN, + MessageTable.EXPIRE_STARTED, + MessageTable.UNIDENTIFIED, + MessageTable.REMOTE_DELETED, + MessageTable.REMOTE_DELETED, + MessageTable.NETWORK_FAILURES, + MessageTable.QUOTE_ID, + MessageTable.QUOTE_AUTHOR, + MessageTable.QUOTE_BODY, + MessageTable.QUOTE_MISSING, + MessageTable.QUOTE_BODY_RANGES, + MessageTable.QUOTE_TYPE, + MessageTable.SHARED_CONTACTS, + MessageTable.LINK_PREVIEWS, + MessageTable.MESSAGE_RANGES, + MessageTable.VIEW_ONCE + ) + + private val REACTION_COLUMNS = arrayOf( + ReactionTable.MESSAGE_ID, + ReactionTable.AUTHOR_ID, + ReactionTable.EMOJI, + ReactionTable.DATE_SENT, + ReactionTable.DATE_RECEIVED + ) + + private val GROUP_RECEIPT_COLUMNS = arrayOf( + GroupReceiptTable.MMS_ID, + GroupReceiptTable.RECIPIENT_ID, + GroupReceiptTable.STATUS, + GroupReceiptTable.TIMESTAMP, + GroupReceiptTable.UNIDENTIFIED + ) + } + + private val selfId = Recipient.self().id + private val buffer: Buffer = Buffer() + private var messageId: Long = SqlUtil.getNextAutoIncrementId(db, MessageTable.TABLE_NAME) + + /** + * Indicate that you want to insert the [ChatItem] into the database. + * If this item causes the buffer to hit the batch size, then a batch of items will actually be inserted. + */ + fun insert(chatItem: ChatItem) { + val fromLocalRecipientId: RecipientId? = backupState.backupToLocalRecipientId[chatItem.authorId] + if (fromLocalRecipientId == null) { + Log.w(TAG, "[insert] Could not find a local recipient for backup recipient ID ${chatItem.authorId}! Skipping.") + return + } + + val chatLocalRecipientId: RecipientId? = backupState.chatIdToLocalRecipientId[chatItem.chatId] + if (chatLocalRecipientId == null) { + Log.w(TAG, "[insert] Could not find a local recipient for chatId ${chatItem.chatId}! Skipping.") + return + } + + val localThreadId: Long? = backupState.chatIdToLocalThreadId[chatItem.chatId] + if (localThreadId == null) { + Log.w(TAG, "[insert] Could not find a local threadId for backup chatId ${chatItem.chatId}! Skipping.") + return + } + + val chatBackupRecipientId: Long? = backupState.chatIdToBackupRecipientId[chatItem.chatId] + if (chatBackupRecipientId == null) { + Log.w(TAG, "[insert] Could not find a backup recipientId for backup chatId ${chatItem.chatId}! Skipping.") + return + } + + buffer.messages += chatItem.toMessageInsert(fromLocalRecipientId, chatLocalRecipientId, localThreadId) + buffer.reactions += chatItem.toReactionContentValues(messageId) + buffer.groupReceipts += chatItem.toGroupReceiptContentValues(messageId, chatBackupRecipientId) + + messageId++ + + if (buffer.size >= batchSize) { + flush() + } + } + + /** Returns true if something was written to the db, otherwise false. */ + fun flush(): Boolean { + if (buffer.size == 0) { + return false + } + + buildBulkInsert(MessageTable.TABLE_NAME, MESSAGE_COLUMNS, buffer.messages).forEach { + db.rawQuery("${it.query.where} RETURNING ${MessageTable.ID}", it.query.whereArgs).use { cursor -> + var index = 0 + while (cursor.moveToNext()) { + val rowId = cursor.requireLong(MessageTable.ID) + val followup = it.inserts[index].followUp + if (followup != null) { + followup(rowId) + } + index++ + } + } + } + + SqlUtil.buildBulkInsert(ReactionTable.TABLE_NAME, REACTION_COLUMNS, buffer.reactions).forEach { + db.execSQL(it.where, it.whereArgs) + } + + SqlUtil.buildBulkInsert(GroupReceiptTable.TABLE_NAME, GROUP_RECEIPT_COLUMNS, buffer.groupReceipts).forEach { + db.execSQL(it.where, it.whereArgs) + } + + messageId = SqlUtil.getNextAutoIncrementId(db, MessageTable.TABLE_NAME) + + return true + } + + private fun buildBulkInsert(tableName: String, columns: Array, messageInserts: List, maxQueryArgs: Int = 999): List { + val batchSize = maxQueryArgs / columns.size + + return messageInserts + .chunked(batchSize) + .map { batch: List -> BatchInsert(batch, SqlUtil.buildSingleBulkInsert(tableName, columns, batch.map { it.contentValues })) } + .toList() + } + + private fun ChatItem.toMessageInsert(fromRecipientId: RecipientId, chatRecipientId: RecipientId, threadId: Long): MessageInsert { + val contentValues = this.toMessageContentValues(fromRecipientId, chatRecipientId, threadId) + + var followUp: ((Long) -> Unit)? = null + if (this.updateMessage != null) { + if (this.updateMessage.callingMessage != null && this.updateMessage.callingMessage.callId != null) { + followUp = { messageRowId -> + val callContentValues = ContentValues() + callContentValues.put(CallTable.MESSAGE_ID, messageRowId) + db.update(CallTable.TABLE_NAME, SQLiteDatabase.CONFLICT_IGNORE, callContentValues, "${CallTable.CALL_ID} = ?", SqlUtil.buildArgs(this.updateMessage.callingMessage.callId)) + } + } + } + return MessageInsert(contentValues, followUp) + } + + private class BatchInsert(val inserts: List, val query: SqlUtil.Query) + + private fun ChatItem.toMessageContentValues(fromRecipientId: RecipientId, chatRecipientId: RecipientId, threadId: Long): ContentValues { + val contentValues = ContentValues() + + contentValues.put(MessageTable.TYPE, this.getMessageType()) + contentValues.put(MessageTable.DATE_SENT, this.dateSent) + contentValues.put(MessageTable.DATE_SERVER, this.incoming?.dateServerSent ?: -1) + contentValues.put(MessageTable.FROM_RECIPIENT_ID, fromRecipientId.serialize()) + contentValues.put(MessageTable.TO_RECIPIENT_ID, (if (this.outgoing != null) chatRecipientId else selfId).serialize()) + contentValues.put(MessageTable.THREAD_ID, threadId) + contentValues.put(MessageTable.DATE_RECEIVED, this.incoming?.dateReceived ?: this.dateSent) + contentValues.put(MessageTable.RECEIPT_TIMESTAMP, this.outgoing?.sendStatus?.maxOf { it.lastStatusUpdateTimestamp } ?: 0) + contentValues.putNull(MessageTable.LATEST_REVISION_ID) + contentValues.putNull(MessageTable.ORIGINAL_MESSAGE_ID) + contentValues.put(MessageTable.REVISION_NUMBER, 0) + contentValues.put(MessageTable.EXPIRES_IN, this.expiresInMs ?: 0) + contentValues.put(MessageTable.EXPIRE_STARTED, this.expireStartDate ?: 0) + + if (this.outgoing != null) { + val viewed = this.outgoing.sendStatus.any { it.deliveryStatus == SendStatus.Status.VIEWED } + val hasReadReceipt = viewed || this.outgoing.sendStatus.any { it.deliveryStatus == SendStatus.Status.READ } + val hasDeliveryReceipt = viewed || hasReadReceipt || this.outgoing.sendStatus.any { it.deliveryStatus == SendStatus.Status.DELIVERED } + + contentValues.put(MessageTable.VIEWED_COLUMN, viewed.toInt()) + contentValues.put(MessageTable.HAS_READ_RECEIPT, hasReadReceipt.toInt()) + contentValues.put(MessageTable.HAS_DELIVERY_RECEIPT, hasDeliveryReceipt.toInt()) + contentValues.put(MessageTable.UNIDENTIFIED, this.outgoing.sendStatus.count { it.sealedSender }) + contentValues.put(MessageTable.READ, 1) + + contentValues.addNetworkFailures(this, backupState) + contentValues.addIdentityKeyMismatches(this, backupState) + } else { + contentValues.put(MessageTable.VIEWED_COLUMN, 0) + contentValues.put(MessageTable.HAS_READ_RECEIPT, 0) + contentValues.put(MessageTable.HAS_DELIVERY_RECEIPT, 0) + contentValues.put(MessageTable.UNIDENTIFIED, this.sealedSender?.toInt()) + contentValues.put(MessageTable.READ, this.incoming?.read?.toInt() ?: 0) + } + + contentValues.put(MessageTable.QUOTE_ID, 0) + contentValues.put(MessageTable.QUOTE_AUTHOR, 0) + contentValues.put(MessageTable.QUOTE_MISSING, 0) + contentValues.put(MessageTable.QUOTE_TYPE, 0) + contentValues.put(MessageTable.VIEW_ONCE, 0) + contentValues.put(MessageTable.REMOTE_DELETED, 0) + + when { + this.standardMessage != null -> contentValues.addStandardMessage(this.standardMessage) + this.remoteDeletedMessage != null -> contentValues.put(MessageTable.REMOTE_DELETED, 1) + this.updateMessage != null -> contentValues.addUpdateMessage(this.updateMessage) + } + + return contentValues + } + + private fun ChatItem.toReactionContentValues(messageId: Long): List { + val reactions: List = when { + this.standardMessage != null -> this.standardMessage.reactions + this.contactMessage != null -> this.contactMessage.reactions + this.voiceMessage != null -> this.voiceMessage.reactions + this.stickerMessage != null -> this.stickerMessage.reactions + else -> emptyList() + } + + return reactions + .mapNotNull { + val authorId: Long? = backupState.backupToLocalRecipientId[it.authorId]?.toLong() + + if (authorId != null) { + contentValuesOf( + ReactionTable.MESSAGE_ID to messageId, + ReactionTable.AUTHOR_ID to authorId, + ReactionTable.DATE_SENT to it.sentTimestamp, + ReactionTable.DATE_RECEIVED to it.receivedTimestamp, + ReactionTable.EMOJI to it.emoji + ) + } else { + Log.w(TAG, "[Reaction] Could not find a local recipient for backup recipient ID ${it.authorId}! Skipping.") + null + } + } + } + + private fun ChatItem.toGroupReceiptContentValues(messageId: Long, chatBackupRecipientId: Long): List { + if (this.outgoing == null) { + return emptyList() + } + + // TODO This seems like an indirect/bad way to detect if this is a 1:1 or group convo + if (this.outgoing.sendStatus.size == 1 && this.outgoing.sendStatus[0].recipientId == chatBackupRecipientId) { + return emptyList() + } + + return this.outgoing.sendStatus.mapNotNull { sendStatus -> + val recipientId = backupState.backupToLocalRecipientId[sendStatus.recipientId] + + if (recipientId != null) { + contentValuesOf( + GroupReceiptTable.MMS_ID to messageId, + GroupReceiptTable.RECIPIENT_ID to recipientId.serialize(), + GroupReceiptTable.STATUS to sendStatus.deliveryStatus.toLocalSendStatus(), + GroupReceiptTable.TIMESTAMP to sendStatus.lastStatusUpdateTimestamp, + GroupReceiptTable.UNIDENTIFIED to sendStatus.sealedSender + ) + } else { + Log.w(TAG, "[GroupReceipts] Could not find a local recipient for backup recipient ID ${sendStatus.recipientId}! Skipping.") + null + } + } + } + + private fun ChatItem.getMessageType(): Long { + var type: Long = if (this.outgoing != null) { + if (this.outgoing.sendStatus.count { it.identityKeyMismatch } > 0) { + MessageTypes.BASE_SENT_FAILED_TYPE + } else if (this.outgoing.sendStatus.count { it.networkFailure } > 0) { + MessageTypes.BASE_SENDING_TYPE + } else { + MessageTypes.BASE_SENT_TYPE + } + } else { + MessageTypes.BASE_INBOX_TYPE + } + + if (!this.sms) { + type = type or MessageTypes.SECURE_MESSAGE_BIT or MessageTypes.PUSH_MESSAGE_BIT + } + + return type + } + + private fun ContentValues.addStandardMessage(standardMessage: StandardMessage) { + if (standardMessage.text != null) { + this.put(MessageTable.BODY, standardMessage.text.body) + + if (standardMessage.text.bodyRanges.isNotEmpty()) { + this.put(MessageTable.MESSAGE_RANGES, standardMessage.text.bodyRanges.toLocalBodyRanges()?.encode() as ByteArray?) + } + } + + if (standardMessage.quote != null) { + this.addQuote(standardMessage.quote) + } + } + + private fun ContentValues.addUpdateMessage(updateMessage: ChatUpdateMessage) { + var typeFlags: Long = 0 + when { + updateMessage.simpleUpdate != null -> { + typeFlags = when (updateMessage.simpleUpdate.type) { + SimpleChatUpdate.Type.UNKNOWN -> 0 + SimpleChatUpdate.Type.JOINED_SIGNAL -> MessageTypes.JOINED_TYPE + SimpleChatUpdate.Type.IDENTITY_UPDATE -> MessageTypes.KEY_EXCHANGE_IDENTITY_UPDATE_BIT + SimpleChatUpdate.Type.IDENTITY_VERIFIED -> MessageTypes.KEY_EXCHANGE_IDENTITY_VERIFIED_BIT + SimpleChatUpdate.Type.IDENTITY_DEFAULT -> MessageTypes.KEY_EXCHANGE_IDENTITY_DEFAULT_BIT + SimpleChatUpdate.Type.CHANGE_NUMBER -> MessageTypes.CHANGE_NUMBER_TYPE + SimpleChatUpdate.Type.BOOST_REQUEST -> MessageTypes.BOOST_REQUEST_TYPE + SimpleChatUpdate.Type.END_SESSION -> MessageTypes.END_SESSION_BIT + SimpleChatUpdate.Type.CHAT_SESSION_REFRESH -> MessageTypes.ENCRYPTION_REMOTE_FAILED_BIT + SimpleChatUpdate.Type.BAD_DECRYPT -> MessageTypes.BAD_DECRYPT_TYPE + SimpleChatUpdate.Type.PAYMENTS_ACTIVATED -> MessageTypes.SPECIAL_TYPE_PAYMENTS_ACTIVATED + SimpleChatUpdate.Type.PAYMENT_ACTIVATION_REQUEST -> MessageTypes.SPECIAL_TYPE_PAYMENTS_ACTIVATE_REQUEST + } + } + updateMessage.expirationTimerChange != null -> { + typeFlags = MessageTypes.EXPIRATION_TIMER_UPDATE_BIT + put(MessageTable.EXPIRES_IN, updateMessage.expirationTimerChange.expiresInMs.toLong()) + } + updateMessage.profileChange != null -> { + typeFlags = MessageTypes.PROFILE_CHANGE_TYPE + val profileChangeDetails = ProfileChangeDetails(profileNameChange = ProfileChangeDetails.StringChange(previous = updateMessage.profileChange.previousName, newValue = updateMessage.profileChange.newName)) + .encode() + put(MessageTable.BODY, Base64.encodeWithPadding(profileChangeDetails)) + } + updateMessage.sessionSwitchover != null -> { + typeFlags = MessageTypes.SESSION_SWITCHOVER_TYPE + val sessionSwitchoverDetails = SessionSwitchoverEvent(e164 = updateMessage.sessionSwitchover.e164.toString()).encode() + put(MessageTable.BODY, Base64.encodeWithPadding(sessionSwitchoverDetails)) + } + updateMessage.threadMerge != null -> { + typeFlags = MessageTypes.THREAD_MERGE_TYPE + val threadMergeDetails = ThreadMergeEvent(previousE164 = updateMessage.threadMerge.previousE164.toString()).encode() + put(MessageTable.BODY, Base64.encodeWithPadding(threadMergeDetails)) + } + updateMessage.callingMessage != null -> { + when { + updateMessage.callingMessage.callId != null -> { + typeFlags = backupState.callIdToType[updateMessage.callingMessage.callId]!! + } + updateMessage.callingMessage.callMessage != null -> { + typeFlags = when (updateMessage.callingMessage.callMessage.type) { + IndividualCallChatUpdate.Type.INCOMING_AUDIO_CALL -> MessageTypes.INCOMING_AUDIO_CALL_TYPE + IndividualCallChatUpdate.Type.INCOMING_VIDEO_CALL -> MessageTypes.INCOMING_VIDEO_CALL_TYPE + IndividualCallChatUpdate.Type.OUTGOING_AUDIO_CALL -> MessageTypes.OUTGOING_AUDIO_CALL_TYPE + IndividualCallChatUpdate.Type.OUTGOING_VIDEO_CALL -> MessageTypes.OUTGOING_VIDEO_CALL_TYPE + IndividualCallChatUpdate.Type.MISSED_AUDIO_CALL -> MessageTypes.MISSED_AUDIO_CALL_TYPE + IndividualCallChatUpdate.Type.MISSED_VIDEO_CALL -> MessageTypes.MISSED_VIDEO_CALL_TYPE + IndividualCallChatUpdate.Type.UNKNOWN -> typeFlags + } + } + } + // Calls don't use the incoming/outgoing flags, so we overwrite the flags here + this.put(MessageTable.TYPE, typeFlags) + } + } + this.put(MessageTable.TYPE, getAsLong(MessageTable.TYPE) or typeFlags) + } + + private fun ContentValues.addQuote(quote: Quote) { + this.put(MessageTable.QUOTE_ID, quote.targetSentTimestamp ?: MessageTable.QUOTE_TARGET_MISSING_ID) + this.put(MessageTable.QUOTE_AUTHOR, backupState.backupToLocalRecipientId[quote.authorId]!!.serialize()) + this.put(MessageTable.QUOTE_BODY, quote.text) + this.put(MessageTable.QUOTE_TYPE, quote.type.toLocalQuoteType()) + this.put(MessageTable.QUOTE_BODY_RANGES, quote.bodyRanges.toLocalBodyRanges()?.encode()) + // TODO quote attachments + this.put(MessageTable.QUOTE_MISSING, (quote.targetSentTimestamp == null).toInt()) + } + + private fun Quote.Type.toLocalQuoteType(): Int { + return when (this) { + Quote.Type.UNKNOWN -> QuoteModel.Type.NORMAL.code + Quote.Type.NORMAL -> QuoteModel.Type.NORMAL.code + Quote.Type.GIFTBADGE -> QuoteModel.Type.GIFT_BADGE.code + } + } + + private fun ContentValues.addNetworkFailures(chatItem: ChatItem, backupState: BackupState) { + if (chatItem.outgoing == null) { + return + } + + val networkFailures = chatItem.outgoing.sendStatus + .filter { status -> status.networkFailure } + .mapNotNull { status -> backupState.backupToLocalRecipientId[status.recipientId] } + .map { recipientId -> NetworkFailure(recipientId) } + .toSet() + + if (networkFailures.isNotEmpty()) { + this.put(MessageTable.NETWORK_FAILURES, JsonUtils.toJson(NetworkFailureSet(networkFailures))) + } + } + + private fun ContentValues.addIdentityKeyMismatches(chatItem: ChatItem, backupState: BackupState) { + if (chatItem.outgoing == null) { + return + } + + val mismatches = chatItem.outgoing.sendStatus + .filter { status -> status.identityKeyMismatch } + .mapNotNull { status -> backupState.backupToLocalRecipientId[status.recipientId] } + .map { recipientId -> IdentityKeyMismatch(recipientId, null) } // TODO We probably want the actual identity key in this status situation? + .toSet() + + if (mismatches.isNotEmpty()) { + this.put(MessageTable.MISMATCHED_IDENTITIES, JsonUtils.toJson(IdentityKeyMismatchSet(mismatches))) + } + } + + private fun List.toLocalBodyRanges(): BodyRangeList? { + if (this.isEmpty()) { + return null + } + + return BodyRangeList( + ranges = this.map { bodyRange -> + BodyRangeList.BodyRange( + mentionUuid = bodyRange.mentionAci?.let { UuidUtil.fromByteString(it) }?.toString(), + style = bodyRange.style?.let { + when (bodyRange.style) { + BodyRange.Style.BOLD -> BodyRangeList.BodyRange.Style.BOLD + BodyRange.Style.ITALIC -> BodyRangeList.BodyRange.Style.ITALIC + BodyRange.Style.MONOSPACE -> BodyRangeList.BodyRange.Style.MONOSPACE + BodyRange.Style.SPOILER -> BodyRangeList.BodyRange.Style.SPOILER + BodyRange.Style.STRIKETHROUGH -> BodyRangeList.BodyRange.Style.STRIKETHROUGH + else -> null + } + }, + start = bodyRange.start ?: 0, + length = bodyRange.length ?: 0 + ) + } + ) + } + + private fun SendStatus.Status.toLocalSendStatus(): Int { + return when (this) { + SendStatus.Status.UNKNOWN -> GroupReceiptTable.STATUS_UNKNOWN + SendStatus.Status.FAILED -> GroupReceiptTable.STATUS_UNKNOWN + SendStatus.Status.PENDING -> GroupReceiptTable.STATUS_UNDELIVERED + SendStatus.Status.SENT -> GroupReceiptTable.STATUS_UNDELIVERED + SendStatus.Status.DELIVERED -> GroupReceiptTable.STATUS_DELIVERED + SendStatus.Status.READ -> GroupReceiptTable.STATUS_READ + SendStatus.Status.VIEWED -> GroupReceiptTable.STATUS_VIEWED + SendStatus.Status.SKIPPED -> GroupReceiptTable.STATUS_SKIPPED + } + } + + private class MessageInsert(val contentValues: ContentValues, val followUp: ((Long) -> Unit)?) + + private class Buffer( + val messages: MutableList = mutableListOf(), + val reactions: MutableList = mutableListOf(), + val groupReceipts: MutableList = mutableListOf() + ) { + val size: Int + get() = listOf(messages.size, reactions.size, groupReceipts.size).max() + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/DistributionListTablesBackupExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/DistributionListTablesBackupExtensions.kt new file mode 100644 index 0000000000..d8e1001d22 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/DistributionListTablesBackupExtensions.kt @@ -0,0 +1,114 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2.database + +import okio.ByteString.Companion.toByteString +import org.signal.core.util.CursorUtil +import org.signal.core.util.delete +import org.signal.core.util.logging.Log +import org.signal.core.util.readToList +import org.signal.core.util.requireLong +import org.signal.core.util.requireNonNullString +import org.signal.core.util.requireObject +import org.signal.core.util.select +import org.thoughtcrime.securesms.backup.v2.BackupState +import org.thoughtcrime.securesms.database.DistributionListTables +import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.database.model.DistributionListId +import org.thoughtcrime.securesms.database.model.DistributionListPrivacyMode +import org.thoughtcrime.securesms.database.model.DistributionListRecord +import org.thoughtcrime.securesms.recipients.RecipientId +import org.whispersystems.signalservice.api.push.DistributionId +import org.whispersystems.signalservice.api.util.UuidUtil +import org.whispersystems.signalservice.api.util.toByteArray +import org.thoughtcrime.securesms.backup.v2.proto.DistributionList as BackupDistributionList + +private val TAG = Log.tag(DistributionListTables::class.java) + +fun DistributionListTables.getAllForBackup(): List { + val records = readableDatabase + .select() + .from(DistributionListTables.ListTable.TABLE_NAME) + .run() + .readToList { cursor -> + val id: DistributionListId = DistributionListId.from(cursor.requireLong(DistributionListTables.ListTable.ID)) + val privacyMode: DistributionListPrivacyMode = cursor.requireObject(DistributionListTables.ListTable.PRIVACY_MODE, DistributionListPrivacyMode.Serializer) + + DistributionListRecord( + id = id, + name = cursor.requireNonNullString(DistributionListTables.ListTable.NAME), + distributionId = DistributionId.from(cursor.requireNonNullString(DistributionListTables.ListTable.DISTRIBUTION_ID)), + allowsReplies = CursorUtil.requireBoolean(cursor, DistributionListTables.ListTable.ALLOWS_REPLIES), + rawMembers = getRawMembers(id, privacyMode), + members = getMembers(id), + deletedAtTimestamp = 0L, + isUnknown = CursorUtil.requireBoolean(cursor, DistributionListTables.ListTable.IS_UNKNOWN), + privacyMode = privacyMode + ) + } + + return records + .map { record -> + BackupRecipient( + distributionList = BackupDistributionList( + name = record.name, + distributionId = record.distributionId.asUuid().toByteArray().toByteString(), + allowReplies = record.allowsReplies, + deletionTimestamp = record.deletedAtTimestamp, + privacyMode = record.privacyMode.toBackupPrivacyMode(), + memberRecipientIds = record.members.map { it.toLong() } + ) + ) + } +} + +fun DistributionListTables.restoreFromBackup(dlist: BackupDistributionList, backupState: BackupState): RecipientId { + val members: List = dlist.memberRecipientIds + .mapNotNull { backupState.backupToLocalRecipientId[it] } + + if (members.size != dlist.memberRecipientIds.size) { + Log.w(TAG, "Couldn't find some member recipients! Missing backup recipientIds: ${dlist.memberRecipientIds.toSet() - members.toSet()}") + } + + val dlistId = this.createList( + name = dlist.name, + members = members, + distributionId = DistributionId.from(UuidUtil.fromByteString(dlist.distributionId)), + allowsReplies = dlist.allowReplies, + deletionTimestamp = dlist.deletionTimestamp, + storageId = null, + privacyMode = dlist.privacyMode.toLocalPrivacyMode() + )!! + + return SignalDatabase.distributionLists.getRecipientId(dlistId)!! +} + +fun DistributionListTables.clearAllDataForBackupRestore() { + writableDatabase + .delete(DistributionListTables.ListTable.TABLE_NAME) + .run() + + writableDatabase + .delete(DistributionListTables.MembershipTable.TABLE_NAME) + .run() +} + +private fun DistributionListPrivacyMode.toBackupPrivacyMode(): BackupDistributionList.PrivacyMode { + return when (this) { + DistributionListPrivacyMode.ONLY_WITH -> BackupDistributionList.PrivacyMode.ONLY_WITH + DistributionListPrivacyMode.ALL -> BackupDistributionList.PrivacyMode.ALL + DistributionListPrivacyMode.ALL_EXCEPT -> BackupDistributionList.PrivacyMode.ALL_EXCEPT + } +} + +private fun BackupDistributionList.PrivacyMode.toLocalPrivacyMode(): DistributionListPrivacyMode { + return when (this) { + BackupDistributionList.PrivacyMode.UNKNOWN -> DistributionListPrivacyMode.ALL + BackupDistributionList.PrivacyMode.ONLY_WITH -> DistributionListPrivacyMode.ONLY_WITH + BackupDistributionList.PrivacyMode.ALL -> DistributionListPrivacyMode.ALL + BackupDistributionList.PrivacyMode.ALL_EXCEPT -> DistributionListPrivacyMode.ALL_EXCEPT + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/MessageTableBackupExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/MessageTableBackupExtensions.kt new file mode 100644 index 0000000000..4b90c64955 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/MessageTableBackupExtensions.kt @@ -0,0 +1,77 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2.database + +import org.signal.core.util.SqlUtil +import org.signal.core.util.logging.Log +import org.signal.core.util.select +import org.thoughtcrime.securesms.backup.v2.BackupState +import org.thoughtcrime.securesms.database.MessageTable +import org.thoughtcrime.securesms.database.MessageTypes + +private val TAG = Log.tag(MessageTable::class.java) +private const val BASE_TYPE = "base_type" + +fun MessageTable.getMessagesForBackup(): ChatItemExportIterator { + val cursor = readableDatabase + .select( + MessageTable.ID, + MessageTable.DATE_SENT, + MessageTable.DATE_RECEIVED, + MessageTable.DATE_SERVER, + MessageTable.TYPE, + MessageTable.THREAD_ID, + MessageTable.BODY, + MessageTable.MESSAGE_RANGES, + MessageTable.FROM_RECIPIENT_ID, + MessageTable.TO_RECIPIENT_ID, + MessageTable.EXPIRES_IN, + MessageTable.EXPIRE_STARTED, + MessageTable.REMOTE_DELETED, + MessageTable.UNIDENTIFIED, + MessageTable.QUOTE_ID, + MessageTable.QUOTE_AUTHOR, + MessageTable.QUOTE_BODY, + MessageTable.QUOTE_MISSING, + MessageTable.QUOTE_BODY_RANGES, + MessageTable.QUOTE_TYPE, + MessageTable.ORIGINAL_MESSAGE_ID, + MessageTable.LATEST_REVISION_ID, + MessageTable.HAS_DELIVERY_RECEIPT, + MessageTable.HAS_READ_RECEIPT, + MessageTable.VIEWED_COLUMN, + MessageTable.RECEIPT_TIMESTAMP, + MessageTable.READ, + MessageTable.NETWORK_FAILURES, + MessageTable.MISMATCHED_IDENTITIES, + "${MessageTable.TYPE} & ${MessageTypes.BASE_TYPE_MASK} AS ${ChatItemExportIterator.COLUMN_BASE_TYPE}" + ) + .from(MessageTable.TABLE_NAME) + .where( + """ + $BASE_TYPE IN ( + ${MessageTypes.BASE_INBOX_TYPE}, + ${MessageTypes.BASE_OUTBOX_TYPE}, + ${MessageTypes.BASE_SENT_TYPE}, + ${MessageTypes.BASE_SENDING_TYPE}, + ${MessageTypes.BASE_SENT_FAILED_TYPE} + ) OR ${MessageTable.IS_CALL_TYPE_CLAUSE} + """ + ) + .orderBy("${MessageTable.DATE_RECEIVED} ASC") + .run() + + return ChatItemExportIterator(cursor, 100) +} + +fun MessageTable.createChatItemInserter(backupState: BackupState): ChatItemImportInserter { + return ChatItemImportInserter(writableDatabase, backupState, 100) +} + +fun MessageTable.clearAllDataForBackupRestore() { + writableDatabase.delete(MessageTable.TABLE_NAME, null, null) + SqlUtil.resetAutoIncrementValue(writableDatabase, MessageTable.TABLE_NAME) +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/RecipientTableBackupExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/RecipientTableBackupExtensions.kt new file mode 100644 index 0000000000..e6ac7e8348 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/RecipientTableBackupExtensions.kt @@ -0,0 +1,332 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2.database + +import android.content.ContentValues +import android.database.Cursor +import okio.ByteString.Companion.toByteString +import org.signal.core.util.Base64 +import org.signal.core.util.SqlUtil +import org.signal.core.util.delete +import org.signal.core.util.logging.Log +import org.signal.core.util.nullIfBlank +import org.signal.core.util.requireBoolean +import org.signal.core.util.requireInt +import org.signal.core.util.requireLong +import org.signal.core.util.requireNonNullBlob +import org.signal.core.util.requireString +import org.signal.core.util.select +import org.signal.core.util.toInt +import org.signal.core.util.update +import org.signal.libsignal.zkgroup.InvalidInputException +import org.thoughtcrime.securesms.backup.v2.BackupState +import org.thoughtcrime.securesms.backup.v2.proto.AccountData +import org.thoughtcrime.securesms.backup.v2.proto.Contact +import org.thoughtcrime.securesms.backup.v2.proto.Group +import org.thoughtcrime.securesms.backup.v2.proto.Self +import org.thoughtcrime.securesms.database.GroupTable +import org.thoughtcrime.securesms.database.RecipientTable +import org.thoughtcrime.securesms.database.RecipientTableCursorUtil +import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.database.model.databaseprotos.RecipientExtras +import org.thoughtcrime.securesms.dependencies.ApplicationDependencies +import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter +import org.thoughtcrime.securesms.profiles.ProfileName +import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.recipients.RecipientId +import org.whispersystems.signalservice.api.push.ServiceId.ACI +import org.whispersystems.signalservice.api.push.ServiceId.PNI +import java.io.Closeable + +typealias BackupRecipient = org.thoughtcrime.securesms.backup.v2.proto.Recipient +typealias BackupGroup = Group + +/** + * Fetches all individual contacts for backups and returns the result as an iterator. + * It's important to note that the iterator still needs to be closed after it's used. + * It's recommended to use `.use` or a try-with-resources pattern. + */ +fun RecipientTable.getContactsForBackup(selfId: Long): BackupContactIterator { + val cursor = readableDatabase + .select( + RecipientTable.ID, + RecipientTable.ACI_COLUMN, + RecipientTable.PNI_COLUMN, + RecipientTable.USERNAME, + RecipientTable.E164, + RecipientTable.BLOCKED, + RecipientTable.HIDDEN, + RecipientTable.REGISTERED, + RecipientTable.UNREGISTERED_TIMESTAMP, + RecipientTable.PROFILE_KEY, + RecipientTable.PROFILE_SHARING, + RecipientTable.PROFILE_GIVEN_NAME, + RecipientTable.PROFILE_FAMILY_NAME, + RecipientTable.PROFILE_JOINED_NAME, + RecipientTable.MUTE_UNTIL, + RecipientTable.EXTRAS + ) + .from(RecipientTable.TABLE_NAME) + .where( + """ + ${RecipientTable.TYPE} = ? AND ( + ${RecipientTable.ACI_COLUMN} NOT NULL OR + ${RecipientTable.PNI_COLUMN} NOT NULL OR + ${RecipientTable.E164} NOT NULL + ) + """, + RecipientTable.RecipientType.INDIVIDUAL.id + ) + .run() + + return BackupContactIterator(cursor, selfId) +} + +fun RecipientTable.getGroupsForBackup(): BackupGroupIterator { + val cursor = readableDatabase + .select( + "${RecipientTable.TABLE_NAME}.${RecipientTable.ID}", + "${RecipientTable.TABLE_NAME}.${RecipientTable.BLOCKED}", + "${RecipientTable.TABLE_NAME}.${RecipientTable.PROFILE_SHARING}", + "${RecipientTable.TABLE_NAME}.${RecipientTable.MUTE_UNTIL}", + "${RecipientTable.TABLE_NAME}.${RecipientTable.EXTRAS}", + "${GroupTable.TABLE_NAME}.${GroupTable.V2_MASTER_KEY}", + "${GroupTable.TABLE_NAME}.${GroupTable.SHOW_AS_STORY_STATE}" + ) + .from( + """ + ${RecipientTable.TABLE_NAME} + INNER JOIN ${GroupTable.TABLE_NAME} ON ${RecipientTable.TABLE_NAME}.${RecipientTable.ID} = ${GroupTable.TABLE_NAME}.${GroupTable.RECIPIENT_ID} + """ + ) + .run() + + return BackupGroupIterator(cursor) +} + +/** + * Takes a [BackupRecipient] and writes it into the database. + */ +fun RecipientTable.restoreRecipientFromBackup(recipient: BackupRecipient, backupState: BackupState): RecipientId? { + // TODO Need to handle groups + // TODO Also, should we move this when statement up to mimic the export? Kinda weird that this calls distributionListTable functions + return when { + recipient.contact != null -> restoreContactFromBackup(recipient.contact) + recipient.distributionList != null -> SignalDatabase.distributionLists.restoreFromBackup(recipient.distributionList, backupState) + recipient.self != null -> Recipient.self().id + else -> { + Log.w(TAG, "Unrecognized recipient type!") + null + } + } +} + +/** + * Given [AccountData], this will insert the necessary data for the local user into the [RecipientTable]. + */ +fun RecipientTable.restoreSelfFromBackup(accountData: AccountData, selfId: RecipientId) { + val values = ContentValues().apply { + put(RecipientTable.PROFILE_GIVEN_NAME, accountData.givenName.nullIfBlank()) + put(RecipientTable.PROFILE_FAMILY_NAME, accountData.familyName.nullIfBlank()) + put(RecipientTable.PROFILE_JOINED_NAME, ProfileName.fromParts(accountData.givenName, accountData.familyName).toString().nullIfBlank()) + put(RecipientTable.PROFILE_AVATAR, accountData.avatarUrlPath.nullIfBlank()) + put(RecipientTable.REGISTERED, RecipientTable.RegisteredState.REGISTERED.id) + put(RecipientTable.PROFILE_SHARING, true) + put(RecipientTable.UNREGISTERED_TIMESTAMP, 0) + put(RecipientTable.EXTRAS, RecipientExtras().encode()) + + try { + put(RecipientTable.PROFILE_KEY, Base64.encodeWithPadding(accountData.profileKey.toByteArray()).nullIfBlank()) + } catch (e: InvalidInputException) { + Log.w(TAG, "Missing profile key during restore") + } + + put(RecipientTable.USERNAME, accountData.username) + } + + writableDatabase + .update(RecipientTable.TABLE_NAME) + .values(values) + .where("${RecipientTable.ID} = ?", selfId) + .run() +} + +fun RecipientTable.clearAllDataForBackupRestore() { + writableDatabase.delete(RecipientTable.TABLE_NAME).run() + SqlUtil.resetAutoIncrementValue(writableDatabase, RecipientTable.TABLE_NAME) + + RecipientId.clearCache() + ApplicationDependencies.getRecipientCache().clear() + ApplicationDependencies.getRecipientCache().clearSelf() +} + +private fun RecipientTable.restoreContactFromBackup(contact: Contact): RecipientId { + val id = getAndPossiblyMergePnpVerified( + aci = ACI.parseOrNull(contact.aci?.toByteArray()), + pni = PNI.parseOrNull(contact.pni?.toByteArray()), + e164 = contact.formattedE164 + ) + + val profileKey = contact.profileKey?.toByteArray() + + writableDatabase + .update(RecipientTable.TABLE_NAME) + .values( + RecipientTable.BLOCKED to contact.blocked, + RecipientTable.HIDDEN to contact.hidden, + RecipientTable.PROFILE_FAMILY_NAME to contact.profileFamilyName.nullIfBlank(), + RecipientTable.PROFILE_GIVEN_NAME to contact.profileGivenName.nullIfBlank(), + RecipientTable.PROFILE_JOINED_NAME to ProfileName.fromParts(contact.profileGivenName.nullIfBlank(), contact.profileFamilyName.nullIfBlank()).toString().nullIfBlank(), + RecipientTable.PROFILE_KEY to if (profileKey == null) null else Base64.encodeWithPadding(profileKey), + RecipientTable.PROFILE_SHARING to contact.profileSharing.toInt(), + RecipientTable.REGISTERED to contact.registered.toLocalRegisteredState().id, + RecipientTable.USERNAME to contact.username, + RecipientTable.UNREGISTERED_TIMESTAMP to contact.unregisteredTimestamp, + RecipientTable.EXTRAS to contact.toLocalExtras().encode() + ) + .where("${RecipientTable.ID} = ?", id) + .run() + + return id +} + +private fun Contact.toLocalExtras(): RecipientExtras { + return RecipientExtras( + hideStory = this.hideStory + ) +} + +/** + * Provides a nice iterable interface over a [RecipientTable] cursor, converting rows to [BackupRecipient]s. + * Important: Because this is backed by a cursor, you must close it. It's recommended to use `.use()` or try-with-resources. + */ +class BackupContactIterator(private val cursor: Cursor, private val selfId: Long) : Iterator, Closeable { + override fun hasNext(): Boolean { + return cursor.count > 0 && !cursor.isLast + } + + override fun next(): BackupRecipient? { + if (!cursor.moveToNext()) { + throw NoSuchElementException() + } + + val id = cursor.requireLong(RecipientTable.ID) + if (id == selfId) { + return BackupRecipient( + id = id, + self = Self() + ) + } + + val aci = ACI.parseOrNull(cursor.requireString(RecipientTable.ACI_COLUMN)) + val pni = PNI.parseOrNull(cursor.requireString(RecipientTable.PNI_COLUMN)) + val e164 = cursor.requireString(RecipientTable.E164)?.e164ToLong() + val registeredState = RecipientTable.RegisteredState.fromId(cursor.requireInt(RecipientTable.REGISTERED)) + val profileKey = cursor.requireString(RecipientTable.PROFILE_KEY) + val extras = RecipientTableCursorUtil.getExtras(cursor) + + if (aci == null && pni == null && e164 == null) { + return null + } + + return BackupRecipient( + id = id, + contact = Contact( + aci = aci?.toByteArray()?.toByteString(), + pni = pni?.toByteArray()?.toByteString(), + username = cursor.requireString(RecipientTable.USERNAME), + e164 = cursor.requireString(RecipientTable.E164)?.e164ToLong(), + blocked = cursor.requireBoolean(RecipientTable.BLOCKED), + hidden = cursor.requireBoolean(RecipientTable.HIDDEN), + registered = registeredState.toContactRegisteredState(), + unregisteredTimestamp = cursor.requireLong(RecipientTable.UNREGISTERED_TIMESTAMP), + profileKey = if (profileKey != null) Base64.decode(profileKey).toByteString() else null, + profileSharing = cursor.requireBoolean(RecipientTable.PROFILE_SHARING), + profileGivenName = cursor.requireString(RecipientTable.PROFILE_GIVEN_NAME).nullIfBlank(), + profileFamilyName = cursor.requireString(RecipientTable.PROFILE_FAMILY_NAME).nullIfBlank(), + hideStory = extras?.hideStory() ?: false + ) + ) + } + + override fun close() { + cursor.close() + } +} + +/** + * Provides a nice iterable interface over a [RecipientTable] cursor, converting rows to [BackupRecipient]s. + * Important: Because this is backed by a cursor, you must close it. It's recommended to use `.use()` or try-with-resources. + */ +class BackupGroupIterator(private val cursor: Cursor) : Iterator, Closeable { + override fun hasNext(): Boolean { + return cursor.count > 0 && !cursor.isLast + } + + override fun next(): BackupRecipient { + if (!cursor.moveToNext()) { + throw NoSuchElementException() + } + + val extras = RecipientTableCursorUtil.getExtras(cursor) + val showAsStoryState: GroupTable.ShowAsStoryState = GroupTable.ShowAsStoryState.deserialize(cursor.requireInt(GroupTable.SHOW_AS_STORY_STATE)) + + return BackupRecipient( + id = cursor.requireLong(RecipientTable.ID), + group = BackupGroup( + masterKey = cursor.requireNonNullBlob(GroupTable.V2_MASTER_KEY).toByteString(), + whitelisted = cursor.requireBoolean(RecipientTable.PROFILE_SHARING), + hideStory = extras?.hideStory() ?: false, + storySendMode = showAsStoryState.toGroupStorySendMode() + ) + ) + } + + override fun close() { + cursor.close() + } +} + +private fun String.e164ToLong(): Long? { + val fixed = if (this.startsWith("+")) { + this.substring(1) + } else { + this + } + + return fixed.toLongOrNull() +} + +private fun RecipientTable.RegisteredState.toContactRegisteredState(): Contact.Registered { + return when (this) { + RecipientTable.RegisteredState.REGISTERED -> Contact.Registered.REGISTERED + RecipientTable.RegisteredState.NOT_REGISTERED -> Contact.Registered.NOT_REGISTERED + RecipientTable.RegisteredState.UNKNOWN -> Contact.Registered.UNKNOWN + } +} + +private fun Contact.Registered.toLocalRegisteredState(): RecipientTable.RegisteredState { + return when (this) { + Contact.Registered.REGISTERED -> RecipientTable.RegisteredState.REGISTERED + Contact.Registered.NOT_REGISTERED -> RecipientTable.RegisteredState.NOT_REGISTERED + Contact.Registered.UNKNOWN -> RecipientTable.RegisteredState.UNKNOWN + } +} + +private fun GroupTable.ShowAsStoryState.toGroupStorySendMode(): Group.StorySendMode { + return when (this) { + GroupTable.ShowAsStoryState.ALWAYS -> Group.StorySendMode.ENABLED + GroupTable.ShowAsStoryState.NEVER -> Group.StorySendMode.DISABLED + GroupTable.ShowAsStoryState.IF_ACTIVE -> Group.StorySendMode.DEFAULT + } +} + +private val Contact.formattedE164: String? + get() { + return e164?.let { + PhoneNumberFormatter.get(ApplicationDependencies.getApplication()).format(e164.toString()) + } + } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ThreadTableBackupExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ThreadTableBackupExtensions.kt new file mode 100644 index 0000000000..8dc6c432d2 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ThreadTableBackupExtensions.kt @@ -0,0 +1,78 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2.database + +import android.database.Cursor +import org.signal.core.util.SqlUtil +import org.signal.core.util.insertInto +import org.signal.core.util.logging.Log +import org.signal.core.util.requireBoolean +import org.signal.core.util.requireInt +import org.signal.core.util.requireLong +import org.signal.core.util.select +import org.signal.core.util.toInt +import org.thoughtcrime.securesms.backup.v2.proto.Chat +import org.thoughtcrime.securesms.database.ThreadTable +import org.thoughtcrime.securesms.recipients.RecipientId +import java.io.Closeable + +private val TAG = Log.tag(ThreadTable::class.java) + +fun ThreadTable.getThreadsForBackup(): ChatIterator { + val cursor = readableDatabase + .select( + ThreadTable.ID, + ThreadTable.RECIPIENT_ID, + ThreadTable.ARCHIVED, + ThreadTable.PINNED, + ThreadTable.EXPIRES_IN + ) + .from(ThreadTable.TABLE_NAME) + .run() + + return ChatIterator(cursor) +} + +fun ThreadTable.clearAllDataForBackupRestore() { + writableDatabase.delete(ThreadTable.TABLE_NAME, null, null) + SqlUtil.resetAutoIncrementValue(writableDatabase, ThreadTable.TABLE_NAME) + clearCache() +} + +fun ThreadTable.restoreFromBackup(chat: Chat, recipientId: RecipientId): Long? { + return writableDatabase + .insertInto(ThreadTable.TABLE_NAME) + .values( + ThreadTable.RECIPIENT_ID to recipientId.serialize(), + ThreadTable.PINNED to chat.pinnedOrder, + ThreadTable.ARCHIVED to chat.archived.toInt() + ) + .run() +} + +class ChatIterator(private val cursor: Cursor) : Iterator, Closeable { + override fun hasNext(): Boolean { + return cursor.count > 0 && !cursor.isLast + } + + override fun next(): Chat { + if (!cursor.moveToNext()) { + throw NoSuchElementException() + } + + return Chat( + id = cursor.requireLong(ThreadTable.ID), + recipientId = cursor.requireLong(ThreadTable.RECIPIENT_ID), + archived = cursor.requireBoolean(ThreadTable.ARCHIVED), + pinnedOrder = cursor.requireInt(ThreadTable.PINNED), + expirationTimerMs = cursor.requireLong(ThreadTable.EXPIRES_IN) + ) + } + + override fun close() { + cursor.close() + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/AccountDataProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/AccountDataProcessor.kt new file mode 100644 index 0000000000..48da26b440 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/AccountDataProcessor.kt @@ -0,0 +1,160 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2.processor + +import okio.ByteString.Companion.EMPTY +import okio.ByteString.Companion.toByteString +import org.thoughtcrime.securesms.backup.v2.database.restoreSelfFromBackup +import org.thoughtcrime.securesms.backup.v2.proto.AccountData +import org.thoughtcrime.securesms.backup.v2.proto.Frame +import org.thoughtcrime.securesms.backup.v2.stream.BackupFrameEmitter +import org.thoughtcrime.securesms.components.settings.app.usernamelinks.UsernameQrCodeColorScheme +import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.database.SignalDatabase.Companion.recipients +import org.thoughtcrime.securesms.dependencies.ApplicationDependencies +import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob +import org.thoughtcrime.securesms.keyvalue.PhoneNumberPrivacyValues +import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.recipients.RecipientId +import org.thoughtcrime.securesms.subscription.Subscriber +import org.thoughtcrime.securesms.util.ProfileUtil +import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.whispersystems.signalservice.api.push.UsernameLinkComponents +import org.whispersystems.signalservice.api.storage.StorageRecordProtoUtil.defaultAccountRecord +import org.whispersystems.signalservice.api.subscriptions.SubscriberId +import org.whispersystems.signalservice.api.util.UuidUtil + +object AccountDataProcessor { + + fun export(emitter: BackupFrameEmitter) { + val context = ApplicationDependencies.getApplication() + + val self = Recipient.self().fresh() + val record = recipients.getRecordForSync(self.id) + + val subscriber: Subscriber? = SignalStore.signalDonationsValues().getSubscriber() + + emitter.emit( + Frame( + account = AccountData( + profileKey = self.profileKey?.toByteString() ?: EMPTY, + givenName = self.profileName.givenName, + familyName = self.profileName.familyName, + avatarUrlPath = self.profileAvatar ?: "", + subscriptionManuallyCancelled = SignalStore.signalDonationsValues().isUserManuallyCancelled(), + username = SignalStore.account().username, + subscriberId = subscriber?.subscriberId?.bytes?.toByteString() ?: defaultAccountRecord.subscriberId, + subscriberCurrencyCode = subscriber?.currencyCode ?: defaultAccountRecord.subscriberCurrencyCode, + accountSettings = AccountData.AccountSettings( + storyViewReceiptsEnabled = SignalStore.storyValues().viewedReceiptsEnabled, + noteToSelfMarkedUnread = record != null && record.syncExtras.isForcedUnread, + typingIndicators = TextSecurePreferences.isTypingIndicatorsEnabled(context), + readReceipts = TextSecurePreferences.isReadReceiptsEnabled(context), + sealedSenderIndicators = TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(context), + linkPreviews = SignalStore.settings().isLinkPreviewsEnabled, + notDiscoverableByPhoneNumber = SignalStore.phoneNumberPrivacy().phoneNumberListingMode.isUnlisted, + phoneNumberSharingMode = SignalStore.phoneNumberPrivacy().phoneNumberSharingMode.toBackupPhoneNumberSharingMode(), + preferContactAvatars = SignalStore.settings().isPreferSystemContactPhotos, + universalExpireTimer = SignalStore.settings().universalExpireTimer, + preferredReactionEmoji = SignalStore.emojiValues().reactions, + storiesDisabled = SignalStore.storyValues().isFeatureDisabled, + hasViewedOnboardingStory = SignalStore.storyValues().userHasViewedOnboardingStory, + hasSetMyStoriesPrivacy = SignalStore.storyValues().userHasBeenNotifiedAboutStories, + keepMutedChatsArchived = SignalStore.settings().shouldKeepMutedChatsArchived(), + displayBadgesOnProfile = SignalStore.signalDonationsValues().getDisplayBadgesOnProfile(), + hasSeenGroupStoryEducationSheet = SignalStore.storyValues().userHasSeenGroupStoryEducationSheet + ) + ) + ) + ) + } + + fun import(accountData: AccountData, selfId: RecipientId) { + recipients.restoreSelfFromBackup(accountData, selfId) + + SignalStore.account().setRegistered(true) + + val context = ApplicationDependencies.getApplication() + val settings = accountData.accountSettings + + if (settings != null) { + TextSecurePreferences.setReadReceiptsEnabled(context, settings.readReceipts) + TextSecurePreferences.setTypingIndicatorsEnabled(context, settings.typingIndicators) + TextSecurePreferences.setShowUnidentifiedDeliveryIndicatorsEnabled(context, settings.sealedSenderIndicators) + SignalStore.settings().isLinkPreviewsEnabled = settings.linkPreviews + SignalStore.phoneNumberPrivacy().phoneNumberListingMode = if (settings.notDiscoverableByPhoneNumber) PhoneNumberPrivacyValues.PhoneNumberListingMode.UNLISTED else PhoneNumberPrivacyValues.PhoneNumberListingMode.LISTED + SignalStore.phoneNumberPrivacy().phoneNumberSharingMode = settings.phoneNumberSharingMode.toLocalPhoneNumberMode() + SignalStore.settings().isPreferSystemContactPhotos = settings.preferContactAvatars + SignalStore.settings().universalExpireTimer = settings.universalExpireTimer + SignalStore.emojiValues().reactions = settings.preferredReactionEmoji + SignalStore.signalDonationsValues().setDisplayBadgesOnProfile(settings.displayBadgesOnProfile) + SignalStore.settings().setKeepMutedChatsArchived(settings.keepMutedChatsArchived) + SignalStore.storyValues().userHasBeenNotifiedAboutStories = settings.hasSetMyStoriesPrivacy + SignalStore.storyValues().userHasViewedOnboardingStory = settings.hasViewedOnboardingStory + SignalStore.storyValues().isFeatureDisabled = settings.storiesDisabled + SignalStore.storyValues().userHasSeenGroupStoryEducationSheet = settings.hasSeenGroupStoryEducationSheet + SignalStore.storyValues().viewedReceiptsEnabled = settings.storyViewReceiptsEnabled ?: settings.readReceipts + + if (accountData.subscriptionManuallyCancelled) { + SignalStore.signalDonationsValues().updateLocalStateForManualCancellation() + } else { + SignalStore.signalDonationsValues().clearUserManuallyCancelled() + } + + if (accountData.subscriberId.size > 0) { + val subscriber = Subscriber(SubscriberId.fromBytes(accountData.subscriberId.toByteArray()), accountData.subscriberCurrencyCode) + SignalStore.signalDonationsValues().setSubscriber(subscriber) + } + + if (accountData.avatarUrlPath.isNotEmpty()) { + ApplicationDependencies.getJobManager().add(RetrieveProfileAvatarJob(Recipient.self().fresh(), accountData.avatarUrlPath)) + } + + if (accountData.usernameLink != null) { + SignalStore.account().usernameLink = UsernameLinkComponents( + accountData.usernameLink.entropy.toByteArray(), + UuidUtil.parseOrThrow(accountData.usernameLink.serverId.toByteArray()) + ) + SignalStore.misc().usernameQrCodeColorScheme = accountData.usernameLink.color.toLocalUsernameColor() + } + } + + SignalDatabase.runPostSuccessfulTransaction { ProfileUtil.handleSelfProfileKeyChange() } + + Recipient.self().live().refresh() + } + + private fun PhoneNumberPrivacyValues.PhoneNumberSharingMode.toBackupPhoneNumberSharingMode(): AccountData.PhoneNumberSharingMode { + return when (this) { + PhoneNumberPrivacyValues.PhoneNumberSharingMode.DEFAULT -> AccountData.PhoneNumberSharingMode.EVERYBODY + PhoneNumberPrivacyValues.PhoneNumberSharingMode.EVERYBODY -> AccountData.PhoneNumberSharingMode.EVERYBODY + PhoneNumberPrivacyValues.PhoneNumberSharingMode.NOBODY -> AccountData.PhoneNumberSharingMode.NOBODY + } + } + + private fun AccountData.PhoneNumberSharingMode.toLocalPhoneNumberMode(): PhoneNumberPrivacyValues.PhoneNumberSharingMode { + return when (this) { + AccountData.PhoneNumberSharingMode.UNKNOWN -> PhoneNumberPrivacyValues.PhoneNumberSharingMode.EVERYBODY + AccountData.PhoneNumberSharingMode.EVERYBODY -> PhoneNumberPrivacyValues.PhoneNumberSharingMode.EVERYBODY + AccountData.PhoneNumberSharingMode.NOBODY -> PhoneNumberPrivacyValues.PhoneNumberSharingMode.NOBODY + } + } + + private fun AccountData.UsernameLink.Color?.toLocalUsernameColor(): UsernameQrCodeColorScheme { + return when (this) { + AccountData.UsernameLink.Color.BLUE -> UsernameQrCodeColorScheme.Blue + AccountData.UsernameLink.Color.WHITE -> UsernameQrCodeColorScheme.White + AccountData.UsernameLink.Color.GREY -> UsernameQrCodeColorScheme.Grey + AccountData.UsernameLink.Color.OLIVE -> UsernameQrCodeColorScheme.Tan + AccountData.UsernameLink.Color.GREEN -> UsernameQrCodeColorScheme.Green + AccountData.UsernameLink.Color.ORANGE -> UsernameQrCodeColorScheme.Orange + AccountData.UsernameLink.Color.PINK -> UsernameQrCodeColorScheme.Pink + AccountData.UsernameLink.Color.PURPLE -> UsernameQrCodeColorScheme.Purple + else -> UsernameQrCodeColorScheme.Blue + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/CallLogBackupProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/CallLogBackupProcessor.kt new file mode 100644 index 0000000000..d17deaec7c --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/CallLogBackupProcessor.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2.processor + +import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.backup.v2.BackupState +import org.thoughtcrime.securesms.backup.v2.database.getCallsForBackup +import org.thoughtcrime.securesms.backup.v2.database.restoreCallLogFromBackup +import org.thoughtcrime.securesms.backup.v2.proto.Frame +import org.thoughtcrime.securesms.backup.v2.stream.BackupFrameEmitter +import org.thoughtcrime.securesms.database.SignalDatabase + +typealias BackupCall = org.thoughtcrime.securesms.backup.v2.proto.Call + +object CallLogBackupProcessor { + + val TAG = Log.tag(CallLogBackupProcessor::class.java) + + fun export(emitter: BackupFrameEmitter) { + SignalDatabase.calls.getCallsForBackup().use { reader -> + for (callLog in reader) { + if (callLog != null) { + emitter.emit(Frame(call = callLog)) + } + } + } + } + + fun import(call: BackupCall, backupState: BackupState) { + SignalDatabase.calls.restoreCallLogFromBackup(call, backupState) + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/ChatBackupProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/ChatBackupProcessor.kt new file mode 100644 index 0000000000..e10d0921e2 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/ChatBackupProcessor.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2.processor + +import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.backup.v2.BackupState +import org.thoughtcrime.securesms.backup.v2.database.getThreadsForBackup +import org.thoughtcrime.securesms.backup.v2.database.restoreFromBackup +import org.thoughtcrime.securesms.backup.v2.proto.Chat +import org.thoughtcrime.securesms.backup.v2.proto.Frame +import org.thoughtcrime.securesms.backup.v2.stream.BackupFrameEmitter +import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.recipients.RecipientId + +object ChatBackupProcessor { + val TAG = Log.tag(ChatBackupProcessor::class.java) + + fun export(emitter: BackupFrameEmitter) { + SignalDatabase.threads.getThreadsForBackup().use { reader -> + for (chat in reader) { + emitter.emit(Frame(chat = chat)) + } + } + } + + fun import(chat: Chat, backupState: BackupState) { + val recipientId: RecipientId? = backupState.backupToLocalRecipientId[chat.recipientId] + if (recipientId == null) { + Log.w(TAG, "Missing recipient for chat ${chat.id}") + return + } + + SignalDatabase.threads.restoreFromBackup(chat, recipientId)?.let { threadId -> + backupState.chatIdToLocalRecipientId[chat.id] = recipientId + backupState.chatIdToLocalThreadId[chat.id] = threadId + backupState.chatIdToBackupRecipientId[chat.id] = chat.recipientId + } + + // TODO there's several fields in the chat that actually need to be restored on the recipient table + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/ChatItemBackupProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/ChatItemBackupProcessor.kt new file mode 100644 index 0000000000..e739fc9a28 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/ChatItemBackupProcessor.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2.processor + +import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.backup.v2.BackupState +import org.thoughtcrime.securesms.backup.v2.database.ChatItemImportInserter +import org.thoughtcrime.securesms.backup.v2.database.createChatItemInserter +import org.thoughtcrime.securesms.backup.v2.database.getMessagesForBackup +import org.thoughtcrime.securesms.backup.v2.proto.Frame +import org.thoughtcrime.securesms.backup.v2.stream.BackupFrameEmitter +import org.thoughtcrime.securesms.database.SignalDatabase + +object ChatItemBackupProcessor { + val TAG = Log.tag(ChatItemBackupProcessor::class.java) + + fun export(emitter: BackupFrameEmitter) { + SignalDatabase.messages.getMessagesForBackup().use { chatItems -> + for (chatItem in chatItems) { + emitter.emit(Frame(chatItem = chatItem)) + } + } + } + + fun beginImport(backupState: BackupState): ChatItemImportInserter { + return SignalDatabase.messages.createChatItemInserter(backupState) + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/RecipientBackupProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/RecipientBackupProcessor.kt new file mode 100644 index 0000000000..0c8db3753e --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/RecipientBackupProcessor.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2.processor + +import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.backup.v2.BackupState +import org.thoughtcrime.securesms.backup.v2.database.getAllForBackup +import org.thoughtcrime.securesms.backup.v2.database.getContactsForBackup +import org.thoughtcrime.securesms.backup.v2.database.getGroupsForBackup +import org.thoughtcrime.securesms.backup.v2.database.restoreRecipientFromBackup +import org.thoughtcrime.securesms.backup.v2.proto.Frame +import org.thoughtcrime.securesms.backup.v2.stream.BackupFrameEmitter +import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.recipients.Recipient + +typealias BackupRecipient = org.thoughtcrime.securesms.backup.v2.proto.Recipient + +object RecipientBackupProcessor { + + val TAG = Log.tag(RecipientBackupProcessor::class.java) + + fun export(emitter: BackupFrameEmitter) { + val selfId = Recipient.self().id.toLong() + + SignalDatabase.recipients.getContactsForBackup(selfId).use { reader -> + for (backupRecipient in reader) { + if (backupRecipient != null) { + emitter.emit(Frame(recipient = backupRecipient)) + } + } + } + + SignalDatabase.recipients.getGroupsForBackup().use { reader -> + for (backupRecipient in reader) { + emitter.emit(Frame(recipient = backupRecipient)) + } + } + + SignalDatabase.distributionLists.getAllForBackup().forEach { + emitter.emit(Frame(recipient = it)) + } + } + + fun import(recipient: BackupRecipient, backupState: BackupState) { + val newId = SignalDatabase.recipients.restoreRecipientFromBackup(recipient, backupState) + if (newId != null) { + backupState.backupToLocalRecipientId[recipient.id] = newId + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/BackupEncryptedOutputStream.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/BackupEncryptedOutputStream.kt new file mode 100644 index 0000000000..5a7c535d13 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/BackupEncryptedOutputStream.kt @@ -0,0 +1,75 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2.stream + +import org.signal.libsignal.protocol.kdf.HKDF +import java.io.FilterOutputStream +import java.io.OutputStream +import javax.crypto.Cipher +import javax.crypto.Mac +import javax.crypto.spec.IvParameterSpec +import javax.crypto.spec.SecretKeySpec + +class BackupEncryptedOutputStream(key: ByteArray, backupId: ByteArray, wrapped: OutputStream) : FilterOutputStream(wrapped) { + + val cipher: Cipher = Cipher.getInstance("AES/CBC/PKCS5Padding") + val mac: Mac = Mac.getInstance("HmacSHA256") + + var finalMac: ByteArray? = null + + init { + if (key.size != 32) { + throw IllegalArgumentException("Key must be 32 bytes!") + } + + if (backupId.size != 16) { + throw IllegalArgumentException("BackupId must be 32 bytes!") + } + + val extendedKey = HKDF.deriveSecrets(key, backupId, "20231003_Signal_Backups_EncryptMessageBackup".toByteArray(), 80) + val macKey = extendedKey.copyOfRange(0, 32) + val cipherKey = extendedKey.copyOfRange(32, 64) + val iv = extendedKey.copyOfRange(64, 80) + + cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(cipherKey, "AES"), IvParameterSpec(iv)) + mac.init(SecretKeySpec(macKey, "HmacSHA256")) + } + + override fun write(b: Int) { + throw UnsupportedOperationException() + } + + override fun write(data: ByteArray) { + write(data, 0, data.size) + } + + override fun write(data: ByteArray, off: Int, len: Int) { + cipher.update(data, off, len)?.let { ciphertext -> + mac.update(ciphertext) + super.write(ciphertext) + } + } + + override fun flush() { + cipher.doFinal()?.let { ciphertext -> + mac.update(ciphertext) + super.write(ciphertext) + } + + finalMac = mac.doFinal() + + super.flush() + } + + override fun close() { + flush() + super.close() + } + + fun getMac(): ByteArray { + return finalMac ?: throw IllegalStateException("Mac not yet available! You must call flush() before asking for the mac.") + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/BackupExportWriter.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/BackupExportWriter.kt new file mode 100644 index 0000000000..24d3e34eb3 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/BackupExportWriter.kt @@ -0,0 +1,12 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2.stream + +import org.thoughtcrime.securesms.backup.v2.proto.Frame + +interface BackupExportWriter : AutoCloseable { + fun write(frame: Frame) +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/BackupFrameEmitter.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/BackupFrameEmitter.kt new file mode 100644 index 0000000000..50ae25584f --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/BackupFrameEmitter.kt @@ -0,0 +1,15 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2.stream + +import org.thoughtcrime.securesms.backup.v2.proto.Frame + +/** + * An interface that lets sub-processors emit [Frame]s as they export data. + */ +fun interface BackupFrameEmitter { + fun emit(frame: Frame) +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/BackupImportStream.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/BackupImportStream.kt new file mode 100644 index 0000000000..b47df24bc5 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/BackupImportStream.kt @@ -0,0 +1,12 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2.stream + +import org.thoughtcrime.securesms.backup.v2.proto.Frame + +interface BackupImportStream { + fun read(): Frame? +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/EncryptedBackupReader.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/EncryptedBackupReader.kt new file mode 100644 index 0000000000..c32da6ab00 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/EncryptedBackupReader.kt @@ -0,0 +1,112 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2.stream + +import org.signal.core.util.readFully +import org.signal.core.util.readNBytesOrThrow +import org.signal.core.util.readVarInt32 +import org.signal.core.util.stream.MacInputStream +import org.signal.core.util.stream.TruncatingInputStream +import org.thoughtcrime.securesms.backup.v2.proto.Frame +import org.whispersystems.signalservice.api.backup.BackupKey +import org.whispersystems.signalservice.api.push.ServiceId.ACI +import java.io.EOFException +import java.io.IOException +import java.io.InputStream +import java.util.zip.GZIPInputStream +import javax.crypto.Cipher +import javax.crypto.CipherInputStream +import javax.crypto.Mac +import javax.crypto.spec.IvParameterSpec +import javax.crypto.spec.SecretKeySpec + +/** + * Provides the ability to read backup frames in a streaming fashion from a target [InputStream]. + * As it's being read, it will be both decrypted and uncompressed. Specifically, the data is decrypted, + * that decrypted data is gunzipped, then that data is read as frames. + */ +class EncryptedBackupReader( + key: BackupKey, + aci: ACI, + streamLength: Long, + dataStream: () -> InputStream +) : Iterator, AutoCloseable { + + var next: Frame? = null + val stream: InputStream + + init { + val keyMaterial = key.deriveSecrets(aci) + + val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding").apply { + init(Cipher.DECRYPT_MODE, SecretKeySpec(keyMaterial.cipherKey, "AES"), IvParameterSpec(keyMaterial.iv)) + } + + validateMac(keyMaterial.macKey, streamLength, dataStream()) + + stream = GZIPInputStream( + CipherInputStream( + TruncatingInputStream( + wrapped = dataStream(), + maxBytes = streamLength - MAC_SIZE + ), + cipher + ) + ) + + next = read() + } + + override fun hasNext(): Boolean { + return next != null + } + + override fun next(): Frame { + next?.let { out -> + next = read() + return out + } ?: throw NoSuchElementException() + } + + private fun read(): Frame? { + try { + val length = stream.readVarInt32().also { if (it < 0) return null } + val frameBytes: ByteArray = stream.readNBytesOrThrow(length) + + return Frame.ADAPTER.decode(frameBytes) + } catch (e: EOFException) { + return null + } + } + + override fun close() { + stream.close() + } + + companion object { + const val MAC_SIZE = 32 + + fun validateMac(macKey: ByteArray, streamLength: Long, dataStream: InputStream) { + val mac = Mac.getInstance("HmacSHA256").apply { + init(SecretKeySpec(macKey, "HmacSHA256")) + } + + val macStream = MacInputStream( + wrapped = TruncatingInputStream(dataStream, maxBytes = streamLength - MAC_SIZE), + mac = mac + ) + + macStream.readFully(false) + + val calculatedMac = macStream.mac.doFinal() + val expectedMac = dataStream.readNBytesOrThrow(MAC_SIZE) + + if (!calculatedMac.contentEquals(expectedMac)) { + throw IOException("Invalid MAC!") + } + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/EncryptedBackupWriter.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/EncryptedBackupWriter.kt new file mode 100644 index 0000000000..530f383195 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/EncryptedBackupWriter.kt @@ -0,0 +1,76 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2.stream + +import org.signal.core.util.stream.MacOutputStream +import org.signal.core.util.writeVarInt32 +import org.thoughtcrime.securesms.backup.v2.proto.Frame +import org.whispersystems.signalservice.api.backup.BackupKey +import org.whispersystems.signalservice.api.push.ServiceId.ACI +import java.io.IOException +import java.io.OutputStream +import java.util.zip.GZIPOutputStream +import javax.crypto.Cipher +import javax.crypto.CipherOutputStream +import javax.crypto.Mac +import javax.crypto.spec.IvParameterSpec +import javax.crypto.spec.SecretKeySpec + +/** + * Provides the ability to write backup frames in a streaming fashion to a target [OutputStream]. + * As it's being written, it will be both encrypted and compressed. Specifically, the backup frames + * are gzipped, that gzipped data is encrypted, and then an HMAC of the encrypted data is appended + * to the end of the [outputStream]. + */ +class EncryptedBackupWriter( + key: BackupKey, + aci: ACI, + private val outputStream: OutputStream, + private val append: (ByteArray) -> Unit +) : BackupExportWriter { + + private val mainStream: GZIPOutputStream + private val macStream: MacOutputStream + + init { + val keyMaterial = key.deriveSecrets(aci) + + val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding").apply { + init(Cipher.ENCRYPT_MODE, SecretKeySpec(keyMaterial.cipherKey, "AES"), IvParameterSpec(keyMaterial.iv)) + } + + val mac = Mac.getInstance("HmacSHA256").apply { + init(SecretKeySpec(keyMaterial.macKey, "HmacSHA256")) + } + + macStream = MacOutputStream(outputStream, mac) + + mainStream = GZIPOutputStream( + CipherOutputStream( + macStream, + cipher + ) + ) + } + + @Throws(IOException::class) + override fun write(frame: Frame) { + val frameBytes: ByteArray = frame.encode() + + mainStream.writeVarInt32(frameBytes.size) + mainStream.write(frameBytes) + } + + @Throws(IOException::class) + override fun close() { + // We need to close the main stream in order for the gzip and all the cipher operations to fully finish before + // we can calculate the MAC. Unfortunately flush()/finish() is not sufficient. So we have to defer to the + // caller to append the bytes to the end of the data however they see fit (like appending to a file). + mainStream.close() + val mac = macStream.mac.doFinal() + append(mac) + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/PlainTextBackupReader.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/PlainTextBackupReader.kt new file mode 100644 index 0000000000..91c9945d9d --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/PlainTextBackupReader.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2.stream + +import org.signal.core.util.readNBytesOrThrow +import org.signal.core.util.readVarInt32 +import org.thoughtcrime.securesms.backup.v2.proto.Frame +import java.io.EOFException +import java.io.InputStream + +/** + * Reads a plaintext backup import stream one frame at a time. + */ +class PlainTextBackupReader(val inputStream: InputStream) : Iterator { + + var next: Frame? = null + + init { + next = read() + } + + override fun hasNext(): Boolean { + return next != null + } + + override fun next(): Frame { + next?.let { out -> + next = read() + return out + } ?: throw NoSuchElementException() + } + + private fun read(): Frame? { + try { + val length = inputStream.readVarInt32().also { if (it < 0) return null } + val frameBytes: ByteArray = inputStream.readNBytesOrThrow(length) + + return Frame.ADAPTER.decode(frameBytes) + } catch (e: EOFException) { + return null + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/PlainTextBackupWriter.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/PlainTextBackupWriter.kt new file mode 100644 index 0000000000..a4e414dcba --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/stream/PlainTextBackupWriter.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.backup.v2.stream + +import org.signal.core.util.writeVarInt32 +import org.thoughtcrime.securesms.backup.v2.proto.Frame +import java.io.IOException +import java.io.OutputStream + +/** + * Writes backup frames to the wrapped stream in plain text. Only for testing! + */ +class PlainTextBackupWriter(private val outputStream: OutputStream) : BackupExportWriter { + + @Throws(IOException::class) + override fun write(frame: Frame) { + val frameBytes: ByteArray = frame.encode() + + outputStream.writeVarInt32(frameBytes.size) + outputStream.write(frameBytes) + } + + override fun close() { + outputStream.close() + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/calls/links/SignalCallRow.kt b/app/src/main/java/org/thoughtcrime/securesms/calls/links/SignalCallRow.kt index 8bea37d63d..543d88bd1a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/calls/links/SignalCallRow.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/calls/links/SignalCallRow.kt @@ -119,7 +119,8 @@ fun SignalCallRow( .align(CenterVertically) ) { Text( - text = callLink.state.name.ifEmpty { stringResource(id = R.string.CreateCallLinkBottomSheetDialogFragment__signal_call) } + text = callLink.state.name.ifEmpty { stringResource(id = R.string.CreateCallLinkBottomSheetDialogFragment__signal_call) }, + color = MaterialTheme.colorScheme.onSurface ) Text( text = callUrl, diff --git a/app/src/main/java/org/thoughtcrime/securesms/calls/links/create/CreateCallLinkBottomSheetDialogFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/calls/links/create/CreateCallLinkBottomSheetDialogFragment.kt index 2462ef9add..fe80e42dd6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/calls/links/create/CreateCallLinkBottomSheetDialogFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/calls/links/create/CreateCallLinkBottomSheetDialogFragment.kt @@ -92,6 +92,7 @@ class CreateCallLinkBottomSheetDialogFragment : ComposeBottomSheetDialogFragment Text( text = stringResource(id = R.string.CreateCallLinkBottomSheetDialogFragment__create_call_link), style = MaterialTheme.typography.titleLarge, + color = MaterialTheme.colorScheme.onSurface, textAlign = TextAlign.Center, modifier = Modifier.fillMaxWidth() ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogAdapter.kt index a9d3f90c3b..9125e44670 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogAdapter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogAdapter.kt @@ -32,6 +32,11 @@ class CallLogAdapter( callbacks: Callbacks ) : PagingMappingAdapter() { + companion object { + private const val PAYLOAD_SELECTION_STATE = "PAYLOAD_SELECTION_STATE" + private const val PAYLOAD_TIMESTAMP = "PAYLOAD_TIMESTAMP" + } + init { registerFactory( CallModel::class.java, @@ -72,6 +77,10 @@ class CallLogAdapter( ) } + fun onTimestampTick() { + notifyItemRangeChanged(0, itemCount, PAYLOAD_TIMESTAMP) + } + fun submitCallRows( rows: List, selectionState: CallLogSelectionState, @@ -98,9 +107,6 @@ class CallLogAdapter( val selectionState: CallLogSelectionState, val itemCount: Int ) : MappingModel { - companion object { - const val PAYLOAD_SELECTION_STATE = "PAYLOAD_SELECTION_STATE" - } override fun areItemsTheSame(newItem: CallModel): Boolean = call.id == newItem.call.id override fun areContentsTheSame(newItem: CallModel): Boolean { @@ -133,10 +139,6 @@ class CallLogAdapter( val itemCount: Int ) : MappingModel { - companion object { - const val PAYLOAD_SELECTION_STATE = "PAYLOAD_SELECTION_STATE" - } - override fun areItemsTheSame(newItem: CallLinkModel): Boolean { return callLink.record.roomId == newItem.callLink.record.roomId } @@ -149,7 +151,7 @@ class CallLogAdapter( override fun getChangePayload(newItem: CallLinkModel): Any? { return if (callLink == newItem.callLink && (!isSelectionStateTheSame(newItem) || !isItemCountTheSame(newItem))) { - CallModel.PAYLOAD_SELECTION_STATE + PAYLOAD_SELECTION_STATE } else { null } @@ -183,6 +185,10 @@ class CallLogAdapter( private val onStartVideoCallClicked: (Recipient) -> Unit ) : BindingViewHolder(binding) { override fun bind(model: CallLinkModel) { + if (payload.size == 1 && payload.contains(PAYLOAD_TIMESTAMP)) { + return + } + itemView.setOnClickListener { onCallLinkClicked(model.callLink) } @@ -195,7 +201,7 @@ class CallLogAdapter( binding.callSelected.isChecked = model.selectionState.contains(model.callLink.id) binding.callSelected.visible = model.selectionState.isNotEmpty(model.itemCount) - if (payload.contains(CallModel.PAYLOAD_SELECTION_STATE)) { + if (payload.isNotEmpty()) { return } @@ -252,7 +258,11 @@ class CallLogAdapter( binding.callSelected.isChecked = model.selectionState.contains(model.call.id) binding.callSelected.visible = model.selectionState.isNotEmpty(model.itemCount) - if (payload.contains(CallModel.PAYLOAD_SELECTION_STATE)) { + if (payload.contains(PAYLOAD_TIMESTAMP)) { + presentCallInfo(model.call, model.call.date) + } + + if (payload.isNotEmpty()) { return } @@ -295,7 +305,7 @@ class CallLogAdapter( val color = ContextCompat.getColor( context, - if (call.record.event == CallTable.Event.MISSED) { + if (call.record.event.isMissedCall()) { R.color.signal_colorError } else { R.color.signal_colorOnSurfaceVariant @@ -365,7 +375,7 @@ class CallLogAdapter( MessageTypes.OUTGOING_AUDIO_CALL_TYPE, MessageTypes.OUTGOING_VIDEO_CALL_TYPE -> R.drawable.symbol_arrow_upright_compact_16 MessageTypes.GROUP_CALL_TYPE -> when { call.type == CallTable.Type.AD_HOC_CALL -> R.drawable.symbol_link_compact_16 - call.event == CallTable.Event.MISSED -> R.drawable.symbol_missed_incoming_compact_16 + call.event.isMissedCall() -> R.drawable.symbol_missed_incoming_compact_16 call.event == CallTable.Event.GENERIC_GROUP_CALL || call.event == CallTable.Event.JOINED -> R.drawable.symbol_group_compact_16 call.direction == CallTable.Direction.INCOMING -> R.drawable.symbol_arrow_downleft_compact_16 call.direction == CallTable.Direction.OUTGOING -> R.drawable.symbol_arrow_upright_compact_16 @@ -379,8 +389,8 @@ class CallLogAdapter( @StringRes private fun getCallStateStringRes(call: CallTable.Call): Int { return when (call.messageType) { - MessageTypes.MISSED_VIDEO_CALL_TYPE -> R.string.CallLogAdapter__missed - MessageTypes.MISSED_AUDIO_CALL_TYPE -> R.string.CallLogAdapter__missed + MessageTypes.MISSED_VIDEO_CALL_TYPE, + MessageTypes.MISSED_AUDIO_CALL_TYPE -> if (call.event == CallTable.Event.MISSED) R.string.CallLogAdapter__missed else R.string.CallLogAdapter__missed_notification_profile MessageTypes.INCOMING_AUDIO_CALL_TYPE -> R.string.CallLogAdapter__incoming MessageTypes.INCOMING_VIDEO_CALL_TYPE -> R.string.CallLogAdapter__incoming MessageTypes.OUTGOING_AUDIO_CALL_TYPE -> R.string.CallLogAdapter__outgoing @@ -388,6 +398,7 @@ class CallLogAdapter( MessageTypes.GROUP_CALL_TYPE -> when { call.type == CallTable.Type.AD_HOC_CALL -> R.string.CallLogAdapter__call_link call.event == CallTable.Event.MISSED -> R.string.CallLogAdapter__missed + call.event == CallTable.Event.MISSED_NOTIFICATION_PROFILE -> R.string.CallLogAdapter__missed_notification_profile call.event == CallTable.Event.GENERIC_GROUP_CALL || call.event == CallTable.Event.JOINED -> R.string.CallPreference__group_call call.direction == CallTable.Direction.INCOMING -> R.string.CallLogAdapter__incoming call.direction == CallTable.Direction.OUTGOING -> R.string.CallLogAdapter__outgoing diff --git a/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogFragment.kt index 45a522f031..54b4bc5084 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogFragment.kt @@ -43,6 +43,7 @@ import org.thoughtcrime.securesms.components.menu.ActionItem import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity import org.thoughtcrime.securesms.components.settings.app.notifications.manual.NotificationProfileSelectionFragment import org.thoughtcrime.securesms.components.settings.conversation.ConversationSettingsActivity +import org.thoughtcrime.securesms.conversation.ConversationUpdateTick import org.thoughtcrime.securesms.conversation.SignalBottomActionBarController import org.thoughtcrime.securesms.conversationlist.ConversationFilterBehavior import org.thoughtcrime.securesms.conversationlist.chatfilter.ConversationFilterSource @@ -81,6 +82,8 @@ class CallLogFragment : Fragment(R.layout.call_log_fragment), CallLogAdapter.Cal private val disposables = LifecycleDisposable() private val callLogContextMenu = CallLogContextMenu(this, this) private val callLogActionMode = CallLogActionMode(CallLogActionModeCallback()) + private val conversationUpdateTick: ConversationUpdateTick = ConversationUpdateTick(this::onTimestampTick) + private var callLogAdapter: CallLogAdapter? = null private lateinit var signalBottomActionBarController: SignalBottomActionBarController @@ -115,20 +118,22 @@ class CallLogFragment : Fragment(R.layout.call_log_fragment), CallLogAdapter.Cal requireActivity().addMenuProvider(menuProvider, viewLifecycleOwner) initializeSharedElementTransition() - val adapter = CallLogAdapter(this) + viewLifecycleOwner.lifecycle.addObserver(conversationUpdateTick) + + val callLogAdapter = CallLogAdapter(this) disposables.bindTo(viewLifecycleOwner) - adapter.setPagingController(viewModel.controller) + callLogAdapter.setPagingController(viewModel.controller) val scrollToPositionDelegate = ScrollToPositionDelegate( recyclerView = binding.recycler, - canJumpToPosition = { adapter.isAvailableAround(it) } + canJumpToPosition = { callLogAdapter.isAvailableAround(it) } ) disposables += scrollToPositionDelegate disposables += Flowables.combineLatest(viewModel.data, viewModel.selected) .observeOn(AndroidSchedulers.mainThread()) .subscribe { (data, selected) -> - val filteredCount = adapter.submitCallRows( + val filteredCount = callLogAdapter.submitCallRows( data, selected, scrollToPositionDelegate::notifyListCommitted @@ -147,7 +152,8 @@ class CallLogFragment : Fragment(R.layout.call_log_fragment), CallLogAdapter.Cal } } - binding.recycler.adapter = adapter + binding.recycler.adapter = callLogAdapter + this.callLogAdapter = callLogAdapter requireListener().bindScrollHelper(binding.recycler) binding.fab.setOnClickListener { @@ -200,6 +206,10 @@ class CallLogFragment : Fragment(R.layout.call_log_fragment), CallLogAdapter.Cal viewModel.markAllCallEventsRead() } + private fun onTimestampTick() { + callLogAdapter?.onTimestampTick() + } + private fun initializeSharedElementTransition() { ViewCompat.setTransitionName(binding.fab, "new_convo_fab") ViewCompat.setTransitionName(binding.fabSharedElementTarget, "camera_fab") diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/AlbumThumbnailView.java b/app/src/main/java/org/thoughtcrime/securesms/components/AlbumThumbnailView.java index 6912873ef2..9ceeaf091c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/AlbumThumbnailView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/AlbumThumbnailView.java @@ -30,9 +30,9 @@ public class AlbumThumbnailView extends FrameLayout { private @Nullable SlideClickListener thumbnailClickListener; - private @Nullable SlidesClickedListener downloadClickListener; - private @Nullable SlidesClickedListener cancelDownloadClickListener; - private @Nullable SlideClickListener playVideoClickListener; + private @Nullable SlidesClickedListener startTransferClickListener; + private @Nullable SlidesClickedListener cancelTransferClickListener; + private @Nullable SlideClickListener playVideoClickListener; private int currentSizeClass; @@ -72,10 +72,16 @@ public void setSlides(@NonNull GlideRequests glideRequests, @NonNull List if (showControls) { transferControlsStub.get().setShowSecondaryText(true); - transferControlsStub.get().setDownloadClickListener( + transferControlsStub.get().setTransferClickListener( v -> { - if (downloadClickListener != null) { - downloadClickListener.onClick(v, slides); + if (startTransferClickListener != null) { + startTransferClickListener.onClick(v, slides); + } + }); + transferControlsStub.get().setCancelClickListener( + v -> { + if (cancelTransferClickListener != null) { + cancelTransferClickListener.onClick(v, slides); } }); transferControlsStub.get().setSlides(slides); @@ -111,12 +117,12 @@ public void setThumbnailClickListener(@Nullable SlideClickListener listener) { thumbnailClickListener = listener; } - public void setDownloadClickListener(SlidesClickedListener listener) { - this.downloadClickListener = listener; + public void setStartTransferClickListener(SlidesClickedListener listener) { + this.startTransferClickListener = listener; } - public void setCancelDownloadClickListener(SlidesClickedListener listener) { - this.cancelDownloadClickListener = listener; + public void setCancelTransferClickListener(SlidesClickedListener listener) { + this.cancelTransferClickListener = listener; } public void setPlayVideoClickListener(SlideClickListener listener) { @@ -282,8 +288,8 @@ private void setSlide(@NonNull GlideRequests glideRequests, @NonNull Slide slide ThumbnailView cell = findViewById(id); cell.showSecondaryText(false); cell.setThumbnailClickListener(defaultThumbnailClickListener); - cell.setDownloadClickListener(downloadClickListener); - cell.setCancelDownloadClickListener(cancelDownloadClickListener); + cell.setStartTransferClickListener(startTransferClickListener); + cell.setCancelTransferClickListener(cancelTransferClickListener); if (MediaUtil.isInstantVideoSupported(slide)) { cell.setPlayVideoClickListener(playVideoClickListener); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/BorderlessImageView.java b/app/src/main/java/org/thoughtcrime/securesms/components/BorderlessImageView.java index 534ea159b0..4ced566fb6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/BorderlessImageView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/BorderlessImageView.java @@ -9,9 +9,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.bumptech.glide.load.resource.bitmap.CenterCrop; -import com.bumptech.glide.load.resource.bitmap.CenterInside; - import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.mms.GlideRequests; import org.thoughtcrime.securesms.mms.Slide; @@ -63,7 +60,7 @@ public void setSlide(@NonNull GlideRequests glideRequests, @NonNull Slide slide) image.setImageResource(glideRequests, slide, showControls, false); } else { image.setScaleType(ImageView.ScaleType.CENTER_CROP); - image.setImageResource(glideRequests, slide, showControls, false, slide.asAttachment().getWidth(), slide.asAttachment().getHeight()); + image.setImageResource(glideRequests, slide, showControls, false, slide.asAttachment().width, slide.asAttachment().height); } missingShade.setVisibility(showControls ? View.VISIBLE : View.GONE); @@ -74,6 +71,6 @@ public void setThumbnailClickListener(@NonNull SlideClickListener listener) { } public void setDownloadClickListener(@NonNull SlidesClickedListener listener) { - image.setDownloadClickListener(listener); + image.setStartTransferClickListener(listener); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemFooter.java b/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemFooter.java index 911863db7b..0fac972dea 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemFooter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemFooter.java @@ -29,9 +29,10 @@ import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.animation.AnimationCompleteListener; import org.thoughtcrime.securesms.conversation.ConversationItemDisplayMode; +import org.thoughtcrime.securesms.conversation.v2.computed.FormattedDate; import org.thoughtcrime.securesms.database.SignalDatabase; -import org.thoughtcrime.securesms.database.model.MmsMessageRecord; import org.thoughtcrime.securesms.database.model.MessageRecord; +import org.thoughtcrime.securesms.database.model.MmsMessageRecord; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.permissions.Permissions; import org.thoughtcrime.securesms.recipients.Recipient; @@ -299,7 +300,9 @@ public void onAnimationEnd(Animator animation) { private void presentDate(@NonNull MessageRecord messageRecord, @NonNull Locale locale, @NonNull ConversationItemDisplayMode displayMode) { dateView.forceLayout(); - if (messageRecord.isFailed()) { + if (messageRecord.isMediaPending()) { + dateView.setText(null); + } else if (messageRecord.isFailed()) { int errorMsg; if (messageRecord.hasFailedWithNetworkFailures()) { errorMsg = R.string.ConversationItem_error_network_not_delivered; @@ -323,11 +326,18 @@ private void presentDate(@NonNull MessageRecord messageRecord, @NonNull Locale l timestamp = messageRecord.getDateSent(); } } - String date = DateUtils.getDatelessRelativeTimeSpanString(getContext(), locale, timestamp); + FormattedDate date = DateUtils.getDatelessRelativeTimeSpanFormattedDate(getContext(), locale, timestamp); + String dateLabel = date.getValue(); if (displayMode != ConversationItemDisplayMode.Detailed.INSTANCE && messageRecord.isEditMessage() && messageRecord.isLatestRevision()) { - date = getContext().getString(R.string.ConversationItem_edited_timestamp_footer, date); + if (date.isNow()) { + dateLabel = getContext().getString(R.string.ConversationItem_edited_now_timestamp_footer); + } else if (date.isRelative()) { + dateLabel = getContext().getString(R.string.ConversationItem_edited_relative_timestamp_footer, date.getValue()); + } else { + dateLabel = getContext().getString(R.string.ConversationItem_edited_absolute_timestamp_footer, date.getValue()); + } } - dateView.setText(date); + dateView.setText(dateLabel); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemThumbnail.kt b/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemThumbnail.kt index 9cecb89ac6..804259c76f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemThumbnail.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemThumbnail.kt @@ -251,10 +251,10 @@ class ConversationItemThumbnail @JvmOverloads constructor( state.applyState(thumbnail, album) } - fun setDownloadClickListener(listener: SlidesClickedListener?) { + fun setStartTransferClickListener(listener: SlidesClickedListener?) { state = state.copy( - thumbnailViewState = state.thumbnailViewState.copy(downloadClickListener = listener), - albumViewState = state.albumViewState.copy(downloadClickListener = listener) + thumbnailViewState = state.thumbnailViewState.copy(startTransferClickListener = listener), + albumViewState = state.albumViewState.copy(startTransferClickListener = listener) ) state.applyState(thumbnail, album) @@ -269,10 +269,10 @@ class ConversationItemThumbnail @JvmOverloads constructor( state.applyState(thumbnail, album) } - fun setCancelDownloadClickListener(listener: SlidesClickedListener?) { + fun setCancelTransferClickListener(listener: SlidesClickedListener?) { state = state.copy( - thumbnailViewState = state.thumbnailViewState.copy(cancelDownloadClickListener = listener), - albumViewState = state.albumViewState.copy(cancelDownloadClickListener = listener) + thumbnailViewState = state.thumbnailViewState.copy(cancelTransferClickListener = listener), + albumViewState = state.albumViewState.copy(cancelTransferClickListener = listener) ) state.applyState(thumbnail, album) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemThumbnailState.kt b/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemThumbnailState.kt index 77681e4c15..4f8efc6366 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemThumbnailState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemThumbnailState.kt @@ -34,9 +34,9 @@ data class ConversationItemThumbnailState( @IgnoredOnParcel private val clickListener: SlideClickListener? = null, @IgnoredOnParcel - private val downloadClickListener: SlidesClickedListener? = null, + private val startTransferClickListener: SlidesClickedListener? = null, @IgnoredOnParcel - private val cancelDownloadClickListener: SlidesClickedListener? = null, + private val cancelTransferClickListener: SlidesClickedListener? = null, @IgnoredOnParcel private val playVideoClickListener: SlideClickListener? = null, @IgnoredOnParcel @@ -63,8 +63,8 @@ data class ConversationItemThumbnailState( thumbnailView.get().isClickable = clickable thumbnailView.get().setRadii(cornerTopLeft, cornerTopRight, cornerBottomRight, cornerBottomLeft) thumbnailView.get().setThumbnailClickListener(clickListener) - thumbnailView.get().setDownloadClickListener(downloadClickListener) - thumbnailView.get().setCancelDownloadClickListener(cancelDownloadClickListener) + thumbnailView.get().setStartTransferClickListener(startTransferClickListener) + thumbnailView.get().setCancelTransferClickListener(cancelTransferClickListener) thumbnailView.get().setPlayVideoClickListener(playVideoClickListener) thumbnailView.get().setOnLongClickListener(longClickListener) thumbnailView.get().setBounds(minWidth, maxWidth, minHeight, maxHeight) @@ -78,9 +78,9 @@ data class ConversationItemThumbnailState( @IgnoredOnParcel private val clickListener: SlideClickListener? = null, @IgnoredOnParcel - private val downloadClickListener: SlidesClickedListener? = null, + private val startTransferClickListener: SlidesClickedListener? = null, @IgnoredOnParcel - private val cancelDownloadClickListener: SlidesClickedListener? = null, + private val cancelTransferClickListener: SlidesClickedListener? = null, @IgnoredOnParcel private val playVideoClickListener: SlideClickListener? = null, @IgnoredOnParcel @@ -103,8 +103,8 @@ data class ConversationItemThumbnailState( albumView.get().isClickable = clickable albumView.get().setRadii(cornerTopLeft, cornerTopRight, cornerBottomRight, cornerBottomLeft) albumView.get().setThumbnailClickListener(clickListener) - albumView.get().setDownloadClickListener(downloadClickListener) - albumView.get().setCancelDownloadClickListener(cancelDownloadClickListener) + albumView.get().setStartTransferClickListener(startTransferClickListener) + albumView.get().setCancelTransferClickListener(cancelTransferClickListener) albumView.get().setPlayVideoClickListener(playVideoClickListener) albumView.get().setOnLongClickListener(longClickListener) albumView.get().setCellBackgroundColor(cellBackgroundColor) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/InputAwareConstraintLayout.kt b/app/src/main/java/org/thoughtcrime/securesms/components/InputAwareConstraintLayout.kt index b1c81148a0..c9df126b89 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/InputAwareConstraintLayout.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/InputAwareConstraintLayout.kt @@ -26,12 +26,20 @@ class InputAwareConstraintLayout @JvmOverloads constructor( private var inputId: Int? = null private var input: Fragment? = null private var wasKeyboardVisibleBeforeToggle: Boolean = false + private val listeners: MutableSet = mutableSetOf() val isInputShowing: Boolean get() = input != null lateinit var fragmentManager: FragmentManager - var listener: Listener? = null + + fun addInputListener(listener: Listener) { + listeners.add(listener) + } + + fun remoteInputListener(listener: Listener) { + listeners.remove(listener) + } fun showSoftkey(editText: EditText) { ViewUtil.focusAndShowKeyboard(editText) @@ -44,6 +52,33 @@ class InputAwareConstraintLayout @JvmOverloads constructor( hideInput(resetKeyboardGuideline = true) } + fun runAfterAllHidden(imeTarget: EditText, onHidden: () -> Unit) { + if (isInputShowing || isKeyboardShowing) { + val listener = object : Listener, KeyboardStateListener { + override fun onInputHidden() { + onHidden() + remoteInputListener(this) + removeKeyboardStateListener(this) + } + + override fun onKeyboardHidden() { + onHidden() + remoteInputListener(this) + removeKeyboardStateListener(this) + } + + override fun onInputShown() = Unit + override fun onKeyboardShown() = Unit + } + + addInputListener(listener) + addKeyboardStateListener(listener) + hideAll(imeTarget) + } else { + onHidden() + } + } + fun toggleInput(fragmentCreator: FragmentCreator, imeTarget: EditText, showSoftKeyOnHide: Boolean = wasKeyboardVisibleBeforeToggle) { if (fragmentCreator.id == inputId) { if (showSoftKeyOnHide) { @@ -85,7 +120,7 @@ class InputAwareConstraintLayout @JvmOverloads constructor( overrideKeyboardGuidelineWithPreviousHeight() ViewUtil.hideKeyboard(context, imeTarget) - listener?.onInputShown() + listeners.forEach { it.onInputShown() } } private fun hideInput(resetKeyboardGuideline: Boolean) { @@ -107,7 +142,7 @@ class InputAwareConstraintLayout @JvmOverloads constructor( } if (inputHidden) { - listener?.onInputHidden() + listeners.forEach { it.onInputHidden() } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/InsetAwareConstraintLayout.kt b/app/src/main/java/org/thoughtcrime/securesms/components/InsetAwareConstraintLayout.kt index 556f8baca3..dd6eb9845f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/InsetAwareConstraintLayout.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/InsetAwareConstraintLayout.kt @@ -51,13 +51,14 @@ open class InsetAwareConstraintLayout @JvmOverloads constructor( private val windowTypes = WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout() } - private val statusBarGuideline: Guideline? by lazy { findViewById(R.id.status_bar_guideline) } + protected val statusBarGuideline: Guideline? by lazy { findViewById(R.id.status_bar_guideline) } private val navigationBarGuideline: Guideline? by lazy { findViewById(R.id.navigation_bar_guideline) } private val parentStartGuideline: Guideline? by lazy { findViewById(R.id.parent_start_guideline) } private val parentEndGuideline: Guideline? by lazy { findViewById(R.id.parent_end_guideline) } private val keyboardGuideline: Guideline? by lazy { findViewById(R.id.keyboard_guideline) } - private val listeners: MutableList = mutableListOf() + private val windowInsetsListeners: MutableSet = mutableSetOf() + private val keyboardStateListeners: MutableSet = mutableSetOf() private val keyboardAnimator = KeyboardInsetAnimator() private val displayMetrics = DisplayMetrics() private var overridingKeyboard: Boolean = false @@ -82,20 +83,35 @@ open class InsetAwareConstraintLayout @JvmOverloads constructor( } fun addKeyboardStateListener(listener: KeyboardStateListener) { - listeners += listener + keyboardStateListeners += listener } fun removeKeyboardStateListener(listener: KeyboardStateListener) { - listeners.remove(listener) + keyboardStateListeners.remove(listener) + } + + fun addWindowInsetsListener(listener: WindowInsetsListener) { + windowInsetsListeners += listener + } + + fun removeWindowInsetsListener(listener: WindowInsetsListener) { + windowInsetsListeners.remove(listener) } private fun applyInsets(windowInsets: Insets, keyboardInsets: Insets) { val isLtr = ViewUtil.isLtr(this) - statusBarGuideline?.setGuidelineBegin(windowInsets.top) - navigationBarGuideline?.setGuidelineEnd(windowInsets.bottom) - parentStartGuideline?.setGuidelineBegin(if (isLtr) windowInsets.left else windowInsets.right) - parentEndGuideline?.setGuidelineEnd(if (isLtr) windowInsets.right else windowInsets.left) + val statusBar = windowInsets.top + val navigationBar = windowInsets.bottom + val parentStart = if (isLtr) windowInsets.left else windowInsets.right + val parentEnd = if (isLtr) windowInsets.right else windowInsets.left + + statusBarGuideline?.setGuidelineBegin(statusBar) + navigationBarGuideline?.setGuidelineEnd(navigationBar) + parentStartGuideline?.setGuidelineBegin(parentStart) + parentEndGuideline?.setGuidelineEnd(parentEnd) + + windowInsetsListeners.forEach { it.onApplyWindowInsets(statusBar, navigationBar, parentStart, parentEnd) } if (keyboardInsets.bottom > 0) { setKeyboardHeight(keyboardInsets.bottom) @@ -113,7 +129,7 @@ open class InsetAwareConstraintLayout @JvmOverloads constructor( } if (previousKeyboardHeight != keyboardInsets.bottom) { - listeners.forEach { + keyboardStateListeners.forEach { if (previousKeyboardHeight <= 0) { it.onKeyboardShown() } else { @@ -191,6 +207,10 @@ open class InsetAwareConstraintLayout @JvmOverloads constructor( fun onKeyboardHidden() } + interface WindowInsetsListener { + fun onApplyWindowInsets(statusBar: Int, navigationBar: Int, parentStart: Int, parentEnd: Int) + } + /** * Adjusts the [keyboardGuideline] to move with the IME keyboard opening or closing. */ diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/LinkPreviewViewThumbnailState.kt b/app/src/main/java/org/thoughtcrime/securesms/components/LinkPreviewViewThumbnailState.kt index 6f18e4e975..b41dc76e49 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/LinkPreviewViewThumbnailState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/LinkPreviewViewThumbnailState.kt @@ -22,7 +22,7 @@ data class LinkPreviewViewThumbnailState( fun applyState(thumbnail: Stub) { if (thumbnail.resolved()) { thumbnail.get().setCorners(cornerTopLeft, cornerTopRight, cornerBottomRight, cornerBottomLeft) - thumbnail.get().setDownloadClickListener(downloadListener) + thumbnail.get().setStartTransferClickListener(downloadListener) } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/SharedContactView.java b/app/src/main/java/org/thoughtcrime/securesms/components/SharedContactView.java index 878643f564..e2068d5b49 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/SharedContactView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/SharedContactView.java @@ -202,7 +202,7 @@ private void presentActionButtons(@NonNull List recipients) { } if (!pushUsers.isEmpty()) { - actionButtonView.setText(R.string.SharedContactView_message); + actionButtonView.setText(R.string.NewConversationActivity__message); actionButtonView.setOnClickListener(v -> { if (eventListener != null) { eventListener.onMessageClicked(pushUsers); diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/ThumbnailView.java b/app/src/main/java/org/thoughtcrime/securesms/components/ThumbnailView.java index 369e8a06a3..42da090d0e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/ThumbnailView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/ThumbnailView.java @@ -86,12 +86,12 @@ public class ThumbnailView extends FrameLayout { private final CornerMask cornerMask; - private final Stub transferControlViewStub; - private SlideClickListener thumbnailClickListener = null; - private SlidesClickedListener downloadClickListener = null; - private SlidesClickedListener cancelDownloadClickListener = null; - private SlideClickListener playVideoClickListener = null; - private Slide slide = null; + private final Stub transferControlViewStub; + private SlideClickListener thumbnailClickListener = null; + private SlidesClickedListener startTransferClickListener = null; + private SlidesClickedListener cancelTransferClickListener = null; + private SlideClickListener playVideoClickListener = null; + private Slide slide = null; public ThumbnailView(Context context) { @@ -363,7 +363,7 @@ public ListenableFuture setImageResource(@NonNull GlideRequests glideRe } if (showControls) { - transferControlViewStub.get().setDownloadClickListener(new DownloadClickDispatcher()); + transferControlViewStub.get().setTransferClickListener(new DownloadClickDispatcher()); transferControlViewStub.get().setCancelClickListener(new CancelClickDispatcher()); if (MediaUtil.isInstantVideoSupported(slide)) { transferControlViewStub.get().setInstantPlaybackClickListener(new InstantVideoClickDispatcher()); @@ -397,7 +397,7 @@ public ListenableFuture setImageResource(@NonNull GlideRequests glideRe Log.i(TAG, "loading part with id " + slide.asAttachment().getUri() + ", progress " + slide.getTransferState() + ", fast preflight id: " + - slide.asAttachment().getFastPreflightId()); + slide.asAttachment().fastPreflightId); BlurHash previousBlurHash = this.slide != null ? this.slide.getPlaceholderBlur() : null; @@ -516,12 +516,12 @@ public void setThumbnailClickListener(SlideClickListener listener) { this.thumbnailClickListener = listener; } - public void setDownloadClickListener(SlidesClickedListener listener) { - this.downloadClickListener = listener; + public void setStartTransferClickListener(SlidesClickedListener listener) { + this.startTransferClickListener = listener; } - public void setCancelDownloadClickListener(SlidesClickedListener listener) { - this.cancelDownloadClickListener = listener; + public void setCancelTransferClickListener(SlidesClickedListener listener) { + this.cancelTransferClickListener = listener; } public void setPlayVideoClickListener(SlideClickListener listener) { @@ -532,8 +532,8 @@ private static boolean hasSameContents(@Nullable Slide slide, @Nullable Slide ot if (Util.equals(slide, other)) { if (slide != null && other != null) { - byte[] digestLeft = slide.asAttachment().getDigest(); - byte[] digestRight = other.asAttachment().getDigest(); + byte[] digestLeft = slide.asAttachment().remoteDigest; + byte[] digestRight = other.asAttachment().remoteDigest; return Arrays.equals(digestLeft, digestRight); } @@ -667,10 +667,10 @@ private class DownloadClickDispatcher implements View.OnClickListener { @Override public void onClick(View view) { Log.i(TAG, "onClick() for download button"); - if (downloadClickListener != null && slide != null) { - downloadClickListener.onClick(view, Collections.singletonList(slide)); + if (startTransferClickListener != null && slide != null) { + startTransferClickListener.onClick(view, Collections.singletonList(slide)); } else { - Log.w(TAG, "Received a download button click, but unable to execute it. slide: " + slide + " downloadClickListener: " + downloadClickListener); + Log.w(TAG, "Received a download button click, but unable to execute it. slide: " + slide + " downloadClickListener: " + startTransferClickListener); } } } @@ -679,10 +679,10 @@ private class CancelClickDispatcher implements View.OnClickListener { @Override public void onClick(View view) { Log.i(TAG, "onClick() for cancel button"); - if (cancelDownloadClickListener != null && slide != null) { - cancelDownloadClickListener.onClick(view, Collections.singletonList(slide)); + if (cancelTransferClickListener != null && slide != null) { + cancelTransferClickListener.onClick(view, Collections.singletonList(slide)); } else { - Log.w(TAG, "Received a cancel button click, but unable to execute it. slide: " + slide + " cancelDownloadClickListener: " + cancelDownloadClickListener); + Log.w(TAG, "Received a cancel button click, but unable to execute it. slide: " + slide + " cancelDownloadClickListener: " + cancelTransferClickListener); } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/TooltipPopup.java b/app/src/main/java/org/thoughtcrime/securesms/components/TooltipPopup.java index 40e12f056d..82622774f1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/TooltipPopup.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/TooltipPopup.java @@ -171,21 +171,23 @@ private void show() { ShapeAppearanceModel.Builder shapeAppearanceModel = ShapeAppearanceModel.builder() .setAllCornerSizes(DimensionUnit.DP.toPixels(18)); - // If the arrow is within the last 20dp of the right hand side, use RIGHT and set corner to 9dp - onLayout(() -> { - if (arrow.getX() > getContentView().getWidth() / 2f) { - arrow.setImageResource(R.drawable.ic_tooltip_arrow_up_right); - } - - float arrowEnd = arrow.getX() + arrow.getRight(); - if (arrowEnd > getContentView().getRight() - DimensionUnit.DP.toPixels(20)) { - shapeableBubbleBackground.setShapeAppearanceModel(shapeAppearanceModel.setTopRightCornerSize(DimensionUnit.DP.toPixels(9f)).build()); - bubble.setBackground(shapeableBubbleBackground); - } else if (arrowEnd < DimensionUnit.DP.toPixels(20)) { - shapeableBubbleBackground.setShapeAppearanceModel(shapeAppearanceModel.setTopLeftCornerSize(DimensionUnit.DP.toPixels(9f)).build()); - bubble.setBackground(shapeableBubbleBackground); - } - }); + if (position == POSITION_BELOW) { + // If the arrow is within the last 20dp of the right hand side, use RIGHT and set corner to 9dp + onLayout(() -> { + if (arrow.getX() > getContentView().getWidth() / 2f) { + arrow.setImageResource(R.drawable.ic_tooltip_arrow_up_right); + } + + float arrowEnd = arrow.getX() + arrow.getRight(); + if (arrowEnd > getContentView().getRight() - DimensionUnit.DP.toPixels(20)) { + shapeableBubbleBackground.setShapeAppearanceModel(shapeAppearanceModel.setTopRightCornerSize(DimensionUnit.DP.toPixels(9f)).build()); + bubble.setBackground(shapeableBubbleBackground); + } else if (arrowEnd < DimensionUnit.DP.toPixels(20)) { + shapeableBubbleBackground.setShapeAppearanceModel(shapeAppearanceModel.setTopLeftCornerSize(DimensionUnit.DP.toPixels(9f)).build()); + bubble.setBackground(shapeableBubbleBackground); + } + }); + } try { showAsDropDown(anchor, xoffset, yoffset); diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiTextView.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiTextView.java index 4846702ee4..136dbfb371 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiTextView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiTextView.java @@ -355,7 +355,7 @@ private void ellipsizeEmojiTextForMaxLines() { } int overflowEnd = getLayout().getLineEnd(maxLines); - CharSequence overflow = getText().subSequence(overflowStart, overflowEnd); + CharSequence overflow = new SpannableString(getText().subSequence(overflowStart, overflowEnd).toString()); float adjust = overflowText != null ? getPaint().measureText(overflowText, 0, overflowText.length()) : 0f; CharSequence ellipsized = TextUtils.ellipsize(overflow, getPaint(), getWidth() - adjust, TextUtils.TruncateAt.END); diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/recyclerview/NoTouchingRecyclerView.kt b/app/src/main/java/org/thoughtcrime/securesms/components/recyclerview/NoTouchingRecyclerView.kt new file mode 100644 index 0000000000..d2b9cd6486 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/recyclerview/NoTouchingRecyclerView.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.components.recyclerview + +import android.annotation.SuppressLint +import android.content.Context +import android.util.AttributeSet +import android.view.MotionEvent +import androidx.recyclerview.widget.RecyclerView + +/** + * Ignores all touch events, purely for rendering views in a recyclable manner. + */ +class NoTouchingRecyclerView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : RecyclerView(context, attrs, defStyleAttr) { + + @SuppressLint("ClickableViewAccessibility") + override fun onTouchEvent(e: MotionEvent?): Boolean { + return false + } + + override fun onInterceptTouchEvent(e: MotionEvent?): Boolean { + return false + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/changenumber/ChangeNumberRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/changenumber/ChangeNumberRepository.kt index 9d323d4235..4e4b36313c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/changenumber/ChangeNumberRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/changenumber/ChangeNumberRepository.kt @@ -264,7 +264,6 @@ class ChangeNumberRepository( accountManager.setPreKeys( PreKeyUpload( serviceIdType = ServiceIdType.PNI, - identityKey = pniProtocolStore.identityKeyPair.publicKey, signedPreKey = signedPreKey, oneTimeEcPreKeys = oneTimePreKeys, lastResortKyberPreKey = null, diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsFragment.kt index 16da89d7ee..cb386bad81 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsFragment.kt @@ -12,7 +12,6 @@ import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.LinearLayoutManager import com.google.android.material.dialog.MaterialAlertDialogBuilder import org.signal.core.util.AppUtil -import org.signal.core.util.Hex import org.signal.core.util.concurrent.SignalExecutors import org.signal.core.util.concurrent.SimpleTask import org.signal.core.util.logging.Log @@ -158,6 +157,14 @@ class InternalSettingsFragment : DSLSettingsFragment(R.string.preferences__inter } ) + clickPref( + title = DSLSettingsText.from("Backup Playground"), + summary = DSLSettingsText.from("Test backup import/export."), + onClick = { + findNavController().safeNavigate(InternalSettingsFragmentDirections.actionInternalSettingsFragmentToInternalBackupPlaygroundFragment()) + } + ) + switchPref( title = DSLSettingsText.from("'Internal Details' button"), summary = DSLSettingsText.from("Show a button in conversation settings that lets you see more information about a user."), @@ -635,7 +642,7 @@ class InternalSettingsFragment : DSLSettingsFragment(R.string.preferences__inter .setTitle("Corrupt your username?") .setMessage("Are you sure? You might not be able to get your original username back.") .setPositiveButton(android.R.string.ok) { _, _ -> - val random = "${Hex.toStringCondensed(Util.getSecretBytes(4))}.${Random.nextInt(1, 100)}" + val random = "${(1..5).map { ('a'..'z').random() }.joinToString(separator = "") }.${Random.nextInt(1, 100)}" SignalStore.account().username = random SignalDatabase.recipients.setUsername(Recipient.self().id, random) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsRepository.kt index b69fbdce15..bb61b1943c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsRepository.kt @@ -64,7 +64,7 @@ class InternalSettingsRepository(context: Context) { } } - fun addRemoteDonateMegaphone() { + fun addRemoteMegaphone(actionId: RemoteMegaphoneRecord.ActionId) { SignalExecutors.UNBOUNDED.execute { val record = RemoteMegaphoneRecord( uuid = UUID.randomUUID().toString(), @@ -75,7 +75,7 @@ class InternalSettingsRepository(context: Context) { doNotShowAfter = System.currentTimeMillis() + 28.days.inWholeMilliseconds, showForNumberOfDays = 30, conditionalId = null, - primaryActionId = RemoteMegaphoneRecord.ActionId.DONATE, + primaryActionId = actionId, secondaryActionId = RemoteMegaphoneRecord.ActionId.SNOOZE, imageUrl = "/static/release-notes/donate-heart.png", title = "Donate Test", diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsViewModel.kt index 40b8280920..44d9ccb118 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsViewModel.kt @@ -4,6 +4,7 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import org.signal.ringrtc.CallManager +import org.thoughtcrime.securesms.database.model.RemoteMegaphoneRecord import org.thoughtcrime.securesms.jobs.StoryOnboardingDownloadJob import org.thoughtcrime.securesms.keyvalue.InternalValues import org.thoughtcrime.securesms.keyvalue.SignalStore @@ -114,7 +115,7 @@ class InternalSettingsViewModel(private val repository: InternalSettingsReposito } fun addRemoteDonateMegaphone() { - repository.addRemoteDonateMegaphone() + repository.addRemoteMegaphone(RemoteMegaphoneRecord.ActionId.DONATE) } fun refresh() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/backup/InternalBackupPlaygroundFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/backup/InternalBackupPlaygroundFragment.kt new file mode 100644 index 0000000000..1fd3173714 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/backup/InternalBackupPlaygroundFragment.kt @@ -0,0 +1,307 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.components.settings.app.internal.backup + +import android.app.Activity.RESULT_OK +import android.content.Intent +import android.content.res.Configuration +import android.os.Bundle +import android.widget.Toast +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Switch +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.fragment.app.viewModels +import org.signal.core.ui.Buttons +import org.signal.core.ui.Dividers +import org.signal.core.ui.theme.SignalTheme +import org.signal.core.util.bytes +import org.signal.core.util.getLength +import org.signal.core.util.roundedString +import org.thoughtcrime.securesms.components.settings.app.internal.backup.InternalBackupPlaygroundViewModel.BackupState +import org.thoughtcrime.securesms.components.settings.app.internal.backup.InternalBackupPlaygroundViewModel.BackupUploadState +import org.thoughtcrime.securesms.components.settings.app.internal.backup.InternalBackupPlaygroundViewModel.ScreenState +import org.thoughtcrime.securesms.compose.ComposeFragment + +class InternalBackupPlaygroundFragment : ComposeFragment() { + + private val viewModel: InternalBackupPlaygroundViewModel by viewModels() + private lateinit var exportFileLauncher: ActivityResultLauncher + private lateinit var importFileLauncher: ActivityResultLauncher + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + exportFileLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> + if (result.resultCode == RESULT_OK) { + result.data?.data?.let { uri -> + requireContext().contentResolver.openOutputStream(uri)?.use { outputStream -> + outputStream.write(viewModel.backupData!!) + Toast.makeText(requireContext(), "Saved successfully", Toast.LENGTH_SHORT).show() + } ?: Toast.makeText(requireContext(), "Failed to open output stream", Toast.LENGTH_SHORT).show() + } ?: Toast.makeText(requireContext(), "No URI selected", Toast.LENGTH_SHORT).show() + } + } + + importFileLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> + if (result.resultCode == RESULT_OK) { + result.data?.data?.let { uri -> + requireContext().contentResolver.getLength(uri)?.let { length -> + viewModel.import(length) { requireContext().contentResolver.openInputStream(uri)!! } + } + } ?: Toast.makeText(requireContext(), "No URI selected", Toast.LENGTH_SHORT).show() + } + } + } + + @Composable + override fun FragmentContent() { + val state by viewModel.state + + Screen( + state = state, + onExportClicked = { viewModel.export() }, + onImportMemoryClicked = { viewModel.import() }, + onImportFileClicked = { + val intent = Intent().apply { + action = Intent.ACTION_GET_CONTENT + type = "application/octet-stream" + addCategory(Intent.CATEGORY_OPENABLE) + } + + importFileLauncher.launch(intent) + }, + onPlaintextClicked = { viewModel.onPlaintextToggled() }, + onSaveToDiskClicked = { + val intent = Intent().apply { + action = Intent.ACTION_CREATE_DOCUMENT + type = "application/octet-stream" + addCategory(Intent.CATEGORY_OPENABLE) + putExtra(Intent.EXTRA_TITLE, "backup-${if (state.plaintext) "plaintext" else "encrypted"}-${System.currentTimeMillis()}.bin") + } + + exportFileLauncher.launch(intent) + }, + onUploadToRemoteClicked = { viewModel.uploadBackupToRemote() }, + onCheckRemoteBackupStateClicked = { viewModel.checkRemoteBackupState() } + ) + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + } +} + +@Composable +fun Screen( + state: ScreenState, + onExportClicked: () -> Unit = {}, + onImportMemoryClicked: () -> Unit = {}, + onImportFileClicked: () -> Unit = {}, + onPlaintextClicked: () -> Unit = {}, + onSaveToDiskClicked: () -> Unit = {}, + onUploadToRemoteClicked: () -> Unit = {}, + onCheckRemoteBackupStateClicked: () -> Unit = {} +) { + Surface { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + modifier = Modifier + .fillMaxSize() + .padding(16.dp) + ) { + Row( + verticalAlignment = Alignment.CenterVertically + ) { + StateLabel(text = "Plaintext?") + Spacer(modifier = Modifier.width(8.dp)) + Switch( + checked = state.plaintext, + onCheckedChange = { onPlaintextClicked() } + ) + } + + Spacer(modifier = Modifier.height(8.dp)) + + Buttons.LargePrimary( + onClick = onExportClicked, + enabled = !state.backupState.inProgress + ) { + Text("Export") + } + + Dividers.Default() + + Buttons.LargeTonal( + onClick = onImportMemoryClicked, + enabled = state.backupState == BackupState.EXPORT_DONE + ) { + Text("Import from memory") + } + Buttons.LargeTonal( + onClick = onImportFileClicked + ) { + Text("Import from file") + } + + Spacer(modifier = Modifier.height(16.dp)) + + when (state.backupState) { + BackupState.NONE -> { + StateLabel("") + } + BackupState.EXPORT_IN_PROGRESS -> { + StateLabel("Export in progress...") + } + BackupState.EXPORT_DONE -> { + StateLabel("Export complete. Sitting in memory. You can click 'Import' to import that data, save it to a file, or upload it to remote.") + + Spacer(modifier = Modifier.height(8.dp)) + + Buttons.MediumTonal(onClick = onSaveToDiskClicked) { + Text("Save to file") + } + } + BackupState.IMPORT_IN_PROGRESS -> { + StateLabel("Import in progress...") + } + } + + Dividers.Default() + + Buttons.LargeTonal( + onClick = onCheckRemoteBackupStateClicked + ) { + Text("Check remote backup state") + } + + Spacer(modifier = Modifier.height(8.dp)) + + when (state.remoteBackupState) { + is InternalBackupPlaygroundViewModel.RemoteBackupState.Available -> { + StateLabel("Exists/allocated. Space used by media: ${state.remoteBackupState.response.usedSpace ?: 0} bytes (${state.remoteBackupState.response.usedSpace?.bytes?.inMebiBytes?.roundedString(3) ?: 0} MiB)") + } + InternalBackupPlaygroundViewModel.RemoteBackupState.GeneralError -> { + StateLabel("Hit an unknown error. Check the logs.") + } + InternalBackupPlaygroundViewModel.RemoteBackupState.NotFound -> { + StateLabel("Not found.") + } + InternalBackupPlaygroundViewModel.RemoteBackupState.Unknown -> { + StateLabel("Hit the button above to check the state.") + } + } + + Spacer(modifier = Modifier.height(8.dp)) + + Buttons.LargePrimary( + onClick = onUploadToRemoteClicked, + enabled = state.backupState == BackupState.EXPORT_DONE + ) { + Text("Upload to remote") + } + + Spacer(modifier = Modifier.height(8.dp)) + + when (state.uploadState) { + BackupUploadState.NONE -> { + StateLabel("") + } + BackupUploadState.UPLOAD_IN_PROGRESS -> { + StateLabel("Upload in progress...") + } + BackupUploadState.UPLOAD_DONE -> { + StateLabel("Upload complete.") + } + BackupUploadState.UPLOAD_FAILED -> { + StateLabel("Upload failed.") + } + } + } + } +} + +@Composable +private fun StateLabel(text: String) { + Text( + text = text, + style = MaterialTheme.typography.labelSmall, + textAlign = TextAlign.Center + ) +} + +@Preview(name = "Light Theme", group = "screen", uiMode = Configuration.UI_MODE_NIGHT_NO) +@Preview(name = "Dark Theme", group = "screen", uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +fun PreviewScreen() { + SignalTheme { + Surface { + Screen(state = ScreenState(backupState = BackupState.NONE, plaintext = false)) + } + } +} + +@Preview(name = "Light Theme", group = "screen", uiMode = Configuration.UI_MODE_NIGHT_NO) +@Preview(name = "Dark Theme", group = "screen", uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +fun PreviewScreenExportInProgress() { + SignalTheme { + Surface { + Screen(state = ScreenState(backupState = BackupState.EXPORT_IN_PROGRESS, plaintext = false)) + } + } +} + +@Preview(name = "Light Theme", group = "screen", uiMode = Configuration.UI_MODE_NIGHT_NO) +@Preview(name = "Dark Theme", group = "screen", uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +fun PreviewScreenExportDone() { + SignalTheme { + Surface { + Screen(state = ScreenState(backupState = BackupState.EXPORT_DONE, plaintext = false)) + } + } +} + +@Preview(name = "Light Theme", group = "screen", uiMode = Configuration.UI_MODE_NIGHT_NO) +@Preview(name = "Dark Theme", group = "screen", uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +fun PreviewScreenImportInProgress() { + SignalTheme { + Surface { + Screen(state = ScreenState(backupState = BackupState.IMPORT_IN_PROGRESS, plaintext = false)) + } + } +} + +@Preview(name = "Light Theme", group = "screen", uiMode = Configuration.UI_MODE_NIGHT_NO) +@Preview(name = "Dark Theme", group = "screen", uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +fun PreviewScreenUploadInProgress() { + SignalTheme { + Surface { + Screen(state = ScreenState(uploadState = BackupUploadState.UPLOAD_IN_PROGRESS, plaintext = false)) + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/backup/InternalBackupPlaygroundViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/backup/InternalBackupPlaygroundViewModel.kt new file mode 100644 index 0000000000..b3d8f7478e --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/backup/InternalBackupPlaygroundViewModel.kt @@ -0,0 +1,142 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.components.settings.app.internal.backup + +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.State +import androidx.compose.runtime.mutableStateOf +import androidx.lifecycle.ViewModel +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers +import io.reactivex.rxjava3.core.Single +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign +import io.reactivex.rxjava3.schedulers.Schedulers +import org.signal.libsignal.zkgroup.profiles.ProfileKey +import org.thoughtcrime.securesms.backup.v2.BackupRepository +import org.thoughtcrime.securesms.recipients.Recipient +import org.whispersystems.signalservice.api.NetworkResult +import org.whispersystems.signalservice.api.archive.ArchiveGetBackupInfoResponse +import java.io.ByteArrayInputStream +import java.io.InputStream + +class InternalBackupPlaygroundViewModel : ViewModel() { + + var backupData: ByteArray? = null + + val disposables = CompositeDisposable() + + private val _state: MutableState = mutableStateOf(ScreenState(backupState = BackupState.NONE, uploadState = BackupUploadState.NONE, plaintext = false)) + val state: State = _state + + fun export() { + _state.value = _state.value.copy(backupState = BackupState.EXPORT_IN_PROGRESS) + val plaintext = _state.value.plaintext + + disposables += Single.fromCallable { BackupRepository.export(plaintext = plaintext) } + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { data -> + backupData = data + _state.value = _state.value.copy(backupState = BackupState.EXPORT_DONE) + } + } + + fun import() { + backupData?.let { + _state.value = _state.value.copy(backupState = BackupState.IMPORT_IN_PROGRESS) + val plaintext = _state.value.plaintext + + val self = Recipient.self() + val selfData = BackupRepository.SelfData(self.aci.get(), self.pni.get(), self.e164.get(), ProfileKey(self.profileKey)) + + disposables += Single.fromCallable { BackupRepository.import(it.size.toLong(), { ByteArrayInputStream(it) }, selfData, plaintext = plaintext) } + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { nothing -> + backupData = null + _state.value = _state.value.copy(backupState = BackupState.NONE) + } + } + } + + fun import(length: Long, inputStreamFactory: () -> InputStream) { + _state.value = _state.value.copy(backupState = BackupState.IMPORT_IN_PROGRESS) + val plaintext = _state.value.plaintext + + val self = Recipient.self() + val selfData = BackupRepository.SelfData(self.aci.get(), self.pni.get(), self.e164.get(), ProfileKey(self.profileKey)) + + disposables += Single.fromCallable { BackupRepository.import(length, inputStreamFactory, selfData, plaintext = plaintext) } + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { nothing -> + backupData = null + _state.value = _state.value.copy(backupState = BackupState.NONE) + } + } + + fun onPlaintextToggled() { + _state.value = _state.value.copy(plaintext = !_state.value.plaintext) + } + + fun uploadBackupToRemote() { + _state.value = _state.value.copy(uploadState = BackupUploadState.UPLOAD_IN_PROGRESS) + + disposables += Single + .fromCallable { BackupRepository.uploadBackupFile(backupData!!.inputStream(), backupData!!.size.toLong()) } + .subscribeOn(Schedulers.io()) + .subscribe { success -> + _state.value = _state.value.copy(uploadState = if (success) BackupUploadState.UPLOAD_DONE else BackupUploadState.UPLOAD_FAILED) + } + } + + fun checkRemoteBackupState() { + _state.value = _state.value.copy(remoteBackupState = RemoteBackupState.Unknown) + + disposables += Single + .fromCallable { BackupRepository.getRemoteBackupState() } + .subscribeOn(Schedulers.io()) + .subscribe { result -> + when { + result is NetworkResult.Success -> { + _state.value = _state.value.copy(remoteBackupState = RemoteBackupState.Available(result.result)) + } + result is NetworkResult.StatusCodeError && result.code == 404 -> { + _state.value = _state.value.copy(remoteBackupState = RemoteBackupState.NotFound) + } + else -> { + _state.value = _state.value.copy(remoteBackupState = RemoteBackupState.GeneralError) + } + } + } + } + + override fun onCleared() { + disposables.clear() + } + + data class ScreenState( + val backupState: BackupState = BackupState.NONE, + val uploadState: BackupUploadState = BackupUploadState.NONE, + val remoteBackupState: RemoteBackupState = RemoteBackupState.Unknown, + val plaintext: Boolean + ) + + enum class BackupState(val inProgress: Boolean = false) { + NONE, EXPORT_IN_PROGRESS(true), EXPORT_DONE, IMPORT_IN_PROGRESS(true) + } + + enum class BackupUploadState(val inProgress: Boolean = false) { + NONE, UPLOAD_IN_PROGRESS(true), UPLOAD_DONE, UPLOAD_FAILED + } + + sealed class RemoteBackupState { + object Unknown : RemoteBackupState() + object NotFound : RemoteBackupState() + object GeneralError : RemoteBackupState() + data class Available(val response: ArchiveGetBackupInfoResponse) : RemoteBackupState() + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/profiles/AddAllowedMembersFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/profiles/AddAllowedMembersFragment.kt index 0220b5d7b5..540661a871 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/profiles/AddAllowedMembersFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/profiles/AddAllowedMembersFragment.kt @@ -7,9 +7,12 @@ import androidx.navigation.fragment.findNavController import com.google.android.material.snackbar.Snackbar import io.reactivex.rxjava3.kotlin.subscribeBy import org.signal.core.util.concurrent.LifecycleDisposable +import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.components.settings.DSLConfiguration import org.thoughtcrime.securesms.components.settings.DSLSettingsFragment +import org.thoughtcrime.securesms.components.settings.DSLSettingsIcon +import org.thoughtcrime.securesms.components.settings.DSLSettingsText import org.thoughtcrime.securesms.components.settings.app.notifications.profiles.models.NotificationProfileAddMembers import org.thoughtcrime.securesms.components.settings.app.notifications.profiles.models.NotificationProfileRecipient import org.thoughtcrime.securesms.components.settings.configure @@ -93,6 +96,32 @@ class AddAllowedMembersFragment : DSLSettingsFragment(layoutId = R.layout.fragme ) ) } + + sectionHeaderPref(R.string.AddAllowedMembers__exceptions) + + switchPref( + title = DSLSettingsText.from(R.string.AddAllowedMembers__allow_all_calls), + icon = DSLSettingsIcon.from(R.drawable.symbol_phone_24), + isChecked = profile.allowAllCalls, + onClick = { + lifecycleDisposable += viewModel.toggleAllowAllCalls() + .subscribeBy( + onError = { Log.w(TAG, "Error updating profile", it) } + ) + } + ) + + switchPref( + title = DSLSettingsText.from(R.string.AddAllowedMembers__notify_for_all_mentions), + icon = DSLSettingsIcon.from(R.drawable.symbol_at_24), + isChecked = profile.allowAllMentions, + onClick = { + lifecycleDisposable += viewModel.toggleAllowAllMentions() + .subscribeBy( + onError = { Log.w(TAG, "Error updating profile", it) } + ) + } + ) } } @@ -100,4 +129,8 @@ class AddAllowedMembersFragment : DSLSettingsFragment(layoutId = R.layout.fragme lifecycleDisposable += viewModel.addMember(id) .subscribe() } + + companion object { + private val TAG = Log.tag(AddAllowedMembersFragment::class.java) + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/profiles/AddAllowedMembersViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/profiles/AddAllowedMembersViewModel.kt index b52e131b68..2248f46a55 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/profiles/AddAllowedMembersViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/profiles/AddAllowedMembersViewModel.kt @@ -30,6 +30,16 @@ class AddAllowedMembersViewModel(private val profileId: Long, private val reposi .observeOn(AndroidSchedulers.mainThread()) } + fun toggleAllowAllMentions(): Single { + return repository.toggleAllowAllMentions(profileId) + .observeOn(AndroidSchedulers.mainThread()) + } + + fun toggleAllowAllCalls(): Single { + return repository.toggleAllowAllCalls(profileId) + .observeOn(AndroidSchedulers.mainThread()) + } + data class NotificationProfileAndRecipients(val profile: NotificationProfile, val recipients: List) class Factory(private val profileId: Long) : ViewModelProvider.Factory { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/profiles/NotificationProfileDetailsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/profiles/NotificationProfileDetailsViewModel.kt index 38a7ced8c3..ef02b10263 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/profiles/NotificationProfileDetailsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/profiles/NotificationProfileDetailsViewModel.kt @@ -9,7 +9,6 @@ import io.reactivex.rxjava3.core.Single import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.kotlin.plusAssign import io.reactivex.rxjava3.kotlin.subscribeBy -import org.thoughtcrime.securesms.database.NotificationProfileDatabase import org.thoughtcrime.securesms.notifications.profiles.NotificationProfile import org.thoughtcrime.securesms.notifications.profiles.NotificationProfiles import org.thoughtcrime.securesms.recipients.Recipient @@ -84,20 +83,12 @@ class NotificationProfileDetailsViewModel(private val profileId: Long, private v } fun toggleAllowAllMentions(): Single { - return repository.getProfile(profileId) - .take(1) - .singleOrError() - .flatMap { repository.updateProfile(it.copy(allowAllMentions = !it.allowAllMentions)) } - .map { (it as NotificationProfileDatabase.NotificationProfileChangeResult.Success).notificationProfile } + return repository.toggleAllowAllMentions(profileId) .observeOn(AndroidSchedulers.mainThread()) } fun toggleAllowAllCalls(): Single { - return repository.getProfile(profileId) - .take(1) - .singleOrError() - .flatMap { repository.updateProfile(it.copy(allowAllCalls = !it.allowAllCalls)) } - .map { (it as NotificationProfileDatabase.NotificationProfileChangeResult.Success).notificationProfile } + return repository.toggleAllowAllCalls(profileId) .observeOn(AndroidSchedulers.mainThread()) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/profiles/NotificationProfilesRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/profiles/NotificationProfilesRepository.kt index eeddaaf7ea..7279c842f8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/profiles/NotificationProfilesRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/profiles/NotificationProfilesRepository.kt @@ -94,6 +94,22 @@ class NotificationProfilesRepository { .subscribeOn(Schedulers.io()) } + fun toggleAllowAllMentions(profileId: Long): Single { + return getProfile(profileId) + .take(1) + .singleOrError() + .flatMap { updateProfile(it.copy(allowAllMentions = !it.allowAllMentions)) } + .map { (it as NotificationProfileDatabase.NotificationProfileChangeResult.Success).notificationProfile } + } + + fun toggleAllowAllCalls(profileId: Long): Single { + return getProfile(profileId) + .take(1) + .singleOrError() + .flatMap { updateProfile(it.copy(allowAllCalls = !it.allowAllCalls)) } + .map { (it as NotificationProfileDatabase.NotificationProfileChangeResult.Success).notificationProfile } + } + fun manuallyToggleProfile(profile: NotificationProfile, now: Long = System.currentTimeMillis()): Completable { return manuallyToggleProfile(profile.id, profile.schedule, now) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/pnp/PhoneNumberPrivacySettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/pnp/PhoneNumberPrivacySettingsViewModel.kt index 1d5893adb3..d6ac569cab 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/pnp/PhoneNumberPrivacySettingsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/pnp/PhoneNumberPrivacySettingsViewModel.kt @@ -5,6 +5,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.dependencies.ApplicationDependencies +import org.thoughtcrime.securesms.jobs.ProfileUploadJob import org.thoughtcrime.securesms.jobs.RefreshAttributesJob import org.thoughtcrime.securesms.jobs.RefreshOwnProfileJob import org.thoughtcrime.securesms.keyvalue.PhoneNumberPrivacyValues.PhoneNumberListingMode @@ -45,6 +46,7 @@ class PhoneNumberPrivacySettingsViewModel : ViewModel() { SignalStore.phoneNumberPrivacy().phoneNumberSharingMode = if (phoneNumberSharingEnabled) PhoneNumberSharingMode.EVERYBODY else PhoneNumberSharingMode.NOBODY SignalDatabase.recipients.markNeedsSync(Recipient.self().id) StorageSyncHelper.scheduleSyncForDataChange() + ApplicationDependencies.getJobManager().add(ProfileUploadJob()) refresh() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/usernamelinks/main/UsernameLinkSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/usernamelinks/main/UsernameLinkSettingsFragment.kt index d5d5b88b84..d843718e39 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/usernamelinks/main/UsernameLinkSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/usernamelinks/main/UsernameLinkSettingsFragment.kt @@ -214,7 +214,7 @@ class UsernameLinkSettingsFragment : ComposeFragment() { }, navigationIcon = { IconButton( - onClick = { requireActivity().onNavigateUp() } + onClick = { requireActivity().onBackPressed() } ) { Icon( painter = painterResource(R.drawable.symbol_x_24), diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsFragment.kt index 6b484c8c5b..f14b253a04 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsFragment.kt @@ -33,7 +33,6 @@ import org.signal.core.util.getParcelableArrayListExtraCompat import org.thoughtcrime.securesms.AvatarPreviewActivity import org.thoughtcrime.securesms.BlockUnblockDialog import org.thoughtcrime.securesms.InviteActivity -import org.thoughtcrime.securesms.MainActivity import org.thoughtcrime.securesms.MuteDialog import org.thoughtcrime.securesms.PushContactSelectionActivity import org.thoughtcrime.securesms.R @@ -79,6 +78,7 @@ import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.RecipientExporter import org.thoughtcrime.securesms.recipients.RecipientId import org.thoughtcrime.securesms.recipients.RecipientUtil +import org.thoughtcrime.securesms.recipients.ui.about.AboutSheet import org.thoughtcrime.securesms.recipients.ui.bottomsheet.RecipientBottomSheetDialogFragment import org.thoughtcrime.securesms.stories.Stories import org.thoughtcrime.securesms.stories.StoryViewerArgs @@ -121,18 +121,6 @@ class ConversationSettingsFragment : DSLSettingsFragment( ContextUtil.requireDrawable(requireContext(), R.drawable.ic_block_tinted_24) } - private val deleteIcon by lazy { - ContextUtil.requireDrawable(requireContext(), R.drawable.ic_trash_24).apply { - colorFilter = PorterDuffColorFilter(alertTint, PorterDuff.Mode.SRC_IN) - } - } - - private val deleteIconDisabled by lazy { - ContextUtil.requireDrawable(requireContext(), R.drawable.ic_trash_24).apply { - colorFilter = PorterDuffColorFilter(alertDisabledTint, PorterDuff.Mode.SRC_IN) - } - } - private val leaveIcon by lazy { ContextUtil.requireDrawable(requireContext(), R.drawable.ic_leave_tinted_24).apply { colorFilter = PorterDuffColorFilter(alertTint, PorterDuff.Mode.SRC_IN) @@ -331,7 +319,10 @@ class ConversationSettingsFragment : DSLSettingsFragment( customPref( BioTextPreference.RecipientModel( recipient = state.recipient, - linkedDevices = RecipientUtil.getSubDeviceCount(requireContext(), state.recipient).orElse(null) + linkedDevices = RecipientUtil.getSubDeviceCount(requireContext(), state.recipient).orElse(null), + onHeadlineClickListener = { + AboutSheet.create(state.recipient).show(parentFragmentManager, null) + } ) ) } @@ -800,27 +791,6 @@ class ConversationSettingsFragment : DSLSettingsFragment( } } ) - - state.withRecipientSettingsState { recipientState -> - clickPref( - title = DSLSettingsText.from(R.string.delete, if (recipientState.canDelete) alertTint else alertDisabledTint), - icon = DSLSettingsIcon.from(if (recipientState.canDelete) deleteIcon else deleteIconDisabled), - isEnabled = recipientState.canDelete, - onClick = { - BlockUnblockDialog.showDeleteFor( - requireContext(), viewLifecycleOwner.lifecycle, state.recipient, - { - viewModel.delete(false) - startActivity(Intent(requireContext(), MainActivity::class.java)) - }, - { - viewModel.delete(true) - startActivity(Intent(requireContext(), MainActivity::class.java)) - } - ) - } - ) - } } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsRepository.kt index da9110a49b..ae48fef267 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsRepository.kt @@ -196,16 +196,6 @@ class ConversationSettingsRepository( } } - fun delete(recipientId: RecipientId, block: Boolean) { - SignalExecutors.BOUNDED.execute { - val recipient = Recipient.resolved(recipientId) - if (block) { - RecipientUtil.blockNonGroup(context, recipient) - } - RecipientUtil.delete(context, recipient) - } - } - fun block(groupId: GroupId) { SignalExecutors.BOUNDED.execute { val recipient = Recipient.externalGroupExact(groupId) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsViewModel.kt index e3a089b00d..c284df7afe 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsViewModel.kt @@ -76,7 +76,7 @@ sealed class ConversationSettingsViewModel( if (!cleared) { state.copy( sharedMedia = mediaRecords, - sharedMediaIds = mediaRecords.mapNotNull { it.attachment?.attachmentId?.rowId }, + sharedMediaIds = mediaRecords.mapNotNull { it.attachment?.attachmentId?.id }, sharedMediaLoaded = true, displayInternalRecipientDetails = repository.isInternalRecipientDetailsEnabled() ) @@ -100,8 +100,6 @@ sealed class ConversationSettingsViewModel( abstract fun unblock() - abstract fun delete(block: Boolean) - abstract fun onAddToGroup() abstract fun onAddToGroupComplete(selected: List, onComplete: () -> Unit) @@ -182,7 +180,6 @@ sealed class ConversationSettingsViewModel( state.copy( specificSettingsState = recipientSettings.copy( - canDelete = groupsInCommon.isEmpty(), allGroupsInCommon = groupsInCommon, groupsInCommon = if (!canShowMore) groupsInCommon else groupsInCommon.take(5), canShowMoreGroupsInCommon = canShowMore @@ -250,10 +247,6 @@ sealed class ConversationSettingsViewModel( override fun unblock() { repository.unblock(recipientId) } - - override fun delete(block: Boolean) { - repository.delete(recipientId, block) - } } private class GroupSettingsViewModel( @@ -466,8 +459,6 @@ sealed class ConversationSettingsViewModel( override fun unblock() { repository.unblock(groupId) } - - override fun delete(block: Boolean) = Unit } class Factory( diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/preferences/BioTextPreference.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/preferences/BioTextPreference.kt index 9e353c0da8..72b0a7f4d3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/preferences/BioTextPreference.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/preferences/BioTextPreference.kt @@ -32,11 +32,14 @@ object BioTextPreference { abstract fun getSubhead1Text(context: Context): String? abstract fun getSubhead2Text(): String? abstract fun getSubhead2ExtraText(context: Context): String? + + open val onHeadlineClickListener: () -> Unit = {} } class RecipientModel( private val recipient: Recipient, private val linkedDevices: Int?, + override val onHeadlineClickListener: () -> Unit ) : BioTextPreferenceModel() { override fun getHeadlineText(context: Context): CharSequence { @@ -46,12 +49,18 @@ object BioTextPreference { recipient.getDisplayNameOrUsername(context) } - return if (recipient.showVerified()) { - SpannableStringBuilder(name).apply { + if (!recipient.showVerified() && !recipient.isIndividual) { + return name + } + + return SpannableStringBuilder(name).apply { + if (recipient.showVerified()) { SpanUtil.appendCenteredImageSpan(this, ContextUtil.requireDrawable(context, R.drawable.ic_official_28), 28, 28) } - } else { - name + + if (recipient.isIndividual) { + SpanUtil.appendCenteredImageSpan(this, ContextUtil.requireDrawable(context, R.drawable.symbol_chevron_right_24_color_on_secondary_container), 24, 24) + } } } @@ -63,7 +72,11 @@ object BioTextPreference { } } - override fun getSubhead2Text(): String? = recipient.e164.map(PhoneNumberFormatter::prettyPrint).orElse(null) + override fun getSubhead2Text(): String? = if (recipient.shouldShowE164()) { + recipient.e164.map(PhoneNumberFormatter::prettyPrint).orElse(null) + } else { + null + } override fun getSubhead2ExtraText(context: Context): String? { if (linkedDevices == null) { @@ -117,6 +130,7 @@ object BioTextPreference { override fun bind(model: T) { headline.text = model.getHeadlineText(context) + headline.setOnClickListener { model.onHeadlineClickListener() } model.getSubhead1Text(context).let { subhead1.text = it diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/preferences/CallPreference.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/preferences/CallPreference.kt index aece9c00fb..6ac7dabbfd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/preferences/CallPreference.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/preferences/CallPreference.kt @@ -49,7 +49,7 @@ object CallPreference { MessageTypes.INCOMING_AUDIO_CALL_TYPE, MessageTypes.INCOMING_VIDEO_CALL_TYPE -> R.drawable.symbol_arrow_downleft_24 MessageTypes.OUTGOING_AUDIO_CALL_TYPE, MessageTypes.OUTGOING_VIDEO_CALL_TYPE -> R.drawable.symbol_arrow_upright_24 MessageTypes.GROUP_CALL_TYPE -> when { - call.event == CallTable.Event.MISSED -> R.drawable.symbol_missed_incoming_24 + call.event.isMissedCall() -> R.drawable.symbol_missed_incoming_24 call.event == CallTable.Event.GENERIC_GROUP_CALL || call.event == CallTable.Event.JOINED -> R.drawable.symbol_group_24 call.direction == CallTable.Direction.INCOMING -> R.drawable.symbol_arrow_downleft_24 call.direction == CallTable.Direction.OUTGOING -> R.drawable.symbol_arrow_upright_24 @@ -61,14 +61,15 @@ object CallPreference { private fun getCallType(call: CallTable.Call): String { val id = when (call.messageType) { - MessageTypes.MISSED_VIDEO_CALL_TYPE -> R.string.MessageRecord_missed_voice_call - MessageTypes.MISSED_AUDIO_CALL_TYPE -> R.string.MessageRecord_missed_video_call + MessageTypes.MISSED_VIDEO_CALL_TYPE -> if (call.event == CallTable.Event.MISSED) R.string.MessageRecord_missed_voice_call else R.string.MessageRecord_missed_voice_call_notification_profile + MessageTypes.MISSED_AUDIO_CALL_TYPE -> if (call.event == CallTable.Event.MISSED) R.string.MessageRecord_missed_video_call else R.string.MessageRecord_missed_video_call_notification_profile MessageTypes.INCOMING_AUDIO_CALL_TYPE -> R.string.MessageRecord_incoming_voice_call MessageTypes.INCOMING_VIDEO_CALL_TYPE -> R.string.MessageRecord_incoming_video_call MessageTypes.OUTGOING_AUDIO_CALL_TYPE -> R.string.MessageRecord_outgoing_voice_call MessageTypes.OUTGOING_VIDEO_CALL_TYPE -> R.string.MessageRecord_outgoing_video_call MessageTypes.GROUP_CALL_TYPE -> when { call.event == CallTable.Event.MISSED -> R.string.CallPreference__missed_group_call + call.event == CallTable.Event.MISSED_NOTIFICATION_PROFILE -> R.string.CallPreference__missed_group_call_notification_profile call.event == CallTable.Event.GENERIC_GROUP_CALL || call.event == CallTable.Event.JOINED -> R.string.CallPreference__group_call call.direction == CallTable.Direction.INCOMING -> R.string.CallPreference__incoming_group_call call.direction == CallTable.Direction.OUTGOING -> R.string.CallPreference__outgoing_group_call diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/transfercontrols/TransferControlView.kt b/app/src/main/java/org/thoughtcrime/securesms/components/transfercontrols/TransferControlView.kt index c55f4be6d5..f18714e494 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/transfercontrols/TransferControlView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/transfercontrols/TransferControlView.kt @@ -4,15 +4,16 @@ */ package org.thoughtcrime.securesms.components.transfercontrols -import android.annotation.SuppressLint import android.content.Context import android.os.Build -import android.text.format.Formatter +import android.text.StaticLayout import android.util.AttributeSet import android.view.LayoutInflater import android.view.View +import android.view.ViewGroup import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.view.children +import androidx.core.view.updateLayoutParams import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode @@ -25,15 +26,19 @@ 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 +import kotlin.math.ceil +import kotlin.math.roundToInt class TransferControlView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : ConstraintLayout(context, attrs, defStyleAttr) { private val uuid = UUID.randomUUID().toString() private val binding: TransferControlsViewBinding private var state = TransferControlViewState() + private val progressUpdateDebouncer: ThrottledDebouncer = ThrottledDebouncer(100) init { tag = uuid @@ -56,8 +61,10 @@ class TransferControlView @JvmOverloads constructor(context: Context, attrs: Att private fun updateState(stateFactory: (TransferControlViewState) -> TransferControlViewState) { val newState = stateFactory.invoke(state) - if (newState != state) { - applyState(newState) + if (newState != state && !(deriveMode(state) == Mode.GONE && deriveMode(newState) == Mode.GONE)) { + progressUpdateDebouncer.publish { + applyState(newState) + } } state = newState } @@ -65,6 +72,11 @@ class TransferControlView @JvmOverloads constructor(context: Context, attrs: Att private fun applyState(currentState: TransferControlViewState) { val mode = deriveMode(currentState) verboseLog("New state applying, mode = $mode") + + children.forEach { + it.clearAnimation() + } + when (mode) { Mode.PENDING_GALLERY -> displayPendingGallery(currentState) Mode.PENDING_GALLERY_CONTAINS_PLAYABLE -> displayPendingGalleryWithPlayable(currentState) @@ -206,7 +218,7 @@ class TransferControlView @JvmOverloads constructor(context: Context, attrs: Att } private fun displayPendingGallery(currentState: TransferControlViewState) { - binding.primaryProgressView.startClickListener = currentState.downloadClickedListener + binding.primaryProgressView.startClickListener = currentState.startTransferClickListener applyFocusableAndClickable( currentState, listOf(binding.primaryProgressView, binding.primaryDetailsText, binding.primaryBackground), @@ -219,26 +231,28 @@ class TransferControlView @JvmOverloads constructor(context: Context, attrs: Att secondaryDetailsText = currentState.showSecondaryText ) - binding.primaryDetailsText.setOnClickListener(currentState.downloadClickedListener) - binding.primaryBackground.setOnClickListener(currentState.downloadClickedListener) + binding.primaryDetailsText.setOnClickListener(currentState.startTransferClickListener) + binding.primaryBackground.setOnClickListener(currentState.startTransferClickListener) binding.primaryDetailsText.translationX = if (ViewUtil.isLtr(this)) { - ViewUtil.dpToPx(-8).toFloat() + ViewUtil.dpToPx(-PRIMARY_TEXT_OFFSET_DP).toFloat() } else { - ViewUtil.dpToPx(8).toFloat() + ViewUtil.dpToPx(PRIMARY_TEXT_OFFSET_DP).toFloat() } - val remainingSlides = currentState.slides.filterNot { it.transferState == AttachmentTable.TRANSFER_PROGRESS_DONE } - val downloadCount = remainingSlides.size - binding.primaryDetailsText.text = context.resources.getQuantityString(R.plurals.TransferControlView_n_items, downloadCount, downloadCount) - val byteCount = remainingSlides.sumOf { it.asAttachment().size } - binding.secondaryDetailsText.text = Formatter.formatShortFileSize(context, byteCount) + setSecondaryDetailsText(currentState) } private fun displayPendingGalleryWithPlayable(currentState: TransferControlViewState) { - binding.secondaryProgressView.startClickListener = currentState.downloadClickedListener + binding.secondaryProgressView.startClickListener = currentState.startTransferClickListener + binding.secondaryDetailsText.setOnClickListener(currentState.startTransferClickListener) + binding.secondaryBackground.setOnClickListener(currentState.startTransferClickListener) super.setClickable(false) binding.secondaryProgressView.isClickable = currentState.showSecondaryText binding.secondaryProgressView.isFocusable = currentState.showSecondaryText + binding.secondaryDetailsText.isClickable = currentState.showSecondaryText + binding.secondaryDetailsText.isFocusable = currentState.showSecondaryText + binding.secondaryBackground.isClickable = currentState.showSecondaryText + binding.secondaryBackground.isFocusable = currentState.showSecondaryText binding.primaryProgressView.isClickable = false binding.primaryProgressView.isFocusable = false showAllViews( @@ -250,13 +264,16 @@ class TransferControlView @JvmOverloads constructor(context: Context, attrs: Att ) binding.secondaryProgressView.setStopped(false) - - val byteCount = currentState.slides.sumOf { it.asAttachment().size } - binding.secondaryDetailsText.text = Formatter.formatShortFileSize(context, byteCount) + setSecondaryDetailsText(currentState) + binding.secondaryDetailsText.translationX = if (ViewUtil.isLtr(this)) { + ViewUtil.dpToPx(-SECONDARY_TEXT_OFFSET_DP).toFloat() + } else { + ViewUtil.dpToPx(SECONDARY_TEXT_OFFSET_DP).toFloat() + } } private fun displayPendingSingleItem(currentState: TransferControlViewState) { - binding.primaryProgressView.startClickListener = currentState.downloadClickedListener + binding.primaryProgressView.startClickListener = currentState.startTransferClickListener applyFocusableAndClickable(currentState, listOf(binding.primaryProgressView), listOf(binding.secondaryProgressView, binding.playVideoButton)) binding.primaryProgressView.setStopped(false) showAllViews( @@ -265,14 +282,20 @@ class TransferControlView @JvmOverloads constructor(context: Context, attrs: Att secondaryProgressView = false, secondaryDetailsText = currentState.showSecondaryText ) - val byteCount = currentState.slides.sumOf { it.asAttachment().size } - binding.secondaryDetailsText.text = Formatter.formatShortFileSize(context, byteCount) + binding.secondaryDetailsText.translationX = 0f + setSecondaryDetailsText(currentState) } private fun displayPendingPlayableVideo(currentState: TransferControlViewState) { - binding.secondaryProgressView.startClickListener = currentState.downloadClickedListener + binding.secondaryProgressView.startClickListener = currentState.startTransferClickListener + binding.secondaryDetailsText.setOnClickListener(currentState.startTransferClickListener) + binding.secondaryBackground.setOnClickListener(currentState.startTransferClickListener) binding.playVideoButton.setOnClickListener(currentState.instantPlaybackClickListener) - applyFocusableAndClickable(currentState, listOf(binding.secondaryProgressView, binding.playVideoButton), listOf(binding.primaryProgressView)) + applyFocusableAndClickable( + currentState, + listOf(binding.secondaryProgressView, binding.secondaryDetailsText, binding.secondaryBackground, binding.playVideoButton), + listOf(binding.primaryProgressView) + ) binding.secondaryProgressView.setStopped(false) showAllViews( primaryProgressView = false, @@ -280,8 +303,12 @@ class TransferControlView @JvmOverloads constructor(context: Context, attrs: Att secondaryDetailsText = currentState.showSecondaryText, secondaryProgressView = currentState.showSecondaryText ) - val byteCount = currentState.slides.sumOf { it.asAttachment().size } - binding.secondaryDetailsText.text = Formatter.formatShortFileSize(context, byteCount) + setSecondaryDetailsText(currentState) + binding.secondaryDetailsText.translationX = if (ViewUtil.isLtr(this)) { + ViewUtil.dpToPx(-SECONDARY_TEXT_OFFSET_DP).toFloat() + } else { + ViewUtil.dpToPx(SECONDARY_TEXT_OFFSET_DP).toFloat() + } } private fun displayDownloadingGallery(currentState: TransferControlViewState) { @@ -295,17 +322,17 @@ class TransferControlView @JvmOverloads constructor(context: Context, attrs: Att val progress = calculateProgress(currentState) if (progress == 0f) { - binding.secondaryProgressView.setUploading(progress) + binding.secondaryProgressView.setProgress(progress) } else { - binding.secondaryProgressView.cancelClickListener = currentState.cancelDownloadClickedListener - binding.secondaryProgressView.setDownloading(progress) + binding.secondaryProgressView.cancelClickListener = currentState.cancelTransferClickedListener + binding.secondaryProgressView.setProgress(progress) } - - binding.secondaryDetailsText.text = deriveSecondaryDetailsText(currentState) + binding.secondaryDetailsText.translationX = 0f + setSecondaryDetailsText(currentState) } private fun displayDownloadingSingleItem(currentState: TransferControlViewState) { - binding.primaryProgressView.cancelClickListener = currentState.cancelDownloadClickedListener + binding.primaryProgressView.cancelClickListener = currentState.cancelTransferClickedListener applyFocusableAndClickable(currentState, listOf(binding.primaryProgressView), listOf(binding.secondaryProgressView, binding.playVideoButton)) showAllViews( playVideoButton = false, @@ -316,16 +343,16 @@ class TransferControlView @JvmOverloads constructor(context: Context, attrs: Att val progress = calculateProgress(currentState) if (progress == 0f) { - binding.primaryProgressView.setUploading(progress) + binding.primaryProgressView.setProgress(progress) } else { - binding.primaryProgressView.setDownloading(progress) + binding.primaryProgressView.setProgress(progress) } - - binding.secondaryDetailsText.text = deriveSecondaryDetailsText(currentState) + binding.secondaryDetailsText.translationX = 0f + setSecondaryDetailsText(currentState) } private fun displayDownloadingPlayableVideo(currentState: TransferControlViewState) { - binding.secondaryProgressView.cancelClickListener = currentState.cancelDownloadClickedListener + binding.secondaryProgressView.cancelClickListener = currentState.cancelTransferClickedListener applyFocusableAndClickable(currentState, listOf(binding.secondaryProgressView, binding.playVideoButton), listOf(binding.primaryProgressView)) showAllViews( primaryDetailsText = false, @@ -337,15 +364,16 @@ class TransferControlView @JvmOverloads constructor(context: Context, attrs: Att val progress = calculateProgress(currentState) if (progress == 0f) { - binding.secondaryProgressView.setUploading(progress) + binding.secondaryProgressView.setProgress(progress) } else { - binding.secondaryProgressView.setDownloading(progress) + binding.secondaryProgressView.setProgress(progress) } - binding.secondaryDetailsText.text = deriveSecondaryDetailsText(currentState) + binding.secondaryDetailsText.translationX = 0f + setSecondaryDetailsText(currentState) } private fun displayUploadingSingleItem(currentState: TransferControlViewState) { - binding.secondaryProgressView.startClickListener = currentState.downloadClickedListener + binding.secondaryProgressView.cancelClickListener = currentState.cancelTransferClickedListener applyFocusableAndClickable(currentState, listOf(binding.secondaryProgressView), listOf(binding.primaryProgressView, binding.playVideoButton)) showAllViews( playVideoButton = false, @@ -355,13 +383,14 @@ class TransferControlView @JvmOverloads constructor(context: Context, attrs: Att ) val progress = calculateProgress(currentState) - binding.secondaryProgressView.setUploading(progress) + binding.secondaryProgressView.setProgress(progress) - binding.secondaryDetailsText.text = deriveSecondaryDetailsText(currentState) + binding.secondaryDetailsText.translationX = 0f + setSecondaryDetailsText(currentState) } private fun displayUploadingGallery(currentState: TransferControlViewState) { - binding.secondaryProgressView.startClickListener = currentState.downloadClickedListener + binding.secondaryProgressView.cancelClickListener = currentState.cancelTransferClickedListener applyFocusableAndClickable(currentState, listOf(binding.secondaryProgressView), listOf(binding.primaryProgressView, binding.playVideoButton)) showAllViews( playVideoButton = false, @@ -370,28 +399,45 @@ class TransferControlView @JvmOverloads constructor(context: Context, attrs: Att ) val progress = calculateProgress(currentState) - binding.secondaryProgressView.setUploading(progress) + binding.secondaryProgressView.setProgress(progress) - binding.secondaryDetailsText.text = deriveSecondaryDetailsText(currentState) + binding.secondaryDetailsText.translationX = 0f + setSecondaryDetailsText(currentState) } private fun displayRetry(currentState: TransferControlViewState, isUploading: Boolean) { - binding.secondaryProgressView.startClickListener = currentState.downloadClickedListener - applyFocusableAndClickable(currentState, listOf(binding.secondaryProgressView), listOf(binding.primaryProgressView, binding.playVideoButton)) + if (currentState.startTransferClickListener == null) { + Log.w(TAG, "No click listener set for retry!") + } + + binding.secondaryProgressView.startClickListener = currentState.startTransferClickListener + applyFocusableAndClickable( + currentState, + listOf(binding.secondaryProgressView, binding.secondaryDetailsText, binding.secondaryBackground), + listOf(binding.primaryProgressView, binding.playVideoButton) + ) showAllViews( playVideoButton = false, primaryProgressView = false, primaryDetailsText = false, secondaryDetailsText = currentState.showSecondaryText ) - + binding.secondaryBackground.setOnClickListener(currentState.startTransferClickListener) + binding.secondaryDetailsText.setOnClickListener(currentState.startTransferClickListener) binding.secondaryProgressView.setStopped(isUploading) - binding.secondaryDetailsText.text = resources.getString(R.string.NetworkFailure__retry) + setSecondaryDetailsText(currentState) + binding.secondaryDetailsText.translationX = if (ViewUtil.isLtr(this)) { + ViewUtil.dpToPx(-RETRY_SECONDARY_TEXT_OFFSET_DP).toFloat() + } else { + ViewUtil.dpToPx(RETRY_SECONDARY_TEXT_OFFSET_DP).toFloat() + } } private fun displayChildrenAsGone() { children.forEach { - it.visible = false + if (it.visible && it.animation == null) { + ViewUtil.fadeOut(it, 250) + } } } @@ -421,7 +467,11 @@ class TransferControlView @JvmOverloads constructor(context: Context, attrs: Att binding.secondaryProgressView.visible = secondaryProgressView binding.secondaryDetailsText.visible = secondaryDetailsText binding.secondaryBackground.visible = secondaryProgressView || secondaryDetailsText - val textPadding = if (secondaryProgressView) 0 else context.resources.getDimensionPixelSize(R.dimen.transfer_control_view_progressbar_to_textview_margin) + val textPadding = if (secondaryProgressView) { + context.resources.getDimensionPixelSize(R.dimen.transfer_control_view_progressbar_to_textview_margin) + } else { + context.resources.getDimensionPixelSize(R.dimen.transfer_control_view_parent_to_textview_margin) + } ViewUtil.setPaddingStart(binding.secondaryDetailsText, textPadding) if (ViewUtil.isLtr(binding.secondaryDetailsText)) { (binding.secondaryDetailsText.layoutParams as MarginLayoutParams).leftMargin = textPadding @@ -467,12 +517,20 @@ class TransferControlView @JvmOverloads constructor(context: Context, attrs: Att if (event.type == PartProgressEvent.Type.COMPRESSION) { val mutableMap = it.compressionProgress.toMutableMap() - mutableMap[attachment] = Progress.fromEvent(event) + val updateEvent = Progress.fromEvent(event) + val existingEvent = mutableMap[attachment] + if (existingEvent == null || updateEvent.completed > existingEvent.completed) { + mutableMap[attachment] = updateEvent + } verboseLog("onEventAsync compression update") return@updateState it.copy(compressionProgress = mutableMap.toMap()) } else { val mutableMap = it.networkProgress.toMutableMap() - mutableMap[attachment] = Progress.fromEvent(event) + val updateEvent = Progress.fromEvent(event) + val existingEvent = mutableMap[attachment] + if (existingEvent == null || updateEvent.completed > existingEvent.completed) { + mutableMap[attachment] = updateEvent + } verboseLog("onEventAsync network update") return@updateState it.copy(networkProgress = mutableMap.toMap()) } @@ -493,7 +551,7 @@ class TransferControlView @JvmOverloads constructor(context: Context, attrs: Att for (slide in slides) { val attachment = slide.asAttachment() if (attachment.transferState == AttachmentTable.TRANSFER_PROGRESS_DONE) { - networkProgress[attachment] = Progress(1L, attachment.size) + networkProgress[attachment] = Progress(attachment.size, attachment.size) } else if (!MediaUtil.isInstantVideoSupported(slide)) { allStreamableOrDone = false } @@ -534,11 +592,11 @@ class TransferControlView @JvmOverloads constructor(context: Context, attrs: Att return true } - fun setDownloadClickListener(listener: OnClickListener) { - verboseLog("downloadClickListener update") + fun setTransferClickListener(listener: OnClickListener) { + verboseLog("transferClickListener update") updateState { it.copy( - downloadClickedListener = listener + startTransferClickListener = listener ) } } @@ -547,7 +605,7 @@ class TransferControlView @JvmOverloads constructor(context: Context, attrs: Att verboseLog("cancelClickListener update") updateState { it.copy( - cancelDownloadClickedListener = listener + cancelTransferClickedListener = listener ) } } @@ -586,8 +644,8 @@ class TransferControlView @JvmOverloads constructor(context: Context, attrs: Att } private fun isCompressing(state: TransferControlViewState): Boolean { - // We never get a completion event so it never actually reaches 100% - return state.compressionProgress.sumTotal() > 0 && state.compressionProgress.values.map { it.completed.toFloat() / it.total }.sum() < 0.99f + val total = state.compressionProgress.sumTotal() + return total > 0 && state.compressionProgress.sumCompleted() / total < 0.99f } private fun calculateProgress(state: TransferControlViewState): Float { @@ -598,14 +656,62 @@ class TransferControlView @JvmOverloads constructor(context: Context, attrs: Att return weightedProgress / weightedTotal } - @SuppressLint("SetTextI18n") - private fun deriveSecondaryDetailsText(currentState: TransferControlViewState): String { - return if (isCompressing(currentState)) { - return context.getString(R.string.TransferControlView__processing) - } else { - val progressText = Formatter.formatShortFileSize(context, currentState.networkProgress.sumCompleted()) - val totalText = Formatter.formatShortFileSize(context, currentState.networkProgress.sumTotal()) - "$progressText/$totalText" + private fun setSecondaryDetailsText(currentState: TransferControlViewState) { + when (deriveMode(currentState)) { + Mode.PENDING_GALLERY -> { + binding.secondaryDetailsText.updateLayoutParams { + width = ViewGroup.LayoutParams.WRAP_CONTENT + } + val remainingSlides = currentState.slides.filterNot { it.transferState == AttachmentTable.TRANSFER_PROGRESS_DONE } + val downloadCount = remainingSlides.size + binding.primaryDetailsText.text = context.resources.getQuantityString(R.plurals.TransferControlView_n_items, downloadCount, downloadCount) + val mebibyteCount = (currentState.networkProgress.sumTotal() - currentState.networkProgress.sumCompleted()) / MEBIBYTE + binding.secondaryDetailsText.text = context.getString(R.string.TransferControlView__filesize, mebibyteCount) + } + + Mode.PENDING_GALLERY_CONTAINS_PLAYABLE -> { + binding.secondaryDetailsText.updateLayoutParams { + width = ViewGroup.LayoutParams.WRAP_CONTENT + } + val mebibyteCount = (currentState.networkProgress.sumTotal() - currentState.networkProgress.sumCompleted()) / MEBIBYTE + binding.secondaryDetailsText.text = context.getString(R.string.TransferControlView__filesize, mebibyteCount) + } + + Mode.PENDING_SINGLE_ITEM, Mode.PENDING_VIDEO_PLAYABLE -> { + binding.secondaryDetailsText.updateLayoutParams { + width = ViewGroup.LayoutParams.WRAP_CONTENT + } + val mebibyteCount = (currentState.slides.sumOf { it.asAttachment().size }) / MEBIBYTE + binding.secondaryDetailsText.text = context.getString(R.string.TransferControlView__filesize, mebibyteCount) + } + + Mode.DOWNLOADING_GALLERY, Mode.DOWNLOADING_SINGLE_ITEM, Mode.DOWNLOADING_VIDEO_PLAYABLE, Mode.UPLOADING_GALLERY, Mode.UPLOADING_SINGLE_ITEM -> { + if (currentState.networkProgress.sumCompleted() == 0L || isCompressing(currentState)) { + binding.secondaryDetailsText.updateLayoutParams { + width = ViewGroup.LayoutParams.WRAP_CONTENT + } + binding.secondaryDetailsText.text = context.getString(R.string.TransferControlView__processing) + } else { + val progressMiB = currentState.networkProgress.sumCompleted() / MEBIBYTE + val totalMiB = currentState.networkProgress.sumTotal() / MEBIBYTE + val completedLabel = context.resources.getString(R.string.TransferControlView__download_progress, totalMiB, totalMiB) + val desiredWidth = StaticLayout.getDesiredWidth(completedLabel, binding.secondaryDetailsText.paint) + binding.secondaryDetailsText.text = context.resources.getString(R.string.TransferControlView__download_progress, progressMiB, totalMiB) + val roundedWidth = ceil(desiredWidth.toDouble()).roundToInt() + binding.secondaryDetailsText.compoundPaddingLeft + binding.secondaryDetailsText.compoundPaddingRight + binding.secondaryDetailsText.updateLayoutParams { + width = roundedWidth + } + } + } + + Mode.RETRY_DOWNLOADING, Mode.RETRY_UPLOADING -> { + binding.secondaryDetailsText.text = resources.getString(R.string.NetworkFailure__retry) + binding.secondaryDetailsText.updateLayoutParams { + width = ViewGroup.LayoutParams.WRAP_CONTENT + } + } + + Mode.GONE -> Unit } } @@ -622,6 +728,10 @@ class TransferControlView @JvmOverloads constructor(context: Context, attrs: Att private const val TAG = "TransferControlView" private const val VERBOSE_DEVELOPMENT_LOGGING = false private const val UPLOAD_TASK_WEIGHT = 1 + private const val SECONDARY_TEXT_OFFSET_DP = 6 + private const val RETRY_SECONDARY_TEXT_OFFSET_DP = 6 + private const val PRIMARY_TEXT_OFFSET_DP = 4 + private const val MEBIBYTE = 1048576f /** * A weighting compared to [UPLOAD_TASK_WEIGHT] diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState.kt b/app/src/main/java/org/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState.kt index 2a1d41b6ab..95159c570a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/transfercontrols/TransferControlViewState.kt @@ -14,8 +14,8 @@ data class TransferControlViewState( val isFocusable: Boolean = true, val isClickable: Boolean = true, val slides: List = emptyList(), - val downloadClickedListener: View.OnClickListener? = null, - val cancelDownloadClickedListener: View.OnClickListener? = null, + val startTransferClickListener: View.OnClickListener? = null, + val cancelTransferClickedListener: View.OnClickListener? = null, val instantPlaybackClickListener: View.OnClickListener? = null, val showSecondaryText: Boolean = true, val networkProgress: Map = HashMap(), diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/transfercontrols/TransferProgressView.kt b/app/src/main/java/org/thoughtcrime/securesms/components/transfercontrols/TransferProgressView.kt index 37467b7233..182779489d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/transfercontrols/TransferProgressView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/transfercontrols/TransferProgressView.kt @@ -32,10 +32,10 @@ class TransferProgressView @JvmOverloads constructor( ) : View(context, attrs, defStyleAttr, defStyleRes) { companion object { const val TAG = "TransferProgressView" - private const val PROGRESS_ARC_STROKE_WIDTH_DP = 1.5f + private const val PROGRESS_ARC_STROKE_WIDTH_DP = 2f private const val ICON_SIZE_DP = 24f private const val STOP_CORNER_RADIUS_DP = 4f - private const val PROGRESS_BAR_INSET_DP = 3 + private const val PROGRESS_BAR_INSET_DP = 2 } private val iconColor: Int @@ -89,14 +89,15 @@ class TransferProgressView @JvmOverloads constructor( super.onDraw(canvas) when (currentState) { - State.IN_PROGRESS_CANCELABLE, State.IN_PROGRESS_NON_CANCELABLE -> drawProgress(canvas, progressPercent) + State.IN_PROGRESS_CANCELABLE -> drawProgress(canvas, progressPercent, true) + State.IN_PROGRESS_NON_CANCELABLE -> drawProgress(canvas, progressPercent, false) State.READY_TO_UPLOAD -> sizeAndDrawDrawable(canvas, uploadDrawable) State.READY_TO_DOWNLOAD -> sizeAndDrawDrawable(canvas, downloadDrawable) State.UNINITIALIZED -> Unit } } - fun setDownloading(progress: Float) { + fun setProgress(progress: Float) { currentState = State.IN_PROGRESS_CANCELABLE if (cancelClickListener == null) { Log.i(TAG, "Illegal click listener attached.") @@ -107,13 +108,6 @@ class TransferProgressView @JvmOverloads constructor( invalidate() } - fun setUploading(progress: Float) { - currentState = State.IN_PROGRESS_NON_CANCELABLE - setOnClickListener { Log.d(TAG, "Not allowed to click an upload.") } - progressPercent = progress - invalidate() - } - fun setStopped(isUpload: Boolean) { val newState = if (isUpload) State.READY_TO_UPLOAD else State.READY_TO_DOWNLOAD currentState = newState @@ -126,11 +120,13 @@ class TransferProgressView @JvmOverloads constructor( invalidate() } - private fun drawProgress(canvas: Canvas, progressPercent: Float) { - stopIconRect.set(0f, 0f, stopIconSize, stopIconSize) + private fun drawProgress(canvas: Canvas, progressPercent: Float, showStopIcon: Boolean) { + if (showStopIcon) { + stopIconRect.set(0f, 0f, stopIconSize, stopIconSize) - canvas.withTranslation(width / 2 - (stopIconSize / 2), height / 2 - (stopIconSize / 2)) { - drawRoundRect(stopIconRect, stopIconCornerRadius, stopIconCornerRadius, stopIconPaint) + canvas.withTranslation(width / 2 - (stopIconSize / 2), height / 2 - (stopIconSize / 2)) { + drawRoundRect(stopIconRect, stopIconCornerRadius, stopIconCornerRadius, stopIconPaint) + } } val trackWidthScaled = progressArcStrokeWidth diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNoteMediaItemFactory.java b/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNoteMediaItemFactory.java index 3b2aa58a6c..1408a1d6de 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNoteMediaItemFactory.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNoteMediaItemFactory.java @@ -175,7 +175,7 @@ private static MediaItem buildMediaItem(@NonNull Context context, return sender.isSelf() && threadRecipient.isSelf() ? context.getString(R.string.note_to_self) : sender.getDisplayName(context); } else { - return context.getString(R.string.MessageNotifier_signal_message); + return context.getString(R.string.NewConversationActivity__message); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNotePlayerCallback.kt b/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNotePlayerCallback.kt index e7dc6270a9..240436aa2c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNotePlayerCallback.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNotePlayerCallback.kt @@ -204,9 +204,11 @@ class VoiceNotePlayerCallback(val context: Context, val player: VoiceNotePlayer) @MainThread private fun addItemsToPlaylist(mediaItems: List) { var mediaItemsWithNextTone = mediaItems.flatMap { listOf(it, VoiceNoteMediaItemFactory.buildNextVoiceNoteMediaItem(it)) }.toMutableList() - mediaItemsWithNextTone = mediaItemsWithNextTone.subList(0, mediaItemsWithNextTone.size - 1).toMutableList() + mediaItemsWithNextTone = mediaItemsWithNextTone.subList(0, mediaItemsWithNextTone.lastIndex).toMutableList() if (player.mediaItemCount == 0) { - mediaItemsWithNextTone += VoiceNoteMediaItemFactory.buildEndVoiceNoteMediaItem(mediaItemsWithNextTone.last()) + if (mediaItems.size > 1) { + mediaItemsWithNextTone += VoiceNoteMediaItemFactory.buildEndVoiceNoteMediaItem(mediaItemsWithNextTone.last()) + } player.addMediaItems(mediaItemsWithNextTone) } else { player.addMediaItems(player.mediaItemCount, mediaItemsWithNextTone) @@ -264,6 +266,6 @@ class VoiceNotePlayerCallback(val context: Context, val player: VoiceNotePlayer) } private fun List.messageRecordsToVoiceNoteMediaItems(): List { - return this.filter { it.hasAudio() }.mapNotNull { VoiceNoteMediaItemFactory.buildMediaItem(context, it) } + return this.takeWhile { it.hasAudio() }.mapNotNull { VoiceNoteMediaItemFactory.buildMediaItem(context, it) } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/AudioIndicatorView.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/AudioIndicatorView.kt index 8edbcd1f03..3dc9af4c31 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/AudioIndicatorView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/AudioIndicatorView.kt @@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.components.webrtc import android.animation.ValueAnimator import android.content.Context +import android.content.res.ColorStateList import android.graphics.Canvas import android.graphics.Color import android.graphics.Paint @@ -10,6 +11,7 @@ import android.util.AttributeSet import android.view.View import android.view.animation.DecelerateInterpolator import android.widget.FrameLayout +import androidx.core.content.ContextCompat import org.signal.core.util.DimensionUnit import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.events.CallParticipant @@ -31,9 +33,9 @@ class AudioIndicatorView(context: Context, attrs: AttributeSet) : FrameLayout(co } private val barRect = RectF() - private val barWidth = DimensionUnit.DP.toPixels(4f) + private val barWidth = DimensionUnit.DP.toPixels(3f) private val barRadius = DimensionUnit.DP.toPixels(32f) - private val barPadding = DimensionUnit.DP.toPixels(4f) + private val barPadding = DimensionUnit.DP.toPixels(3f) private var middleBarAnimation: ValueAnimator? = null private var sideBarAnimation: ValueAnimator? = null @@ -43,11 +45,15 @@ class AudioIndicatorView(context: Context, attrs: AttributeSet) : FrameLayout(co init { inflate(context, R.layout.audio_indicator_view, this) setWillNotDraw(false) + + backgroundTintList = ColorStateList.valueOf(ContextCompat.getColor(context, R.color.transparent_black_70)) } private val micMuted: View = findViewById(R.id.mic_muted) fun bind(microphoneEnabled: Boolean, level: CallParticipant.AudioLevel?) { + setBackgroundResource(R.drawable.circle_tintable) + micMuted.visible = !microphoneEnabled val wasShowingAudioLevel = showAudioLevel @@ -55,11 +61,11 @@ class AudioIndicatorView(context: Context, attrs: AttributeSet) : FrameLayout(co if (showAudioLevel) { val scaleFactor = when (level!!) { - CallParticipant.AudioLevel.LOWEST -> 0.2f - CallParticipant.AudioLevel.LOW -> 0.4f - CallParticipant.AudioLevel.MEDIUM -> 0.6f - CallParticipant.AudioLevel.HIGH -> 0.8f - CallParticipant.AudioLevel.HIGHEST -> 1.0f + CallParticipant.AudioLevel.LOWEST -> 0.1f + CallParticipant.AudioLevel.LOW -> 0.3f + CallParticipant.AudioLevel.MEDIUM -> 0.5f + CallParticipant.AudioLevel.HIGH -> 0.65f + CallParticipant.AudioLevel.HIGHEST -> 0.8f } middleBarAnimation?.end() diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallLinkInfoSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallLinkInfoSheet.kt deleted file mode 100644 index 1998697f44..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallLinkInfoSheet.kt +++ /dev/null @@ -1,374 +0,0 @@ -/* - * Copyright 2023 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.thoughtcrime.securesms.components.webrtc - -import android.content.ActivityNotFoundException -import android.content.Intent -import android.os.Bundle -import android.view.View -import android.widget.Toast -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.defaultMinSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Surface -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.livedata.observeAsState -import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalInspectionMode -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.res.vectorResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.compose.ui.viewinterop.AndroidView -import androidx.core.app.ShareCompat -import androidx.core.os.BundleCompat -import androidx.core.os.bundleOf -import androidx.fragment.app.FragmentManager -import androidx.fragment.app.activityViewModels -import androidx.fragment.app.viewModels -import androidx.lifecycle.toLiveData -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers -import io.reactivex.rxjava3.core.BackpressureStrategy -import io.reactivex.rxjava3.kotlin.subscribeBy -import kotlinx.collections.immutable.toImmutableList -import org.signal.core.ui.BottomSheets -import org.signal.core.ui.Dividers -import org.signal.core.ui.Rows -import org.signal.core.ui.theme.SignalTheme -import org.signal.core.util.concurrent.LifecycleDisposable -import org.signal.core.util.concurrent.addTo -import org.signal.core.util.logging.Log -import org.signal.ringrtc.CallLinkState.Restrictions -import org.thoughtcrime.securesms.R -import org.thoughtcrime.securesms.calls.links.CallLinks -import org.thoughtcrime.securesms.calls.links.EditCallLinkNameDialogFragment -import org.thoughtcrime.securesms.calls.links.EditCallLinkNameDialogFragmentArgs -import org.thoughtcrime.securesms.calls.links.SignalCallRow -import org.thoughtcrime.securesms.calls.links.details.CallLinkDetailsViewModel -import org.thoughtcrime.securesms.components.AvatarImageView -import org.thoughtcrime.securesms.compose.ComposeBottomSheetDialogFragment -import org.thoughtcrime.securesms.database.CallLinkTable -import org.thoughtcrime.securesms.dependencies.ApplicationDependencies -import org.thoughtcrime.securesms.events.CallParticipant -import org.thoughtcrime.securesms.recipients.Recipient -import org.thoughtcrime.securesms.recipients.RecipientId -import org.thoughtcrime.securesms.service.webrtc.links.CallLinkCredentials -import org.thoughtcrime.securesms.service.webrtc.links.CallLinkRoomId -import org.thoughtcrime.securesms.service.webrtc.links.SignalCallLinkState -import org.thoughtcrime.securesms.service.webrtc.links.UpdateCallLinkResult -import org.thoughtcrime.securesms.util.BottomSheetUtil - -/** - * Displays information about the in-progress CallLink call from - * within WebRtcActivity. If the user is able to modify call link - * state, provides options to do so. - */ -class CallLinkInfoSheet : ComposeBottomSheetDialogFragment() { - - companion object { - - private val TAG = Log.tag(CallLinkInfoSheet::class.java) - private const val CALL_LINK_ROOM_ID = "call_link_room_id" - - @JvmStatic - fun show(fragmentManager: FragmentManager, callLinkRoomId: CallLinkRoomId) { - CallLinkInfoSheet().apply { - arguments = bundleOf(CALL_LINK_ROOM_ID to callLinkRoomId) - }.show(fragmentManager, BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG) - } - } - - override val forceDarkTheme = true - - private val webRtcCallViewModel: WebRtcCallViewModel by activityViewModels() - private val callLinkDetailsViewModel: CallLinkDetailsViewModel by viewModels(factoryProducer = { - CallLinkDetailsViewModel.Factory(BundleCompat.getParcelable(requireArguments(), CALL_LINK_ROOM_ID, CallLinkRoomId::class.java)!!) - }) - - private val lifecycleDisposable = LifecycleDisposable() - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - lifecycleDisposable.bindTo(viewLifecycleOwner) - parentFragmentManager.setFragmentResultListener(EditCallLinkNameDialogFragment.RESULT_KEY, viewLifecycleOwner) { resultKey, bundle -> - if (bundle.containsKey(resultKey)) { - setName(bundle.getString(resultKey)!!) - } - } - } - - @Composable - override fun SheetContent() { - val callLinkDetailsState by callLinkDetailsViewModel.state - val callParticipantsState by webRtcCallViewModel.callParticipantsState - .toFlowable(BackpressureStrategy.LATEST) - .toLiveData() - .observeAsState() - - val participants: List by webRtcCallViewModel.callParticipantsState - .toFlowable(BackpressureStrategy.LATEST) - .map { state -> state.allRemoteParticipants } - .toLiveData() - .observeAsState(emptyList()) - - val onEditNameClicked: () -> Unit = remember(callLinkDetailsState) { - { - EditCallLinkNameDialogFragment().apply { - arguments = EditCallLinkNameDialogFragmentArgs.Builder(callLinkDetailsState.callLink?.state?.name ?: "").build().toBundle() - }.show(parentFragmentManager, null) - } - } - - val callLink = callLinkDetailsState.callLink - if (callLink != null) { - Sheet( - callLink = callLink, - participants = participants, - onShareLinkClicked = this::shareLink, - onEditNameClicked = onEditNameClicked, - onToggleAdminApprovalClicked = this::onApproveAllMembersChanged, - onBlock = this::onBlockParticipant - ) - } - } - - private fun onBlockParticipant(callParticipant: CallParticipant) { - MaterialAlertDialogBuilder(requireContext()) - .setNegativeButton(android.R.string.cancel, null) - .setMessage(getString(R.string.CallLinkInfoSheet__remove_s_from_the_call, callParticipant.recipient.getShortDisplayName(requireContext()))) - .setPositiveButton(R.string.CallLinkInfoSheet__remove) { _, _ -> - ApplicationDependencies.getSignalCallManager().removeFromCallLink(callParticipant) - } - .setNeutralButton(R.string.CallLinkInfoSheet__block_from_call) { _, _ -> - ApplicationDependencies.getSignalCallManager().blockFromCallLink(callParticipant) - } - .show() - } - - private fun onApproveAllMembersChanged(checked: Boolean) { - callLinkDetailsViewModel.setApproveAllMembers(checked) - .observeOn(AndroidSchedulers.mainThread()) - .subscribeBy(onSuccess = { - if (it !is UpdateCallLinkResult.Success) { - Log.w(TAG, "Failed to change restrictions. $it") - toastFailure() - } - }, onError = handleError("onApproveAllMembersChanged")) - .addTo(lifecycleDisposable) - } - - private fun shareLink() { - val mimeType = Intent.normalizeMimeType("text/plain") - val shareIntent = ShareCompat.IntentBuilder(requireContext()) - .setText(CallLinks.url(callLinkDetailsViewModel.rootKeySnapshot)) - .setType(mimeType) - .createChooserIntent() - - try { - startActivity(shareIntent) - } catch (e: ActivityNotFoundException) { - Toast.makeText(requireContext(), R.string.CreateCallLinkBottomSheetDialogFragment__failed_to_open_share_sheet, Toast.LENGTH_LONG).show() - } - } - - private fun setName(name: String) { - callLinkDetailsViewModel.setName(name) - .observeOn(AndroidSchedulers.mainThread()) - .subscribeBy( - onSuccess = { - if (it !is UpdateCallLinkResult.Success) { - Log.w(TAG, "Failed to set name. $it") - toastFailure() - } - }, - onError = handleError("setName") - ) - .addTo(lifecycleDisposable) - } - - private fun handleError(method: String): (throwable: Throwable) -> Unit { - return { - Log.w(TAG, "Failure during $method", it) - toastFailure() - } - } - - private fun toastFailure() { - Toast.makeText(requireContext(), R.string.CallLinkDetailsFragment__couldnt_save_changes, Toast.LENGTH_LONG).show() - } -} - -@Preview -@Composable -private fun SheetPreview() { - SignalTheme(isDarkMode = true) { - Surface { - Sheet( - callLink = CallLinkTable.CallLink( - recipientId = RecipientId.UNKNOWN, - roomId = CallLinkRoomId.fromBytes(byteArrayOf(1, 2, 3, 4, 5)), - credentials = CallLinkCredentials( - linkKeyBytes = byteArrayOf(1, 2, 3, 4, 5), - adminPassBytes = byteArrayOf(1, 2, 3, 4, 5) - ), - state = SignalCallLinkState() - ), - participants = listOf(CallParticipant(recipient = Recipient.UNKNOWN)).toImmutableList(), - onShareLinkClicked = {}, - onEditNameClicked = {}, - onToggleAdminApprovalClicked = {}, - onBlock = {} - ) - } - } -} - -@Composable -private fun Sheet( - callLink: CallLinkTable.CallLink, - participants: List, - onShareLinkClicked: () -> Unit, - onEditNameClicked: () -> Unit, - onToggleAdminApprovalClicked: (Boolean) -> Unit, - onBlock: (CallParticipant) -> Unit -) { - LazyColumn( - horizontalAlignment = Alignment.CenterHorizontally - ) { - item { - BottomSheets.Handle() - - Text( - text = stringResource(id = R.string.CallLinkInfoSheet__call_info), - style = MaterialTheme.typography.titleLarge, - modifier = Modifier.padding(vertical = 24.dp) - ) - - SignalCallRow(callLink = callLink, onJoinClicked = null) - Rows.TextRow( - text = stringResource(id = R.string.CallLinkDetailsFragment__share_link), - icon = ImageVector.vectorResource(id = R.drawable.symbol_link_24), - iconModifier = Modifier - .background( - color = MaterialTheme.colorScheme.surfaceVariant, - shape = CircleShape - ) - .size(42.dp) - .padding(9.dp), - modifier = Modifier - .defaultMinSize(minHeight = 64.dp) - .clickable(onClick = onShareLinkClicked) - ) - } - - items(participants, { it.callParticipantId }, { null }) { - CallLinkMemberRow( - callParticipant = it, - isSelfAdmin = callLink.credentials?.adminPassBytes != null, - onBlockClicked = onBlock - ) - } - - if (callLink.credentials?.adminPassBytes != null) { - item { - Dividers.Default() - Rows.TextRow( - text = stringResource(id = R.string.CallLinkDetailsFragment__add_call_name), - modifier = Modifier.clickable(onClick = onEditNameClicked) - ) - Rows.ToggleRow( - checked = callLink.state.restrictions == Restrictions.ADMIN_APPROVAL, - text = stringResource(id = R.string.CallLinkDetailsFragment__approve_all_members), - onCheckChanged = onToggleAdminApprovalClicked - ) - } - } - } -} - -@Preview -@Composable -private fun CallLinkMemberRowPreview() { - SignalTheme(isDarkMode = true) { - Surface { - CallLinkMemberRow( - CallParticipant(recipient = Recipient.UNKNOWN), - isSelfAdmin = true, - {} - ) - } - } -} - -@Composable -private fun CallLinkMemberRow( - callParticipant: CallParticipant, - isSelfAdmin: Boolean, - onBlockClicked: (CallParticipant) -> Unit -) { - Row( - modifier = Modifier - .fillMaxWidth() - .padding(Rows.defaultPadding()) - ) { - val recipient by Recipient.observable(callParticipant.recipient.id) - .toFlowable(BackpressureStrategy.LATEST) - .toLiveData() - .observeAsState(initial = callParticipant.recipient) - - if (LocalInspectionMode.current) { - Spacer( - modifier = Modifier - .size(40.dp) - .background(color = Color.Red, shape = CircleShape) - ) - } else { - AndroidView( - factory = ::AvatarImageView, - modifier = Modifier.size(40.dp) - ) { - it.setAvatarUsingProfile(recipient) - } - } - - Spacer(modifier = Modifier.width(24.dp)) - - Text( - text = recipient.getShortDisplayName(LocalContext.current), - modifier = Modifier - .weight(1f) - .align(Alignment.CenterVertically) - ) - - if (isSelfAdmin && !recipient.isSelf) { - Icon( - imageVector = ImageVector.vectorResource(id = R.drawable.symbol_minus_circle_24), - contentDescription = null, - modifier = Modifier - .clickable(onClick = { onBlockClicked(callParticipant) }) - .align(Alignment.CenterVertically) - ) - } - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallOverflowPopupWindow.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallOverflowPopupWindow.kt new file mode 100644 index 0000000000..801438d234 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallOverflowPopupWindow.kt @@ -0,0 +1,93 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.components.webrtc + +import android.graphics.Rect +import android.view.Gravity +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.LinearLayout +import android.widget.PopupWindow +import android.widget.TextView +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.widget.PopupWindowCompat +import androidx.fragment.app.FragmentActivity +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.dependencies.ApplicationDependencies +import org.thoughtcrime.securesms.util.FeatureFlags +import org.thoughtcrime.securesms.util.visible + +/** + * A popup window for calls that holds extra actions, such as reactions, raise hand, and screen sharing. + * + */ +class CallOverflowPopupWindow(private val activity: FragmentActivity, parentViewGroup: ViewGroup, private val raisedHandDelegate: RaisedHandDelegate) : PopupWindow( + LayoutInflater.from(activity).inflate(R.layout.call_overflow_holder, parentViewGroup, false), + activity.resources.getDimension(R.dimen.calling_reaction_popup_menu_width).toInt(), + activity.resources.getDimension(R.dimen.calling_reaction_popup_menu_height).toInt() + +) { + private val raiseHandLabel: TextView = (contentView as LinearLayout).findViewById(R.id.raise_hand_label) + + init { + val root = (contentView as LinearLayout) + if (FeatureFlags.groupCallReactions()) { + val reactionScrubber = root.findViewById(R.id.reaction_scrubber) + reactionScrubber.visible = true + reactionScrubber.initialize(activity.supportFragmentManager) { + ApplicationDependencies.getSignalCallManager().react(it) + dismiss() + } + } + if (FeatureFlags.groupCallRaiseHand()) { + val raiseHand = root.findViewById(R.id.raise_hand_layout_parent) + raiseHand.visible = true + raiseHand.setOnClickListener { + if (raisedHandDelegate.isSelfHandRaised()) { + MaterialAlertDialogBuilder(activity) + .setTitle(R.string.CallOverflowPopupWindow__lower_your_hand) + .setPositiveButton(R.string.CallOverflowPopupWindow__lower_hand) { _, _ -> + ApplicationDependencies.getSignalCallManager().raiseHand(false) + this@CallOverflowPopupWindow.dismiss() + } + .setNegativeButton(R.string.CallOverflowPopupWindow__cancel, null) + .show() + } else { + ApplicationDependencies.getSignalCallManager().raiseHand(true) + dismiss() + } + } + } + } + + fun show(anchor: View) { + isFocusable = true + + val resources = activity.resources + + val margin = resources.getDimension(R.dimen.calling_reaction_scrubber_margin).toInt() + + val windowRect = Rect() + contentView.getWindowVisibleDisplayFrame(windowRect) + val windowWidth = windowRect.width() + val popupWidth = resources.getDimension(R.dimen.reaction_scrubber_width).toInt() + + val popupHeight = resources.getDimension(R.dimen.calling_reaction_popup_menu_height).toInt() + + val xOffset = windowWidth - popupWidth - margin + val yOffset = -popupHeight - margin + + raiseHandLabel.setText(if (raisedHandDelegate.isSelfHandRaised()) R.string.CallOverflowPopupWindow__lower_hand else R.string.CallOverflowPopupWindow__raise_hand) + + PopupWindowCompat.showAsDropDown(this, anchor, xOffset, yOffset, Gravity.NO_GRAVITY) + } + + interface RaisedHandDelegate { + fun isSelfHandRaised(): Boolean + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallParticipantView.java b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallParticipantView.java index dc26d5d2ae..71246b9ecd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallParticipantView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallParticipantView.java @@ -6,15 +6,17 @@ import android.view.ViewGroup; import android.widget.Button; import android.widget.ImageView; +import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.appcompat.app.AlertDialog; import androidx.appcompat.widget.AppCompatImageView; import androidx.constraintlayout.widget.ConstraintLayout; +import androidx.constraintlayout.widget.ConstraintSet; import androidx.core.content.ContextCompat; import androidx.core.view.ViewKt; import androidx.core.widget.ImageViewCompat; +import androidx.transition.Transition; import androidx.transition.TransitionManager; import com.bumptech.glide.load.engine.DiskCacheStrategy; @@ -35,8 +37,10 @@ import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.util.AvatarUtil; +import org.thoughtcrime.securesms.util.FeatureFlags; import org.thoughtcrime.securesms.util.ViewUtil; import org.webrtc.RendererCommon; +import org.whispersystems.signalservice.api.util.Preconditions; import java.util.Objects; import java.util.concurrent.TimeUnit; @@ -55,8 +59,11 @@ public class CallParticipantView extends ConstraintLayout { private RecipientId recipientId; private boolean infoMode; + private boolean raiseHandAllowed; private Runnable missingMediaKeysUpdater; + private SelfPipMode selfPipMode = SelfPipMode.NOT_SELF_PIP; + private AppCompatImageView backgroundAvatar; private AvatarImageView avatar; private BadgeImageView badge; @@ -70,6 +77,10 @@ public class CallParticipantView extends ConstraintLayout { private EmojiTextView infoMessage; private Button infoMoreInfo; private AppCompatImageView infoIcon; + private View switchCameraIconFrame; + private View switchCameraIcon; + private ImageView raiseHandIcon; + private TextView nameLabel; public CallParticipantView(@NonNull Context context) { super(context); @@ -88,18 +99,22 @@ public CallParticipantView(@NonNull Context context, @Nullable AttributeSet attr protected void onFinishInflate() { super.onFinishInflate(); - backgroundAvatar = findViewById(R.id.call_participant_background_avatar); - avatar = findViewById(R.id.call_participant_item_avatar); - pipAvatar = findViewById(R.id.call_participant_item_pip_avatar); - rendererFrame = findViewById(R.id.call_participant_renderer_frame); - renderer = findViewById(R.id.call_participant_renderer); - audioIndicator = findViewById(R.id.call_participant_audio_indicator); - infoOverlay = findViewById(R.id.call_participant_info_overlay); - infoIcon = findViewById(R.id.call_participant_info_icon); - infoMessage = findViewById(R.id.call_participant_info_message); - infoMoreInfo = findViewById(R.id.call_participant_info_more_info); - badge = findViewById(R.id.call_participant_item_badge); - pipBadge = findViewById(R.id.call_participant_item_pip_badge); + backgroundAvatar = findViewById(R.id.call_participant_background_avatar); + avatar = findViewById(R.id.call_participant_item_avatar); + pipAvatar = findViewById(R.id.call_participant_item_pip_avatar); + rendererFrame = findViewById(R.id.call_participant_renderer_frame); + renderer = findViewById(R.id.call_participant_renderer); + audioIndicator = findViewById(R.id.call_participant_audio_indicator); + infoOverlay = findViewById(R.id.call_participant_info_overlay); + infoIcon = findViewById(R.id.call_participant_info_icon); + infoMessage = findViewById(R.id.call_participant_info_message); + infoMoreInfo = findViewById(R.id.call_participant_info_more_info); + badge = findViewById(R.id.call_participant_item_badge); + pipBadge = findViewById(R.id.call_participant_item_pip_badge); + switchCameraIconFrame = findViewById(R.id.call_participant_switch_camera); + switchCameraIcon = findViewById(R.id.call_participant_switch_camera_icon); + raiseHandIcon = findViewById(R.id.call_participant_raise_hand_icon); + nameLabel = findViewById(R.id.call_participant_name_label); avatar.setFallbackPhotoProvider(FALLBACK_PHOTO_PROVIDER); useLargeAvatar(); @@ -164,6 +179,15 @@ void setCallParticipant(@NonNull CallParticipant participant) { audioIndicator.setVisibility(View.VISIBLE); audioIndicator.bind(participant.isMicrophoneEnabled(), participant.getAudioLevel()); + final String shortRecipientDisplayName = participant.getShortRecipientDisplayName(getContext()); + if (FeatureFlags.groupCallRaiseHand() && raiseHandAllowed && participant.isHandRaised()) { + raiseHandIcon.setVisibility(View.VISIBLE); + nameLabel.setVisibility(View.VISIBLE); + nameLabel.setText(shortRecipientDisplayName); + } else { + raiseHandIcon.setVisibility(View.GONE); + nameLabel.setVisibility(View.GONE); + } } if (participantChanged || !Objects.equals(contactPhoto, participant.getRecipient().getContactPhoto())) { @@ -211,6 +235,132 @@ void setRenderInPip(boolean shouldRenderInPip) { pipBadge.setVisibility(shouldRenderInPip ? View.VISIBLE : View.GONE); } + public void setRaiseHandAllowed(boolean raiseHandAllowed) { + this.raiseHandAllowed = raiseHandAllowed; + } + + /** + * Adjust UI elements for the various self PIP positions. If called after a {@link TransitionManager#beginDelayedTransition(ViewGroup, Transition)}, + * the changes to the UI elements will animate. + */ + void setSelfPipMode(@NonNull SelfPipMode selfPipMode) { + Preconditions.checkArgument(selfPipMode != SelfPipMode.NOT_SELF_PIP); + + if (this.selfPipMode == selfPipMode) { + return; + } + + this.selfPipMode = selfPipMode; + + ConstraintSet constraints = new ConstraintSet(); + constraints.clone(this); + + switch (selfPipMode) { + case NORMAL_SELF_PIP -> { + constraints.connect( + R.id.call_participant_audio_indicator, + ConstraintSet.START, + ConstraintSet.PARENT_ID, + ConstraintSet.START, + ViewUtil.dpToPx(6) + ); + constraints.clear( + R.id.call_participant_audio_indicator, + ConstraintSet.END + ); + constraints.setMargin( + R.id.call_participant_audio_indicator, + ConstraintSet.BOTTOM, + ViewUtil.dpToPx(6) + ); + + constraints.setVisibility(R.id.call_participant_switch_camera, View.VISIBLE); + constraints.setMargin( + R.id.call_participant_switch_camera, + ConstraintSet.END, + ViewUtil.dpToPx(6) + ); + constraints.setMargin( + R.id.call_participant_switch_camera, + ConstraintSet.BOTTOM, + ViewUtil.dpToPx(6) + ); + constraints.constrainWidth(R.id.call_participant_switch_camera, ViewUtil.dpToPx(28)); + constraints.constrainHeight(R.id.call_participant_switch_camera, ViewUtil.dpToPx(28)); + + ViewGroup.LayoutParams params = switchCameraIcon.getLayoutParams(); + params.width = params.height = ViewUtil.dpToPx(16); + switchCameraIcon.setLayoutParams(params); + + switchCameraIconFrame.setClickable(false); + switchCameraIconFrame.setEnabled(false); + } + case EXPANDED_SELF_PIP -> { + constraints.connect( + R.id.call_participant_audio_indicator, + ConstraintSet.START, + ConstraintSet.PARENT_ID, + ConstraintSet.START, + ViewUtil.dpToPx(8) + ); + constraints.clear( + R.id.call_participant_audio_indicator, + ConstraintSet.END + ); + constraints.setMargin( + R.id.call_participant_audio_indicator, + ConstraintSet.BOTTOM, + ViewUtil.dpToPx(8) + ); + + constraints.setVisibility(R.id.call_participant_switch_camera, View.VISIBLE); + constraints.setMargin( + R.id.call_participant_switch_camera, + ConstraintSet.END, + ViewUtil.dpToPx(8) + ); + constraints.setMargin( + R.id.call_participant_switch_camera, + ConstraintSet.BOTTOM, + ViewUtil.dpToPx(8) + ); + constraints.constrainWidth(R.id.call_participant_switch_camera, ViewUtil.dpToPx(48)); + constraints.constrainHeight(R.id.call_participant_switch_camera, ViewUtil.dpToPx(48)); + + ViewGroup.LayoutParams params = switchCameraIcon.getLayoutParams(); + params.width = params.height = ViewUtil.dpToPx(24); + switchCameraIcon.setLayoutParams(params); + + switchCameraIconFrame.setClickable(true); + switchCameraIconFrame.setEnabled(true); + } + case MINI_SELF_PIP -> { + constraints.connect( + R.id.call_participant_audio_indicator, + ConstraintSet.START, + ConstraintSet.PARENT_ID, + ConstraintSet.START, + 0 + ); + constraints.connect( + R.id.call_participant_audio_indicator, + ConstraintSet.END, + ConstraintSet.PARENT_ID, + ConstraintSet.END, + 0 + ); + constraints.setMargin( + R.id.call_participant_audio_indicator, + ConstraintSet.BOTTOM, + ViewUtil.dpToPx(6) + ); + constraints.setVisibility(R.id.call_participant_switch_camera, View.GONE); + } + } + + constraints.applyTo(this); + } + void hideAvatar() { avatar.setAlpha(0f); badge.setAlpha(0f); @@ -302,4 +452,11 @@ private static final class FallbackPhotoProvider extends Recipient.FallbackPhoto return photo; } } + + public enum SelfPipMode { + NOT_SELF_PIP, + NORMAL_SELF_PIP, + EXPANDED_SELF_PIP, + MINI_SELF_PIP + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallParticipantsLayout.java b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallParticipantsLayout.java index a6d2835e14..dfeb82d538 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallParticipantsLayout.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallParticipantsLayout.java @@ -28,6 +28,7 @@ public class CallParticipantsLayout extends FlexboxLayout { private static final int MULTIPLE_PARTICIPANT_SPACING = ViewUtil.dpToPx(3); private static final int CORNER_RADIUS = ViewUtil.dpToPx(10); + private static final int RAISE_HAND_MINIMUM_COUNT = 2; private List callParticipants = Collections.emptyList(); private CallParticipant focusedParticipant = null; @@ -145,6 +146,8 @@ private void update(int index, int count, @NonNull CallParticipant participant) callParticipantView.useLargeAvatar(); } + callParticipantView.setRaiseHandAllowed(count >= RAISE_HAND_MINIMUM_COUNT); + layoutStrategy.setChildLayoutParams(view, index, getChildCount()); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallParticipantsState.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallParticipantsState.kt index 8686355cba..326145ae37 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallParticipantsState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallParticipantsState.kt @@ -4,10 +4,14 @@ import android.content.Context import androidx.annotation.PluralsRes import androidx.annotation.StringRes import com.annimon.stream.OptionalLong +import kotlinx.collections.immutable.toImmutableList import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.components.webrtc.WebRtcControls.FoldableState import org.thoughtcrime.securesms.events.CallParticipant +import org.thoughtcrime.securesms.events.CallParticipant.Companion.HAND_LOWERED import org.thoughtcrime.securesms.events.CallParticipant.Companion.createLocal +import org.thoughtcrime.securesms.events.GroupCallRaiseHandEvent +import org.thoughtcrime.securesms.events.GroupCallReactionEvent import org.thoughtcrime.securesms.events.WebRtcViewModel import org.thoughtcrime.securesms.groups.ui.GroupMemberEntry import org.thoughtcrime.securesms.recipients.Recipient @@ -25,15 +29,17 @@ data class CallParticipantsState( val callState: WebRtcViewModel.State = WebRtcViewModel.State.CALL_DISCONNECTED, val groupCallState: WebRtcViewModel.GroupCallState = WebRtcViewModel.GroupCallState.IDLE, private val remoteParticipants: ParticipantCollection = ParticipantCollection(SMALL_GROUP_MAX), - val localParticipant: CallParticipant = createLocal(CameraState.UNKNOWN, BroadcastVideoSink(), false), + val localParticipant: CallParticipant = createLocal(CameraState.UNKNOWN, BroadcastVideoSink(), microphoneEnabled = false, handRaisedTimestamp = HAND_LOWERED), val focusedParticipant: CallParticipant = CallParticipant.EMPTY, val localRenderState: WebRtcLocalRenderState = WebRtcLocalRenderState.GONE, + val reactions: List = emptyList(), val isInPipMode: Boolean = false, private val showVideoForOutgoing: Boolean = false, val isViewingFocusedParticipant: Boolean = false, val remoteDevicesCount: OptionalLong = OptionalLong.empty(), private val foldableState: FoldableState = FoldableState.flat(), val isInOutgoingRingingMode: Boolean = false, + val recipient: Recipient = Recipient.UNKNOWN, val ringGroup: Boolean = false, val ringerRecipient: Recipient = Recipient.UNKNOWN, val groupMembers: List = emptyList(), @@ -42,9 +48,18 @@ data class CallParticipantsState( val allRemoteParticipants: List = remoteParticipants.allParticipants val isFolded: Boolean = foldableState.isFolded - val isLargeVideoGroup: Boolean = allRemoteParticipants.size > SMALL_GROUP_MAX + val isLargeVideoGroup: Boolean = allRemoteParticipants.size > SMALL_GROUP_MAX && !isInPipMode && !isFolded val isIncomingRing: Boolean = callState == WebRtcViewModel.State.CALL_INCOMING + val raisedHands: List + get() { + val results = allRemoteParticipants.filter { it.isHandRaised }.map { GroupCallRaiseHandEvent(it.recipient, it.handRaisedTimestamp) }.toMutableList() + if (localParticipant.isHandRaised) { + results.add(GroupCallRaiseHandEvent(localParticipant.recipient, localParticipant.handRaisedTimestamp)) + } + return results.toImmutableList() + } + val gridParticipants: List get() { return remoteParticipants.gridParticipants @@ -223,6 +238,7 @@ data class CallParticipantsState( focusedParticipant = getFocusedParticipant(webRtcViewModel.remoteParticipants), localRenderState = localRenderState, showVideoForOutgoing = newShowVideoForOutgoing, + recipient = webRtcViewModel.recipient, remoteDevicesCount = webRtcViewModel.remoteDevicesCount, ringGroup = webRtcViewModel.ringGroup, isInOutgoingRingingMode = isInOutgoingRingingMode, @@ -269,7 +285,8 @@ data class CallParticipantsState( return oldState.copy( remoteParticipants = oldState.remoteParticipants.map { p -> p.copy(audioLevel = ephemeralState.remoteAudioLevels[p.callParticipantId]) }, localParticipant = oldState.localParticipant.copy(audioLevel = ephemeralState.localAudioLevel), - focusedParticipant = oldState.focusedParticipant.copy(audioLevel = ephemeralState.remoteAudioLevels[oldState.focusedParticipant.callParticipantId]) + focusedParticipant = oldState.focusedParticipant.copy(audioLevel = ephemeralState.remoteAudioLevels[oldState.focusedParticipant.callParticipantId]), + reactions = ephemeralState.getUnexpiredReactions() ) } @@ -287,7 +304,7 @@ data class CallParticipantsState( val displayLocal: Boolean = (numberOfRemoteParticipants == 0 || !isInPip) && (isNonIdleGroupCall || localParticipant.isVideoEnabled) var localRenderState: WebRtcLocalRenderState = WebRtcLocalRenderState.GONE - if (!isInPip && isExpanded && (localParticipant.isVideoEnabled || isNonIdleGroupCall)) { + if (!isInPip && isExpanded && localParticipant.isVideoEnabled) { return WebRtcLocalRenderState.EXPANDED } else if (displayLocal || showVideoForOutgoing) { if (callState == WebRtcViewModel.State.CALL_CONNECTED || callState == WebRtcViewModel.State.CALL_RECONNECTING) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallReactionScrubber.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallReactionScrubber.kt new file mode 100644 index 0000000000..fb2809a8a1 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallReactionScrubber.kt @@ -0,0 +1,60 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.components.webrtc + +import android.content.Context +import android.util.AttributeSet +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.content.ContextCompat +import androidx.fragment.app.FragmentManager +import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.components.emoji.EmojiImageView +import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.reactions.any.ReactWithAnyEmojiBottomSheetDialogFragment + +class CallReactionScrubber @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : ConstraintLayout(context, attrs, defStyleAttr) { + + private val emojiViews: Array + private var customEmojiIndex = 0 + + init { + inflate(context, R.layout.call_overflow_popup, this) + + emojiViews = arrayOf( + findViewById(R.id.reaction_1), + findViewById(R.id.reaction_2), + findViewById(R.id.reaction_3), + findViewById(R.id.reaction_4), + findViewById(R.id.reaction_5), + findViewById(R.id.reaction_6), + findViewById(R.id.reaction_7) + ) + customEmojiIndex = emojiViews.size - 1 + } + + fun initialize(fragmentManager: FragmentManager, listener: (String) -> Unit) { + val emojis = SignalStore.emojiValues().reactions + for (i in emojiViews.indices) { + val view = emojiViews[i] + val isAtCustomIndex = i == customEmojiIndex + if (isAtCustomIndex) { + view.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_any_emoji_32)) + view.setOnClickListener { + val bottomSheet = ReactWithAnyEmojiBottomSheetDialogFragment.createForCallingReactions() + bottomSheet.show(fragmentManager, "CallReaction") + } + } else { + val preferredVariation = SignalStore.emojiValues().getPreferredVariation(emojis[i]) + view.setImageEmoji(preferredVariation) + view.setOnClickListener { listener(preferredVariation) } + } + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallStateUpdatePopupWindow.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallStateUpdatePopupWindow.kt index 5af2d95172..ffc8ab3907 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallStateUpdatePopupWindow.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallStateUpdatePopupWindow.kt @@ -84,7 +84,7 @@ class CallStateUpdatePopupWindow(private val parent: ViewGroup) : PopupWindow( measureChild() - val anchor: View = ViewCompat.requireViewById(parent, R.id.call_screen_footer_gradient_barrier) + val anchor: View = ViewCompat.requireViewById(parent, R.id.call_screen_above_controls_guideline) val pill: View = ViewCompat.requireViewById(contentView, R.id.call_state_pill) // 54 is the top margin of the contentView (30) plus the desired padding (24) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/LayoutPositions.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/LayoutPositions.kt new file mode 100644 index 0000000000..e493f0efd2 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/LayoutPositions.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ +package org.thoughtcrime.securesms.components.webrtc + +import androidx.annotation.Dimension +import androidx.annotation.IdRes +import androidx.constraintlayout.widget.ConstraintSet +import org.thoughtcrime.securesms.R + +/** Constraints to apply for different call sizes */ +enum class LayoutPositions( + @JvmField @IdRes val participantBottomViewId: Int, + @JvmField @Dimension val participantBottomMargin: Int, + @JvmField @IdRes val reactionBottomViewId: Int, + @JvmField @Dimension val reactionBottomMargin: Int +) { + /** 1:1 or small calls anchor full screen or controls */ + SMALL_GROUP( + participantBottomViewId = ConstraintSet.PARENT_ID, + participantBottomMargin = 0, + reactionBottomViewId = R.id.call_screen_above_controls_guideline, + reactionBottomMargin = 8 + ), + + /** Large calls have a participant rail to anchor to */ + LARGE_GROUP( + participantBottomViewId = R.id.call_screen_participants_recycler, + participantBottomMargin = 16, + reactionBottomViewId = R.id.call_screen_participants_recycler, + reactionBottomMargin = 20 + ); + + @JvmField + val participantBottomViewEndSide: Int = if (participantBottomViewId == ConstraintSet.PARENT_ID) ConstraintSet.BOTTOM else ConstraintSet.TOP +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/PictureInPictureExpansionHelper.java b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/PictureInPictureExpansionHelper.java index d216407ee3..593f5d1725 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/PictureInPictureExpansionHelper.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/PictureInPictureExpansionHelper.java @@ -1,10 +1,17 @@ package org.thoughtcrime.securesms.components.webrtc; +import android.graphics.Point; import android.view.View; import android.view.ViewGroup; import androidx.annotation.MainThread; import androidx.annotation.NonNull; +import androidx.transition.AutoTransition; +import androidx.transition.Transition; +import androidx.transition.TransitionListenerAdapter; +import androidx.transition.TransitionManager; + +import org.thoughtcrime.securesms.util.ViewUtil; /** * Helps manage the expansion and shrinking of the in-app pip. @@ -12,7 +19,29 @@ @MainThread final class PictureInPictureExpansionHelper { + private static final int PIP_RESIZE_DURATION_MS = 300; + private static final int EXPANDED_PIP_WIDTH_DP = 170; + private static final int EXPANDED_PIP_HEIGHT_DP = 300; + + public static final int NORMAL_PIP_WIDTH_DP = 90; + public static final int NORMAL_PIP_HEIGHT_DP = 160; + + public static final int MINI_PIP_WIDTH_DP = 40; + public static final int MINI_PIP_HEIGHT_DP = 72; + + private final View selfPip; + private final ViewGroup parent; + private final Point expandedDimensions; + private State state = State.IS_SHRUNKEN; + private Point defaultDimensions; + + public PictureInPictureExpansionHelper(@NonNull View selfPip) { + this.selfPip = selfPip; + this.parent = (ViewGroup) selfPip.getParent(); + this.defaultDimensions = new Point(selfPip.getLayoutParams().width, selfPip.getLayoutParams().height); + this.expandedDimensions = new Point(ViewUtil.dpToPx(EXPANDED_PIP_WIDTH_DP), ViewUtil.dpToPx(EXPANDED_PIP_HEIGHT_DP)); + } public boolean isExpandedOrExpanding() { return state == State.IS_EXPANDED || state == State.IS_EXPANDING; @@ -22,144 +51,84 @@ public boolean isShrunkenOrShrinking() { return state == State.IS_SHRUNKEN || state == State.IS_SHRINKING; } - public void expand(@NonNull View toExpand, @NonNull Callback callback) { + public boolean isMiniSize() { + return defaultDimensions.x < ViewUtil.dpToPx(NORMAL_PIP_WIDTH_DP); + } + + public void startDefaultSizeTransition(@NonNull Point dimensions, @NonNull Callback callback) { + if (defaultDimensions.equals(dimensions)) { + return; + } + + defaultDimensions = dimensions; + if (isExpandedOrExpanding()) { return; } - performExpandAnimation(toExpand, new Callback() { - @Override - public void onAnimationWillStart() { - state = State.IS_EXPANDING; - callback.onAnimationWillStart(); - } + beginResizeSelfPipTransition(defaultDimensions, callback); + } - @Override - public void onPictureInPictureExpanded() { - callback.onPictureInPictureExpanded(); - } + public void beginExpandTransition() { + if (isExpandedOrExpanding()) { + return; + } + beginResizeSelfPipTransition(expandedDimensions, new Callback() { @Override - public void onPictureInPictureNotVisible() { - callback.onPictureInPictureNotVisible(); + public void onAnimationWillStart() { + state = State.IS_EXPANDING; } @Override public void onAnimationHasFinished() { state = State.IS_EXPANDED; - callback.onAnimationHasFinished(); } }); } - public void shrink(@NonNull View toExpand, @NonNull Callback callback) { + public void beginShrinkTransition() { if (isShrunkenOrShrinking()) { return; } - performShrinkAnimation(toExpand, new Callback() { + beginResizeSelfPipTransition(defaultDimensions, new Callback() { @Override public void onAnimationWillStart() { state = State.IS_SHRINKING; - callback.onAnimationWillStart(); } @Override - public void onPictureInPictureExpanded() { - callback.onPictureInPictureExpanded(); + public void onAnimationHasFinished() { + state = State.IS_SHRUNKEN; } + }); + } + private void beginResizeSelfPipTransition(@NonNull Point dimension, @NonNull Callback callback) { + TransitionManager.endTransitions(parent); + + Transition transition = new AutoTransition().setDuration(PIP_RESIZE_DURATION_MS); + transition.addListener(new TransitionListenerAdapter() { @Override - public void onPictureInPictureNotVisible() { - callback.onPictureInPictureNotVisible(); + public void onTransitionStart(@NonNull Transition transition) { + callback.onAnimationWillStart(); } @Override - public void onAnimationHasFinished() { - state = State.IS_SHRUNKEN; + public void onTransitionEnd(@NonNull Transition transition) { callback.onAnimationHasFinished(); } }); - } - private void performExpandAnimation(@NonNull View target, @NonNull Callback callback) { - ViewGroup parent = (ViewGroup) target.getParent(); - - float x = target.getX(); - float y = target.getY(); - float scaleX = parent.getMeasuredWidth() / (float) target.getMeasuredWidth(); - float scaleY = parent.getMeasuredHeight() / (float) target.getMeasuredHeight(); - float scale = Math.max(scaleX, scaleY); - - callback.onAnimationWillStart(); - - target.animate() - .setDuration(200) - .x((parent.getMeasuredWidth() - target.getMeasuredWidth()) / 2f) - .y((parent.getMeasuredHeight() - target.getMeasuredHeight()) / 2f) - .scaleX(scale) - .scaleY(scale) - .withEndAction(() -> { - callback.onPictureInPictureExpanded(); - target.animate() - .setDuration(100) - .alpha(0f) - .withEndAction(() -> { - callback.onPictureInPictureNotVisible(); - - target.setX(x); - target.setY(y); - target.setScaleX(0f); - target.setScaleY(0f); - target.setAlpha(1f); - - target.animate() - .setDuration(200) - .scaleX(1f) - .scaleY(1f) - .withEndAction(callback::onAnimationHasFinished); - }); - }); - } + TransitionManager.beginDelayedTransition(parent, transition); - private void performShrinkAnimation(@NonNull View target, @NonNull Callback callback) { - ViewGroup parent = (ViewGroup) target.getParent(); - - float x = target.getX(); - float y = target.getY(); - float scaleX = parent.getMeasuredWidth() / (float) target.getMeasuredWidth(); - float scaleY = parent.getMeasuredHeight() / (float) target.getMeasuredHeight(); - float scale = Math.max(scaleX, scaleY); - - callback.onAnimationWillStart(); - - target.animate() - .setDuration(200) - .scaleX(0f) - .scaleY(0f) - .withEndAction(() -> { - target.setX((parent.getMeasuredWidth() - target.getMeasuredWidth()) / 2f); - target.setY((parent.getMeasuredHeight() - target.getMeasuredHeight()) / 2f); - target.setAlpha(0f); - target.setScaleX(scale); - target.setScaleY(scale); - - callback.onPictureInPictureNotVisible(); - - target.animate() - .setDuration(100) - .alpha(1f) - .withEndAction(() -> { - callback.onPictureInPictureExpanded(); - - target.animate() - .scaleX(1f) - .scaleY(1f) - .x(x) - .y(y) - .withEndAction(callback::onAnimationHasFinished); - }); - }); + ViewGroup.LayoutParams params = selfPip.getLayoutParams(); + + params.width = dimension.x; + params.height = dimension.y; + + selfPip.setLayoutParams(params); } enum State { @@ -174,24 +143,12 @@ public interface Callback { * Called when an animation (shrink or expand) will begin. This happens before any animation * is executed. */ - void onAnimationWillStart(); - - /** - * Called when the PiP is covering the whole screen. This is when any staging / teardown of the - * large local renderer should occur. - */ - void onPictureInPictureExpanded(); - - /** - * Called when the PiP is not visible on the screen anymore. This is when any staging / teardown - * of the pip should occur. - */ - void onPictureInPictureNotVisible(); + default void onAnimationWillStart() {} /** * Called when the animation is complete. Useful for e.g. adjusting the pip's final location to * make sure it is respecting the screen space available. */ - void onAnimationHasFinished(); + default void onAnimationHasFinished() {} } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/PictureInPictureGestureHelper.java b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/PictureInPictureGestureHelper.java index 28ddf3aefa..2ff05130c8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/PictureInPictureGestureHelper.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/PictureInPictureGestureHelper.java @@ -1,9 +1,9 @@ package org.thoughtcrime.securesms.components.webrtc; -import android.animation.Animator; import android.annotation.SuppressLint; import android.graphics.Point; import android.view.GestureDetector; +import android.view.Gravity; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; @@ -11,19 +11,16 @@ import android.view.ViewGroup; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.Interpolator; +import android.widget.FrameLayout; import androidx.annotation.NonNull; import androidx.core.view.GestureDetectorCompat; import org.thoughtcrime.securesms.R; -import org.thoughtcrime.securesms.animation.AnimationCompleteListener; import org.thoughtcrime.securesms.util.ViewUtil; import org.thoughtcrime.securesms.util.views.TouchInterceptingFrameLayout; import java.util.Arrays; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.Queue; public class PictureInPictureGestureHelper extends GestureDetector.SimpleOnGestureListener { @@ -31,18 +28,15 @@ public class PictureInPictureGestureHelper extends GestureDetector.SimpleOnGestu private static final Interpolator FLING_INTERPOLATOR = new ViscousFluidInterpolator(); private static final Interpolator ADJUST_INTERPOLATOR = new AccelerateDecelerateInterpolator(); - private final ViewGroup parent; - private final View child; - private final int framePadding; - private final Queue runAfterFling; + private final ViewGroup parent; + private final View child; + private final int framePadding; private int pipWidth; private int pipHeight; - private int activePointerId = MotionEvent.INVALID_POINTER_ID; + private int activePointerId = MotionEvent.INVALID_POINTER_ID; private float lastTouchX; private float lastTouchY; - private boolean isDragging; - private boolean isAnimating; private int extraPaddingTop; private int extraPaddingBottom; private double projectionX; @@ -51,6 +45,9 @@ public class PictureInPictureGestureHelper extends GestureDetector.SimpleOnGestu private int maximumFlingVelocity; private boolean isLockedToBottomEnd; private Interpolator interpolator; + private Corner currentCornerPosition = Corner.BOTTOM_RIGHT; + private int previousTopBoundary = -1; + private int previousBottomBoundary = -1; @SuppressLint("ClickableViewAccessibility") public static PictureInPictureGestureHelper applyTo(@NonNull View child) { @@ -111,21 +108,31 @@ private PictureInPictureGestureHelper(@NonNull ViewGroup parent, @NonNull View c this.pipWidth = child.getResources().getDimensionPixelSize(R.dimen.picture_in_picture_gesture_helper_pip_width); this.pipHeight = child.getResources().getDimensionPixelSize(R.dimen.picture_in_picture_gesture_helper_pip_height); this.maximumFlingVelocity = ViewConfiguration.get(child.getContext()).getScaledMaximumFlingVelocity(); - this.runAfterFling = new LinkedList<>(); this.interpolator = ADJUST_INTERPOLATOR; } - public void clearVerticalBoundaries() { - setTopVerticalBoundary(parent.getTop()); - setBottomVerticalBoundary(parent.getMeasuredHeight() + parent.getTop()); - } - public void setTopVerticalBoundary(int topBoundary) { + if (topBoundary == previousTopBoundary) { + return; + } + previousTopBoundary = topBoundary; + extraPaddingTop = topBoundary - parent.getTop(); + + ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) child.getLayoutParams(); + layoutParams.setMargins(layoutParams.leftMargin, extraPaddingTop + framePadding, layoutParams.rightMargin, layoutParams.bottomMargin); + child.setLayoutParams(layoutParams); } public void setBottomVerticalBoundary(int bottomBoundary) { + if (bottomBoundary == previousBottomBoundary) { + return; + } + previousBottomBoundary = bottomBoundary; + extraPaddingBottom = parent.getMeasuredHeight() + parent.getTop() - bottomBoundary; + + ViewUtil.setBottomMargin(child, extraPaddingBottom + framePadding); } private boolean onGestureFinished(MotionEvent e) { @@ -139,43 +146,20 @@ private boolean onGestureFinished(MotionEvent e) { return false; } - public void adjustPip() { - pipWidth = child.getMeasuredWidth(); - pipHeight = child.getMeasuredHeight(); - - if (isAnimating) { - interpolator = ADJUST_INTERPOLATOR; - - fling(); - } else if (!isDragging) { - interpolator = ADJUST_INTERPOLATOR; - - onFling(null, null, 0, 0); - } - } - public void lockToBottomEnd() { isLockedToBottomEnd = true; + fling(); } public void enableCorners() { isLockedToBottomEnd = false; } - public void performAfterFling(@NonNull Runnable runnable) { - if (isAnimating) { - runAfterFling.add(runnable); - } else { - runnable.run(); - } - } - @Override public boolean onDown(MotionEvent e) { activePointerId = e.getPointerId(0); lastTouchX = e.getX(0) + child.getX(); lastTouchY = e.getY(0) + child.getY(); - isDragging = true; pipWidth = child.getMeasuredWidth(); pipHeight = child.getMeasuredHeight(); interpolator = FLING_INTERPOLATOR; @@ -185,6 +169,10 @@ public boolean onDown(MotionEvent e) { @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { + if (isLockedToBottomEnd) { + return false; + } + int pointerIndex = e2.findPointerIndex(activePointerId); if (pointerIndex == -1) { @@ -192,10 +180,10 @@ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float d return false; } - float x = e2.getX(pointerIndex) + child.getX(); - float y = e2.getY(pointerIndex) + child.getY(); - float dx = x - lastTouchX; - float dy = y - lastTouchY; + float x = e2.getX(pointerIndex) + child.getX(); + float y = e2.getY(pointerIndex) + child.getY(); + float dx = x - lastTouchX; + float dy = y - lastTouchY; child.setTranslationX(child.getTranslationX() + dx); child.setTranslationY(child.getTranslationY() + dy); @@ -208,6 +196,10 @@ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float d @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { + if (isLockedToBottomEnd) { + return false; + } + if (velocityTracker != null) { velocityTracker.computeCurrentVelocity(1000, maximumFlingVelocity); @@ -225,92 +217,75 @@ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float ve @Override public boolean onSingleTapUp(MotionEvent e) { - isDragging = false; - child.performClick(); return true; } private void fling() { Point projection = new Point((int) projectionX, (int) projectionY); - Point nearestCornerPosition = findNearestCornerPosition(projection); + Corner nearestCornerPosition = findNearestCornerPosition(projection); + + FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) child.getLayoutParams(); + layoutParams.gravity = nearestCornerPosition.gravity; + + if (currentCornerPosition != null && currentCornerPosition != nearestCornerPosition) { + adjustTranslationFrameOfReference(child, currentCornerPosition, nearestCornerPosition); + } + currentCornerPosition = nearestCornerPosition; - isAnimating = true; - isDragging = false; + child.setLayoutParams(layoutParams); child.animate() - .translationX(getTranslationXForPoint(nearestCornerPosition)) - .translationY(getTranslationYForPoint(nearestCornerPosition)) + .translationX(0) + .translationY(0) .setDuration(250) .setInterpolator(interpolator) - .setListener(new AnimationCompleteListener() { - @Override - public void onAnimationEnd(Animator animation) { - isAnimating = false; - - Iterator afterFlingRunnables = runAfterFling.iterator(); - while (afterFlingRunnables.hasNext()) { - Runnable runnable = afterFlingRunnables.next(); - - runnable.run(); - afterFlingRunnables.remove(); - } - } - }) .start(); } - private Point findNearestCornerPosition(Point projection) { + private Corner findNearestCornerPosition(Point projection) { if (isLockedToBottomEnd) { - return ViewUtil.isLtr(parent) ? calculateBottomRightCoordinates(parent) - : calculateBottomLeftCoordinates(parent); + return ViewUtil.isLtr(parent) ? Corner.BOTTOM_RIGHT + : Corner.BOTTOM_LEFT; } - Point maxPoint = null; - double maxDistance = Double.MAX_VALUE; + CornerPoint maxPoint = null; + double maxDistance = Double.MAX_VALUE; - for (Point point : Arrays.asList(calculateTopLeftCoordinates(), - calculateTopRightCoordinates(parent), - calculateBottomLeftCoordinates(parent), - calculateBottomRightCoordinates(parent))) - { - double distance = distance(point, projection); + for (CornerPoint cornerPoint : Arrays.asList(calculateTopLeftCoordinates(), + calculateTopRightCoordinates(parent), + calculateBottomLeftCoordinates(parent), + calculateBottomRightCoordinates(parent))) { + double distance = distance(cornerPoint.point, projection); if (distance < maxDistance) { maxDistance = distance; - maxPoint = point; + maxPoint = cornerPoint; } } - return maxPoint; - } - - private float getTranslationXForPoint(Point destination) { - return destination.x - child.getLeft(); - } - - private float getTranslationYForPoint(Point destination) { - return destination.y - child.getTop(); + //noinspection DataFlowIssue + return maxPoint.corner; } - private Point calculateTopLeftCoordinates() { - return new Point(framePadding, - framePadding + extraPaddingTop); + private CornerPoint calculateTopLeftCoordinates() { + return new CornerPoint(new Point(framePadding, framePadding + extraPaddingTop), + Corner.TOP_LEFT); } - private Point calculateTopRightCoordinates(@NonNull ViewGroup parent) { - return new Point(parent.getMeasuredWidth() - pipWidth - framePadding, - framePadding + extraPaddingTop); + private CornerPoint calculateTopRightCoordinates(@NonNull ViewGroup parent) { + return new CornerPoint(new Point(parent.getMeasuredWidth() - pipWidth - framePadding, framePadding + extraPaddingTop), + Corner.TOP_RIGHT); } - private Point calculateBottomLeftCoordinates(@NonNull ViewGroup parent) { - return new Point(framePadding, - parent.getMeasuredHeight() - pipHeight - framePadding - extraPaddingBottom); + private CornerPoint calculateBottomLeftCoordinates(@NonNull ViewGroup parent) { + return new CornerPoint(new Point(framePadding, parent.getMeasuredHeight() - pipHeight - framePadding - extraPaddingBottom), + Corner.BOTTOM_LEFT); } - private Point calculateBottomRightCoordinates(@NonNull ViewGroup parent) { - return new Point(parent.getMeasuredWidth() - pipWidth - framePadding, - parent.getMeasuredHeight() - pipHeight - framePadding - extraPaddingBottom); + private CornerPoint calculateBottomRightCoordinates(@NonNull ViewGroup parent) { + return new CornerPoint(new Point(parent.getMeasuredWidth() - pipWidth - framePadding, parent.getMeasuredHeight() - pipHeight - framePadding - extraPaddingBottom), + Corner.BOTTOM_RIGHT); } private static float project(float initialVelocity) { @@ -321,9 +296,80 @@ private static double distance(Point a, Point b) { return Math.sqrt(Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2)); } - /** Borrowed from ScrollView */ + + /** + * User drag is implemented by translating the view from the current gravity anchor (corner). When the user drags + * to a new corner, we need to adjust the translations for the new corner so the animation of translation X/Y to 0 + * works correctly. + * + * For example, if in bottom right and need to move to top right, we need to calculate a new translation Y since instead + * of being translated up from bottom it's translated down from the top. + */ + private void adjustTranslationFrameOfReference(@NonNull View child, @NonNull Corner previous, @NonNull Corner next) { + TouchInterceptingFrameLayout parent = (TouchInterceptingFrameLayout) child.getParent(); + FrameLayout.LayoutParams childLayoutParams = (FrameLayout.LayoutParams) child.getLayoutParams(); + int parentWidth = parent.getWidth(); + int parentHeight = parent.getHeight(); + + if (previous.topHalf != next.topHalf) { + int childHeight = childLayoutParams.height + childLayoutParams.topMargin + childLayoutParams.bottomMargin; + + float adjustedTranslationY; + if (previous.topHalf) { + adjustedTranslationY = -(parentHeight - child.getTranslationY() - childHeight); + } else { + adjustedTranslationY = parentHeight + child.getTranslationY() - childHeight; + } + child.setTranslationY(adjustedTranslationY); + } + + if (previous.leftSide != next.leftSide) { + int childWidth = childLayoutParams.width + childLayoutParams.leftMargin + childLayoutParams.rightMargin; + + float adjustedTranslationX; + if (previous.leftSide) { + adjustedTranslationX = -(parentWidth - child.getTranslationX() - childWidth); + } else { + adjustedTranslationX = parentWidth + child.getTranslationX() - childWidth; + } + child.setTranslationX(adjustedTranslationX); + } + } + + private static class CornerPoint { + final Point point; + final Corner corner; + + public CornerPoint(@NonNull Point point, @NonNull Corner corner) { + this.point = point; + this.corner = corner; + } + } + + private enum Corner { + TOP_LEFT(Gravity.TOP | Gravity.START, true, true), + TOP_RIGHT(Gravity.TOP | Gravity.END, false, true), + BOTTOM_LEFT(Gravity.BOTTOM | Gravity.START, true, false), + BOTTOM_RIGHT(Gravity.BOTTOM | Gravity.END, false, false); + + final int gravity; + final boolean leftSide; + final boolean topHalf; + + Corner(int gravity, boolean leftSide, boolean topHalf) { + this.gravity = gravity; + this.leftSide = leftSide; + this.topHalf = topHalf; + } + } + + /** + * Borrowed from ScrollView + */ private static class ViscousFluidInterpolator implements Interpolator { - /** Controls the viscous fluid effect (how much of it). */ + /** + * Controls the viscous fluid effect (how much of it). + */ private static final float VISCOUS_FLUID_SCALE = 8.0f; private static final float VISCOUS_FLUID_NORMALIZE; @@ -340,10 +386,10 @@ private static class ViscousFluidInterpolator implements Interpolator { private static float viscousFluid(float x) { x *= VISCOUS_FLUID_SCALE; if (x < 1.0f) { - x -= (1.0f - (float)Math.exp(-x)); + x -= (1.0f - (float) Math.exp(-x)); } else { float start = 0.36787944117f; // 1/e == exp(-1) - x = 1.0f - (float)Math.exp(1.0f - x); + x = 1.0f - (float) Math.exp(1.0f - x); x = start + x * (1.0f - start); } return x; diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallParticipantsPagerAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallParticipantsPagerAdapter.java index 93b941af24..7412511261 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallParticipantsPagerAdapter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallParticipantsPagerAdapter.java @@ -112,6 +112,7 @@ void bind(WebRtcCallParticipantsPage page) { CallParticipant participant = page.getCallParticipants().get(0); callParticipantView.setCallParticipant(participant); callParticipantView.setRenderInPip(page.isRenderInPip()); + callParticipantView.setRaiseHandAllowed(false); if (participant.isScreenSharing()) { callParticipantView.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT); } else { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallView.java b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallView.java index dd1060be18..0738480c32 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallView.java @@ -1,16 +1,19 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + package org.thoughtcrime.securesms.components.webrtc; import android.content.Context; import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; import android.graphics.Point; -import android.graphics.Rect; import android.os.Build; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import android.view.WindowInsets; -import android.view.animation.Animation; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.TextView; @@ -21,44 +24,39 @@ import androidx.annotation.StringRes; import androidx.appcompat.content.res.AppCompatResources; import androidx.appcompat.widget.Toolbar; +import androidx.compose.ui.platform.ComposeView; import androidx.constraintlayout.widget.ConstraintLayout; import androidx.constraintlayout.widget.ConstraintSet; import androidx.constraintlayout.widget.Guideline; import androidx.core.util.Consumer; -import androidx.core.view.ViewKt; import androidx.core.view.WindowInsetsCompat; import androidx.recyclerview.widget.DefaultItemAnimator; import androidx.recyclerview.widget.RecyclerView; -import androidx.transition.AutoTransition; -import androidx.transition.Transition; -import androidx.transition.TransitionManager; -import androidx.transition.TransitionSet; import androidx.viewpager2.widget.MarginPageTransformer; import androidx.viewpager2.widget.ViewPager2; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.bumptech.glide.load.resource.bitmap.CenterCrop; import com.google.android.material.button.MaterialButton; -import com.google.common.collect.Sets; import org.signal.core.util.DimensionUnit; import org.signal.core.util.SetUtil; import org.signal.core.util.ThreadUtil; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.R; -import org.thoughtcrime.securesms.animation.ResizeAnimation; import org.thoughtcrime.securesms.components.AccessibleToggleButton; import org.thoughtcrime.securesms.components.AvatarImageView; +import org.thoughtcrime.securesms.components.InsetAwareConstraintLayout; import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto; import org.thoughtcrime.securesms.contacts.avatars.ProfileContactPhoto; import org.thoughtcrime.securesms.events.CallParticipant; import org.thoughtcrime.securesms.events.WebRtcViewModel; -import org.thoughtcrime.securesms.mediasend.SimpleAnimationListener; import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.ringrtc.CameraState; import org.thoughtcrime.securesms.service.webrtc.PendingParticipantCollection; +import org.thoughtcrime.securesms.stories.viewer.reply.reaction.MultiReactionBurstLayout; import org.thoughtcrime.securesms.util.BlurTransformation; import org.thoughtcrime.securesms.util.ThrottledDebouncer; import org.thoughtcrime.securesms.util.ViewUtil; @@ -72,7 +70,7 @@ import java.util.List; import java.util.Set; -public class WebRtcCallView extends ConstraintLayout { +public class WebRtcCallView extends InsetAwareConstraintLayout { private static final String TAG = Log.tag(WebRtcCallView.class); @@ -80,10 +78,6 @@ public class WebRtcCallView extends ConstraintLayout { private static final int SMALL_ONGOING_CALL_BUTTON_MARGIN_DP = 8; private static final int LARGE_ONGOING_CALL_BUTTON_MARGIN_DP = 16; - public static final int FADE_OUT_DELAY = 5000; - public static final int PIP_RESIZE_DURATION = 300; - public static final int CONTROLS_HEIGHT = 98; - private WebRtcAudioOutputToggleButton audioToggle; private AccessibleToggleButton videoToggle; private AccessibleToggleButton micToggle; @@ -96,8 +90,6 @@ public class WebRtcCallView extends ConstraintLayout { private TextView recipientName; private TextView status; private TextView incomingRingStatus; - private ConstraintLayout parent; - private ConstraintLayout participantsParent; private ControlsListener controlsListener; private RecipientId recipientId; private ImageView answer; @@ -105,6 +97,7 @@ public class WebRtcCallView extends ConstraintLayout { private ImageView cameraDirectionToggle; private AccessibleToggleButton ringToggle; private PictureInPictureGestureHelper pictureInPictureGestureHelper; + private ImageView overflow; private ImageView hangup; private View answerWithoutVideo; private View topGradient; @@ -117,23 +110,26 @@ public class WebRtcCallView extends ConstraintLayout { private Stub groupCallSpeakerHint; private Stub groupCallFullStub; private View errorButton; - private int pagerBottomMarginDp; - private boolean controlsVisible = true; private Guideline showParticipantsGuideline; + private Guideline aboveControlsGuideline; private Guideline topFoldGuideline; private Guideline callScreenTopFoldGuideline; private AvatarImageView largeHeaderAvatar; - private Guideline statusBarGuideline; - private Guideline navigationBarGuideline; private int navBarBottomInset; private View fullScreenShade; private Toolbar collapsedToolbar; private Toolbar headerToolbar; private Stub pendingParticipantsViewStub; private Stub callLinkWarningCard; + private RecyclerView groupReactionsFeed; + private MultiReactionBurstLayout reactionViews; + private ComposeView raiseHandSnackbar; + + private WebRtcCallParticipantsPagerAdapter pagerAdapter; private WebRtcCallParticipantsRecyclerAdapter recyclerAdapter; + private WebRtcReactionsRecyclerAdapter reactionsAdapter; private PictureInPictureExpansionHelper pictureInPictureExpansionHelper; private PendingParticipantsView.Listener pendingParticipantsViewListener; @@ -141,18 +137,14 @@ public class WebRtcCallView extends ConstraintLayout { private final Set topViews = new HashSet<>(); private final Set visibleViewSet = new HashSet<>(); private final Set allTimeVisibleViews = new HashSet<>(); - private final Set adjustableMarginsSet = new HashSet<>(); private final Set rotatableControls = new HashSet<>(); - private final ThrottledDebouncer throttledDebouncer = new ThrottledDebouncer(TRANSITION_DURATION_MILLIS); private WebRtcControls controls = WebRtcControls.NONE; - private final Runnable fadeOutRunnable = () -> { - if (isAttachedToWindow() && controls.isFadeOutEnabled()) fadeOutControls(); - }; private CallParticipantsViewState lastState; private ContactPhoto previousLocalAvatar; + private LayoutPositions previousLayoutPositions = null; public WebRtcCallView(@NonNull Context context) { this(context, null); @@ -181,12 +173,11 @@ protected void onFinishInflate() { recipientName = findViewById(R.id.call_screen_recipient_name); status = findViewById(R.id.call_screen_status); incomingRingStatus = findViewById(R.id.call_screen_incoming_ring_status); - parent = findViewById(R.id.call_screen); - participantsParent = findViewById(R.id.call_screen_participants_parent); answer = findViewById(R.id.call_screen_answer_call); answerWithoutVideoLabel = findViewById(R.id.call_screen_answer_without_video_label); cameraDirectionToggle = findViewById(R.id.call_screen_camera_direction_toggle); ringToggle = findViewById(R.id.call_screen_audio_ring_toggle); + overflow = findViewById(R.id.call_screen_overflow_button); hangup = findViewById(R.id.call_screen_end_call); answerWithoutVideo = findViewById(R.id.call_screen_answer_without_video); topGradient = findViewById(R.id.call_screen_header_gradient); @@ -200,33 +191,40 @@ protected void onFinishInflate() { groupCallSpeakerHint = new Stub<>(findViewById(R.id.call_screen_group_call_speaker_hint)); groupCallFullStub = new Stub<>(findViewById(R.id.group_call_call_full_view)); showParticipantsGuideline = findViewById(R.id.call_screen_show_participants_guideline); + aboveControlsGuideline = findViewById(R.id.call_screen_above_controls_guideline); topFoldGuideline = findViewById(R.id.fold_top_guideline); callScreenTopFoldGuideline = findViewById(R.id.fold_top_call_screen_guideline); largeHeaderAvatar = findViewById(R.id.call_screen_header_avatar); - statusBarGuideline = findViewById(R.id.call_screen_status_bar_guideline); - navigationBarGuideline = findViewById(R.id.call_screen_navigation_bar_guideline); fullScreenShade = findViewById(R.id.call_screen_full_shade); collapsedToolbar = findViewById(R.id.webrtc_call_view_toolbar_text); headerToolbar = findViewById(R.id.webrtc_call_view_toolbar_no_text); pendingParticipantsViewStub = new Stub<>(findViewById(R.id.call_screen_pending_recipients)); callLinkWarningCard = new Stub<>(findViewById(R.id.call_screen_call_link_warning)); + groupReactionsFeed = findViewById(R.id.call_screen_reactions_feed); + reactionViews = findViewById(R.id.call_screen_reactions_container); + raiseHandSnackbar = findViewById(R.id.call_screen_raise_hand_view); - View decline = findViewById(R.id.call_screen_decline_call); - View answerLabel = findViewById(R.id.call_screen_answer_call_label); - View declineLabel = findViewById(R.id.call_screen_decline_call_label); + View decline = findViewById(R.id.call_screen_decline_call); + View answerLabel = findViewById(R.id.call_screen_answer_call_label); + View declineLabel = findViewById(R.id.call_screen_decline_call_label); callParticipantsPager.setPageTransformer(new MarginPageTransformer(ViewUtil.dpToPx(4))); - pagerAdapter = new WebRtcCallParticipantsPagerAdapter(this::toggleControls); - recyclerAdapter = new WebRtcCallParticipantsRecyclerAdapter(); + pagerAdapter = new WebRtcCallParticipantsPagerAdapter(this::toggleControls); + recyclerAdapter = new WebRtcCallParticipantsRecyclerAdapter(); + reactionsAdapter = new WebRtcReactionsRecyclerAdapter(); callParticipantsPager.setAdapter(pagerAdapter); callParticipantsRecycler.setAdapter(recyclerAdapter); + groupReactionsFeed.setAdapter(reactionsAdapter); DefaultItemAnimator animator = new DefaultItemAnimator(); animator.setSupportsChangeAnimations(false); callParticipantsRecycler.setItemAnimator(animator); + groupReactionsFeed.addItemDecoration(new WebRtcReactionsAlphaItemDecoration()); + groupReactionsFeed.setItemAnimator(new WebRtcReactionsItemAnimator()); + callParticipantsPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() { @Override public void onPageSelected(int position) { @@ -234,8 +232,6 @@ public void onPageSelected(int position) { } }); - topViews.add(collapsedToolbar); - topViews.add(headerToolbar); topViews.add(largeHeader); topViews.add(topGradient); @@ -246,11 +242,6 @@ public void onPageSelected(int position) { incomingCallViews.add(footerGradient); incomingCallViews.add(incomingRingStatus); - adjustableMarginsSet.add(micToggle); - adjustableMarginsSet.add(cameraDirectionToggle); - adjustableMarginsSet.add(videoToggle); - adjustableMarginsSet.add(audioToggle); - audioToggle.setOnAudioOutputChangedListener(webRtcAudioDevice -> { runIfNonNull(controlsListener, listener -> { @@ -279,6 +270,11 @@ public void onPageSelected(int position) { }); cameraDirectionToggle.setOnClickListener(v -> runIfNonNull(controlsListener, ControlsListener::onCameraDirectionChanged)); + smallLocalRender.findViewById(R.id.call_participant_switch_camera).setOnClickListener(v -> runIfNonNull(controlsListener, ControlsListener::onCameraDirectionChanged)); + + overflow.setOnClickListener(v -> { + runIfNonNull(controlsListener, ControlsListener::onOverflowClicked); + }); hangup.setOnClickListener(v -> runIfNonNull(controlsListener, ControlsListener::onEndCallPressed)); decline.setOnClickListener(v -> runIfNonNull(controlsListener, ControlsListener::onDenyCallPressed)); @@ -287,7 +283,7 @@ public void onPageSelected(int position) { answerWithoutVideo.setOnClickListener(v -> runIfNonNull(controlsListener, ControlsListener::onAcceptCallWithVoiceOnlyPressed)); pictureInPictureGestureHelper = PictureInPictureGestureHelper.applyTo(smallLocalRenderFrame); - pictureInPictureExpansionHelper = new PictureInPictureExpansionHelper(); + pictureInPictureExpansionHelper = new PictureInPictureExpansionHelper(smallLocalRenderFrame); smallLocalRenderFrame.setOnClickListener(v -> { if (controlsListener != null) { @@ -348,6 +344,7 @@ public void onPageSelected(int position) { return false; }); + rotatableControls.add(overflow); rotatableControls.add(hangup); rotatableControls.add(answer); rotatableControls.add(answerWithoutVideo); @@ -360,25 +357,6 @@ public void onPageSelected(int position) { rotatableControls.add(ringToggle); } - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - - if (controls.isFadeOutEnabled()) { - scheduleFadeOut(); - } - } - - @Override - protected boolean fitSystemWindows(Rect insets) { - if (insets.top != 0) { - statusBarGuideline.setGuidelineBegin(insets.top); - } - navigationBarGuideline.setGuidelineEnd(insets.bottom); - - return true; - } - @Override public WindowInsets onApplyWindowInsets(WindowInsets insets) { navBarBottomInset = WindowInsetsCompat.toWindowInsetsCompat(insets).getInsets(WindowInsetsCompat.Type.navigationBars()).bottom; @@ -392,25 +370,14 @@ public WindowInsets onApplyWindowInsets(WindowInsets insets) { @Override public void onWindowSystemUiVisibilityChanged(int visible) { + final Guideline statusBarGuideline = getStatusBarGuideline(); if ((visible & SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0) { - if (controls.adjustForFold()) { - pictureInPictureGestureHelper.clearVerticalBoundaries(); - pictureInPictureGestureHelper.setTopVerticalBoundary(getPipBarrier().getTop()); - } else { - pictureInPictureGestureHelper.setTopVerticalBoundary(getPipBarrier().getBottom()); - pictureInPictureGestureHelper.setBottomVerticalBoundary(videoToggle.getTop()); - } + pictureInPictureGestureHelper.setTopVerticalBoundary(collapsedToolbar.getBottom()); + } else if (statusBarGuideline != null) { + pictureInPictureGestureHelper.setTopVerticalBoundary(statusBarGuideline.getBottom()); } else { - pictureInPictureGestureHelper.clearVerticalBoundaries(); + Log.d(TAG, "Could not update PiP gesture helper."); } - - pictureInPictureGestureHelper.adjustPip(); - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - cancelFadeOut(); } public void rotateControls(int degrees) { @@ -489,21 +456,25 @@ public void updateCallParticipants(@NonNull CallParticipantsViewState callPartic pagerAdapter.submitList(pages); recyclerAdapter.submitList(state.getListParticipants()); + reactionsAdapter.submitList(state.getReactions()); + + reactionViews.displayReactions(state.getReactions()); boolean displaySmallSelfPipInLandscape = !isPortrait && isLandscapeEnabled; - updateLocalCallParticipant(state.getLocalRenderState(), state.getLocalParticipant(), state.getFocusedParticipant(), displaySmallSelfPipInLandscape); + updateLocalCallParticipant(state.getLocalRenderState(), state.getLocalParticipant(), displaySmallSelfPipInLandscape); - if (state.isLargeVideoGroup() && !state.isInPipMode() && !state.isFolded()) { - layoutParticipantsForLargeCount(); + if (state.isLargeVideoGroup()) { + moveSnackbarAboveParticipantRail(true); + adjustLayoutForLargeCount(); } else { - layoutParticipantsForSmallCount(); + moveSnackbarAboveParticipantRail(state.isViewingFocusedParticipant()); + adjustLayoutForSmallCount(); } } public void updateLocalCallParticipant(@NonNull WebRtcLocalRenderState state, @NonNull CallParticipant localCallParticipant, - @NonNull CallParticipant focusedParticipant, boolean displaySmallSelfPipInLandscape) { largeLocalRender.setMirror(localCallParticipant.getCameraDirection() == CameraState.Direction.FRONT); @@ -518,17 +489,20 @@ public void updateLocalCallParticipant(@NonNull WebRtcLocalRenderState state, videoToggle.setChecked(localCallParticipant.isVideoEnabled(), false); smallLocalRender.setRenderInPip(true); + smallLocalRender.setCallParticipant(localCallParticipant); + smallLocalRender.setMirror(localCallParticipant.getCameraDirection() == CameraState.Direction.FRONT); if (state == WebRtcLocalRenderState.EXPANDED) { - expandPip(localCallParticipant, focusedParticipant); - smallLocalRender.setCallParticipant(focusedParticipant); - return; - } else if ((state == WebRtcLocalRenderState.SMALL_RECTANGLE || state == WebRtcLocalRenderState.GONE) && pictureInPictureExpansionHelper.isExpandedOrExpanding()) { - shrinkPip(localCallParticipant); + pictureInPictureExpansionHelper.beginExpandTransition(); + smallLocalRender.setSelfPipMode(CallParticipantView.SelfPipMode.EXPANDED_SELF_PIP); return; - } else { - smallLocalRender.setCallParticipant(localCallParticipant); - smallLocalRender.setMirror(localCallParticipant.getCameraDirection() == CameraState.Direction.FRONT); + } else if ((state.isAnySmall() || state == WebRtcLocalRenderState.GONE) && pictureInPictureExpansionHelper.isExpandedOrExpanding()) { + pictureInPictureExpansionHelper.beginShrinkTransition(); + smallLocalRender.setSelfPipMode(pictureInPictureExpansionHelper.isMiniSize() ? CallParticipantView.SelfPipMode.MINI_SELF_PIP : CallParticipantView.SelfPipMode.NORMAL_SELF_PIP); + + if (state != WebRtcLocalRenderState.GONE) { + return; + } } switch (state) { @@ -674,7 +648,7 @@ public void setWebRtcControls(@NonNull WebRtcControls webRtcControls) { topFoldGuideline.setGuidelineEnd(webRtcControls.getFold()); callScreenTopFoldGuideline.setGuidelineEnd(webRtcControls.getFold()); } else { - showParticipantsGuideline.setGuidelineBegin(((LayoutParams) statusBarGuideline.getLayoutParams()).guideBegin); + showParticipantsGuideline.setGuidelineBegin(((LayoutParams) getStatusBarGuideline().getLayoutParams()).guideBegin); showParticipantsGuideline.setGuidelineEnd(-1); topFoldGuideline.setGuidelineEnd(0); callScreenTopFoldGuideline.setGuidelineEnd(0); @@ -724,8 +698,6 @@ public void setWebRtcControls(@NonNull WebRtcControls webRtcControls) { } if (webRtcControls.displayAudioToggle()) { - visibleViewSet.add(audioToggle); - audioToggle.setControlAvailability(webRtcControls.isEarpieceAvailableForAudioToggle(), webRtcControls.isBluetoothHeadsetAvailableForAudioToggle(), webRtcControls.isWiredHeadsetAvailableForAudioToggle()); @@ -733,26 +705,9 @@ public void setWebRtcControls(@NonNull WebRtcControls webRtcControls) { audioToggle.updateAudioOutputState(webRtcControls.getAudioOutput()); } - if (webRtcControls.displayCameraToggle()) { - visibleViewSet.add(cameraDirectionToggle); - } - - if (webRtcControls.displayEndCall()) { - visibleViewSet.add(hangup); - visibleViewSet.add(footerGradient); - } - - if (webRtcControls.displayMuteAudio()) { - visibleViewSet.add(micToggle); - } - - if (webRtcControls.displayVideoToggle()) { - visibleViewSet.add(videoToggle); - } - - if (webRtcControls.displaySmallOngoingCallButtons()) { + if (webRtcControls.displaySmallCallButtons()) { updateButtonStateForSmallButtons(); - } else if (webRtcControls.displayLargeOngoingCallButtons()) { + } else { updateButtonStateForLargeButtons(); } @@ -770,32 +725,20 @@ public void setWebRtcControls(@NonNull WebRtcControls webRtcControls) { fullScreenShade.setVisibility(GONE); } - if (webRtcControls.displayRingToggle()) { - visibleViewSet.add(ringToggle); + if (webRtcControls.displayReactions()) { + visibleViewSet.add(reactionViews); + visibleViewSet.add(groupReactionsFeed); } - - if (webRtcControls.isFadeOutEnabled()) { - if (!controls.isFadeOutEnabled()) { - scheduleFadeOut(); - } - } else { - cancelFadeOut(); - - if (controlsListener != null) { - controlsListener.showSystemUI(); - } - } - - if (webRtcControls.adjustForFold() && webRtcControls.isFadeOutEnabled() && !controls.adjustForFold()) { - scheduleFadeOut(); + if (webRtcControls.displayRaiseHand()) { + visibleViewSet.add(raiseHandSnackbar); } boolean forceUpdate = webRtcControls.adjustForFold() && !controls.adjustForFold(); controls = webRtcControls; if (!controls.isFadeOutEnabled()) { - controlsVisible = true; + boolean controlsVisible = true; } allTimeVisibleViews.addAll(visibleViewSet); @@ -806,12 +749,7 @@ public void setWebRtcControls(@NonNull WebRtcControls webRtcControls) { (!webRtcControls.showSmallHeader() && largeHeaderAvatar.getVisibility() == View.GONE) || forceUpdate) { - - if (controlsListener != null) { - controlsListener.showSystemUI(); - } - - throttledDebouncer.publish(() -> fadeInNewUiState(webRtcControls.displaySmallOngoingCallButtons(), webRtcControls.showSmallHeader())); + throttledDebouncer.publish(() -> fadeInNewUiState(webRtcControls.showSmallHeader())); } onWindowSystemUiVisibilityChanged(getWindowSystemUiVisibility()); @@ -821,6 +759,10 @@ public void setWebRtcControls(@NonNull WebRtcControls webRtcControls) { return videoToggle; } + public @NonNull View getSwitchCameraTooltipTarget() { + return smallLocalRenderFrame; + } + public void showSpeakerViewHint() { groupCallSpeakerHint.get().setVisibility(View.VISIBLE); } @@ -831,231 +773,112 @@ public void hideSpeakerViewHint() { } } - private void expandPip(@NonNull CallParticipant localCallParticipant, @NonNull CallParticipant focusedParticipant) { - pictureInPictureExpansionHelper.expand(smallLocalRenderFrame, new PictureInPictureExpansionHelper.Callback() { - @Override - public void onAnimationWillStart() { - largeLocalRender.attachBroadcastVideoSink(localCallParticipant.getVideoSink()); - } - - @Override - public void onPictureInPictureExpanded() { - largeLocalRenderFrame.setVisibility(View.VISIBLE); - largeLocalRenderNoVideo.setVisibility(View.GONE); - largeLocalRenderNoVideoAvatar.setVisibility(View.GONE); - } - - @Override - public void onPictureInPictureNotVisible() { - smallLocalRender.setCallParticipant(focusedParticipant); - smallLocalRender.setMirror(false); - } - - @Override - public void onAnimationHasFinished() { - pictureInPictureGestureHelper.adjustPip(); - } - }); - } - - private void shrinkPip(@NonNull CallParticipant localCallParticipant) { - pictureInPictureExpansionHelper.shrink(smallLocalRenderFrame, new PictureInPictureExpansionHelper.Callback() { - @Override - public void onAnimationWillStart() { - } - - @Override - public void onPictureInPictureExpanded() { - largeLocalRenderFrame.setVisibility(View.GONE); - largeLocalRender.attachBroadcastVideoSink(null); - } - - @Override - public void onPictureInPictureNotVisible() { - smallLocalRender.setCallParticipant(localCallParticipant); - smallLocalRender.setMirror(localCallParticipant.getCameraDirection() == CameraState.Direction.FRONT); - - if (!localCallParticipant.isVideoEnabled()) { - smallLocalRenderFrame.setVisibility(View.GONE); - } - } - - @Override - public void onAnimationHasFinished() { - pictureInPictureGestureHelper.adjustPip(); - } - }); - } - private void animatePipToLargeRectangle(boolean isLandscape) { final Point dimens; if (isLandscape) { - dimens = new Point(ViewUtil.dpToPx(160), ViewUtil.dpToPx(90)); + dimens = new Point(ViewUtil.dpToPx(PictureInPictureExpansionHelper.NORMAL_PIP_HEIGHT_DP), + ViewUtil.dpToPx(PictureInPictureExpansionHelper.NORMAL_PIP_WIDTH_DP)); } else { - dimens = new Point(ViewUtil.dpToPx(90), ViewUtil.dpToPx(160)); + dimens = new Point(ViewUtil.dpToPx(PictureInPictureExpansionHelper.NORMAL_PIP_WIDTH_DP), + ViewUtil.dpToPx(PictureInPictureExpansionHelper.NORMAL_PIP_HEIGHT_DP)); } - SimpleAnimationListener animationListener = new SimpleAnimationListener() { + pictureInPictureExpansionHelper.startDefaultSizeTransition(dimens, new PictureInPictureExpansionHelper.Callback() { @Override - public void onAnimationEnd(Animation animation) { + public void onAnimationHasFinished() { pictureInPictureGestureHelper.enableCorners(); - pictureInPictureGestureHelper.adjustPip(); } - }; - - ViewGroup.LayoutParams layoutParams = smallLocalRenderFrame.getLayoutParams(); - if (layoutParams.width == dimens.x && layoutParams.height == dimens.y) { - animationListener.onAnimationEnd(null); - return; - } - - ResizeAnimation animation = new ResizeAnimation(smallLocalRenderFrame, dimens.x, dimens.y); - animation.setDuration(PIP_RESIZE_DURATION); - animation.setAnimationListener(animationListener); + }); - smallLocalRenderFrame.startAnimation(animation); + smallLocalRender.setSelfPipMode(CallParticipantView.SelfPipMode.NORMAL_SELF_PIP); } private void animatePipToSmallRectangle() { - pictureInPictureGestureHelper.lockToBottomEnd(); - - pictureInPictureGestureHelper.performAfterFling(() -> { - ResizeAnimation animation = new ResizeAnimation(smallLocalRenderFrame, ViewUtil.dpToPx(54), ViewUtil.dpToPx(72)); - animation.setDuration(PIP_RESIZE_DURATION); - animation.setAnimationListener(new SimpleAnimationListener() { - @Override - public void onAnimationEnd(Animation animation) { - pictureInPictureGestureHelper.adjustPip(); - } - }); + pictureInPictureExpansionHelper.startDefaultSizeTransition(new Point(ViewUtil.dpToPx(PictureInPictureExpansionHelper.MINI_PIP_WIDTH_DP), + ViewUtil.dpToPx(PictureInPictureExpansionHelper.MINI_PIP_HEIGHT_DP)), + new PictureInPictureExpansionHelper.Callback() { + @Override + public void onAnimationHasFinished() { + pictureInPictureGestureHelper.lockToBottomEnd(); + } + }); - smallLocalRenderFrame.startAnimation(animation); - }); + smallLocalRender.setSelfPipMode(CallParticipantView.SelfPipMode.MINI_SELF_PIP); } private void toggleControls() { - if (controls.isFadeOutEnabled() && largeHeader.getVisibility() == VISIBLE) { - fadeOutControls(); - } else { - fadeInControls(); - } + controlsListener.toggleControls(); } - private void fadeOutControls() { - fadeControls(ConstraintSet.GONE); - controlsListener.onControlsFadeOut(); + private void adjustLayoutForSmallCount() { + adjustLayoutPositions(LayoutPositions.SMALL_GROUP); } - private void fadeInControls() { - fadeControls(ConstraintSet.VISIBLE); - - scheduleFadeOut(); + private void adjustLayoutForLargeCount() { + adjustLayoutPositions(LayoutPositions.LARGE_GROUP); } - private void layoutParticipantsForSmallCount() { - pagerBottomMarginDp = 0; - - layoutParticipants(); - } - - private void layoutParticipantsForLargeCount() { - pagerBottomMarginDp = 104; - - layoutParticipants(); - } - - private int withControlsHeight(int margin) { - if (margin == 0) { - return 0; - } - - return (controlsVisible || controls.adjustForFold()) ? margin + CONTROLS_HEIGHT : margin; - } - - private void layoutParticipants() { - int desiredMargin = ViewUtil.dpToPx(withControlsHeight(pagerBottomMarginDp)); - if (ViewKt.getMarginBottom(callParticipantsPager) == desiredMargin) { + private void adjustLayoutPositions(@NonNull LayoutPositions layoutPositions) { + if (previousLayoutPositions == layoutPositions) { return; } - Transition transition = new AutoTransition().setDuration(TRANSITION_DURATION_MILLIS); - - TransitionManager.beginDelayedTransition(participantsParent, transition); + previousLayoutPositions = layoutPositions; ConstraintSet constraintSet = new ConstraintSet(); - constraintSet.clone(participantsParent); - - constraintSet.setMargin(R.id.call_screen_participants_pager, ConstraintSet.BOTTOM, desiredMargin); - constraintSet.applyTo(participantsParent); - } - - private void fadeControls(int visibility) { - controlsVisible = visibility == VISIBLE; + constraintSet.clone(this); - Transition transition = new AutoTransition().setOrdering(TransitionSet.ORDERING_TOGETHER) - .setDuration(TRANSITION_DURATION_MILLIS); + constraintSet.connect(R.id.call_screen_participants_parent, + ConstraintSet.BOTTOM, + layoutPositions.participantBottomViewId, + layoutPositions.participantBottomViewEndSide, + ViewUtil.dpToPx(layoutPositions.participantBottomMargin)); - TransitionManager.endTransitions(parent); + constraintSet.connect(R.id.call_screen_reactions_feed, + ConstraintSet.BOTTOM, + layoutPositions.reactionBottomViewId, + ConstraintSet.TOP, + ViewUtil.dpToPx(layoutPositions.reactionBottomMargin)); - if (controlsListener != null) { - if (controlsVisible) { - controlsListener.showSystemUI(); - } else { - controlsListener.hideSystemUI(); - } - } + constraintSet.connect(pendingParticipantsViewStub.getId(), + ConstraintSet.BOTTOM, + layoutPositions.reactionBottomViewId, + ConstraintSet.TOP, + ViewUtil.dpToPx(layoutPositions.reactionBottomMargin)); - TransitionManager.beginDelayedTransition(parent, transition); - - ConstraintSet constraintSet = new ConstraintSet(); - constraintSet.clone(parent); - - for (View view : controlsToFade()) { - constraintSet.setVisibility(view.getId(), visibility); - } - - adjustParticipantsRecycler(constraintSet); - - constraintSet.applyTo(parent); - - layoutParticipants(); + constraintSet.applyTo(this); } - private Set controlsToFade() { - if (controls.adjustForFold()) { - return Sets.intersection(topViews, visibleViewSet); + private void moveSnackbarAboveParticipantRail(boolean aboveRail) { + if (aboveRail) { + updateSnackbarBottomConstraint(callParticipantsRecycler); } else { - return visibleViewSet; + updateSnackbarBottomConstraint(aboveControlsGuideline); } } - private void fadeInNewUiState(boolean useSmallMargins, boolean showSmallHeader) { - Transition transition = new AutoTransition().setDuration(TRANSITION_DURATION_MILLIS); + private void updateSnackbarBottomConstraint(View anchor) { + ConstraintSet constraintSet = new ConstraintSet(); + constraintSet.clone(this); - TransitionManager.beginDelayedTransition(parent, transition); + constraintSet.connect(R.id.call_screen_raise_hand_view, + ConstraintSet.BOTTOM, + anchor.getId(), + ConstraintSet.TOP, + ViewUtil.dpToPx(8)); - ConstraintSet constraintSet = new ConstraintSet(); - constraintSet.clone(parent); + constraintSet.applyTo(this); + } + private void fadeInNewUiState(boolean showSmallHeader) { for (View view : SetUtil.difference(allTimeVisibleViews, visibleViewSet)) { - constraintSet.setVisibility(view.getId(), ConstraintSet.GONE); + view.setVisibility(GONE); } for (View view : visibleViewSet) { - constraintSet.setVisibility(view.getId(), ConstraintSet.VISIBLE); - - if (adjustableMarginsSet.contains(view)) { - constraintSet.setMargin(view.getId(), - ConstraintSet.END, - ViewUtil.dpToPx(useSmallMargins ? SMALL_ONGOING_CALL_BUTTON_MARGIN_DP - : LARGE_ONGOING_CALL_BUTTON_MARGIN_DP)); - } + view.setVisibility(VISIBLE); } - adjustParticipantsRecycler(constraintSet); - - constraintSet.applyTo(parent); - if (showSmallHeader) { collapsedToolbar.setEnabled(true); collapsedToolbar.setAlpha(1); @@ -1073,28 +896,6 @@ private void fadeInNewUiState(boolean useSmallMargins, boolean showSmallHeader) } } - private void adjustParticipantsRecycler(@NonNull ConstraintSet constraintSet) { - if (controlsVisible || controls.adjustForFold()) { - constraintSet.connect(R.id.call_screen_participants_recycler, ConstraintSet.BOTTOM, R.id.call_screen_video_toggle, ConstraintSet.TOP); - } else { - constraintSet.connect(R.id.call_screen_participants_recycler, ConstraintSet.BOTTOM, ConstraintSet.PARENT_ID, ConstraintSet.BOTTOM); - } - - constraintSet.setHorizontalBias(R.id.call_screen_participants_recycler, controls.adjustForFold() ? 0.5f : 1f); - } - - private void scheduleFadeOut() { - cancelFadeOut(); - - if (getHandler() == null) return; - getHandler().postDelayed(fadeOutRunnable, FADE_OUT_DELAY); - } - - private void cancelFadeOut() { - if (getHandler() == null) return; - getHandler().removeCallbacks(fadeOutRunnable); - } - private static void runIfNonNull(@Nullable T listener, @NonNull Consumer listenerConsumer) { if (listener != null) { listenerConsumer.accept(listener); @@ -1104,19 +905,23 @@ private static void runIfNonNull(@Nullable T listener, @NonNull Consumer private void updateButtonStateForLargeButtons() { cameraDirectionToggle.setImageResource(R.drawable.webrtc_call_screen_camera_toggle); hangup.setImageResource(R.drawable.webrtc_call_screen_hangup); + overflow.setImageResource(R.drawable.webrtc_call_screen_overflow_menu); micToggle.setBackgroundResource(R.drawable.webrtc_call_screen_mic_toggle); videoToggle.setBackgroundResource(R.drawable.webrtc_call_screen_video_toggle); audioToggle.setImageResource(R.drawable.webrtc_call_screen_speaker_toggle); ringToggle.setBackgroundResource(R.drawable.webrtc_call_screen_ring_toggle); + overflow.setBackgroundResource(R.drawable.webrtc_call_screen_overflow_menu); } private void updateButtonStateForSmallButtons() { cameraDirectionToggle.setImageResource(R.drawable.webrtc_call_screen_camera_toggle_small); hangup.setImageResource(R.drawable.webrtc_call_screen_hangup_small); + overflow.setImageResource(R.drawable.webrtc_call_screen_overflow_menu_small); micToggle.setBackgroundResource(R.drawable.webrtc_call_screen_mic_toggle_small); videoToggle.setBackgroundResource(R.drawable.webrtc_call_screen_video_toggle_small); audioToggle.setImageResource(R.drawable.webrtc_call_screen_speaker_toggle_small); ringToggle.setBackgroundResource(R.drawable.webrtc_call_screen_ring_toggle_small); + overflow.setBackgroundResource(R.drawable.webrtc_call_screen_overflow_menu_small); } public void switchToSpeakerView() { @@ -1133,17 +938,26 @@ public void enableRingGroup(boolean enabled) { ringToggle.setActivated(enabled); } + public void onControlTopChanged(int guidelineTop, int snackBarHeight) { + int offset = 0; + if (lastState != null) { + CallParticipantsState state = lastState.getCallParticipantsState(); + if (!state.isViewingFocusedParticipant() && !state.isLargeVideoGroup()) { + offset = snackBarHeight; + } + pictureInPictureGestureHelper.setBottomVerticalBoundary(guidelineTop - offset); + } + } + public interface ControlsListener { void onStartCall(boolean isVideoCall); void onCancelStartCall(); - void onControlsFadeOut(); - void showSystemUI(); - void hideSystemUI(); void onAudioOutputChanged(@NonNull WebRtcAudioOutput audioOutput); @RequiresApi(31) void onAudioOutputChanged31(@NonNull WebRtcAudioDevice audioOutput); void onVideoChanged(boolean isVideoEnabled); void onMicChanged(boolean isMicEnabled); + void onOverflowClicked(); void onCameraDirectionChanged(); void onEndCallPressed(); void onDenyCallPressed(); @@ -1154,5 +968,6 @@ public interface ControlsListener { void onRingGroupChanged(boolean ringGroup, boolean ringingAllowed); void onCallInfoClicked(); void onNavigateUpClicked(); + void toggleControls(); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallViewModel.java index da5438e5ca..3170e6824e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallViewModel.java @@ -81,17 +81,18 @@ public class WebRtcCallViewModel extends ViewModel { private final Runnable elapsedTimeRunnable = this::handleTick; private final Runnable stopOutgoingRingingMode = this::stopOutgoingRingingMode; - private boolean canDisplayTooltipIfNeeded = true; - private boolean canDisplayPopupIfNeeded = true; - private boolean hasEnabledLocalVideo = false; - private boolean wasInOutgoingRingingMode = false; - private long callConnectedTime = -1; - private boolean answerWithVideoAvailable = false; - private boolean canEnterPipMode = false; - private List previousParticipantsList = Collections.emptyList(); - private boolean callStarting = false; - private boolean switchOnFirstScreenShare = true; - private boolean showScreenShareTip = true; + private boolean canDisplayTooltipIfNeeded = true; + private boolean canDisplaySwitchCameraTooltipIfNeeded = true; + private boolean canDisplayPopupIfNeeded = true; + private boolean hasEnabledLocalVideo = false; + private boolean wasInOutgoingRingingMode = false; + private long callConnectedTime = -1; + private boolean answerWithVideoAvailable = false; + private boolean canEnterPipMode = false; + private List previousParticipantsList = Collections.emptyList(); + private boolean callStarting = false; + private boolean switchOnFirstScreenShare = true; + private boolean showScreenShareTip = true; private final WebRtcCallRepository repository = new WebRtcCallRepository(ApplicationDependencies.getApplication()); @@ -253,10 +254,6 @@ public void setIsViewingFocusedParticipant(@NonNull CallParticipantsState.Select public void onLocalPictureInPictureClicked() { CallParticipantsState state = participantsState.getValue(); - if (state.getGroupCallState() != WebRtcViewModel.GroupCallState.IDLE) { - return; - } - participantsState.onNext(CallParticipantsState.setExpanded(participantsState.getValue(), state.getLocalRenderState() != WebRtcLocalRenderState.EXPANDED)); } @@ -265,6 +262,11 @@ public void onDismissedVideoTooltip() { canDisplayTooltipIfNeeded = false; } + public void onDismissedSwitchCameraTooltip() { + canDisplaySwitchCameraTooltipIfNeeded = false; + SignalStore.tooltips().markCallingSwitchCameraTooltipSeen(); + } + @MainThread public void updateFromWebRtcViewModel(@NonNull WebRtcViewModel webRtcViewModel, boolean enableVideo) { canEnterPipMode = !webRtcViewModel.getState().isPreJoinOrNetworkUnavailable(); @@ -346,6 +348,16 @@ public void updateFromWebRtcViewModel(@NonNull WebRtcViewModel webRtcViewModel, } else if (!webRtcViewModel.isCellularConnection()) { canDisplayPopupIfNeeded = true; } + + if (SignalStore.tooltips().showCallingSwitchCameraTooltip() && + canDisplaySwitchCameraTooltipIfNeeded && + localParticipant.getCameraState().isEnabled() && + webRtcViewModel.getState() == WebRtcViewModel.State.CALL_CONNECTED && + !newState.getAllRemoteParticipants().isEmpty() + ) { + canDisplaySwitchCameraTooltipIfNeeded = false; + events.setValue(new Event.ShowSwitchCameraTooltip()); + } } @MainThread @@ -541,6 +553,12 @@ public static class DismissVideoTooltip extends Event { public static class ShowWifiToCellularPopup extends Event { } + public static class ShowSwitchCameraTooltip extends Event { + } + + public static class DismissSwitchCameraTooltip extends Event { + } + public static class StartCall extends Event { private final boolean isVideoCall; diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcControls.java b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcControls.java index 16e97ef81b..724536de66 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcControls.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcControls.java @@ -8,6 +8,7 @@ import androidx.annotation.StringRes; import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.util.FeatureFlags; import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager; import java.util.Set; @@ -100,23 +101,28 @@ private WebRtcControls() { isCallLink); } - boolean displayErrorControls() { + /** This is only true at the very start of a call and will then never be true again */ + public boolean hideControlsSheetInitially() { + return displayIncomingCallButtons() || callState == CallState.NONE; + } + + public boolean displayErrorControls() { return isError(); } - boolean displayStartCallControls() { + public boolean displayStartCallControls() { return isPreJoin(); } - boolean adjustForFold() { + public boolean adjustForFold() { return foldableState.isFolded(); } - @Px int getFold() { + public @Px int getFold() { return foldableState.getFoldPoint(); } - @StringRes int getStartCallButtonText() { + public @StringRes int getStartCallButtonText() { if (isGroupCall()) { if (groupCallState == GroupCallState.FULL) { return R.string.WebRtcCallView__call_is_full; @@ -127,86 +133,94 @@ boolean adjustForFold() { return R.string.WebRtcCallView__start_call; } - boolean isStartCallEnabled() { + public boolean isStartCallEnabled() { return groupCallState != GroupCallState.FULL; } - boolean displayGroupCallFull() { + public boolean displayGroupCallFull() { return groupCallState == GroupCallState.FULL; } - @NonNull String getGroupCallFullMessage(@NonNull Context context) { + public @NonNull String getGroupCallFullMessage(@NonNull Context context) { if (participantLimit != null) { return context.getString(R.string.WebRtcCallView__the_maximum_number_of_d_participants_has_been_Reached_for_this_call, participantLimit); } return ""; } - boolean displayGroupMembersButton() { + public boolean displayGroupMembersButton() { return (groupCallState.isAtLeast(GroupCallState.CONNECTING) && hasAtLeastOneRemote) || groupCallState.isAtLeast(GroupCallState.FULL); } - boolean displayEndCall() { + public boolean displayEndCall() { return isAtLeastOutgoing() || callState == CallState.RECONNECTING; } - boolean displayMuteAudio() { + public boolean displayOverflow() { + return FeatureFlags.groupCallReactions() && isAtLeastOutgoing() && hasAtLeastOneRemote && isGroupCall(); + } + + public boolean displayMuteAudio() { return isPreJoin() || isAtLeastOutgoing(); } - boolean displayVideoToggle() { + public boolean displayVideoToggle() { return isPreJoin() || isAtLeastOutgoing(); } - boolean displayAudioToggle() { + public boolean displayAudioToggle() { return (isPreJoin() || isAtLeastOutgoing()) && (!isLocalVideoEnabled || isBluetoothHeadsetAvailableForAudioToggle() || isWiredHeadsetAvailableForAudioToggle()); } - boolean displayCameraToggle() { - return (isPreJoin() || isAtLeastOutgoing()) && isLocalVideoEnabled && isMoreThanOneCameraAvailable; + public boolean displayCameraToggle() { + return (isPreJoin() || (isAtLeastOutgoing() && !hasAtLeastOneRemote)) && isLocalVideoEnabled && isMoreThanOneCameraAvailable && !isInPipMode; } - boolean displayRemoteVideoRecycler() { + public boolean displayRemoteVideoRecycler() { return isOngoing(); } - boolean displayAnswerWithoutVideo() { + public boolean displayAnswerWithoutVideo() { return isIncoming() && isRemoteVideoEnabled; } - boolean displayIncomingCallButtons() { + public boolean displayIncomingCallButtons() { return isIncoming(); } - boolean isEarpieceAvailableForAudioToggle() { + public boolean isEarpieceAvailableForAudioToggle() { return !isLocalVideoEnabled; } - boolean isBluetoothHeadsetAvailableForAudioToggle() { + public boolean isBluetoothHeadsetAvailableForAudioToggle() { return availableDevices.contains(SignalAudioManager.AudioDevice.BLUETOOTH); } - boolean isWiredHeadsetAvailableForAudioToggle() { + public boolean isWiredHeadsetAvailableForAudioToggle() { return availableDevices.contains(SignalAudioManager.AudioDevice.WIRED_HEADSET); } - boolean isFadeOutEnabled() { + public boolean isFadeOutEnabled() { return isAtLeastOutgoing() && isRemoteVideoEnabled && callState != CallState.RECONNECTING; } - boolean displaySmallOngoingCallButtons() { - return isAtLeastOutgoing() && displayAudioToggle() && displayCameraToggle(); + public boolean displaySmallCallButtons() { + return displayedButtonCount() >= 5; } - boolean displayLargeOngoingCallButtons() { - return isAtLeastOutgoing() && !(displayAudioToggle() && displayCameraToggle()); + public boolean displayTopViews() { + return !isInPipMode; } - boolean displayTopViews() { + public boolean displayReactions() { return !isInPipMode; } - @NonNull WebRtcAudioOutput getAudioOutput() { + public boolean displayRaiseHand() { + return FeatureFlags.groupCallRaiseHand() && !isInPipMode; + } + + public @NonNull WebRtcAudioOutput getAudioOutput() { switch (activeDevice) { case SPEAKER_PHONE: return WebRtcAudioOutput.SPEAKER; @@ -219,15 +233,15 @@ boolean displayTopViews() { } } - boolean showSmallHeader() { + public boolean showSmallHeader() { return isAtLeastOutgoing(); } - boolean showFullScreenShade() { + public boolean showFullScreenShade() { return isPreJoin() || isIncoming(); } - boolean displayRingToggle() { + public boolean displayRingToggle() { return isPreJoin() && isGroupCall() && !isCallLink && !hasAtLeastOneRemote; } @@ -255,6 +269,16 @@ private boolean isGroupCall() { return groupCallState != GroupCallState.NONE; } + private int displayedButtonCount() { + return (displayAudioToggle() ? 1 : 0) + + (displayCameraToggle() ? 1 : 0) + + (displayVideoToggle() ? 1 : 0) + + (displayMuteAudio() ? 1 : 0) + + (displayRingToggle() ? 1 : 0) + + (displayOverflow() ? 1 : 0) + + (displayEndCall() ? 1 : 0); + } + public enum CallState { NONE, ERROR, diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcLocalRenderState.java b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcLocalRenderState.java index f23df83ec9..b2de4757ef 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcLocalRenderState.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcLocalRenderState.java @@ -6,5 +6,9 @@ public enum WebRtcLocalRenderState { SMALLER_RECTANGLE, LARGE, LARGE_NO_VIDEO, - EXPANDED + EXPANDED; + + public boolean isAnySmall() { + return this == SMALL_RECTANGLE || this == SMALLER_RECTANGLE; + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcReactionsAlphaItemDecoration.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcReactionsAlphaItemDecoration.kt new file mode 100644 index 0000000000..c6fb0f58d3 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcReactionsAlphaItemDecoration.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.components.webrtc + +import android.graphics.Canvas +import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.ItemDecoration + +/** + * This fades the top 2 reactions slightly inside their recyclerview. + */ +class WebRtcReactionsAlphaItemDecoration : ItemDecoration() { + override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { + for (i in 0..parent.childCount) { + val child = parent.getChildAt(i) ?: continue + when (parent.getChildAdapterPosition(child)) { + WebRtcReactionsRecyclerAdapter.MAX_REACTION_NUMBER - 1 -> child.alpha = 0.7f + WebRtcReactionsRecyclerAdapter.MAX_REACTION_NUMBER - 2 -> child.alpha = 0.9f + else -> child.alpha = 1f + } + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcReactionsItemAnimator.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcReactionsItemAnimator.kt new file mode 100644 index 0000000000..4b281b9cc3 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcReactionsItemAnimator.kt @@ -0,0 +1,235 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.components.webrtc + +import android.animation.Animator +import android.animation.ValueAnimator +import android.os.Build +import android.view.animation.AccelerateInterpolator +import android.view.animation.DecelerateInterpolator +import android.view.animation.Interpolator +import androidx.core.animation.doOnEnd +import androidx.recyclerview.widget.RecyclerView +import org.signal.core.util.logging.Log + +/** + * Reactions item animator based on [org.thoughtcrime.securesms.conversation.mutiselect.ConversationItemAnimator] + */ +class WebRtcReactionsItemAnimator : RecyclerView.ItemAnimator() { + + private data class TweeningInfo( + val property: AnimatedProperty, + val interpolator: Interpolator, + val startValue: Float, + val endValue: Float + ) { + private val range = endValue - startValue + + fun calculateCurrentValue(progress: Float): Float { + val interpolatedProgress = interpolator.getInterpolation(progress) + return startValue + (interpolatedProgress * range) + } + } + + private data class AnimationInfo( + val sharedAnimator: ValueAnimator, + val tweeningInfos: List + ) + + private enum class AnimatedProperty { + TRANSLATION_Y, + ALPHA + } + + private val accelerateInterpolator: Interpolator = AccelerateInterpolator() + private val decelerateInterpolator: Interpolator = DecelerateInterpolator() + + private val pendingAnimations: MutableMap> = mutableMapOf() + private val activeAnimations: MutableMap = mutableMapOf() + + override fun animateDisappearance(viewHolder: RecyclerView.ViewHolder, preLayoutInfo: ItemHolderInfo, postLayoutInfo: ItemHolderInfo?): Boolean { + if (!pendingAnimations.containsKey(viewHolder) && + !activeAnimations.containsKey(viewHolder) + ) { + val existingAnimations = pendingAnimations[viewHolder]?.toMutableList() ?: mutableListOf() + + val startingAlpha = when (viewHolder.layoutPosition) { + WebRtcReactionsRecyclerAdapter.MAX_REACTION_NUMBER - 2 -> 0.7f + WebRtcReactionsRecyclerAdapter.MAX_REACTION_NUMBER - 3 -> 0.9f + else -> 1f + } + existingAnimations.add(TweeningInfo(AnimatedProperty.ALPHA, accelerateInterpolator, startingAlpha, 0f)) + existingAnimations.add(TweeningInfo(AnimatedProperty.TRANSLATION_Y, accelerateInterpolator, 0f, (preLayoutInfo.top - preLayoutInfo.bottom) / 2f)) + + pendingAnimations[viewHolder] = existingAnimations + dispatchAnimationStarted(viewHolder) + return true + } + + dispatchAnimationFinished(viewHolder) + return false + } + + override fun animateAppearance(viewHolder: RecyclerView.ViewHolder, preLayoutInfo: ItemHolderInfo?, postLayoutInfo: ItemHolderInfo): Boolean { + if (viewHolder.absoluteAdapterPosition > 1) { + dispatchAnimationFinished(viewHolder) + return false + } + val existingAnimations = pendingAnimations[viewHolder]?.toMutableList() ?: mutableListOf() + existingAnimations.add(TweeningInfo(AnimatedProperty.ALPHA, accelerateInterpolator, 0f, 1f)) + pendingAnimations[viewHolder] = existingAnimations + return animateSlideIn(viewHolder, preLayoutInfo, postLayoutInfo) + } + + private fun animateSlideIn(viewHolder: RecyclerView.ViewHolder, preLayoutInfo: ItemHolderInfo?, postLayoutInfo: ItemHolderInfo): Boolean { + if (activeAnimations.containsKey(viewHolder)) { + dispatchAnimationFinished(viewHolder) + return false + } + + val translationY = if (preLayoutInfo == null) { + postLayoutInfo.bottom - postLayoutInfo.top + } else { + preLayoutInfo.top - postLayoutInfo.top + }.toFloat() + + if (translationY == 0f) { + viewHolder.itemView.translationY = 0f + dispatchAnimationFinished(viewHolder) + return false + } + + viewHolder.itemView.translationY = translationY + + val existingAnimations = pendingAnimations[viewHolder]?.toMutableList() ?: mutableListOf() + existingAnimations.add(TweeningInfo(AnimatedProperty.TRANSLATION_Y, decelerateInterpolator, translationY, 0f)) + pendingAnimations[viewHolder] = existingAnimations + dispatchAnimationStarted(viewHolder) + + return true + } + + override fun animatePersistence(viewHolder: RecyclerView.ViewHolder, preLayoutInfo: ItemHolderInfo, postLayoutInfo: ItemHolderInfo): Boolean { + return if (pendingAnimations.contains(viewHolder) || activeAnimations.containsKey(viewHolder)) { + dispatchAnimationFinished(viewHolder) + false + } else { + animateSlideIn(viewHolder, preLayoutInfo, postLayoutInfo) + } + } + + override fun animateChange(oldHolder: RecyclerView.ViewHolder, newHolder: RecyclerView.ViewHolder, preLayoutInfo: ItemHolderInfo, postLayoutInfo: ItemHolderInfo): Boolean { + if (oldHolder != newHolder) { + dispatchAnimationFinished(oldHolder) + } + + return animatePersistence(newHolder, preLayoutInfo, postLayoutInfo) + } + + override fun runPendingAnimations() { + Log.d(TAG, "Starting ${pendingAnimations.size} animations.") + runPendingAnimationsInternal() + } + + private fun runPendingAnimationsInternal() { + activeAnimations.filter { pendingAnimations.containsKey(it.key) } + .forEach { + it.value.sharedAnimator.end() + handleAnimationEnd(it.key) + } + val animator = ValueAnimator.ofFloat(0f, 1f) + + animator.duration = ANIMATION_DURATION + animator.addUpdateListener { + activeAnimationsByAnimator(it).forEach { (viewHolder, animationInfo) -> + val itemView = viewHolder.itemView + animationInfo.tweeningInfos.forEach { tween -> + val currentValue = tween.calculateCurrentValue(it.animatedFraction) + when (tween.property) { + AnimatedProperty.TRANSLATION_Y -> itemView.translationY = currentValue + AnimatedProperty.ALPHA -> { + if (Build.VERSION.SDK_INT >= 29) { + itemView.transitionAlpha = currentValue + } else { + itemView.alpha = currentValue + } + } + } + } + } + } + + animator.doOnEnd { + activeAnimationsByAnimator(it) + .forEach { (viewHolder, _) -> + handleAnimationEnd(viewHolder) + } + } + + animator.start() + + activeAnimations.putAll(pendingAnimations.mapValues { AnimationInfo(animator, it.value) }) + + pendingAnimations.clear() + } + + private fun handleAnimationEnd(viewHolder: RecyclerView.ViewHolder) { + viewHolder.itemView.translationY = 0f + if (Build.VERSION.SDK_INT >= 29) { + viewHolder.itemView.transitionAlpha = 1f + } else { + viewHolder.itemView.alpha = 1f + } + activeAnimations.remove(viewHolder) + + dispatchAnimationFinished(viewHolder) + dispatchFinishedWhenDone() + } + + override fun endAnimation(item: RecyclerView.ViewHolder) { + endSlideAnimation(item) + } + + override fun endAnimations() { + endSlideAnimations() + dispatchAnimationsFinished() + } + + override fun isRunning(): Boolean { + return activeAnimations.values.any { it.sharedAnimator.isRunning } + } + + override fun onAnimationFinished(viewHolder: RecyclerView.ViewHolder) { + val parent = (viewHolder.itemView.parent as? RecyclerView) + parent?.post { parent.invalidate() } + } + + private fun endSlideAnimation(item: RecyclerView.ViewHolder) { + activeAnimations[item]?.sharedAnimator?.cancel() + } + + private fun endSlideAnimations() { + activeAnimations.values.map { it.sharedAnimator }.forEach { + it.cancel() + } + } + + private fun dispatchFinishedWhenDone() { + if (!isRunning) { + Log.d(TAG, "Finished running animations.") + dispatchAnimationsFinished() + } + } + + private fun activeAnimationsByAnimator(it: Animator): Map { + return activeAnimations.filterValues { animationInfo: AnimationInfo -> animationInfo.sharedAnimator == it } + } + + companion object { + private val TAG = Log.tag(WebRtcReactionsItemAnimator::class.java) + private const val ANIMATION_DURATION = 150L + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcReactionsRecyclerAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcReactionsRecyclerAdapter.kt new file mode 100644 index 0000000000..d82413e045 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcReactionsRecyclerAdapter.kt @@ -0,0 +1,80 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.components.webrtc + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.components.emoji.EmojiImageView +import org.thoughtcrime.securesms.components.emoji.EmojiTextView +import org.thoughtcrime.securesms.events.GroupCallReactionEvent +import org.thoughtcrime.securesms.recipients.Recipient + +/** + * RecyclerView adapter for the reactions feed. This takes in a list of [GroupCallReactionEvent] and renders them onto the screen. + * This adapter also encapsulates logic for whether the reaction should be displayed, such as expiration and maximum visible count. + */ +class WebRtcReactionsRecyclerAdapter : ListAdapter(DiffCallback()) { + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.webrtc_call_reaction_recycler_item, parent, false)) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val item = getItem(position) + holder.bind(item) + } + + override fun submitList(list: MutableList?) { + if (list == null) { + super.submitList(null) + } else { + super.submitList( + list.filter { it.getExpirationTimestamp() > System.currentTimeMillis() } + .sortedBy { it.timestamp } + .takeLast(MAX_REACTION_NUMBER) + .reversed() + ) + } + } + + class ViewHolder(val itemView: View) : RecyclerView.ViewHolder(itemView) { + private val emojiView: EmojiImageView = itemView.findViewById(R.id.webrtc_call_reaction_emoji_view) + private val textView: EmojiTextView = itemView.findViewById(R.id.webrtc_call_reaction_name_textview) + fun bind(item: GroupCallReactionEvent) { + emojiView.setImageEmoji(item.reaction) + textView.text = getName(item.sender) + itemView.isClickable = false + textView.isClickable = false + emojiView.isClickable = false + } + + private fun getName(recipient: Recipient): String { + return if (recipient.isSelf) { + itemView.context.getString(R.string.CallParticipant__you) + } else { + recipient.getDisplayName(itemView.context) + } + } + } + + private class DiffCallback : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: GroupCallReactionEvent, newItem: GroupCallReactionEvent): Boolean { + return oldItem == newItem + } + + override fun areContentsTheSame(oldItem: GroupCallReactionEvent, newItem: GroupCallReactionEvent): Boolean { + return oldItem == newItem + } + } + + companion object { + const val MAX_REACTION_NUMBER = 5 + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/CallInfoView.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/CallInfoView.kt new file mode 100644 index 0000000000..22083b910d --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/CallInfoView.kt @@ -0,0 +1,543 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.components.webrtc.controls + +import android.content.Context +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.defaultMinSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.livedata.observeAsState +import androidx.compose.runtime.remember +import androidx.compose.runtime.rxjava3.subscribeAsState +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalInspectionMode +import androidx.compose.ui.res.pluralStringResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.viewinterop.AndroidView +import androidx.lifecycle.toLiveData +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import io.reactivex.rxjava3.core.BackpressureStrategy +import io.reactivex.rxjava3.core.Observable +import org.signal.core.ui.Dividers +import org.signal.core.ui.Rows +import org.signal.core.ui.theme.SignalTheme +import org.signal.ringrtc.CallLinkState +import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.calls.links.SignalCallRow +import org.thoughtcrime.securesms.components.AvatarImageView +import org.thoughtcrime.securesms.components.webrtc.WebRtcCallViewModel +import org.thoughtcrime.securesms.dependencies.ApplicationDependencies +import org.thoughtcrime.securesms.events.CallParticipant +import org.thoughtcrime.securesms.events.GroupCallRaiseHandEvent +import org.thoughtcrime.securesms.events.WebRtcViewModel +import org.thoughtcrime.securesms.groups.ui.GroupMemberEntry +import org.thoughtcrime.securesms.recipients.Recipient + +/** + * Renders information about a call (1:1, group, or call link) and provides actions available for + * said call (e.g., raise hand, kick, etc) + */ +object CallInfoView { + + @Composable + fun View( + webRtcCallViewModel: WebRtcCallViewModel, + controlsAndInfoViewModel: ControlsAndInfoViewModel, + callbacks: Callbacks, + modifier: Modifier + ) { + val participantsState: ParticipantsState by webRtcCallViewModel.callParticipantsState + .toFlowable(BackpressureStrategy.LATEST) + .map { state -> + ParticipantsState( + inCallLobby = state.callState == WebRtcViewModel.State.CALL_PRE_JOIN, + ringGroup = state.ringGroup, + includeSelf = state.groupCallState === WebRtcViewModel.GroupCallState.CONNECTED_AND_JOINED || state.groupCallState === WebRtcViewModel.GroupCallState.IDLE, + participantCount = if (state.participantCount.isPresent) state.participantCount.asLong.toInt() else 0, + remoteParticipants = state.allRemoteParticipants.sortedBy { it.callParticipantId.recipientId }, + localParticipant = state.localParticipant, + groupMembers = state.groupMembers.filterNot { it.member.isSelf }, + callRecipient = state.recipient, + raisedHands = state.raisedHands + ) + } + .subscribeAsState(ParticipantsState()) + + val controlAndInfoState: ControlAndInfoState by controlsAndInfoViewModel.state + + val onEditNameClicked: () -> Unit = remember(controlAndInfoState) { + { + callbacks.onEditNameClicked(controlAndInfoState.callLink?.state?.name ?: "") + } + } + + SignalTheme( + isDarkMode = true + ) { + Surface { + CallInfo( + participantsState = participantsState, + controlAndInfoState = controlAndInfoState, + onShareLinkClicked = callbacks::onShareLinkClicked, + onEditNameClicked = onEditNameClicked, + onToggleAdminApprovalClicked = callbacks::onToggleAdminApprovalClicked, + onBlock = callbacks::onBlock, + modifier = modifier + ) + } + } + } + + interface Callbacks { + fun onShareLinkClicked() + fun onEditNameClicked(name: String) + fun onToggleAdminApprovalClicked(checked: Boolean) + fun onBlock(callParticipant: CallParticipant) + } +} + +@Preview +@Composable +private fun CallInfoPreview() { + SignalTheme(isDarkMode = true) { + Surface { + val remoteParticipants = listOf(CallParticipant(recipient = Recipient.UNKNOWN)) + CallInfo( + participantsState = ParticipantsState(remoteParticipants = remoteParticipants, raisedHands = remoteParticipants.map { GroupCallRaiseHandEvent(it.recipient, System.currentTimeMillis()) }), + controlAndInfoState = ControlAndInfoState(), + onShareLinkClicked = { }, + onEditNameClicked = { }, + onToggleAdminApprovalClicked = { }, + onBlock = { } + ) + } + } +} + +@Composable +private fun CallInfo( + participantsState: ParticipantsState, + controlAndInfoState: ControlAndInfoState, + onShareLinkClicked: () -> Unit, + onEditNameClicked: () -> Unit, + onToggleAdminApprovalClicked: (Boolean) -> Unit, + onBlock: (CallParticipant) -> Unit, + modifier: Modifier = Modifier +) { + val listState = rememberLazyListState() + + LaunchedEffect(controlAndInfoState.resetScrollState) { + listState.scrollToItem(0) + } + + LazyColumn( + state = listState, + horizontalAlignment = Alignment.CenterHorizontally, + modifier = modifier + ) { + item { + Text( + text = stringResource(id = R.string.CallLinkInfoSheet__call_info), + style = MaterialTheme.typography.titleLarge, + modifier = Modifier.padding(bottom = 24.dp) + ) + } + + if (controlAndInfoState.callLink != null) { + item { + SignalCallRow(callLink = controlAndInfoState.callLink, onJoinClicked = null) + + Rows.TextRow( + text = stringResource(id = R.string.CallLinkDetailsFragment__share_link), + icon = ImageVector.vectorResource(id = R.drawable.symbol_link_24), + iconModifier = Modifier + .background( + color = MaterialTheme.colorScheme.surfaceVariant, + shape = CircleShape + ) + .size(42.dp) + .padding(9.dp), + onClick = onShareLinkClicked, + modifier = Modifier + .defaultMinSize(minHeight = 64.dp) + ) + + Dividers.Default() + } + } + + if (participantsState.raisedHands.isNotEmpty()) { + item { + Box( + modifier = Modifier + .padding(horizontal = 24.dp) + .defaultMinSize(minHeight = 52.dp) + .fillMaxWidth(), + contentAlignment = Alignment.CenterStart + ) { + Text( + text = pluralStringResource(id = R.plurals.CallParticipantsListDialog__raised_hands, count = participantsState.raisedHands.size, participantsState.raisedHands.size), + style = MaterialTheme.typography.titleSmall + ) + } + } + + items( + items = participantsState.raisedHands, + key = { it.sender.id }, + contentType = { null } + ) { + HandRaisedRow(recipient = it.sender) + } + + item { + Dividers.Default() + } + } + + var includeAdminControlsDivider = true + if (controlAndInfoState.callLink == null || participantsState.isOngoing()) { + item { + Box( + modifier = Modifier + .padding(horizontal = 24.dp) + .defaultMinSize(minHeight = 52.dp) + .fillMaxWidth(), + contentAlignment = Alignment.CenterStart + ) { + Text( + text = getCallSheetLabel(participantsState), + style = MaterialTheme.typography.titleSmall + ) + } + } + } else { + includeAdminControlsDivider = false + } + + if (!participantsState.inCallLobby || participantsState.isOngoing()) { + items( + items = participantsState.participantsForList, + key = { it.callParticipantId }, + contentType = { null } + ) { + CallParticipantRow( + callParticipant = it, + isSelfAdmin = controlAndInfoState.isSelfAdmin() && !participantsState.inCallLobby, + onBlockClicked = onBlock + ) + } + } else if (participantsState.isGroupCall()) { + items( + items = participantsState.groupMembers, + key = { it.member.id.toLong() }, + contentType = { null } + ) { + GroupMemberRow( + groupMember = it, + isSelfAdmin = false + ) + } + } else if (controlAndInfoState.callLink == null) { + item { + CallParticipantRow( + initialRecipient = participantsState.callRecipient, + name = participantsState.callRecipient.getShortDisplayName(LocalContext.current), + showIcons = false, + isVideoEnabled = false, + isMicrophoneEnabled = false, + showHandRaised = false, + canLowerHand = false, + isSelfAdmin = false, + onBlockClicked = {} + ) + } + } + + if (controlAndInfoState.callLink?.credentials?.adminPassBytes != null) { + item { + if (includeAdminControlsDivider) { + Dividers.Default() + } + + Rows.TextRow( + text = stringResource(id = R.string.CallLinkDetailsFragment__add_call_name), + onClick = onEditNameClicked + ) + Rows.ToggleRow( + checked = controlAndInfoState.callLink.state.restrictions == CallLinkState.Restrictions.ADMIN_APPROVAL, + text = stringResource(id = R.string.CallLinkDetailsFragment__approve_all_members), + onCheckChanged = onToggleAdminApprovalClicked + ) + } + } + + item { + Spacer(modifier = Modifier.size(48.dp)) + } + } +} + +@Composable +private fun getCallSheetLabel(state: ParticipantsState): String { + return if (!state.inCallLobby || state.isOngoing()) { + pluralStringResource(id = R.plurals.CallParticipantsListDialog_in_this_call, count = state.participantCountForDisplay, state.participantCountForDisplay) + } else if (state.isGroupCall()) { + val groupSize = state.groupMembers.size + if (state.ringGroup) { + pluralStringResource(id = R.plurals.CallParticipantsListDialog__signal_will_ring, count = groupSize, groupSize) + } else { + pluralStringResource(id = R.plurals.CallParticipantsListDialog__signal_will_notify, count = groupSize, groupSize) + } + } else { + pluralStringResource(id = R.plurals.CallParticipantsListDialog__signal_will_ring, count = 1, 1) + } +} + +@Preview +@Composable +private fun CallParticipantRowPreview() { + SignalTheme(isDarkMode = true) { + Surface { + CallParticipantRow( + CallParticipant(recipient = Recipient.UNKNOWN), + isSelfAdmin = true + ) {} + } + } +} + +@Preview +@Composable +private fun HandRaisedRowPreview() { + SignalTheme(isDarkMode = true) { + Surface { + HandRaisedRow(Recipient.UNKNOWN, canLowerHand = true) + } + } +} + +@Composable +private fun CallParticipantRow( + callParticipant: CallParticipant, + isSelfAdmin: Boolean, + onBlockClicked: (CallParticipant) -> Unit +) { + CallParticipantRow( + initialRecipient = callParticipant.recipient, + name = callParticipant.getShortRecipientDisplayName(LocalContext.current), + showIcons = true, + isVideoEnabled = callParticipant.isVideoEnabled, + isMicrophoneEnabled = callParticipant.isMicrophoneEnabled, + showHandRaised = false, + canLowerHand = false, + isSelfAdmin = isSelfAdmin, + onBlockClicked = { onBlockClicked(callParticipant) } + ) +} + +@Composable +private fun HandRaisedRow(recipient: Recipient, canLowerHand: Boolean = recipient.isSelf) { + CallParticipantRow( + initialRecipient = recipient, + name = recipient.getShortDisplayName(LocalContext.current), + showIcons = true, + isVideoEnabled = true, + isMicrophoneEnabled = true, + showHandRaised = true, + canLowerHand = canLowerHand, + isSelfAdmin = false, + onBlockClicked = {} + ) +} + +@Composable +private fun CallParticipantRow( + initialRecipient: Recipient, + name: String, + showIcons: Boolean, + isVideoEnabled: Boolean, + isMicrophoneEnabled: Boolean, + showHandRaised: Boolean, + canLowerHand: Boolean, + isSelfAdmin: Boolean, + onBlockClicked: () -> Unit +) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(Rows.defaultPadding()) + ) { + val recipient by ((if (LocalInspectionMode.current) Observable.just(Recipient.UNKNOWN) else Recipient.observable(initialRecipient.id))) + .toFlowable(BackpressureStrategy.LATEST) + .toLiveData() + .observeAsState(initial = initialRecipient) + + if (LocalInspectionMode.current) { + Spacer( + modifier = Modifier + .size(40.dp) + .background(color = Color.Red, shape = CircleShape) + ) + } else { + AndroidView( + factory = ::AvatarImageView, + modifier = Modifier.size(40.dp) + ) { + it.setAvatarUsingProfile(recipient) + } + } + + Spacer(modifier = Modifier.width(24.dp)) + + Text( + text = name, + modifier = Modifier + .weight(1f) + .align(Alignment.CenterVertically) + ) + + if (showIcons && showHandRaised && canLowerHand) { + val context = LocalContext.current + TextButton(onClick = { + if (recipient.isSelf) { + showLowerHandDialog(context) + } + }) { + Text(text = stringResource(id = R.string.CallOverflowPopupWindow__lower_hand)) + } + Spacer(modifier = Modifier.width(16.dp)) + } + + if (showIcons && showHandRaised) { + Icon( + imageVector = ImageVector.vectorResource(id = R.drawable.symbol_raise_hand_24), + contentDescription = null, + modifier = Modifier.align(Alignment.CenterVertically) + ) + } + + if (showIcons && !isVideoEnabled) { + Icon( + imageVector = ImageVector.vectorResource(id = R.drawable.symbol_video_slash_24), + contentDescription = null, + modifier = Modifier.align(Alignment.CenterVertically) + ) + } + + if (showIcons && !isMicrophoneEnabled) { + if (!isVideoEnabled) { + Spacer(modifier = Modifier.width(16.dp)) + } + + Icon( + imageVector = ImageVector.vectorResource(id = R.drawable.symbol_mic_slash_24), + contentDescription = null, + modifier = Modifier.align(Alignment.CenterVertically) + ) + } + + if (showIcons && isSelfAdmin && !recipient.isSelf) { + if (!isMicrophoneEnabled) { + Spacer(modifier = Modifier.width(16.dp)) + } + + Icon( + imageVector = ImageVector.vectorResource(id = R.drawable.symbol_minus_circle_24), + contentDescription = null, + modifier = Modifier + .clickable(onClick = onBlockClicked) + .align(Alignment.CenterVertically) + ) + } + } +} + +private fun showLowerHandDialog(context: Context) { + MaterialAlertDialogBuilder(context) + .setTitle(R.string.CallOverflowPopupWindow__lower_your_hand) + .setPositiveButton( + R.string.CallOverflowPopupWindow__lower_hand + ) { _, _ -> ApplicationDependencies.getSignalCallManager().raiseHand(false) } + .setNegativeButton(R.string.CallOverflowPopupWindow__cancel, null) + .show() +} + +@Composable +private fun GroupMemberRow( + groupMember: GroupMemberEntry.FullMember, + isSelfAdmin: Boolean +) { + CallParticipantRow( + initialRecipient = groupMember.member, + name = groupMember.member.getShortDisplayName(LocalContext.current), + showIcons = false, + isVideoEnabled = false, + isMicrophoneEnabled = false, + showHandRaised = false, + canLowerHand = false, + isSelfAdmin = isSelfAdmin + ) {} +} + +private data class ParticipantsState( + val inCallLobby: Boolean = false, + val ringGroup: Boolean = true, + val includeSelf: Boolean = false, + val participantCount: Int = 0, + val remoteParticipants: List = emptyList(), + val localParticipant: CallParticipant? = null, + val groupMembers: List = emptyList(), + val callRecipient: Recipient = Recipient.UNKNOWN, + val raisedHands: List = emptyList() +) { + + val participantsForList: List = if (includeSelf && localParticipant != null) { + listOf(localParticipant) + remoteParticipants + } else { + remoteParticipants + } + + val participantCountForDisplay: Int = if (participantCount == 0) { + participantsForList.size + } else { + participantCount + } + + fun isGroupCall(): Boolean { + return groupMembers.isNotEmpty() + } + + fun isOngoing(): Boolean { + return remoteParticipants.isNotEmpty() + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/ControlAndInfoState.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/ControlAndInfoState.kt new file mode 100644 index 0000000000..93a60a0c83 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/ControlAndInfoState.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.components.webrtc.controls + +import androidx.compose.runtime.Immutable +import org.thoughtcrime.securesms.database.CallLinkTable + +@Immutable +data class ControlAndInfoState( + val callLink: CallLinkTable.CallLink? = null, + val resetScrollState: Long = 0 +) { + fun isSelfAdmin(): Boolean { + return callLink?.credentials?.adminPassBytes != null + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/ControlsAndInfoController.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/ControlsAndInfoController.kt new file mode 100644 index 0000000000..1d87e98e20 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/ControlsAndInfoController.kt @@ -0,0 +1,465 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.components.webrtc.controls + +import android.content.ActivityNotFoundException +import android.content.Intent +import android.content.res.ColorStateList +import android.os.Handler +import android.view.View +import android.widget.FrameLayout +import android.widget.Toast +import androidx.annotation.IdRes +import androidx.annotation.Px +import androidx.compose.ui.Modifier +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.platform.ViewCompositionStrategy +import androidx.compose.ui.platform.rememberNestedScrollInteropConnection +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.constraintlayout.widget.ConstraintSet +import androidx.constraintlayout.widget.Guideline +import androidx.coordinatorlayout.widget.CoordinatorLayout +import androidx.core.app.ShareCompat +import androidx.core.content.ContextCompat +import androidx.transition.AutoTransition +import androidx.transition.TransitionManager +import androidx.transition.TransitionSet +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback +import com.google.android.material.bottomsheet.BottomSheetBehaviorHack +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.google.android.material.shape.CornerFamily +import com.google.android.material.shape.MaterialShapeDrawable +import com.google.android.material.shape.ShapeAppearanceModel +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.disposables.Disposable +import io.reactivex.rxjava3.kotlin.addTo +import io.reactivex.rxjava3.kotlin.subscribeBy +import org.signal.core.util.dp +import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.WebRtcCallActivity +import org.thoughtcrime.securesms.calls.links.CallLinks +import org.thoughtcrime.securesms.calls.links.EditCallLinkNameDialogFragment +import org.thoughtcrime.securesms.calls.links.EditCallLinkNameDialogFragmentArgs +import org.thoughtcrime.securesms.components.InsetAwareConstraintLayout +import org.thoughtcrime.securesms.components.webrtc.CallOverflowPopupWindow +import org.thoughtcrime.securesms.components.webrtc.WebRtcCallView +import org.thoughtcrime.securesms.components.webrtc.WebRtcCallViewModel +import org.thoughtcrime.securesms.components.webrtc.WebRtcControls +import org.thoughtcrime.securesms.dependencies.ApplicationDependencies +import org.thoughtcrime.securesms.events.CallParticipant +import org.thoughtcrime.securesms.service.webrtc.links.UpdateCallLinkResult +import org.thoughtcrime.securesms.util.padding +import org.thoughtcrime.securesms.util.visible +import kotlin.math.max +import kotlin.time.Duration.Companion.seconds + +/** + * Brain for rendering the call controls and info within a bottom sheet. + */ +class ControlsAndInfoController( + private val webRtcCallActivity: WebRtcCallActivity, + private val webRtcCallView: WebRtcCallView, + private val overflowPopupWindow: CallOverflowPopupWindow, + private val viewModel: WebRtcCallViewModel, + private val controlsAndInfoViewModel: ControlsAndInfoViewModel +) : CallInfoView.Callbacks, Disposable { + + companion object { + private val TAG = Log.tag(ControlsAndInfoController::class.java) + + private const val CONTROL_FADE_OUT_START = 0f + private const val CONTROL_FADE_OUT_DONE = 0.23f + private const val INFO_FADE_IN_START = CONTROL_FADE_OUT_DONE + private const val INFO_FADE_IN_DONE = 0.8f + private const val CONTROL_TRANSITION_DURATION = 250L + + private val HIDE_CONTROL_DELAY = 5.seconds.inWholeMilliseconds + } + + private val disposables = CompositeDisposable() + + private val coordinator: CoordinatorLayout + private val frame: FrameLayout + private val behavior: BottomSheetBehavior + private val callInfoComposeView: ComposeView + private val raiseHandComposeView: ComposeView + private val callControls: ConstraintLayout + private val aboveControlsGuideline: Guideline + private val bottomSheetVisibilityListeners = mutableSetOf() + private val scheduleHideControlsRunnable: Runnable = Runnable { onScheduledHide() } + private val handler: Handler? + get() = webRtcCallView.handler + + private var previousCallControlHeight = 0 + private var controlPeakHeight = 0 + private var controlState: WebRtcControls = WebRtcControls.NONE + + init { + val infoTranslationDistance = 24f.dp + coordinator = webRtcCallView.findViewById(R.id.call_controls_info_coordinator) + frame = webRtcCallView.findViewById(R.id.call_controls_info_parent) + behavior = BottomSheetBehavior.from(frame) + callInfoComposeView = webRtcCallView.findViewById(R.id.call_info_compose) + callControls = webRtcCallView.findViewById(R.id.call_controls_constraint_layout) + raiseHandComposeView = webRtcCallView.findViewById(R.id.call_screen_raise_hand_view) + aboveControlsGuideline = webRtcCallView.findViewById(R.id.call_screen_above_controls_guideline) + + callInfoComposeView.apply { + setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) + setContent { + val nestedScrollInterop = rememberNestedScrollInteropConnection() + CallInfoView.View(viewModel, controlsAndInfoViewModel, this@ControlsAndInfoController, Modifier.nestedScroll(nestedScrollInterop)) + } + } + + callInfoComposeView.alpha = 0f + callInfoComposeView.translationY = infoTranslationDistance + + raiseHandComposeView.apply { + setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) + setContent { + RaiseHandSnackbar.View(viewModel, showCallInfoListener = ::showCallInfo) + } + } + + frame.background = MaterialShapeDrawable( + ShapeAppearanceModel.builder() + .setTopLeftCorner(CornerFamily.ROUNDED, 18.dp.toFloat()) + .setTopRightCorner(CornerFamily.ROUNDED, 18.dp.toFloat()) + .build() + ).apply { + fillColor = ColorStateList.valueOf(ContextCompat.getColor(webRtcCallActivity, R.color.signal_colorSurface)) + } + + behavior.isHideable = true + behavior.peekHeight = 0 + behavior.state = BottomSheetBehavior.STATE_HIDDEN + BottomSheetBehaviorHack.setNestedScrollingChild(behavior, callInfoComposeView) + + coordinator.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ -> + webRtcCallView.post { onControlTopChanged() } + } + + raiseHandComposeView.addOnLayoutChangeListener { _, _, top, _, bottom, _, _, _, _ -> + onControlTopChanged(composeViewSize = bottom - top) + } + + callControls.viewTreeObserver.addOnGlobalLayoutListener { + if (callControls.height > 0 && callControls.height != previousCallControlHeight) { + previousCallControlHeight = callControls.height + controlPeakHeight = callControls.height + callControls.y.toInt() + behavior.peekHeight = controlPeakHeight + frame.minimumHeight = coordinator.height / 2 + behavior.maxHeight = (coordinator.height.toFloat() * 0.66f).toInt() + + webRtcCallView.post { onControlTopChanged() } + } + } + + behavior.addBottomSheetCallback(object : BottomSheetCallback() { + override fun onStateChanged(bottomSheet: View, newState: Int) { + overflowPopupWindow.dismiss() + if (newState == BottomSheetBehavior.STATE_COLLAPSED) { + controlsAndInfoViewModel.resetScrollState() + if (controlState.isFadeOutEnabled) { + hide(delay = HIDE_CONTROL_DELAY) + } + } else if (newState == BottomSheetBehavior.STATE_EXPANDED || newState == BottomSheetBehavior.STATE_DRAGGING) { + cancelScheduledHide() + } else if (newState == BottomSheetBehavior.STATE_HIDDEN) { + controlsAndInfoViewModel.resetScrollState() + } + } + + override fun onSlide(bottomSheet: View, slideOffset: Float) { + callControls.alpha = alphaControls(slideOffset) + callControls.visible = callControls.alpha > 0f + + callInfoComposeView.alpha = alphaCallInfo(slideOffset) + callInfoComposeView.translationY = infoTranslationDistance - (infoTranslationDistance * callInfoComposeView.alpha) + + onControlTopChanged() + } + }) + + webRtcCallView.addWindowInsetsListener(object : InsetAwareConstraintLayout.WindowInsetsListener { + override fun onApplyWindowInsets(statusBar: Int, navigationBar: Int, parentStart: Int, parentEnd: Int) { + if (navigationBar > 0) { + callControls.padding(bottom = navigationBar) + } + } + }) + + overflowPopupWindow.setOnDismissListener { + hide(delay = HIDE_CONTROL_DELAY) + } + + webRtcCallActivity + .supportFragmentManager + .setFragmentResultListener(EditCallLinkNameDialogFragment.RESULT_KEY, webRtcCallActivity) { resultKey, bundle -> + if (bundle.containsKey(resultKey)) { + setName(bundle.getString(resultKey)!!) + } + } + } + + fun onControlTopChanged(composeViewSize: Int = raiseHandComposeView.height) { + val guidelineTop = max(frame.top, coordinator.height - behavior.peekHeight) + aboveControlsGuideline.setGuidelineBegin(guidelineTop) + webRtcCallView.onControlTopChanged(guidelineTop, composeViewSize) + } + + fun addVisibilityListener(listener: BottomSheetVisibilityListener): Boolean { + return bottomSheetVisibilityListeners.add(listener) + } + + fun showCallInfo() { + cancelScheduledHide() + behavior.isHideable = false + behavior.state = BottomSheetBehavior.STATE_EXPANDED + } + + private fun showControls() { + cancelScheduledHide() + behavior.isHideable = false + behavior.state = BottomSheetBehavior.STATE_COLLAPSED + + bottomSheetVisibilityListeners.forEach { it.onShown() } + } + + private fun hide(delay: Long = 0L) { + if (delay == 0L) { + if (controlState.isFadeOutEnabled || controlState == WebRtcControls.PIP) { + behavior.isHideable = true + behavior.state = BottomSheetBehavior.STATE_HIDDEN + + bottomSheetVisibilityListeners.forEach { it.onHidden() } + } + } else { + cancelScheduledHide() + handler?.postDelayed(scheduleHideControlsRunnable, delay) + } + } + + fun toggleControls() { + if (behavior.state == BottomSheetBehavior.STATE_EXPANDED || behavior.state == BottomSheetBehavior.STATE_HIDDEN) { + showControls() + } else { + hide() + } + } + + fun toggleOverflowPopup() { + if (overflowPopupWindow.isShowing) { + overflowPopupWindow.dismiss() + } else { + cancelScheduledHide() + overflowPopupWindow.show(aboveControlsGuideline) + } + } + + fun updateControls(newControlState: WebRtcControls) { + val previousState = controlState + controlState = newControlState + + showOrHideControlsOnUpdate(previousState) + + if (controlState != WebRtcControls.PIP && controlState.controlVisibilitiesChanged(previousState)) { + updateControlVisibilities() + } + } + + fun restartHideControlsTimer() { + hide(delay = HIDE_CONTROL_DELAY) + } + + private fun showOrHideControlsOnUpdate(previousState: WebRtcControls) { + if (controlState == WebRtcControls.PIP) { + hide() + return + } + + if (controlState.hideControlsSheetInitially()) { + return + } + + if (previousState.hideControlsSheetInitially() && (previousState != WebRtcControls.PIP)) { + showControls() + return + } + + if (controlState.isFadeOutEnabled) { + if (!previousState.isFadeOutEnabled) { + hide(delay = HIDE_CONTROL_DELAY) + } + } else { + cancelScheduledHide() + if (behavior.state != BottomSheetBehavior.STATE_EXPANDED) { + showControls() + } + } + } + + private fun updateControlVisibilities() { + TransitionManager.endTransitions(callControls) + TransitionManager.beginDelayedTransition( + callControls, + AutoTransition().apply { + ordering = TransitionSet.ORDERING_TOGETHER + duration = CONTROL_TRANSITION_DURATION + } + ) + + val constraints = ConstraintSet().apply { + clone(callControls) + val margin = if (controlState.displaySmallCallButtons()) 4.dp else 8.dp + + setControlConstraints(R.id.call_screen_speaker_toggle, controlState.displayAudioToggle(), margin) + setControlConstraints(R.id.call_screen_camera_direction_toggle, controlState.displayCameraToggle(), margin) + setControlConstraints(R.id.call_screen_video_toggle, controlState.displayVideoToggle(), margin) + setControlConstraints(R.id.call_screen_audio_mic_toggle, controlState.displayMuteAudio(), margin) + setControlConstraints(R.id.call_screen_audio_ring_toggle, controlState.displayRingToggle(), margin) + setControlConstraints(R.id.call_screen_overflow_button, controlState.displayOverflow(), margin) + setControlConstraints(R.id.call_screen_end_call, controlState.displayEndCall(), margin) + } + + constraints.applyTo(callControls) + } + + private fun onScheduledHide() { + if (behavior.state != BottomSheetBehavior.STATE_EXPANDED && !isDisposed) { + hide() + } + } + + private fun cancelScheduledHide() { + handler?.removeCallbacks(scheduleHideControlsRunnable) + } + + private fun alphaControls(slideOffset: Float): Float { + return if (slideOffset <= CONTROL_FADE_OUT_START) { + 1f + } else if (slideOffset >= CONTROL_FADE_OUT_DONE) { + 0f + } else { + 1f - (1f * (slideOffset - CONTROL_FADE_OUT_START) / (CONTROL_FADE_OUT_DONE - CONTROL_FADE_OUT_START)) + } + } + + private fun alphaCallInfo(slideOffset: Float): Float { + return if (slideOffset >= INFO_FADE_IN_DONE) { + 1f + } else if (slideOffset <= INFO_FADE_IN_START) { + 0f + } else { + (1f * (slideOffset - INFO_FADE_IN_START) / (INFO_FADE_IN_DONE - INFO_FADE_IN_START)) + } + } + + override fun dispose() { + disposables.dispose() + } + + override fun isDisposed(): Boolean { + return disposables.isDisposed + } + + override fun onShareLinkClicked() { + val mimeType = Intent.normalizeMimeType("text/plain") + val shareIntent = ShareCompat.IntentBuilder(webRtcCallActivity) + .setText(CallLinks.url(controlsAndInfoViewModel.rootKeySnapshot)) + .setType(mimeType) + .createChooserIntent() + + try { + webRtcCallActivity.startActivity(shareIntent) + } catch (e: ActivityNotFoundException) { + Toast.makeText(webRtcCallActivity, R.string.CreateCallLinkBottomSheetDialogFragment__failed_to_open_share_sheet, Toast.LENGTH_LONG).show() + } + } + + override fun onEditNameClicked(name: String) { + EditCallLinkNameDialogFragment().apply { + arguments = EditCallLinkNameDialogFragmentArgs.Builder(name).build().toBundle() + }.show(webRtcCallActivity.supportFragmentManager, null) + } + + override fun onToggleAdminApprovalClicked(checked: Boolean) { + controlsAndInfoViewModel.setApproveAllMembers(checked) + .observeOn(AndroidSchedulers.mainThread()) + .subscribeBy(onSuccess = { + if (it !is UpdateCallLinkResult.Success) { + Log.w(TAG, "Failed to change restrictions. $it") + toastFailure() + } + }, onError = handleError("onApproveAllMembersChanged")) + .addTo(disposables) + } + + override fun onBlock(callParticipant: CallParticipant) { + MaterialAlertDialogBuilder(webRtcCallActivity) + .setNegativeButton(android.R.string.cancel, null) + .setMessage(webRtcCallView.resources.getString(R.string.CallLinkInfoSheet__remove_s_from_the_call, callParticipant.recipient.getShortDisplayName(webRtcCallActivity))) + .setPositiveButton(R.string.CallLinkInfoSheet__remove) { _, _ -> + ApplicationDependencies.getSignalCallManager().removeFromCallLink(callParticipant) + } + .setNeutralButton(R.string.CallLinkInfoSheet__block_from_call) { _, _ -> + ApplicationDependencies.getSignalCallManager().blockFromCallLink(callParticipant) + } + .show() + } + + private fun setName(name: String) { + controlsAndInfoViewModel.setName(name) + .observeOn(AndroidSchedulers.mainThread()) + .subscribeBy( + onSuccess = { + if (it !is UpdateCallLinkResult.Success) { + Log.w(TAG, "Failed to set name. $it") + toastFailure() + } + }, + onError = handleError("setName") + ) + .addTo(disposables) + } + + private fun handleError(method: String): (throwable: Throwable) -> Unit { + return { + Log.w(TAG, "Failure during $method", it) + toastFailure() + } + } + + private fun toastFailure() { + Toast.makeText(webRtcCallActivity, R.string.CallLinkDetailsFragment__couldnt_save_changes, Toast.LENGTH_LONG).show() + } + + private fun ConstraintSet.setControlConstraints(@IdRes viewId: Int, visible: Boolean, @Px horizontalMargins: Int) { + setVisibility(viewId, if (visible) View.VISIBLE else View.GONE) + setMargin(viewId, ConstraintSet.START, horizontalMargins) + setMargin(viewId, ConstraintSet.END, horizontalMargins) + } + + private fun WebRtcControls.controlVisibilitiesChanged(previousState: WebRtcControls): Boolean { + return displayAudioToggle() != previousState.displayAudioToggle() || + displayCameraToggle() != previousState.displayCameraToggle() || + displayVideoToggle() != previousState.displayVideoToggle() || + displayMuteAudio() != previousState.displayMuteAudio() || + displayRingToggle() != previousState.displayRingToggle() || + displayOverflow() != previousState.displayOverflow() || + displayEndCall() != previousState.displayEndCall() + } + + interface BottomSheetVisibilityListener { + fun onShown() + fun onHidden() + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/ControlsAndInfoViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/ControlsAndInfoViewModel.kt new file mode 100644 index 0000000000..635e08a823 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/ControlsAndInfoViewModel.kt @@ -0,0 +1,73 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.components.webrtc.controls + +import androidx.compose.runtime.State +import androidx.compose.runtime.mutableStateOf +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import io.reactivex.rxjava3.core.Single +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign +import io.reactivex.rxjava3.kotlin.subscribeBy +import org.signal.ringrtc.CallLinkState +import org.thoughtcrime.securesms.calls.links.CallLinks +import org.thoughtcrime.securesms.calls.links.UpdateCallLinkRepository +import org.thoughtcrime.securesms.calls.links.details.CallLinkDetailsRepository +import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.recipients.RecipientId +import org.thoughtcrime.securesms.service.webrtc.links.UpdateCallLinkResult + +/** + * Provides a view model communicating with the Controls and Info view, [CallInfoView]. + */ +class ControlsAndInfoViewModel( + private val repository: CallLinkDetailsRepository = CallLinkDetailsRepository(), + private val mutationRepository: UpdateCallLinkRepository = UpdateCallLinkRepository() +) : ViewModel() { + private val disposables = CompositeDisposable() + private var callRecipientId: RecipientId? = null + private val _state = mutableStateOf(ControlAndInfoState()) + val state: State = _state + + val rootKeySnapshot: ByteArray + get() = state.value.callLink?.credentials?.linkKeyBytes ?: error("Call link not loaded yet.") + + fun setRecipient(recipient: Recipient) { + if (recipient.isCallLink && callRecipientId != recipient.id) { + callRecipientId = recipient.id + disposables += repository.refreshCallLinkState(recipient.requireCallLinkRoomId()) + disposables += CallLinks.watchCallLink(recipient.requireCallLinkRoomId()).subscribeBy { + _state.value = _state.value.copy(callLink = it) + } + } + } + + override fun onCleared() { + super.onCleared() + disposables.dispose() + } + + fun resetScrollState() { + _state.value = _state.value.copy(resetScrollState = System.currentTimeMillis()) + } + + fun setApproveAllMembers(approveAllMembers: Boolean): Single { + val credentials = _state.value.callLink?.credentials ?: error("User cannot change the name of this call.") + return mutationRepository.setCallRestrictions(credentials, if (approveAllMembers) CallLinkState.Restrictions.ADMIN_APPROVAL else CallLinkState.Restrictions.NONE) + } + + fun setName(name: String): Single { + val credentials = _state.value.callLink?.credentials ?: error("User cannot change the name of this call.") + return mutationRepository.setCallName(credentials, name) + } + + class Factory : ViewModelProvider.Factory { + override fun create(modelClass: Class): T { + return modelClass.cast(ControlsAndInfoViewModel()) as T + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/RaiseHandSnackbar.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/RaiseHandSnackbar.kt new file mode 100644 index 0000000000..cee9975ca0 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/RaiseHandSnackbar.kt @@ -0,0 +1,241 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.components.webrtc.controls + +import android.content.Context +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.animateContentSize +import androidx.compose.animation.expandIn +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.shrinkOut +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.wrapContentWidth +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rxjava3.subscribeAsState +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.pluralStringResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import io.reactivex.rxjava3.core.BackpressureStrategy +import kotlinx.coroutines.delay +import org.signal.core.ui.theme.SignalTheme +import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.components.webrtc.WebRtcCallViewModel +import org.thoughtcrime.securesms.dependencies.ApplicationDependencies +import org.thoughtcrime.securesms.events.GroupCallRaiseHandEvent +import org.thoughtcrime.securesms.recipients.Recipient +import java.util.concurrent.TimeUnit + +/** + * This is a UI element to display the status of one or more people with raised hands in a group call. + * It supports both an expanded and collapsed mode. + */ +object RaiseHandSnackbar { + const val TAG = "RaiseHandSnackbar" + private val COLLAPSE_DELAY_MS = TimeUnit.SECONDS.toMillis(4L) + + @Composable + fun View(webRtcCallViewModel: WebRtcCallViewModel, showCallInfoListener: () -> Unit, modifier: Modifier = Modifier) { + var expansionState by remember { mutableStateOf(ExpansionState(shouldExpand = false, forced = false)) } + + val webRtcState by webRtcCallViewModel.callParticipantsState + .toFlowable(BackpressureStrategy.LATEST) + .map { state -> + val raisedHands = state.raisedHands.sortedByDescending { it.timestamp } + val shouldExpand = RaiseHandState.shouldExpand(raisedHands) + if (!expansionState.forced) { + expansionState = ExpansionState(shouldExpand, false) + } + raisedHands + }.subscribeAsState(initial = emptyList()) + + val state by remember { + derivedStateOf { + RaiseHandState(raisedHands = webRtcState, expansionState = expansionState) + } + } + + LaunchedEffect(expansionState) { + delay(COLLAPSE_DELAY_MS) + expansionState = ExpansionState(shouldExpand = false, forced = false) + } + + RaiseHand(state, modifier, { expansionState = ExpansionState(shouldExpand = true, forced = true) }, showCallInfoListener = showCallInfoListener) + } +} + +@Preview +@Composable +private fun RaiseHandSnackbarPreview() { + RaiseHand( + state = RaiseHandState(listOf(GroupCallRaiseHandEvent(Recipient.UNKNOWN, System.currentTimeMillis()))) + ) +} + +@Composable +private fun RaiseHand( + state: RaiseHandState, + modifier: Modifier = Modifier, + setExpanded: (Boolean) -> Unit = {}, + showCallInfoListener: () -> Unit = {} +) { + AnimatedVisibility( + visible = state.raisedHands.isNotEmpty(), + enter = fadeIn() + expandIn(expandFrom = Alignment.CenterEnd), + exit = shrinkOut(shrinkTowards = Alignment.CenterEnd) + fadeOut() + ) { + SignalTheme( + isDarkMode = true + ) { + Surface( + modifier = modifier + .padding(horizontal = 16.dp) + .clip(shape = RoundedCornerShape(16.dp, 16.dp, 16.dp, 16.dp)) + .background(MaterialTheme.colorScheme.surface) + .animateContentSize() + ) { + val boxModifier = modifier + .padding(horizontal = 16.dp) + .clickable( + !state.isExpanded, + stringResource(id = R.string.CallOverflowPopupWindow__expand_snackbar_accessibility_label), + Role.Button + ) { setExpanded(true) } + + Box( + contentAlignment = Alignment.CenterStart, + modifier = if (state.isExpanded) { + boxModifier.fillMaxWidth() + } else { + boxModifier.wrapContentWidth() + } + ) { + Row(verticalAlignment = Alignment.CenterVertically) { + Icon( + imageVector = ImageVector.vectorResource(id = R.drawable.symbol_raise_hand_24), + contentDescription = null, + modifier = Modifier.align(Alignment.CenterVertically).padding(vertical = 8.dp) + ) + + Text( + text = getSnackbarText(state), + color = MaterialTheme.colorScheme.onSurface, + modifier = Modifier + .padding(start = 16.dp) + .weight(1f, fill = state.isExpanded) + .wrapContentWidth(Alignment.Start) + .padding(vertical = 16.dp) + ) + if (state.isExpanded) { + if (state.raisedHands.first().sender.isSelf) { + val context = LocalContext.current + TextButton( + onClick = { + showLowerHandDialog(context) + }, + modifier = Modifier.wrapContentWidth(Alignment.End) + ) { + Text(text = stringResource(id = R.string.CallOverflowPopupWindow__lower_hand), maxLines = 1) + } + } else { + TextButton( + onClick = showCallInfoListener, + modifier = Modifier.wrapContentWidth(Alignment.End) + ) { + Text(text = stringResource(id = R.string.CallOverflowPopupWindow__view), maxLines = 1) + } + } + } + } + } + } + } + } +} + +private fun showLowerHandDialog(context: Context) { + MaterialAlertDialogBuilder(context) + .setTitle(R.string.CallOverflowPopupWindow__lower_your_hand) + .setPositiveButton( + R.string.CallOverflowPopupWindow__lower_hand + ) { _, _ -> ApplicationDependencies.getSignalCallManager().raiseHand(false) } + .setNegativeButton(R.string.CallOverflowPopupWindow__cancel, null) + .show() +} + +@Composable +private fun getSnackbarText(state: RaiseHandState): String { + if (state.isEmpty) { + return "" + } + return if (!state.isExpanded) { + pluralStringResource(id = R.plurals.CallRaiseHandSnackbar_raised_hands, count = state.raisedHands.size, getShortDisplayName(state.raisedHands), state.raisedHands.size - 1) + } else { + if (state.raisedHands.size == 1 && state.raisedHands.first().sender.isSelf) { + stringResource(id = R.string.CallOverflowPopupWindow__you_raised_your_hand) + } else { + pluralStringResource(id = R.plurals.CallOverflowPopupWindow__raised_a_hand, count = state.raisedHands.size, state.raisedHands.first().sender.getShortDisplayName(LocalContext.current), state.raisedHands.size - 1) + } + } +} + +@Composable +private fun getShortDisplayName(raisedHands: List): String { + val recipient = raisedHands.first().sender + return if (recipient.isSelf) { + stringResource(id = R.string.CallParticipant__you) + } else { + recipient.getShortDisplayName(LocalContext.current) + } +} + +private data class RaiseHandState( + val raisedHands: List = emptyList(), + val expansionState: ExpansionState = ExpansionState(shouldExpand = false, forced = false) +) { + val isExpanded = expansionState.shouldExpand && raisedHands.isNotEmpty() + + val isEmpty = raisedHands.isEmpty() + + companion object { + @JvmStatic + fun shouldExpand(raisedHands: List): Boolean { + val now = System.currentTimeMillis() + return raisedHands.any { it.getCollapseTimestamp() > now } + } + } +} + +private data class ExpansionState( + val shouldExpand: Boolean, + val forced: Boolean +) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/participantslist/CallParticipantsListHeader.java b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/participantslist/CallParticipantsListHeader.java index d8327dd6b2..1405f173a3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/participantslist/CallParticipantsListHeader.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/participantslist/CallParticipantsListHeader.java @@ -16,7 +16,7 @@ public CallParticipantsListHeader(int participantCount) { } @NonNull String getHeader(@NonNull Context context) { - return context.getResources().getQuantityString(R.plurals.CallParticipantsListDialog_in_this_call_d_people, participantCount, participantCount); + return context.getResources().getQuantityString(R.plurals.CallParticipantsListDialog_in_this_call, participantCount, participantCount); } @Override diff --git a/app/src/main/java/org/thoughtcrime/securesms/compose/ComposeBottomSheetDialogFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/compose/ComposeBottomSheetDialogFragment.kt index 1539d5e1ff..9a892d6900 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/compose/ComposeBottomSheetDialogFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/compose/ComposeBottomSheetDialogFragment.kt @@ -5,6 +5,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.runtime.Composable import androidx.compose.ui.platform.ComposeView @@ -26,7 +27,11 @@ abstract class ComposeBottomSheetDialogFragment : FixedRoundedCornerBottomSheetD SignalTheme( isDarkMode = forceDarkTheme || DynamicTheme.isDarkTheme(LocalContext.current) ) { - Surface(shape = RoundedCornerShape(18.dp, 18.dp), color = SignalTheme.colors.colorSurface1) { + Surface( + shape = RoundedCornerShape(18.dp, 18.dp), + color = SignalTheme.colors.colorSurface1, + contentColor = MaterialTheme.colorScheme.onSurface + ) { SheetContent() } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/contacts/TurnOffContactJoinedNotificationsActivity.java b/app/src/main/java/org/thoughtcrime/securesms/contacts/TurnOffContactJoinedNotificationsActivity.java index adb6ae567f..b4a21ad87d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contacts/TurnOffContactJoinedNotificationsActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/contacts/TurnOffContactJoinedNotificationsActivity.java @@ -47,8 +47,8 @@ protected void onResume() { }) .setNegativeButton(android.R.string.cancel, ((dialog, which) -> { dialog.dismiss(); - finish(); })) + .setOnDismissListener(dialog -> finish()) .show(); } @@ -65,7 +65,6 @@ private void handlePositiveAction(@NonNull DialogInterface dialog) { return null; }, unused -> { dialog.dismiss(); - finish(); }); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchAdapter.kt index ff1cbbcf85..a8d2118483 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchAdapter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchAdapter.kt @@ -27,7 +27,6 @@ import org.thoughtcrime.securesms.contacts.avatars.GeneratedContactPhoto import org.thoughtcrime.securesms.database.model.DistributionListPrivacyMode import org.thoughtcrime.securesms.database.model.StoryViewState import org.thoughtcrime.securesms.keyvalue.SignalStore -import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.util.adapter.mapping.LayoutFactory import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter @@ -437,8 +436,7 @@ open class ContactSearchAdapter( number.text = recipient.combinedAboutAndEmoji number.visible = true } else if (displayOptions.displaySecondaryInformation == DisplaySecondaryInformation.ALWAYS && recipient.hasE164()) { - number.text = PhoneNumberFormatter.prettyPrint(recipient.requireE164()) - number.visible = true + number.visible = false } else { super.bindNumberField(model) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchKey.kt b/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchKey.kt index a30dc35739..08d91cd8db 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchKey.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchKey.kt @@ -34,7 +34,7 @@ sealed class ContactSearchKey { data class UnknownRecipientKey(val sectionKey: ContactSearchConfiguration.SectionKey, val query: String) : ContactSearchKey() { override fun requireSelectedContact(): SelectedContact = when (sectionKey) { - ContactSearchConfiguration.SectionKey.USERNAME -> SelectedContact.forPhone(null, query) + ContactSearchConfiguration.SectionKey.USERNAME -> SelectedContact.forUsername(null, query) ContactSearchConfiguration.SectionKey.PHONE_NUMBER -> SelectedContact.forPhone(null, query) else -> error("Unexpected section for unknown recipient: $sectionKey") } diff --git a/app/src/main/java/org/thoughtcrime/securesms/contacts/sync/ContactDiscovery.kt b/app/src/main/java/org/thoughtcrime/securesms/contacts/sync/ContactDiscovery.kt index a88f17ece4..04eb95c69b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contacts/sync/ContactDiscovery.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/contacts/sync/ContactDiscovery.kt @@ -206,7 +206,7 @@ object ContactDiscovery { .forEach { result -> val hour = Calendar.getInstance()[Calendar.HOUR_OF_DAY] if (hour in 9..22) { - ApplicationDependencies.getMessageNotifier().updateNotification(context, ConversationId.forConversation(result.threadId), true) + ApplicationDependencies.getMessageNotifier().updateNotification(context, ConversationId.forConversation(result.threadId)) } else { Log.i(TAG, "Not notifying of a new user due to the time of day. (Hour: $hour)") } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/AttachmentKeyboard.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/AttachmentKeyboard.java index 11aac58539..3d0180cf77 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/AttachmentKeyboard.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/AttachmentKeyboard.java @@ -28,9 +28,9 @@ public class AttachmentKeyboard extends FrameLayout implements InputAwareLayout. private static final List DEFAULT_BUTTONS = Arrays.asList( AttachmentKeyboardButton.GALLERY, AttachmentKeyboardButton.FILE, - AttachmentKeyboardButton.PAYMENT, AttachmentKeyboardButton.CONTACT, - AttachmentKeyboardButton.LOCATION + AttachmentKeyboardButton.LOCATION, + AttachmentKeyboardButton.PAYMENT ); private View container; diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationHeaderView.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationHeaderView.java index 328be627b5..6baf455bb0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationHeaderView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationHeaderView.java @@ -1,39 +1,38 @@ package org.thoughtcrime.securesms.conversation; import android.content.Context; +import android.graphics.PorterDuff; +import android.graphics.drawable.Drawable; import android.text.SpannableStringBuilder; import android.text.TextUtils; import android.util.AttributeSet; import android.view.View; -import android.widget.TextView; +import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.constraintlayout.widget.ConstraintLayout; +import androidx.core.content.ContextCompat; +import androidx.core.view.ViewKt; +import org.signal.core.util.DimensionUnit; import org.signal.core.util.concurrent.SignalExecutors; import org.thoughtcrime.securesms.R; -import org.thoughtcrime.securesms.badges.BadgeImageView; -import org.thoughtcrime.securesms.components.AvatarImageView; import org.thoughtcrime.securesms.components.emoji.EmojiTextView; import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto; import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto; import org.thoughtcrime.securesms.database.SignalDatabase; +import org.thoughtcrime.securesms.databinding.ConversationHeaderViewBinding; import org.thoughtcrime.securesms.mms.GlideRequests; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.util.ContextUtil; import org.thoughtcrime.securesms.util.LongClickMovementMethod; import org.thoughtcrime.securesms.util.SpanUtil; +import org.whispersystems.signalservice.api.util.Preconditions; public class ConversationHeaderView extends ConstraintLayout { - private AvatarImageView contactAvatar; - private TextView contactTitle; - private TextView contactAbout; - private TextView contactSubtitle; - private EmojiTextView contactDescription; - private View tapToView; - private BadgeImageView contactBadge; + private final ConversationHeaderViewBinding binding; public ConversationHeaderView(Context context) { this(context, null); @@ -46,38 +45,32 @@ public ConversationHeaderView(Context context, AttributeSet attrs) { public ConversationHeaderView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - inflate(getContext(), R.layout.conversation_banner_view, this); + inflate(getContext(), R.layout.conversation_header_view, this); - contactAvatar = findViewById(R.id.message_request_avatar); - contactBadge = findViewById(R.id.message_request_badge); - contactTitle = findViewById(R.id.message_request_title); - contactAbout = findViewById(R.id.message_request_about); - contactSubtitle = findViewById(R.id.message_request_subtitle); - contactDescription = findViewById(R.id.message_request_description); - tapToView = findViewById(R.id.message_request_avatar_tap_to_view); + binding = ConversationHeaderViewBinding.bind(this); - contactAvatar.setFallbackPhotoProvider(new FallbackPhotoProvider()); + binding.messageRequestAvatar.setFallbackPhotoProvider(new FallbackPhotoProvider()); } public void setBadge(@Nullable Recipient recipient) { if (recipient == null || recipient.isSelf()) { - contactBadge.setBadge(null); + binding.messageRequestBadge.setBadge(null); } else { - contactBadge.setBadgeFromRecipient(recipient); + binding.messageRequestBadge.setBadgeFromRecipient(recipient); } } public void setAvatar(@NonNull GlideRequests requests, @Nullable Recipient recipient) { - contactAvatar.setAvatar(requests, recipient, false); + binding.messageRequestAvatar.setAvatar(requests, recipient, false); if (recipient != null && recipient.shouldBlurAvatar() && recipient.getContactPhoto() != null) { - tapToView.setVisibility(VISIBLE); - tapToView.setOnClickListener(v -> { + binding.messageRequestAvatarTapToView.setVisibility(VISIBLE); + binding.messageRequestAvatarTapToView.setOnClickListener(v -> { SignalExecutors.BOUNDED.execute(() -> SignalDatabase.recipients().manuallyShowAvatar(recipient.getId())); }); } else { - tapToView.setVisibility(GONE); - tapToView.setOnClickListener(null); + binding.messageRequestAvatarTapToView.setVisibility(GONE); + binding.messageRequestAvatarTapToView.setOnClickListener(null); } } @@ -86,7 +79,7 @@ public String setTitle(@NonNull Recipient recipient) { if (recipient.showVerified()) { SpanUtil.appendCenteredImageSpan(title, ContextUtil.requireDrawable(getContext(), R.drawable.ic_official_28), 28, 28); } - contactTitle.setText(title); + binding.messageRequestTitle.setText(title); return title.toString(); } @@ -98,46 +91,83 @@ public void setAbout(@NonNull Recipient recipient) { about = recipient.getCombinedAboutAndEmoji(); } - contactAbout.setText(about); - contactAbout.setVisibility(TextUtils.isEmpty(about) ? GONE : VISIBLE); + binding.messageRequestAbout.setText(about); + binding.messageRequestAbout.setVisibility(TextUtils.isEmpty(about) ? GONE : VISIBLE); } - public void setSubtitle(@Nullable CharSequence subtitle) { - contactSubtitle.setText(subtitle); - contactSubtitle.setVisibility(TextUtils.isEmpty(subtitle) ? GONE : VISIBLE); + public void setSubtitle(@NonNull CharSequence subtitle, @DrawableRes int iconRes) { + if (TextUtils.isEmpty(subtitle)) { + hideSubtitle(); + return; + } + + binding.messageRequestSubtitle.setText(prependIcon(subtitle, iconRes)); + binding.messageRequestSubtitle.setVisibility(View.VISIBLE); } - public void setDescription(@Nullable CharSequence description) { - contactDescription.setText(description); - contactDescription.setVisibility(TextUtils.isEmpty(description) ? GONE : VISIBLE); + public void setDescription(@Nullable CharSequence description, @DrawableRes int iconRes) { + if (TextUtils.isEmpty(description)) { + hideDescription(); + return; + } + + binding.messageRequestDescription.setText(prependIcon(description, iconRes)); + binding.messageRequestDescription.setVisibility(View.VISIBLE); } public @NonNull EmojiTextView getDescription() { - return contactDescription; + return binding.messageRequestDescription; } public void showBackgroundBubble(boolean enabled) { if (enabled) { - setBackgroundResource(R.drawable.wallpaper_bubble_background_12); + setBackgroundResource(R.drawable.wallpaper_bubble_background_18); + binding.messageRequestInfoOutline.setVisibility(View.INVISIBLE); + binding.messageRequestDivider.setVisibility(View.VISIBLE); } else { setBackground(null); + binding.messageRequestInfoOutline.setVisibility(View.VISIBLE); + binding.messageRequestDivider.setVisibility(View.INVISIBLE); } + + hideDecoratorsIfContentIsNotPresent(); } public void hideSubtitle() { - contactSubtitle.setVisibility(View.GONE); + binding.messageRequestSubtitle.setVisibility(View.GONE); } public void showDescription() { - contactDescription.setVisibility(View.VISIBLE); + binding.messageRequestDescription.setVisibility(View.VISIBLE); } public void hideDescription() { - contactDescription.setVisibility(View.GONE); + binding.messageRequestDescription.setVisibility(View.GONE); } public void setLinkifyDescription(boolean enable) { - contactDescription.setMovementMethod(enable ? LongClickMovementMethod.getInstance(getContext()) : null); + binding.messageRequestDescription.setMovementMethod(enable ? LongClickMovementMethod.getInstance(getContext()) : null); + } + + private void hideDecoratorsIfContentIsNotPresent() { + if (ViewKt.isVisible(binding.messageRequestSubtitle) || ViewKt.isVisible(binding.messageRequestDescription)) { + return; + } + + binding.messageRequestInfoOutline.setVisibility(View.GONE); + binding.messageRequestDivider.setVisibility(View.GONE); + } + + private @NonNull CharSequence prependIcon(@NonNull CharSequence input, @DrawableRes int iconRes) { + Drawable drawable = ContextCompat.getDrawable(getContext(), iconRes); + Preconditions.checkNotNull(drawable); + drawable.setBounds(0, 0, (int) DimensionUnit.DP.toPixels(20), (int) DimensionUnit.DP.toPixels(20)); + drawable.setColorFilter(ContextCompat.getColor(getContext(), R.color.signal_colorOnSurface), PorterDuff.Mode.SRC_ATOP); + + return new SpannableStringBuilder() + .append(SpanUtil.buildCenteredImageSpan(drawable)) + .append(SpanUtil.space(8, DimensionUnit.DP)) + .append(input); } private static final class FallbackPhotoProvider extends Recipient.FallbackPhotoProvider { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java index 2493db8c2f..2ddd3de655 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java @@ -90,6 +90,8 @@ import org.thoughtcrime.securesms.components.emoji.EmojiTextView; import org.thoughtcrime.securesms.components.mention.MentionAnnotation; import org.thoughtcrime.securesms.contactshare.Contact; +import org.thoughtcrime.securesms.conversation.clicklisteners.AttachmentCancelClickListener; +import org.thoughtcrime.securesms.conversation.clicklisteners.ResendClickListener; import org.thoughtcrime.securesms.conversation.colors.Colorizer; import org.thoughtcrime.securesms.conversation.mutiselect.MultiselectCollection; import org.thoughtcrime.securesms.conversation.mutiselect.MultiselectPart; @@ -699,8 +701,6 @@ public void unbind() { bodyBubble.setVideoPlayerProjection(null); bodyBubble.setQuoteViewProjection(null); - playVideoClickListener.cleanup(); - glideRequests = null; } @@ -1070,7 +1070,7 @@ private void setMediaAttributes(@NonNull MessageRecord messageRecord, boolean messageRequestAccepted, boolean allowedToPlayInline) { - boolean showControls = !messageRecord.isFailed() && !MessageRecordUtil.isScheduled(messageRecord); + boolean showControls = messageRecord.isMediaPending() || (!messageRecord.isFailed() && !MessageRecordUtil.isScheduled(messageRecord)); ViewUtil.setTopMargin(bodyText, readDimen(R.dimen.message_bubble_top_padding)); @@ -1158,8 +1158,8 @@ private void setMediaAttributes(@NonNull MessageRecord messageRecord, mediaThumbnailStub.require().setMaximumThumbnailHeight(readDimen(R.dimen.media_bubble_max_height)); mediaThumbnailStub.require().setImageResource(glideRequests, Collections.singletonList(new ImageSlide(linkPreview.getThumbnail().get())), showControls, false); mediaThumbnailStub.require().setThumbnailClickListener(new LinkPreviewThumbnailClickListener()); - mediaThumbnailStub.require().setDownloadClickListener(downloadClickListener); - mediaThumbnailStub.require().setCancelDownloadClickListener(attachmentCancelClickListener); + mediaThumbnailStub.require().setStartTransferClickListener(downloadClickListener); + mediaThumbnailStub.require().setCancelTransferClickListener(attachmentCancelClickListener); mediaThumbnailStub.require().setPlayVideoClickListener(playVideoClickListener); mediaThumbnailStub.require().setOnLongClickListener(passthroughClickListener); @@ -1292,8 +1292,7 @@ private void setMediaAttributes(@NonNull MessageRecord messageRecord, : R.dimen.media_bubble_max_height)); mediaThumbnailStub.require().setThumbnailClickListener(new ThumbnailClickListener()); - mediaThumbnailStub.require().setDownloadClickListener(downloadClickListener); - mediaThumbnailStub.require().setCancelDownloadClickListener(attachmentCancelClickListener); + mediaThumbnailStub.require().setCancelTransferClickListener(attachmentCancelClickListener); mediaThumbnailStub.require().setPlayVideoClickListener(playVideoClickListener); mediaThumbnailStub.require().setOnLongClickListener(passthroughClickListener); mediaThumbnailStub.require().setOnClickListener(passthroughClickListener); @@ -1304,8 +1303,10 @@ private void setMediaAttributes(@NonNull MessageRecord messageRecord, false); if (!messageRecord.isOutgoing()) { mediaThumbnailStub.require().setConversationColor(getDefaultBubbleColor(hasWallpaper)); + mediaThumbnailStub.require().setStartTransferClickListener(downloadClickListener); } else { mediaThumbnailStub.require().setConversationColor(Color.TRANSPARENT); + mediaThumbnailStub.require().setStartTransferClickListener(new ResendClickListener(messageRecord)); } mediaThumbnailStub.require().setBorderless(false); @@ -1540,7 +1541,7 @@ private void linkifyMessageBody(@NonNull Spannable messageBody, private void setStatusIcons(MessageRecord messageRecord, boolean hasWallpaper) { bodyText.setCompoundDrawablesWithIntrinsicBounds(0, 0, messageRecord.isKeyExchange() ? R.drawable.ic_menu_login : 0, 0); - if (messageRecord.isFailed()) { + if (!messageRecord.isMediaPending() && messageRecord.isFailed()) { alertView.setFailed(); } else if (messageRecord.isPendingInsecureSmsFallback()) { alertView.setPendingApproval(); @@ -1997,10 +1998,10 @@ private Spannable getLongMessageSpan(@NonNull MessageRecord messageRecord) { if (messageRecord.isMms()) { TextSlide slide = ((MmsMessageRecord) messageRecord).getSlideDeck().getTextSlide(); - if (slide != null && (slide.asAttachment().getTransferState() == AttachmentTable.TRANSFER_PROGRESS_DONE || MessageRecordUtil.isScheduled(messageRecord))) { + if (slide != null && (slide.asAttachment().transferState == AttachmentTable.TRANSFER_PROGRESS_DONE || MessageRecordUtil.isScheduled(messageRecord))) { message = getResources().getString(R.string.ConversationItem_read_more); action = () -> eventListener.onMoreTextClicked(conversationRecipient.getId(), messageRecord.getId(), messageRecord.isMms()); - } else if (slide != null && slide.asAttachment().getTransferState() == AttachmentTable.TRANSFER_PROGRESS_STARTED) { + } else if (slide != null && slide.asAttachment().transferState == AttachmentTable.TRANSFER_PROGRESS_STARTED) { message = getResources().getString(R.string.ConversationItem_pending); action = () -> {}; } else if (slide != null) { @@ -2368,32 +2369,13 @@ public void onClick(View v, final List slides) { for (Slide slide : slides) { ApplicationDependencies.getJobManager().add(new AttachmentDownloadJob(messageRecord.getId(), - ((DatabaseAttachment) slide.asAttachment()).getAttachmentId(), + ((DatabaseAttachment) slide.asAttachment()).attachmentId, true)); } } } } - private class AttachmentCancelClickListener implements SlidesClickedListener { - @Override - public void onClick(View v, List slides) { - Log.i(TAG, "onClick() for attachment cancellation"); - final JobManager jobManager = ApplicationDependencies.getJobManager(); - if (messageRecord.isMmsNotification()) { - Log.i(TAG, "Canceling MMS attachments download"); - jobManager.cancel("mms-operation"); - } else { - Log.i(TAG, "Canceling push attachment downloads for " + slides.size() + " items"); - - for (Slide slide : slides) { - final String queue = AttachmentDownloadJob.constructQueueString(((DatabaseAttachment) slide.asAttachment()).getAttachmentId()); - jobManager.cancelAllInQueue(queue); - } - } - } - } - private class PlayVideoClickListener implements SlideClickListener { private static final float MINIMUM_DOWNLOADED_THRESHOLD = 0.05f; private View parentView; @@ -2407,8 +2389,8 @@ public void onClick(View v, Slide slide) { } if (MediaUtil.isInstantVideoSupported(slide)) { final DatabaseAttachment databaseAttachment = (DatabaseAttachment) slide.asAttachment(); - if (databaseAttachment.getTransferState() != AttachmentTable.TRANSFER_PROGRESS_STARTED) { - final AttachmentId attachmentId = databaseAttachment.getAttachmentId(); + if (databaseAttachment.transferState != AttachmentTable.TRANSFER_PROGRESS_STARTED) { + final AttachmentId attachmentId = databaseAttachment.attachmentId; final JobManager jobManager = ApplicationDependencies.getJobManager(); final String queue = AttachmentDownloadJob.constructQueueString(attachmentId); setup(v, slide); @@ -2445,10 +2427,14 @@ private void cleanup() { @Subscribe(sticky = true, threadMode = ThreadMode.MAIN) public void onEventAsync(PartProgressEvent event) { - float progressPercent = ((float) event.progress) / event.total; - final View currentParentView = parentView; final Slide currentActiveSlide = activeSlide; - if (progressPercent >= MINIMUM_DOWNLOADED_THRESHOLD && currentParentView != null && currentActiveSlide != null) { + if (currentActiveSlide == null || !event.attachment.equals(currentActiveSlide.asAttachment())) { + return; + } + + final View currentParentView = parentView; + float progressPercent = ((float) event.progress) / event.total; + if (progressPercent >= MINIMUM_DOWNLOADED_THRESHOLD && currentParentView != null) { cleanup(); launchMediaPreview(currentParentView, currentActiveSlide); } @@ -2476,7 +2462,7 @@ public void onClick(View v, Slide slide) { performClick(); } else if (eventListener != null && hasSticker(messageRecord)) { //noinspection ConstantConditions - eventListener.onStickerClicked(((MmsMessageRecord) messageRecord).getSlideDeck().getStickerSlide().asAttachment().getSticker()); + eventListener.onStickerClicked(((MmsMessageRecord) messageRecord).getSlideDeck().getStickerSlide().asAttachment().stickerLocator); } } } @@ -2543,7 +2529,7 @@ private void launchMediaPreview(View v, Slide slide) { messageRecord.getTimestamp(), mediaUri, slide.getContentType(), - slide.asAttachment().getSize(), + slide.asAttachment().size, slide.getCaption().orElse(null), false, false, @@ -2552,8 +2538,8 @@ private void launchMediaPreview(View v, Slide slide) { MediaTable.Sorting.Newest, slide.isVideoGif(), new MediaIntentFactory.SharedElementArgs( - slide.asAttachment().getWidth(), - slide.asAttachment().getHeight(), + slide.asAttachment().width, + slide.asAttachment().height, mediaThumbnailStub.require().getCorners().getTopLeft(), mediaThumbnailStub.require().getCorners().getTopRight(), mediaThumbnailStub.require().getCorners().getBottomRight(), diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationMessage.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationMessage.java index 9fcd3ac98d..0e8488dea0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationMessage.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationMessage.java @@ -161,7 +161,7 @@ public boolean hasBeenScheduled() { } public static @NonNull FormattedDate getFormattedDate(@NonNull Context context, @NonNull MessageRecord messageRecord) { - return MessageRecordUtil.isScheduled(messageRecord) ? new FormattedDate(false, DateUtils.getOnlyTimeString(context, ((MmsMessageRecord) messageRecord).getScheduledDate())) + return MessageRecordUtil.isScheduled(messageRecord) ? new FormattedDate(false, false, DateUtils.getOnlyTimeString(context, ((MmsMessageRecord) messageRecord).getScheduledDate())) : DateUtils.getDatelessRelativeTimeSpanFormattedDate(context, Locale.getDefault(), messageRecord.getTimestamp()); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/MessageSendType.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/MessageSendType.kt index 86169ce111..a7e39b45b9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/MessageSendType.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/MessageSendType.kt @@ -53,8 +53,8 @@ sealed class MessageSendType( */ @Parcelize object SignalMessageSendType : MessageSendType( - titleRes = R.string.ConversationActivity_transport_signal, - composeHintRes = R.string.ConversationSettingsFragment__message, + titleRes = R.string.ConversationActivity_send_message_content_description, + composeHintRes = R.string.conversation_activity__type_message_push, buttonDrawableRes = R.drawable.ic_send_lock_24, menuDrawableRes = R.drawable.ic_secure_24, backgroundColorRes = R.color.core_ultramarine, diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/clicklisteners/AttachmentCancelClickListener.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/clicklisteners/AttachmentCancelClickListener.kt new file mode 100644 index 0000000000..4cbda4fdd4 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/clicklisteners/AttachmentCancelClickListener.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ +package org.thoughtcrime.securesms.conversation.clicklisteners + +import android.view.View +import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.attachments.DatabaseAttachment +import org.thoughtcrime.securesms.dependencies.ApplicationDependencies +import org.thoughtcrime.securesms.jobs.AttachmentCompressionJob +import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob +import org.thoughtcrime.securesms.jobs.AttachmentUploadJob +import org.thoughtcrime.securesms.mms.Slide +import org.thoughtcrime.securesms.mms.SlidesClickedListener + +internal class AttachmentCancelClickListener : SlidesClickedListener { + override fun onClick(v: View, slides: List) { + Log.i(TAG, "Canceling compression/upload/download jobs for ${slides.size} items") + val jobManager = ApplicationDependencies.getJobManager() + var cancelCount = 0 + for (slide in slides) { + val attachmentId = (slide.asAttachment() as DatabaseAttachment).attachmentId + val jobsToCancel = jobManager.find { + when (it.factoryKey) { + AttachmentDownloadJob.KEY -> AttachmentDownloadJob.jobSpecMatchesAttachmentId(it, attachmentId) + AttachmentCompressionJob.KEY -> AttachmentCompressionJob.jobSpecMatchesAttachmentId(it, attachmentId) + AttachmentUploadJob.KEY -> AttachmentUploadJob.jobSpecMatchesAttachmentId(it, attachmentId) + else -> false + } + } + jobsToCancel.forEach { + jobManager.cancel(it.id) + cancelCount++ + } + } + Log.i(TAG, "Canceled $cancelCount jobs.") + } + + companion object { + private val TAG = Log.tag(AttachmentCancelClickListener::class.java) + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/clicklisteners/ResendClickListener.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/clicklisteners/ResendClickListener.kt new file mode 100644 index 0000000000..60dede9140 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/clicklisteners/ResendClickListener.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.conversation.clicklisteners + +import android.view.View +import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.database.model.MessageRecord +import org.thoughtcrime.securesms.mms.Slide +import org.thoughtcrime.securesms.mms.SlidesClickedListener +import org.thoughtcrime.securesms.sms.MessageSender + +class ResendClickListener(private val messageRecord: MessageRecord) : SlidesClickedListener { + override fun onClick(v: View?, slides: MutableList?) { + if (v == null) { + Log.w(TAG, "Could not resend message, view was null!") + return + } + + MessageSender.resend(v.context, messageRecord) + } + + companion object { + private val TAG = Log.tag(ResendClickListener::class.java) + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/mutiselect/forward/MultiselectForwardFragmentArgs.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/mutiselect/forward/MultiselectForwardFragmentArgs.kt index c6cc81f38c..d13aa0c441 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/mutiselect/forward/MultiselectForwardFragmentArgs.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/mutiselect/forward/MultiselectForwardFragmentArgs.kt @@ -175,7 +175,7 @@ data class MultiselectForwardFragmentArgs @JvmOverloads constructor( if (mediaMessage.slideDeck.stickerSlide != null) { builder.withDataUri(mediaMessage.slideDeck.stickerSlide?.asAttachment()?.uri) - builder.withStickerLocator(mediaMessage.slideDeck.stickerSlide?.asAttachment()?.sticker) + builder.withStickerLocator(mediaMessage.slideDeck.stickerSlide?.asAttachment()?.stickerLocator) builder.withDataType(mediaMessage.slideDeck.stickerSlide?.asAttachment()?.contentType) } @@ -203,11 +203,11 @@ data class MultiselectForwardFragmentArgs @JvmOverloads constructor( height, size, 0, - isBorderless, - isVideoGif, + borderless, + videoGif, Optional.empty(), Optional.ofNullable(caption), - Optional.of(transformProperties) + Optional.ofNullable(transformProperties) ) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/error/SafetyNumberChangeRepository.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/error/SafetyNumberChangeRepository.java index 31589b2ef6..cb385292c1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/error/SafetyNumberChangeRepository.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/error/SafetyNumberChangeRepository.java @@ -155,7 +155,7 @@ private TrustAndVerifyResult trustOrVerifyChangedRecipientsAndResendInternal(@No IdentityKey newIdentityKey = messageRecord.getIdentityKeyMismatches() .stream() - .filter(mismatch -> mismatch.getRecipientId(context).equals(changedRecipient.getRecipient().getId())) + .filter(mismatch -> mismatch.getRecipientId().equals(changedRecipient.getRecipient().getId())) .map(IdentityKeyMismatch::getIdentityKey) .filter(Objects::nonNull) .findFirst() diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2.kt index 2fa7cd25f1..5ec574ca43 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapterV2.kt @@ -53,7 +53,6 @@ import org.thoughtcrime.securesms.mms.GlideRequests import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.util.CachedInflater -import org.thoughtcrime.securesms.util.HtmlUtil import org.thoughtcrime.securesms.util.Projection import org.thoughtcrime.securesms.util.ProjectionList import org.thoughtcrime.securesms.util.adapter.mapping.MappingViewHolder @@ -572,20 +571,20 @@ class ConversationAdapterV2( if (recipient.isGroup) { if (groupInfo.pendingMemberCount > 0) { val invited = context.resources.getQuantityString(R.plurals.MessageRequestProfileView_invited, groupInfo.pendingMemberCount, groupInfo.pendingMemberCount) - conversationBanner.setSubtitle(context.resources.getQuantityString(R.plurals.MessageRequestProfileView_members_and_invited, groupInfo.fullMemberCount, groupInfo.fullMemberCount, invited)) + conversationBanner.setSubtitle(context.resources.getQuantityString(R.plurals.MessageRequestProfileView_members_and_invited, groupInfo.fullMemberCount, groupInfo.fullMemberCount, invited), R.drawable.symbol_group_light_20) } else if (groupInfo.fullMemberCount > 0) { - conversationBanner.setSubtitle(context.resources.getQuantityString(R.plurals.MessageRequestProfileView_members, groupInfo.fullMemberCount, groupInfo.fullMemberCount)) + conversationBanner.setSubtitle(context.resources.getQuantityString(R.plurals.MessageRequestProfileView_members, groupInfo.fullMemberCount, groupInfo.fullMemberCount), R.drawable.symbol_group_light_20) } else { - conversationBanner.setSubtitle(null) + conversationBanner.hideSubtitle() } } else if (isSelf) { - conversationBanner.setSubtitle(context.getString(R.string.ConversationFragment__you_can_add_notes_for_yourself_in_this_conversation)) + conversationBanner.setSubtitle(context.getString(R.string.ConversationFragment__you_can_add_notes_for_yourself_in_this_conversation), R.drawable.symbol_person_light_24) } else { - val subtitle: String? = recipient.e164.map { e164: String? -> PhoneNumberFormatter.prettyPrint(e164!!) }.orElse(null) + val subtitle: String? = recipient.takeIf { it.shouldShowE164() }?.e164?.map { e164: String? -> PhoneNumberFormatter.prettyPrint(e164!!) }?.orElse(null) if (subtitle == null || subtitle == title) { conversationBanner.hideSubtitle() } else { - conversationBanner.setSubtitle(subtitle) + conversationBanner.setSubtitle(subtitle, R.drawable.symbol_phone_light_20) } } @@ -609,21 +608,20 @@ class ConversationAdapterV2( } } else { val description: String = when (sharedGroups.size) { - 1 -> context.getString(R.string.MessageRequestProfileView_member_of_one_group, HtmlUtil.bold(sharedGroups[0])) - 2 -> context.getString(R.string.MessageRequestProfileView_member_of_two_groups, HtmlUtil.bold(sharedGroups[0]), HtmlUtil.bold(sharedGroups[1])) - 3 -> context.getString(R.string.MessageRequestProfileView_member_of_many_groups, HtmlUtil.bold(sharedGroups[0]), HtmlUtil.bold(sharedGroups[1]), HtmlUtil.bold(sharedGroups[2])) + 1 -> context.getString(R.string.MessageRequestProfileView_member_of_one_group, sharedGroups[0]) + 2 -> context.getString(R.string.MessageRequestProfileView_member_of_two_groups, sharedGroups[0], sharedGroups[1]) + 3 -> context.getString(R.string.MessageRequestProfileView_member_of_many_groups, sharedGroups[0], sharedGroups[1], sharedGroups[2]) else -> { val others: Int = sharedGroups.size - 2 context.getString( R.string.MessageRequestProfileView_member_of_many_groups, - HtmlUtil.bold(sharedGroups[0]), - HtmlUtil.bold(sharedGroups[1]), + sharedGroups[0], + sharedGroups[1], context.resources.getQuantityString(R.plurals.MessageRequestProfileView_member_of_d_additional_groups, others, others) ) } } - conversationBanner.setDescription(HtmlCompat.fromHtml(description, 0)) - conversationBanner.showDescription() + conversationBanner.setDescription(HtmlCompat.fromHtml(description, 0), R.drawable.symbol_group_light_20) } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt index 15cb19e4d4..7cb7b80632 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt @@ -945,7 +945,7 @@ class ConversationFragment : adapter.registerAdapterDataObserver(dataObserver!!) val keyboardEvents = KeyboardEvents() - container.listener = keyboardEvents + container.addInputListener(keyboardEvents) container.addKeyboardStateListener(keyboardEvents) requireActivity() .onBackPressedDispatcher @@ -1462,7 +1462,9 @@ class ConversationFragment : findViewById(R.id.scheduled_messages_show_all) .setOnClickListener { val recipient = viewModel.recipientSnapshot ?: return@setOnClickListener - ScheduledMessagesBottomSheet.show(childFragmentManager, args.threadId, recipient.id) + container.runAfterAllHidden(composeText) { + ScheduledMessagesBottomSheet.show(childFragmentManager, args.threadId, recipient.id) + } } findViewById(R.id.scheduled_messages_text).text = resources.getQuantityString(R.plurals.conversation_scheduled_messages_bar__number_of_messages, count, count) @@ -2473,11 +2475,13 @@ class ConversationFragment : activity ?: return val recipientId = viewModel.recipientSnapshot?.id ?: return - MessageQuotesBottomSheet.show( - childFragmentManager, - MessageId(messageRecord.id), - recipientId - ) + container.runAfterAllHidden(composeText) { + MessageQuotesBottomSheet.show( + childFragmentManager, + MessageId(messageRecord.id), + recipientId + ) + } } override fun onMoreTextClicked(conversationRecipientId: RecipientId, messageId: Long, isMms: Boolean) { @@ -2503,6 +2507,7 @@ class ConversationFragment : } toast(toastText) + return } disposables += viewModel.getTemporaryViewOnceUri(messageRecord).subscribeBy( diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationItemDecorations.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationItemDecorations.kt index 9e596066a4..1164aad3d4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationItemDecorations.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationItemDecorations.kt @@ -125,8 +125,8 @@ class ConversationItemDecorations(hasWallpaper: Boolean = false, private val sch val state: UnreadState = unreadState if (state is UnreadState.InitialUnreadState) { - val firstUnread = items[(state.unreadCount - 1).coerceIn(items.indices)] - val timestamp = (firstUnread as? ConversationMessageElement)?.timestamp() + val firstUnread: ConversationMessageElement? = findFirstUnreadStartingAt(items, (state.unreadCount - 1).coerceIn(items.indices)) + val timestamp = firstUnread?.timestamp() if (timestamp != null) { unreadState = UnreadState.CompleteUnreadState(unreadCount = state.unreadCount, firstUnreadTimestamp = timestamp) } @@ -149,6 +149,17 @@ class ConversationItemDecorations(hasWallpaper: Boolean = false, private val sch } } + private fun findFirstUnreadStartingAt(items: List, startingIndex: Int): ConversationMessageElement? { + val endingIndex = (startingIndex + 20).coerceAtMost(items.lastIndex) + for (index in startingIndex..endingIndex) { + val item = items[index] as? ConversationMessageElement + if ((item?.conversationMessage?.messageRecord as? MmsMessageRecord)?.isRead == false) { + return item + } + } + return items[startingIndex] as? ConversationMessageElement + } + private fun isFirstUnread(bindingAdapterPosition: Int): Boolean { val state = unreadState diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationRepository.kt index 16f8fb2304..2d870a9d21 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationRepository.kt @@ -10,6 +10,7 @@ import android.graphics.Bitmap import android.graphics.drawable.Drawable import android.net.Uri import android.os.Build +import android.text.SpannableStringBuilder import androidx.core.content.pm.ShortcutInfoCompat import androidx.core.graphics.drawable.IconCompat import com.bumptech.glide.request.target.CustomTarget @@ -470,7 +471,7 @@ class ConversationRepository( } } .filterNot(Util::isEmpty) - .joinToString("\n") + .joinTo(buffer = SpannableStringBuilder(), separator = "\n") } fun getRecipientContactPhotoBitmap(context: Context, glideRequests: GlideRequests, recipient: Recipient): Single { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/VoiceMessageRecordingDelegate.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/VoiceMessageRecordingDelegate.kt index f199253f93..9c25359be5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/VoiceMessageRecordingDelegate.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/VoiceMessageRecordingDelegate.kt @@ -152,7 +152,7 @@ class VoiceMessageRecordingDelegate( fun discardRecording() { saveDraft = false shouldSend = false - audioRecorder.stopRecording() + audioRecorder.discardRecording() } fun saveDraft() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/computed/FormattedDate.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/computed/FormattedDate.kt index 52176d9142..d28dfb185c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/computed/FormattedDate.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/computed/FormattedDate.kt @@ -7,5 +7,6 @@ package org.thoughtcrime.securesms.conversation.v2.computed data class FormattedDate( val isRelative: Boolean, + val isNow: Boolean, val value: String ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder.kt index 9f0da5a09b..086d13bd21 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemTextOnlyViewHolder.kt @@ -35,6 +35,7 @@ import org.thoughtcrime.securesms.conversation.colors.ChatColors import org.thoughtcrime.securesms.conversation.mutiselect.Multiselect import org.thoughtcrime.securesms.conversation.mutiselect.MultiselectPart import org.thoughtcrime.securesms.conversation.mutiselect.Multiselectable +import org.thoughtcrime.securesms.conversation.v2.computed.FormattedDate import org.thoughtcrime.securesms.conversation.v2.data.ConversationMessageElement import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.database.model.MmsMessageRecord @@ -100,7 +101,7 @@ open class V2ConversationItemTextOnlyViewHolder>( override val badgeImageView: View? = binding.senderBadge private var reactionMeasureListener: ReactionMeasureListener = ReactionMeasureListener() - private var dateString: String = "" + private var formattedDate: FormattedDate? = null private val bodyBubbleDrawable = ChatColorsDrawable() private val footerDrawable = ChatColorsDrawable() @@ -136,10 +137,7 @@ open class V2ConversationItemTextOnlyViewHolder>( ) } - binding.root.setOnClickListener { - conversationContext.clickListener.onItemClick(getMultiselectPartForLatestTouch()) - } - + binding.root.setOnClickListener { onBubbleClicked() } binding.root.setOnLongClickListener { conversationContext.clickListener.onItemLongClick(binding.root, getMultiselectPartForLatestTouch()) @@ -200,7 +198,7 @@ open class V2ConversationItemTextOnlyViewHolder>( ) if (ConversationAdapterBridge.PAYLOAD_TIMESTAMP in payload) { - if (conversationMessage.computedProperties.formattedDate.value != dateString) { + if (conversationMessage.computedProperties.formattedDate != formattedDate) { presentDate() } hasProcessedSupportedPayload = true @@ -619,7 +617,7 @@ open class V2ConversationItemTextOnlyViewHolder>( return } - dateString = conversationMessage.computedProperties.formattedDate.value + formattedDate = conversationMessage.computedProperties.formattedDate binding.footerDate.setOnClickListener(null) binding.footerDate.visible = true @@ -641,16 +639,22 @@ open class V2ConversationItemTextOnlyViewHolder>( } else if (record.isScheduled()) { binding.footerDate.text = conversationMessage.computedProperties.formattedDate.value } else { - var date = dateString + var dateLabel = conversationMessage.computedProperties.formattedDate.value if (conversationContext.displayMode != ConversationItemDisplayMode.Detailed && record is MmsMessageRecord && record.isEditMessage()) { - date = getContext().getString(R.string.ConversationItem_edited_timestamp_footer, date) + dateLabel = if (conversationMessage.computedProperties.formattedDate.isNow) { + getContext().getString(R.string.ConversationItem_edited_now_timestamp_footer) + } else if (conversationMessage.computedProperties.formattedDate.isRelative) { + getContext().getString(R.string.ConversationItem_edited_relative_timestamp_footer, dateLabel) + } else { + getContext().getString(R.string.ConversationItem_edited_absolute_timestamp_footer, dateLabel) + } binding.footerDate.setOnClickListener { conversationContext.clickListener.onEditedIndicatorClicked(record) } } - binding.footerDate.text = date + binding.footerDate.text = dateLabel } } @@ -706,6 +710,28 @@ open class V2ConversationItemTextOnlyViewHolder>( } } + private fun onBubbleClicked() { + val messageRecord = conversationMessage.messageRecord + + when { + conversationContext.selectedItems.isNotEmpty() -> { + conversationContext.clickListener.onItemClick(getMultiselectPartForLatestTouch()) + } + messageRecord.isFailed -> { + conversationContext.clickListener.onMessageWithErrorClicked(messageRecord) + } + messageRecord.isRateLimited && SignalStore.rateLimit().needsRecaptcha() -> { + conversationContext.clickListener.onMessageWithRecaptchaNeededClicked(messageRecord) + } + messageRecord.isOutgoing && messageRecord.isIdentityMismatchFailure -> { + conversationContext.clickListener.onIncomingIdentityMismatchClicked(messageRecord.fromRecipient.id) + } + else -> { + conversationContext.clickListener.onItemClick(getMultiselectPartForLatestTouch()) + } + } + } + override fun disallowSwipe(latestDownX: Float, latestDownY: Float): Boolean { return false } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListAdapter.java index bb47ac5db3..e9962a59fe 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListAdapter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListAdapter.java @@ -29,7 +29,7 @@ import java.util.Objects; import java.util.Set; -class ConversationListAdapter extends ListAdapter { +class ConversationListAdapter extends ListAdapter implements TimestampPayloadSupport { private static final int TYPE_THREAD = 1; private static final int TYPE_ACTION = 2; @@ -41,7 +41,8 @@ class ConversationListAdapter extends ListAdapter vh.getConversationListItem().updateTypingIndicator(typingSet); + case SELECTION -> vh.getConversationListItem().setSelectedConversations(selectedConversations); + case TIMESTAMP -> vh.getConversationListItem().updateTimestamp(); } } } @@ -190,6 +192,11 @@ protected Conversation getItem(int position) { return super.getItem(position); } + @Override + public void notifyTimestampPayloadUpdate() { + notifyItemRangeChanged(0, getItemCount(), Payload.TIMESTAMP); + } + public void setPagingController(@Nullable PagingController pagingController) { this.pagingController = pagingController; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java index d1270674f1..4862446a9c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java @@ -464,8 +464,8 @@ public void onResume() { setAdapter(defaultAdapter); } - if (activeAdapter != null) { - activeAdapter.notifyItemRangeChanged(0, activeAdapter.getItemCount()); + if (activeAdapter instanceof TimestampPayloadSupport) { + ((TimestampPayloadSupport) activeAdapter).notifyTimestampPayloadUpdate(); } if (SignalStore.rateLimit().needsRecaptcha()) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItem.java b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItem.java index 2ccfb89dea..7318afa06d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItem.java @@ -141,6 +141,7 @@ public final class ConversationListItem extends ConstraintLayout implements Bind private LiveData displayBody; private Disposable joinMembersDisposable = Disposable.empty(); + private Runnable updateDateView = null; public ConversationListItem(Context context) { this(context, null); @@ -249,11 +250,16 @@ public void bindThread(@NonNull LifecycleOwner lifecycleOwner, observeDisplayBody(lifecycleOwner, displayBody); if (thread.getDate() > 0) { - CharSequence date = DateUtils.getBriefRelativeTimeSpanString(getContext(), locale, thread.getDate()); - dateView.setText(date); dateView.setTypeface(thread.isRead() ? LIGHT_TYPEFACE : BOLD_TYPEFACE); dateView.setTextColor(thread.isRead() ? ContextCompat.getColor(getContext(), R.color.signal_text_secondary) : ContextCompat.getColor(getContext(), R.color.signal_text_primary)); + + updateDateView = () -> { + CharSequence date = DateUtils.getBriefRelativeTimeSpanString(getContext(), locale, thread.getDate()); + dateView.setText(date); + }; + + updateDateView.run(); } if (thread.isArchived()) { @@ -278,35 +284,6 @@ private void setBadgeFromRecipient(Recipient recipient) { } } - public void bindContact(@NonNull LifecycleOwner lifecycleOwner, - @NonNull Recipient contact, - @NonNull GlideRequests glideRequests, - @NonNull Locale locale, - @Nullable String highlightSubstring) - { - this.glideRequests = glideRequests; - this.locale = locale; - this.highlightSubstring = highlightSubstring; - - observeRecipient(lifecycleOwner, contact.live()); - observeDisplayBody(null, null); - joinMembersDisposable.dispose(); - setSubjectViewText(null); - - fromView.setText(contact, SearchUtil.getHighlightedSpan(locale, searchStyleFactory, new SpannableString(contact.getDisplayName(getContext())), highlightSubstring, SearchUtil.MATCH_ALL), true, null); - setSubjectViewText(SearchUtil.getHighlightedSpan(locale, searchStyleFactory, contact.getE164().orElse(""), highlightSubstring, SearchUtil.MATCH_ALL)); - dateView.setText(""); - archivedView.setVisibility(GONE); - unreadIndicator.setVisibility(GONE); - unreadMentions.setVisibility(GONE); - deliveryStatusIndicator.setNone(); - alertView.setNone(); - - setSelectedConversations(new ConversationSet()); - setBadgeFromRecipient(recipient.get()); - contactPhotoImage.setAvatar(glideRequests, recipient.get(), !batchMode); - } - public void bindMessage(@NonNull LifecycleOwner lifecycleOwner, @NonNull MessageResult messageResult, @NonNull GlideRequests glideRequests, @@ -324,7 +301,10 @@ public void bindMessage(@NonNull LifecycleOwner lifecycleOwner, fromView.setText(recipient.get(), recipient.get().getDisplayNameOrUsername(getContext()), false, null, false); setSubjectViewText(SearchUtil.getHighlightedSpan(locale, searchStyleFactory, messageResult.getBodySnippet(), highlightSubstring, SearchUtil.MATCH_ALL)); - dateView.setText(DateUtils.getBriefRelativeTimeSpanString(getContext(), locale, messageResult.getReceivedTimestampMs())); + + updateDateView = () -> dateView.setText(DateUtils.getBriefRelativeTimeSpanString(getContext(), locale, messageResult.getReceivedTimestampMs())); + + updateDateView.run(); archivedView.setVisibility(GONE); unreadIndicator.setVisibility(GONE); unreadMentions.setVisibility(GONE); @@ -353,7 +333,9 @@ public void bindGroupWithMembers(@NonNull LifecycleOwner lifecycleOwner, }); fromView.setText(recipient.get(), false); - dateView.setText(DateUtils.getBriefRelativeTimeSpanString(getContext(), locale, groupWithMembers.getDate())); + + updateDateView = () -> dateView.setText(DateUtils.getBriefRelativeTimeSpanString(getContext(), locale, groupWithMembers.getDate())); + updateDateView.run(); archivedView.setVisibility(GONE); unreadIndicator.setVisibility(GONE); unreadMentions.setVisibility(GONE); @@ -386,6 +368,7 @@ public void unbind() { observeDisplayBody(null, null); joinMembersDisposable.dispose(); + updateDateView = null; } @Override @@ -429,6 +412,13 @@ public void updateTypingIndicator(@NonNull Set typingThreads) { } } + @Override + public void updateTimestamp() { + if (updateDateView != null) { + updateDateView.run(); + } + } + public Recipient getRecipient() { return recipient.get(); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItemAction.java b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItemAction.java index a3b01a7606..e88dbd2909 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItemAction.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItemAction.java @@ -64,4 +64,9 @@ public void setSelectedConversations(@NonNull ConversationSet conversations) { public void updateTypingIndicator(@NonNull Set typingThreads) { } + + @Override + public void updateTimestamp() { + // Intentionally left blank. + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListSearchAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListSearchAdapter.kt index 39264b8594..797650ddab 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListSearchAdapter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListSearchAdapter.kt @@ -33,7 +33,11 @@ class ConversationListSearchAdapter( callButtonClickCallbacks: CallButtonClickCallbacks, lifecycleOwner: LifecycleOwner, glideRequests: GlideRequests -) : ContactSearchAdapter(context, fixedContacts, displayOptions, onClickedCallbacks, longClickCallbacks, storyContextMenuCallbacks, callButtonClickCallbacks) { +) : ContactSearchAdapter(context, fixedContacts, displayOptions, onClickedCallbacks, longClickCallbacks, storyContextMenuCallbacks, callButtonClickCallbacks), TimestampPayloadSupport { + + companion object { + private const val PAYLOAD_TIMESTAMP = 0 + } init { registerFactory( @@ -62,6 +66,27 @@ class ConversationListSearchAdapter( ) } + override fun notifyTimestampPayloadUpdate() { + notifyItemRangeChanged(0, itemCount, PAYLOAD_TIMESTAMP) + } + + private abstract class ConversationListItemViewHolder>( + itemView: View + ) : MappingViewHolder(itemView) { + private val conversationListItem: ConversationListItem = itemView as ConversationListItem + + override fun bind(model: M) { + if (payload.contains(PAYLOAD_TIMESTAMP)) { + conversationListItem.updateTimestamp() + return + } + + fullBind(model) + } + + abstract fun fullBind(model: M) + } + private class EmptyViewHolder( itemView: View ) : MappingViewHolder(itemView) { @@ -69,6 +94,10 @@ class ConversationListSearchAdapter( private val noResults = itemView.findViewById(R.id.search_no_results) override fun bind(model: EmptyModel) { + if (payload.isNotEmpty()) { + return + } + noResults.text = context.getString(R.string.SearchFragment_no_results, model.empty.query ?: "") } } @@ -78,8 +107,8 @@ class ConversationListSearchAdapter( private val lifecycleOwner: LifecycleOwner, private val glideRequests: GlideRequests, itemView: View - ) : MappingViewHolder(itemView) { - override fun bind(model: ThreadModel) { + ) : ConversationListItemViewHolder(itemView) { + override fun fullBind(model: ThreadModel) { itemView.setOnClickListener { threadListener.onClicked(itemView, model.thread, false) } @@ -101,8 +130,8 @@ class ConversationListSearchAdapter( private val lifecycleOwner: LifecycleOwner, private val glideRequests: GlideRequests, itemView: View - ) : MappingViewHolder(itemView) { - override fun bind(model: MessageModel) { + ) : ConversationListItemViewHolder(itemView) { + override fun fullBind(model: MessageModel) { itemView.setOnClickListener { messageListener.onClicked(itemView, model.message, false) } @@ -122,8 +151,8 @@ class ConversationListSearchAdapter( private val lifecycleOwner: LifecycleOwner, private val glideRequests: GlideRequests, itemView: View - ) : MappingViewHolder(itemView) { - override fun bind(model: GroupWithMembersModel) { + ) : ConversationListItemViewHolder(itemView) { + override fun fullBind(model: GroupWithMembersModel) { itemView.setOnClickListener { groupWithMembersListener.onClicked(itemView, model.groupWithMembers, false) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/TimestampPayloadSupport.kt b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/TimestampPayloadSupport.kt new file mode 100644 index 0000000000..921bb60c5d --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/TimestampPayloadSupport.kt @@ -0,0 +1,14 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.conversationlist + +/** + * Generic interface for the adapters to support updating the + * timestamp in a given row as opposed to rebinding every item. + */ +interface TimestampPayloadSupport { + fun notifyTimestampPayloadUpdate() +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/UnidentifiedAccessUtil.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/UnidentifiedAccessUtil.java index 0b8e46a34b..fe7c1b9540 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/crypto/UnidentifiedAccessUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/UnidentifiedAccessUtil.java @@ -23,18 +23,17 @@ import org.thoughtcrime.securesms.recipients.RecipientId; import org.signal.core.util.Base64; import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.Util; import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess; import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair; import java.io.IOException; -import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.stream.Collectors; public class UnidentifiedAccessUtil { @@ -76,7 +75,7 @@ public static Map> getAccessMapFor return accessMap; } - + @WorkerThread public static List> getAccessFor(@NonNull Context context, @NonNull List recipients, boolean log) { return getAccessFor(context, recipients, false, log); @@ -84,37 +83,35 @@ public static List> getAccessFor(@NonNull Conte @WorkerThread public static List> getAccessFor(@NonNull Context context, @NonNull List recipients, boolean isForStory, boolean log) { - byte[] ourUnidentifiedAccessKey = UnidentifiedAccess.deriveAccessKeyFrom(ProfileKeyUtil.getSelfProfileKey()); + final byte[] ourUnidentifiedAccessKey; if (TextSecurePreferences.isUniversalUnidentifiedAccess(context)) { ourUnidentifiedAccessKey = UNRESTRICTED_KEY; + } else { + ourUnidentifiedAccessKey = ProfileKeyUtil.getSelfProfileKey().deriveAccessKey(); } - List> access = new ArrayList<>(recipients.size()); - CertificateType certificateType = getUnidentifiedAccessCertificateType(); byte[] ourUnidentifiedAccessCertificate = SignalStore.certificateValues().getUnidentifiedAccessCertificate(certificateType); - for (Recipient recipient : recipients) { + List> access = recipients.parallelStream().map(recipient -> { + UnidentifiedAccessPair unidentifiedAccessPair = null; if (ourUnidentifiedAccessCertificate != null) { try { UnidentifiedAccess theirAccess = getTargetUnidentifiedAccess(recipient, ourUnidentifiedAccessCertificate, isForStory); UnidentifiedAccess ourAccess = new UnidentifiedAccess(ourUnidentifiedAccessKey, ourUnidentifiedAccessCertificate, false); if (theirAccess != null) { - access.add(Optional.of(new UnidentifiedAccessPair(theirAccess, ourAccess))); - } else { - access.add(Optional.empty()); + unidentifiedAccessPair = new UnidentifiedAccessPair(theirAccess, ourAccess); } } catch (InvalidCertificateException e) { Log.w(TAG, "Invalid unidentified access certificate!", e); - access.add(Optional.empty()); } } else { Log.w(TAG, "Missing unidentified access certificate!"); - access.add(Optional.empty()); } - } + return Optional.ofNullable(unidentifiedAccessPair); + }).collect(Collectors.toList()); int unidentifiedCount = Stream.of(access).filter(Optional::isPresent).toList().size(); int otherCount = access.size() - unidentifiedCount; @@ -178,7 +175,7 @@ private static byte[] getUnidentifiedAccessCertificate() { accessKey = UNRESTRICTED_KEY; } } else { - accessKey = UnidentifiedAccess.deriveAccessKeyFrom(theirProfileKey); + accessKey = theirProfileKey.deriveAccessKey(); } break; case DISABLED: @@ -188,7 +185,7 @@ private static byte[] getUnidentifiedAccessCertificate() { if (theirProfileKey == null) { accessKey = null; } else { - accessKey = UnidentifiedAccess.deriveAccessKeyFrom(theirProfileKey); + accessKey = theirProfileKey.deriveAccessKey(); } break; case UNRESTRICTED: diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/SignalServiceAccountDataStoreImpl.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/SignalServiceAccountDataStoreImpl.java index d00f4c4149..dcb40d4c75 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/SignalServiceAccountDataStoreImpl.java +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/SignalServiceAccountDataStoreImpl.java @@ -21,6 +21,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.UUID; @@ -126,7 +127,7 @@ public List getSubDeviceSessions(String number) { } @Override - public Set getAllAddressesWithActiveSessions(List addressNames) { + public Map getAllAddressesWithActiveSessions(List addressNames) { return sessionStore.getAllAddressesWithActiveSessions(addressNames); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/TextSecureSessionStore.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/TextSecureSessionStore.java index da84874432..6aa2b6d7c0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/TextSecureSessionStore.java +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/TextSecureSessionStore.java @@ -17,6 +17,7 @@ import org.whispersystems.signalservice.api.push.ServiceId; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; @@ -104,14 +105,13 @@ public List getSubDeviceSessions(String name) { } @Override - public Set getAllAddressesWithActiveSessions(List addressNames) { + public Map getAllAddressesWithActiveSessions(List addressNames) { try (SignalSessionLock.Lock unused = ReentrantSessionLock.INSTANCE.acquire()) { return SignalDatabase.sessions() .getAllFor(accountId, addressNames) .stream() .filter(row -> isActive(row.getRecord())) - .map(row -> new SignalProtocolAddress(row.getAddress(), row.getDeviceId())) - .collect(Collectors.toSet()); + .collect(Collectors.toMap(row -> new SignalProtocolAddress(row.getAddress(), row.getDeviceId()), SessionTable.SessionRow::getRecord)); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.java b/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.java deleted file mode 100644 index c6672dd832..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.java +++ /dev/null @@ -1,1724 +0,0 @@ -/* - * Copyright (C) 2011 Whisper Systems - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.thoughtcrime.securesms.database; - -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.media.MediaDataSource; -import android.os.Parcel; -import android.os.Parcelable; -import android.text.TextUtils; -import android.util.Pair; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; -import androidx.annotation.VisibleForTesting; -import androidx.annotation.WorkerThread; - -import com.bumptech.glide.Glide; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -import org.json.JSONArray; -import org.json.JSONException; -import org.signal.core.util.Base64; -import org.signal.core.util.CursorExtensionsKt; -import org.signal.core.util.CursorUtil; -import org.signal.core.util.SQLiteDatabaseExtensionsKt; -import org.signal.core.util.SetUtil; -import org.signal.core.util.SqlUtil; -import org.signal.core.util.StreamUtil; -import org.signal.core.util.ThreadUtil; -import org.signal.core.util.logging.Log; -import org.thoughtcrime.securesms.attachments.Attachment; -import org.thoughtcrime.securesms.attachments.AttachmentId; -import org.thoughtcrime.securesms.attachments.DatabaseAttachment; -import org.thoughtcrime.securesms.audio.AudioHash; -import org.thoughtcrime.securesms.blurhash.BlurHash; -import org.thoughtcrime.securesms.crypto.AttachmentSecret; -import org.thoughtcrime.securesms.crypto.ModernDecryptingPartInputStream; -import org.thoughtcrime.securesms.crypto.ModernEncryptingPartOutputStream; -import org.thoughtcrime.securesms.database.model.databaseprotos.AudioWaveFormData; -import org.thoughtcrime.securesms.jobs.GenerateAudioWaveFormJob; -import org.thoughtcrime.securesms.mms.MediaStream; -import org.thoughtcrime.securesms.mms.MmsException; -import org.thoughtcrime.securesms.mms.PartAuthority; -import org.thoughtcrime.securesms.mms.SentMediaQuality; -import org.thoughtcrime.securesms.stickers.StickerLocator; -import org.thoughtcrime.securesms.util.FileUtils; -import org.thoughtcrime.securesms.util.JsonUtils; -import org.thoughtcrime.securesms.util.MediaUtil; -import org.thoughtcrime.securesms.util.StorageUtil; -import org.thoughtcrime.securesms.video.EncryptedMediaDataSource; -import org.whispersystems.signalservice.internal.util.JsonUtil; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.security.DigestInputStream; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; - -public class AttachmentTable extends DatabaseTable { - - public static final String TAG = Log.tag(AttachmentTable.class); - - public static final String TABLE_NAME = "part"; - public static final String ROW_ID = "_id"; - static final String ATTACHMENT_JSON_ALIAS = "attachment_json"; - public static final String MMS_ID = "mid"; - static final String CONTENT_TYPE = "ct"; - static final String NAME = "name"; - static final String CONTENT_DISPOSITION = "cd"; - static final String CONTENT_LOCATION = "cl"; - public static final String DATA = "_data"; - static final String TRANSFER_STATE = "pending_push"; - public static final String TRANSFER_FILE = "transfer_file"; - public static final String SIZE = "data_size"; - static final String FILE_NAME = "file_name"; - public static final String UNIQUE_ID = "unique_id"; - static final String DIGEST = "digest"; - static final String VOICE_NOTE = "voice_note"; - static final String BORDERLESS = "borderless"; - static final String VIDEO_GIF = "video_gif"; - static final String QUOTE = "quote"; - public static final String STICKER_PACK_ID = "sticker_pack_id"; - public static final String STICKER_PACK_KEY = "sticker_pack_key"; - static final String STICKER_ID = "sticker_id"; - static final String STICKER_EMOJI = "sticker_emoji"; - static final String FAST_PREFLIGHT_ID = "fast_preflight_id"; - public static final String DATA_RANDOM = "data_random"; - static final String WIDTH = "width"; - static final String HEIGHT = "height"; - static final String CAPTION = "caption"; - static final String DATA_HASH = "data_hash"; - static final String VISUAL_HASH = "blur_hash"; - static final String TRANSFORM_PROPERTIES = "transform_properties"; - static final String DISPLAY_ORDER = "display_order"; - static final String UPLOAD_TIMESTAMP = "upload_timestamp"; - static final String CDN_NUMBER = "cdn_number"; - static final String MAC_DIGEST = "incremental_mac_digest"; - static final String INCREMENTAL_MAC_CHUNK_SIZE = "incremental_mac_chunk_size"; - - private static final String DIRECTORY = "parts"; - - public static final int TRANSFER_PROGRESS_DONE = 0; - public static final int TRANSFER_PROGRESS_STARTED = 1; - public static final int TRANSFER_PROGRESS_PENDING = 2; - public static final int TRANSFER_PROGRESS_FAILED = 3; - public static final int TRANSFER_PROGRESS_PERMANENT_FAILURE = 4; - - public static final long PREUPLOAD_MESSAGE_ID = -8675309; - - private static final String PART_ID_WHERE = ROW_ID + " = ? AND " + UNIQUE_ID + " = ?"; - private static final String PART_ID_WHERE_NOT = ROW_ID + " != ? AND " + UNIQUE_ID + " != ?"; - - private static final String[] PROJECTION = new String[] {ROW_ID, - MMS_ID, CONTENT_TYPE, NAME, CONTENT_DISPOSITION, - CDN_NUMBER, CONTENT_LOCATION, DATA, - TRANSFER_STATE, SIZE, FILE_NAME, UNIQUE_ID, DIGEST, MAC_DIGEST, INCREMENTAL_MAC_CHUNK_SIZE, - FAST_PREFLIGHT_ID, VOICE_NOTE, BORDERLESS, VIDEO_GIF, QUOTE, DATA_RANDOM, - WIDTH, HEIGHT, CAPTION, STICKER_PACK_ID, - STICKER_PACK_KEY, STICKER_ID, STICKER_EMOJI, DATA_HASH, VISUAL_HASH, - TRANSFORM_PROPERTIES, TRANSFER_FILE, DISPLAY_ORDER, - UPLOAD_TIMESTAMP }; - - public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ROW_ID + " INTEGER PRIMARY KEY, " + - MMS_ID + " INTEGER, " + - "seq" + " INTEGER DEFAULT 0, " + - CONTENT_TYPE + " TEXT, " + - NAME + " TEXT, " + - "chset" + " INTEGER, " + - CONTENT_DISPOSITION + " TEXT, " + - "fn" + " TEXT, " + - "cid" + " TEXT, " + - CONTENT_LOCATION + " TEXT, " + - "ctt_s" + " INTEGER, " + - "ctt_t" + " TEXT, " + - "encrypted" + " INTEGER, " + - TRANSFER_STATE + " INTEGER, " + - DATA + " TEXT, " + - SIZE + " INTEGER, " + - FILE_NAME + " TEXT, " + - UNIQUE_ID + " INTEGER NOT NULL, " + - DIGEST + " BLOB, " + - FAST_PREFLIGHT_ID + " TEXT, " + - VOICE_NOTE + " INTEGER DEFAULT 0, " + - BORDERLESS + " INTEGER DEFAULT 0, " + - VIDEO_GIF + " INTEGER DEFAULT 0, " + - DATA_RANDOM + " BLOB, " + - QUOTE + " INTEGER DEFAULT 0, " + - WIDTH + " INTEGER DEFAULT 0, " + - HEIGHT + " INTEGER DEFAULT 0, " + - CAPTION + " TEXT DEFAULT NULL, " + - STICKER_PACK_ID + " TEXT DEFAULT NULL, " + - STICKER_PACK_KEY + " DEFAULT NULL, " + - STICKER_ID + " INTEGER DEFAULT -1, " + - STICKER_EMOJI + " STRING DEFAULT NULL, " + - DATA_HASH + " TEXT DEFAULT NULL, " + - VISUAL_HASH + " TEXT DEFAULT NULL, " + - TRANSFORM_PROPERTIES + " TEXT DEFAULT NULL, " + - TRANSFER_FILE + " TEXT DEFAULT NULL, " + - DISPLAY_ORDER + " INTEGER DEFAULT 0, " + - UPLOAD_TIMESTAMP + " INTEGER DEFAULT 0, " + - CDN_NUMBER + " INTEGER DEFAULT 0, " + - MAC_DIGEST + " BLOB, " + - INCREMENTAL_MAC_CHUNK_SIZE + " INTEGER DEFAULT 0);"; - - public static final String[] CREATE_INDEXS = { - "CREATE INDEX IF NOT EXISTS part_mms_id_index ON " + TABLE_NAME + " (" + MMS_ID + ");", - "CREATE INDEX IF NOT EXISTS pending_push_index ON " + TABLE_NAME + " (" + TRANSFER_STATE + ");", - "CREATE INDEX IF NOT EXISTS part_sticker_pack_id_index ON " + TABLE_NAME + " (" + STICKER_PACK_ID + ");", - "CREATE INDEX IF NOT EXISTS part_data_hash_index ON " + TABLE_NAME + " (" + DATA_HASH + ");", - "CREATE INDEX IF NOT EXISTS part_data_index ON " + TABLE_NAME + " (" + DATA + ");" - }; - - private final AttachmentSecret attachmentSecret; - - public AttachmentTable(Context context, SignalDatabase databaseHelper, AttachmentSecret attachmentSecret) { - super(context, databaseHelper); - this.attachmentSecret = attachmentSecret; - } - - public @NonNull InputStream getAttachmentStream(AttachmentId attachmentId, long offset) - throws IOException - { - InputStream dataStream; - - try { - dataStream = getDataStream(attachmentId, DATA, offset); - } catch (FileNotFoundException e) { - throw new IOException("No stream for: " + attachmentId, e); - } - - if (dataStream == null) throw new IOException("No stream for: " + attachmentId); - else return dataStream; - } - - public boolean containsStickerPackId(@NonNull String stickerPackId) { - String selection = STICKER_PACK_ID + " = ?"; - String[] args = new String[] { stickerPackId }; - - try (Cursor cursor = databaseHelper.getSignalReadableDatabase().query(TABLE_NAME, null, selection, args, null, null, null, "1")) { - return cursor != null && cursor.moveToFirst(); - } - } - - public void setTransferProgressFailed(AttachmentId attachmentId, long mmsId) - throws MmsException - { - SQLiteDatabase database = databaseHelper.getSignalWritableDatabase(); - ContentValues values = new ContentValues(); - values.put(TRANSFER_STATE, TRANSFER_PROGRESS_FAILED); - - database.update(TABLE_NAME, values, PART_ID_WHERE + " AND " + TRANSFER_STATE + " < " + TRANSFER_PROGRESS_PERMANENT_FAILURE, attachmentId.toStrings()); - notifyConversationListeners(SignalDatabase.messages().getThreadIdForMessage(mmsId)); - } - - public void setTransferProgressPermanentFailure(AttachmentId attachmentId, long mmsId) - throws MmsException - { - SQLiteDatabase database = databaseHelper.getSignalWritableDatabase(); - ContentValues values = new ContentValues(); - values.put(TRANSFER_STATE, TRANSFER_PROGRESS_PERMANENT_FAILURE); - - database.update(TABLE_NAME, values, PART_ID_WHERE, attachmentId.toStrings()); - notifyConversationListeners(SignalDatabase.messages().getThreadIdForMessage(mmsId)); - } - - public @Nullable DatabaseAttachment getAttachment(@NonNull AttachmentId attachmentId) - { - SQLiteDatabase database = databaseHelper.getSignalReadableDatabase(); - Cursor cursor = null; - - try { - cursor = database.query(TABLE_NAME, PROJECTION, PART_ID_WHERE, attachmentId.toStrings(), null, null, null); - - if (cursor != null && cursor.moveToFirst()) { - List list = getAttachments(cursor); - - if (list != null && list.size() > 0) { - return list.get(0); - } - } - - return null; - } finally { - if (cursor != null) - cursor.close(); - } - } - - public @NonNull List getAttachmentsForMessage(long mmsId) { - SQLiteDatabase database = databaseHelper.getSignalReadableDatabase(); - List results = new LinkedList<>(); - Cursor cursor = null; - - try { - cursor = database.query(TABLE_NAME, PROJECTION, MMS_ID + " = ?", new String[] {mmsId+""}, - null, null, UNIQUE_ID + " ASC, " + ROW_ID + " ASC"); - - while (cursor != null && cursor.moveToNext()) { - results.addAll(getAttachments(cursor)); - } - - return results; - } finally { - if (cursor != null) - cursor.close(); - } - } - - public @NonNull Map> getAttachmentsForMessages(@NonNull Collection mmsIds) { - if (mmsIds.isEmpty()) { - return Collections.emptyMap(); - } - - SQLiteDatabase database = databaseHelper.getSignalReadableDatabase(); - SqlUtil.Query query = SqlUtil.buildSingleCollectionQuery(MMS_ID, mmsIds); - - Map> output = new HashMap<>(); - - try (Cursor cursor = database.query(TABLE_NAME, PROJECTION, query.getWhere(), query.getWhereArgs(), null, null, UNIQUE_ID + " ASC, " + ROW_ID + " ASC")) { - while (cursor.moveToNext()) { - DatabaseAttachment attachment = getAttachment(cursor); - List attachments = output.get(attachment.getMmsId()); - - if (attachments == null) { - attachments = new LinkedList<>(); - output.put(attachment.getMmsId(), attachments); - } - - attachments.add(attachment); - } - } - - return output; - } - - public boolean hasAttachment(@NonNull AttachmentId id) { - SQLiteDatabase database = databaseHelper.getSignalReadableDatabase(); - - try (Cursor cursor = database.query(TABLE_NAME, - new String[]{ROW_ID, UNIQUE_ID}, - PART_ID_WHERE, - id.toStrings(), - null, - null, - null)) { - if (cursor != null && cursor.getCount() > 0) { - return true; - } - } - return false; - } - - public boolean deleteAttachmentsForMessage(long mmsId) { - Log.d(TAG, "[deleteAttachmentsForMessage] mmsId: " + mmsId); - - SQLiteDatabase db = databaseHelper.getSignalWritableDatabase(); - - db.beginTransaction(); - try { - try (Cursor cursor = db.query(TABLE_NAME, new String[] { DATA, CONTENT_TYPE, ROW_ID, UNIQUE_ID }, MMS_ID + " = ?", new String[] { mmsId + "" }, null, null, null)) { - while (cursor.moveToNext()) { - deleteAttachmentOnDisk(CursorUtil.requireString(cursor, DATA), - CursorUtil.requireString(cursor, CONTENT_TYPE), - new AttachmentId(CursorUtil.requireLong(cursor, ROW_ID), - CursorUtil.requireLong(cursor, UNIQUE_ID))); - } - } - - int deleteCount = db.delete(TABLE_NAME, MMS_ID + " = ?", new String[] { mmsId + "" }); - notifyAttachmentListeners(); - db.setTransactionSuccessful(); - - return deleteCount > 0; - } finally { - db.endTransaction(); - } - } - - /** - * Deletes all attachments with an ID of {@link #PREUPLOAD_MESSAGE_ID}. These represent - * attachments that were pre-uploaded and haven't been assigned to a message. This should only be - * done when you *know* that all attachments *should* be assigned a real mmsId. For instance, when - * the app starts. Otherwise you could delete attachments that are legitimately being - * pre-uploaded. - */ - public int deleteAbandonedPreuploadedAttachments() { - SQLiteDatabase db = databaseHelper.getSignalWritableDatabase(); - String query = MMS_ID + " = ?"; - String[] args = new String[] { String.valueOf(PREUPLOAD_MESSAGE_ID) }; - int count = 0; - - try (Cursor cursor = db.query(TABLE_NAME, null, query, args, null, null, null)) { - while (cursor != null && cursor.moveToNext()) { - long rowId = cursor.getLong(cursor.getColumnIndexOrThrow(ROW_ID)); - long uniqueId = cursor.getLong(cursor.getColumnIndexOrThrow(UNIQUE_ID)); - AttachmentId id = new AttachmentId(rowId, uniqueId); - - deleteAttachment(id); - count++; - } - } - - return count; - } - - public void deleteAttachmentFilesForViewOnceMessage(long mmsId) { - Log.d(TAG, "[deleteAttachmentFilesForViewOnceMessage] mmsId: " + mmsId); - - SQLiteDatabase db = databaseHelper.getSignalWritableDatabase(); - - db.beginTransaction(); - try { - try (Cursor cursor = db.query(TABLE_NAME, new String[] { DATA, CONTENT_TYPE, ROW_ID, UNIQUE_ID }, MMS_ID + " = ?", new String[] { mmsId + "" }, null, null, null)) { - while (cursor != null && cursor.moveToNext()) { - deleteAttachmentOnDisk(CursorUtil.requireString(cursor, DATA), - CursorUtil.requireString(cursor, CONTENT_TYPE), - new AttachmentId(CursorUtil.requireLong(cursor, ROW_ID), - CursorUtil.requireLong(cursor, UNIQUE_ID))); - } - } - - ContentValues values = new ContentValues(); - values.put(DATA, (String) null); - values.put(DATA_RANDOM, (byte[]) null); - values.put(DATA_HASH, (String) null); - values.put(FILE_NAME, (String) null); - values.put(CAPTION, (String) null); - values.put(SIZE, 0); - values.put(WIDTH, 0); - values.put(HEIGHT, 0); - values.put(TRANSFER_STATE, TRANSFER_PROGRESS_DONE); - values.put(VISUAL_HASH, (String) null); - values.put(CONTENT_TYPE, MediaUtil.VIEW_ONCE); - - db.update(TABLE_NAME, values, MMS_ID + " = ?", new String[] { mmsId + "" }); - notifyAttachmentListeners(); - - long threadId = SignalDatabase.messages().getThreadIdForMessage(mmsId); - if (threadId > 0) { - notifyConversationListeners(threadId); - } - - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } - } - - public void deleteAttachment(@NonNull AttachmentId id) { - Log.d(TAG, "[deleteAttachment] attachmentId: " + id); - SQLiteDatabase db = databaseHelper.getSignalWritableDatabase(); - - db.beginTransaction(); - try { - try (Cursor cursor = db.query(TABLE_NAME, new String[]{DATA, CONTENT_TYPE}, PART_ID_WHERE, id.toStrings(), null, null, null)) { - if (!cursor.moveToNext()) { - Log.w(TAG, "Tried to delete an attachment, but it didn't exist."); - db.setTransactionSuccessful(); - return; - } - String data = CursorUtil.requireString(cursor, DATA); - String contentType = CursorUtil.requireString(cursor, CONTENT_TYPE); - - db.delete(TABLE_NAME, PART_ID_WHERE, id.toStrings()); - deleteAttachmentOnDisk(data, contentType, id); - notifyAttachmentListeners(); - db.setTransactionSuccessful(); - } - } finally { - db.endTransaction(); - } - } - - public void trimAllAbandonedAttachments() { - SQLiteDatabase db = databaseHelper.getSignalWritableDatabase(); - String selectAllMmsIds = "SELECT " + MessageTable.ID + " FROM " + MessageTable.TABLE_NAME; - String where = MMS_ID + " != " + PREUPLOAD_MESSAGE_ID + " AND " + MMS_ID + " NOT IN (" + selectAllMmsIds + ")"; - - int deletes = db.delete(TABLE_NAME, where, null); - if (deletes > 0) { - Log.i(TAG, "Trimmed " + deletes + " abandoned attachments."); - } - } - - public int deleteAbandonedAttachmentFiles() { - File[] diskFiles = context.getDir(DIRECTORY, Context.MODE_PRIVATE).listFiles(); - - if (diskFiles == null) { - return 0; - } - - Set filesOnDisk = Arrays.stream(diskFiles) - .filter(f -> !PartFileProtector.isProtected(f)) - .map(File::getAbsolutePath) - .collect(Collectors.toSet()); - - Set filesInDb = new HashSet<>(); - try (Cursor cursor = databaseHelper.getSignalReadableDatabase().query(true, TABLE_NAME, new String[] { DATA }, null, null, null, null, null, null)) { - while (cursor != null && cursor.moveToNext()) { - filesInDb.add(CursorUtil.requireString(cursor, DATA)); - } - } - - filesInDb.addAll(SignalDatabase.stickers().getAllStickerFiles()); - - Set onDiskButNotInDatabase = SetUtil.difference(filesOnDisk, filesInDb); - - for (String filePath : onDiskButNotInDatabase) { - //noinspection ResultOfMethodCallIgnored - new File(filePath).delete(); - } - - return onDiskButNotInDatabase.size(); - } - - @SuppressWarnings("ResultOfMethodCallIgnored") - void deleteAllAttachments() { - SQLiteDatabase database = databaseHelper.getSignalWritableDatabase(); - database.delete(TABLE_NAME, null, null); - - FileUtils.deleteDirectoryContents(context.getDir(DIRECTORY, Context.MODE_PRIVATE)); - - notifyAttachmentListeners(); - } - - private void deleteAttachmentOnDisk(@Nullable String data, - @Nullable String contentType, - @NonNull AttachmentId attachmentId) - { - SQLiteDatabase db = databaseHelper.getSignalWritableDatabase(); - - if (!db.inTransaction()) { - throw new IllegalStateException("Must be in a transaction!"); - } - - DataUsageResult dataUsage = getAttachmentFileUsages(data, attachmentId); - - if (dataUsage.hasStrongReference()) { - Log.i(TAG, "[deleteAttachmentOnDisk] Attachment in use. Skipping deletion. " + data + " " + attachmentId); - return; - } - - Log.i(TAG, "[deleteAttachmentOnDisk] No other strong uses of this attachment. Safe to delete. " + data + " " + attachmentId); - - if (!TextUtils.isEmpty(data)) { - if (new File(data).delete()) { - Log.i(TAG, "[deleteAttachmentOnDisk] Deleted attachment file. " + data + " " + attachmentId); - - List removableWeakReferences = dataUsage.getRemovableWeakReferences(); - - if (removableWeakReferences.size() > 0) { - Log.i(TAG, String.format(Locale.US, "[deleteAttachmentOnDisk] Deleting %d weak references for %s", removableWeakReferences.size(), data)); - int deletedCount = 0; - - for (AttachmentId weakReference : removableWeakReferences) { - Log.i(TAG, String.format("[deleteAttachmentOnDisk] Clearing weak reference for %s %s", data, weakReference)); - ContentValues values = new ContentValues(); - values.putNull(DATA); - values.putNull(DATA_RANDOM); - values.putNull(DATA_HASH); - deletedCount += db.update(TABLE_NAME, values, PART_ID_WHERE, weakReference.toStrings()); - } - - String logMessage = String.format(Locale.US, "[deleteAttachmentOnDisk] Cleared %d/%d weak references for %s", deletedCount, removableWeakReferences.size(), data); - if (deletedCount != removableWeakReferences.size()) { - Log.w(TAG, logMessage); - } else { - Log.i(TAG, logMessage); - } - } - } else { - Log.w(TAG, "[deleteAttachmentOnDisk] Failed to delete attachment. " + data + " " + attachmentId); - } - } - - if (MediaUtil.isImageType(contentType) || MediaUtil.isVideoType(contentType)) { - Glide.get(context).clearDiskCache(); - ThreadUtil.runOnMain(() -> Glide.get(context).clearMemory()); - } - } - - private @NonNull DataUsageResult getAttachmentFileUsages(@Nullable String data, @NonNull AttachmentId attachmentId) { - SQLiteDatabase database = databaseHelper.getSignalReadableDatabase(); - - if (!database.inTransaction()) { - throw new IllegalArgumentException("Must be in a transaction!"); - } - - if (data == null) return DataUsageResult.NOT_IN_USE; - - String selection = DATA + " = ? AND " + UNIQUE_ID + " != ? AND " + ROW_ID + " != ?"; - String[] args = {data, Long.toString(attachmentId.getUniqueId()), Long.toString(attachmentId.getRowId())}; - List quoteRows = new LinkedList<>(); - - try (Cursor cursor = database.query(TABLE_NAME, new String[]{ROW_ID, UNIQUE_ID, QUOTE}, selection, args, null, null, null, null)) { - while (cursor.moveToNext()) { - boolean isQuote = cursor.getInt(cursor.getColumnIndexOrThrow(QUOTE)) == 1; - if (isQuote) { - quoteRows.add(new AttachmentId(cursor.getLong(cursor.getColumnIndexOrThrow(ROW_ID)), cursor.getLong(cursor.getColumnIndexOrThrow(UNIQUE_ID)))); - } else { - return DataUsageResult.IN_USE; - } - } - } - - return new DataUsageResult(quoteRows); - } - - /** - * Check if data file is in use by another attachment row with a different hash. Rows with the same data and hash - * will be fixed in a later call to {@link #updateAttachmentAndMatchingHashes(SQLiteDatabase, AttachmentId, String, ContentValues)}. - */ - private boolean isAttachmentFileUsedByOtherAttachments(@Nullable AttachmentId attachmentId, @NonNull DataInfo dataInfo) { - if (attachmentId == null) { - return false; - } - - return SQLiteDatabaseExtensionsKt.exists(getReadableDatabase(), TABLE_NAME) - .where(DATA + " = ? AND " + DATA_HASH + " != ?", dataInfo.file.getAbsolutePath(), dataInfo.hash) - .run(); - } - - public void insertAttachmentsForPlaceholder(long mmsId, @NonNull AttachmentId attachmentId, @NonNull InputStream inputStream) - throws MmsException - { - DatabaseAttachment placeholder = getAttachment(attachmentId); - SQLiteDatabase database = databaseHelper.getSignalWritableDatabase(); - ContentValues values = new ContentValues(); - DataInfo oldInfo = getAttachmentDataFileInfo(attachmentId, DATA); - DataInfo dataInfo = storeAttachmentStream(inputStream); - File transferFile = getTransferFile(databaseHelper.getSignalReadableDatabase(), attachmentId); - boolean updated = false; - - database.beginTransaction(); - try { - dataInfo = deduplicateAttachment(dataInfo, attachmentId, placeholder != null ? placeholder.getTransformProperties() : TransformProperties.empty()); - if (oldInfo != null) { - updateAttachmentDataHash(database, oldInfo.hash, dataInfo); - } - - values.put(DATA, dataInfo.file.getAbsolutePath()); - values.put(SIZE, dataInfo.length); - values.put(DATA_RANDOM, dataInfo.random); - values.put(DATA_HASH, dataInfo.hash); - - String visualHashString = getVisualHashStringOrNull(placeholder); - if (visualHashString != null) { - values.put(VISUAL_HASH, visualHashString); - } - - values.put(TRANSFER_STATE, TRANSFER_PROGRESS_DONE); - values.put(TRANSFER_FILE, (String) null); - - values.put(TRANSFORM_PROPERTIES, TransformProperties.forSkipTransform().serialize()); - - updated = database.update(TABLE_NAME, values, PART_ID_WHERE, attachmentId.toStrings()) > 0; - - database.setTransactionSuccessful(); - } finally { - database.endTransaction(); - } - - if (updated) { - long threadId = SignalDatabase.messages().getThreadIdForMessage(mmsId); - - if (!SignalDatabase.messages().isStory(mmsId)) { - SignalDatabase.threads().updateSnippetUriSilently(threadId, PartAuthority.getAttachmentDataUri(attachmentId)); - } - - notifyConversationListeners(threadId); - notifyConversationListListeners(); - notifyAttachmentListeners(); - } else { - if (!dataInfo.file.delete()) { - Log.w(TAG, "Failed to delete unused attachment"); - } - } - - if (transferFile != null) { - if (!transferFile.delete()) { - Log.w(TAG, "Unable to delete transfer file."); - } - } - - if (placeholder != null && MediaUtil.isAudio(placeholder)) { - GenerateAudioWaveFormJob.enqueue(placeholder.getAttachmentId()); - } - } - - private static @Nullable String getVisualHashStringOrNull(@Nullable Attachment attachment) { - if (attachment == null) return null; - else if (attachment.getBlurHash() != null) return attachment.getBlurHash().getHash(); - else if (attachment.getAudioHash() != null) return attachment.getAudioHash().getHash(); - else return null; - } - - public void copyAttachmentData(@NonNull AttachmentId sourceId, @NonNull AttachmentId destinationId) - throws MmsException - { - DatabaseAttachment sourceAttachment = getAttachment(sourceId); - - if (sourceAttachment == null) { - throw new MmsException("Cannot find attachment for source!"); - } - - SQLiteDatabase database = databaseHelper.getSignalWritableDatabase(); - DataInfo sourceDataInfo = getAttachmentDataFileInfo(sourceId, DATA); - - if (sourceDataInfo == null) { - throw new MmsException("No attachment data found for source!"); - } - - ContentValues contentValues = new ContentValues(); - - contentValues.put(DATA, sourceDataInfo.file.getAbsolutePath()); - contentValues.put(DATA_HASH, sourceDataInfo.hash); - contentValues.put(SIZE, sourceDataInfo.length); - contentValues.put(DATA_RANDOM, sourceDataInfo.random); - - contentValues.put(TRANSFER_STATE, sourceAttachment.getTransferState()); - contentValues.put(CDN_NUMBER, sourceAttachment.getCdnNumber()); - contentValues.put(CONTENT_LOCATION, sourceAttachment.getLocation()); - contentValues.put(DIGEST, sourceAttachment.getDigest()); - contentValues.put(MAC_DIGEST, sourceAttachment.getIncrementalDigest()); - contentValues.put(INCREMENTAL_MAC_CHUNK_SIZE, sourceAttachment.getIncrementalMacChunkSize()); - contentValues.put(CONTENT_DISPOSITION, sourceAttachment.getKey()); - contentValues.put(NAME, sourceAttachment.getRelay()); - contentValues.put(SIZE, sourceAttachment.getSize()); - contentValues.put(FAST_PREFLIGHT_ID, sourceAttachment.getFastPreflightId()); - contentValues.put(WIDTH, sourceAttachment.getWidth()); - contentValues.put(HEIGHT, sourceAttachment.getHeight()); - contentValues.put(CONTENT_TYPE, sourceAttachment.getContentType()); - contentValues.put(VISUAL_HASH, getVisualHashStringOrNull(sourceAttachment)); - contentValues.put(TRANSFORM_PROPERTIES, sourceAttachment.getTransformProperties().serialize()); - - database.update(TABLE_NAME, contentValues, PART_ID_WHERE, destinationId.toStrings()); - } - - public void updateAttachmentCaption(@NonNull AttachmentId id, @Nullable String caption) { - ContentValues values = new ContentValues(1); - values.put(CAPTION, caption); - - databaseHelper.getSignalWritableDatabase().update(TABLE_NAME, values, PART_ID_WHERE, id.toStrings()); - } - - public void updateDisplayOrder(@NonNull Map orderMap) { - SQLiteDatabase db = databaseHelper.getSignalWritableDatabase(); - - db.beginTransaction(); - try { - for (Map.Entry entry : orderMap.entrySet()) { - ContentValues values = new ContentValues(1); - values.put(DISPLAY_ORDER, entry.getValue()); - - databaseHelper.getSignalWritableDatabase().update(TABLE_NAME, values, PART_ID_WHERE, entry.getKey().toStrings()); - } - - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } - - } - - public void updateAttachmentAfterUpload(@NonNull AttachmentId id, @NonNull Attachment attachment, long uploadTimestamp) { - SQLiteDatabase database = databaseHelper.getSignalWritableDatabase(); - DataInfo dataInfo = getAttachmentDataFileInfo(id, DATA); - ContentValues values = new ContentValues(); - - values.put(TRANSFER_STATE, TRANSFER_PROGRESS_DONE); - values.put(CDN_NUMBER, attachment.getCdnNumber()); - values.put(CONTENT_LOCATION, attachment.getLocation()); - values.put(DIGEST, attachment.getDigest()); - values.put(MAC_DIGEST, attachment.getIncrementalDigest()); - values.put(INCREMENTAL_MAC_CHUNK_SIZE, attachment.getIncrementalMacChunkSize()); - values.put(CONTENT_DISPOSITION, attachment.getKey()); - values.put(NAME, attachment.getRelay()); - values.put(SIZE, attachment.getSize()); - values.put(FAST_PREFLIGHT_ID, attachment.getFastPreflightId()); - values.put(VISUAL_HASH, getVisualHashStringOrNull(attachment)); - values.put(UPLOAD_TIMESTAMP, uploadTimestamp); - - if (dataInfo != null && dataInfo.hash != null) { - updateAttachmentAndMatchingHashes(database, id, dataInfo.hash, values); - } else { - database.update(TABLE_NAME, values, PART_ID_WHERE, id.toStrings()); - } - } - - public @NonNull DatabaseAttachment insertAttachmentForPreUpload(@NonNull Attachment attachment) throws MmsException { - Map result = insertAttachmentsForMessage(PREUPLOAD_MESSAGE_ID, - Collections.singletonList(attachment), - Collections.emptyList()); - - if (result.values().isEmpty()) { - throw new MmsException("Bad attachment result!"); - } - - DatabaseAttachment databaseAttachment = getAttachment(result.values().iterator().next()); - - if (databaseAttachment == null) { - throw new MmsException("Failed to retrieve attachment we just inserted!"); - } - - return databaseAttachment; - } - - public void updateMessageId(@NonNull Collection attachmentIds, long mmsId, boolean isStory) { - SQLiteDatabase db = databaseHelper.getSignalWritableDatabase(); - - db.beginTransaction(); - try { - ContentValues values = new ContentValues(1); - values.put(MMS_ID, mmsId); - - if (!isStory) { - values.putNull(CAPTION); - } - - int updatedCount = 0; - int attachmentIdSize = 0; - for (AttachmentId attachmentId : attachmentIds) { - attachmentIdSize++; - updatedCount = db.update(TABLE_NAME, values, PART_ID_WHERE, attachmentId.toStrings()); - } - - Log.d(TAG, "[updateMessageId] Updated " + updatedCount + " out of " + attachmentIdSize + " ids."); - - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } - } - - @NonNull Map insertAttachmentsForMessage(long mmsId, @NonNull List attachments, @NonNull List quoteAttachment) - throws MmsException - { - if (attachments.isEmpty() && quoteAttachment.isEmpty()) { - return Collections.emptyMap(); - } - - Log.d(TAG, "insertParts(" + attachments.size() + ")"); - - Map insertedAttachments = new HashMap<>(); - - for (Attachment attachment : attachments) { - AttachmentId attachmentId = insertAttachment(mmsId, attachment, attachment.isQuote()); - insertedAttachments.put(attachment, attachmentId); - Log.i(TAG, "Inserted attachment at ID: " + attachmentId); - } - - try { - for (Attachment attachment : quoteAttachment) { - AttachmentId attachmentId = insertAttachment(mmsId, attachment, true); - insertedAttachments.put(attachment, attachmentId); - Log.i(TAG, "Inserted quoted attachment at ID: " + attachmentId); - } - } catch (MmsException e) { - Log.w(TAG, "Failed to insert quote attachment! messageId: " + mmsId); - } - - return insertedAttachments; - } - - /** - * @param onlyModifyThisAttachment If false and more than one attachment shares this file and quality, they will all - * be updated. If true, then guarantees not to affect other attachments. - */ - public void updateAttachmentData(@NonNull DatabaseAttachment databaseAttachment, - @NonNull MediaStream mediaStream, - boolean onlyModifyThisAttachment) - throws MmsException, IOException - { - SQLiteDatabase database = databaseHelper.getSignalWritableDatabase(); - DataInfo oldDataInfo = getAttachmentDataFileInfo(databaseAttachment.getAttachmentId(), DATA); - - if (oldDataInfo == null) { - throw new MmsException("No attachment data found!"); - } - - File destination = oldDataInfo.file; - - boolean isSingleUseOfData = onlyModifyThisAttachment || oldDataInfo.hash == null; - if (isSingleUseOfData) { - if (fileReferencedByMoreThanOneAttachment(destination)) { - Log.i(TAG, "Creating a new file as this one is used by more than one attachment"); - destination = newFile(); - } - } - - DataInfo dataInfo = storeAttachmentStream(destination, mediaStream.getStream()); - - database.beginTransaction(); - try { - dataInfo = deduplicateAttachment(dataInfo, databaseAttachment.getAttachmentId(), databaseAttachment.getTransformProperties()); - - ContentValues contentValues = new ContentValues(); - contentValues.put(SIZE, dataInfo.length); - contentValues.put(CONTENT_TYPE, mediaStream.getMimeType()); - contentValues.put(WIDTH, mediaStream.getWidth()); - contentValues.put(HEIGHT, mediaStream.getHeight()); - contentValues.put(DATA, dataInfo.file.getAbsolutePath()); - contentValues.put(DATA_RANDOM, dataInfo.random); - contentValues.put(DATA_HASH, dataInfo.hash); - - int updateCount = updateAttachmentAndMatchingHashes(database, - databaseAttachment.getAttachmentId(), - isSingleUseOfData ? dataInfo.hash : oldDataInfo.hash, - contentValues); - - Log.i(TAG, "[updateAttachmentData] Updated " + updateCount + " rows."); - - database.setTransactionSuccessful(); - } finally { - database.endTransaction(); - } - } - - /** - * Returns true if the file referenced by two or more attachments. - * Returns false if the file is referenced by zero or one attachments. - */ - private boolean fileReferencedByMoreThanOneAttachment(@NonNull File file) { - SQLiteDatabase database = databaseHelper.getSignalReadableDatabase(); - String selection = DATA + " = ?"; - String[] args = new String[]{file.getAbsolutePath()}; - - try (Cursor cursor = database.query(TABLE_NAME, null, selection, args, null, null, null, "2")) { - return cursor != null && cursor.moveToFirst() && cursor.moveToNext(); - } - } - - public void markAttachmentAsTransformed(@NonNull AttachmentId attachmentId) { - getWritableDatabase().beginTransaction(); - try { - updateAttachmentTransformProperties(attachmentId, getTransformProperties(attachmentId).withSkipTransform()); - getWritableDatabase().setTransactionSuccessful(); - } catch (Exception e) { - Log.w(TAG, "Could not mark attachment as transformed.", e); - } finally { - getWritableDatabase().endTransaction(); - } - } - - public @NonNull TransformProperties getTransformProperties(@NonNull AttachmentId attachmentId) { - String[] projection = SqlUtil.buildArgs(TRANSFORM_PROPERTIES); - String[] args = attachmentId.toStrings(); - - try (Cursor cursor = getWritableDatabase().query(TABLE_NAME, projection, PART_ID_WHERE, args, null, null, null, null)) { - if (cursor.moveToFirst()) { - String serializedProperties = CursorUtil.requireString(cursor, TRANSFORM_PROPERTIES); - return TransformProperties.parse(serializedProperties); - } else { - throw new AssertionError("No such attachment."); - } - } - } - - private void updateAttachmentTransformProperties(@NonNull AttachmentId attachmentId, @NonNull TransformProperties transformProperties) { - DataInfo dataInfo = getAttachmentDataFileInfo(attachmentId, DATA); - - if (dataInfo == null) { - Log.w(TAG, "[updateAttachmentTransformProperties] No data info found!"); - return; - } - - ContentValues contentValues = new ContentValues(); - contentValues.put(TRANSFORM_PROPERTIES, transformProperties.serialize()); - - int updateCount = updateAttachmentAndMatchingHashes(databaseHelper.getSignalWritableDatabase(), attachmentId, dataInfo.hash, contentValues); - Log.i(TAG, "[updateAttachmentTransformProperties] Updated " + updateCount + " rows."); - } - - public @NonNull File getOrCreateTransferFile(@NonNull AttachmentId attachmentId) throws IOException { - SQLiteDatabase db = databaseHelper.getSignalWritableDatabase(); - File existing = getTransferFile(db, attachmentId); - - if (existing != null) { - return existing; - } - - File transferFile = newTransferFile(); - ContentValues values = new ContentValues(); - values.put(TRANSFER_FILE, transferFile.getAbsolutePath()); - - db.update(TABLE_NAME, values, PART_ID_WHERE, attachmentId.toStrings()); - - return transferFile; - } - - private @Nullable static File getTransferFile(@NonNull SQLiteDatabase db, @NonNull AttachmentId attachmentId) { - try (Cursor cursor = db.query(TABLE_NAME, new String[] { TRANSFER_FILE }, PART_ID_WHERE, attachmentId.toStrings(), null, null, "1")) { - if (cursor != null && cursor.moveToFirst()) { - String path = cursor.getString(cursor.getColumnIndexOrThrow(TRANSFER_FILE)); - if (path != null) { - return new File(path); - } - } - } - - return null; - } - - private static int updateAttachmentAndMatchingHashes(@NonNull SQLiteDatabase database, - @NonNull AttachmentId attachmentId, - @Nullable String dataHash, - @NonNull ContentValues contentValues) - { - String selection = "(" + ROW_ID + " = ? AND " + UNIQUE_ID + " = ?) OR " + - "(" + DATA_HASH + " NOT NULL AND " + DATA_HASH + " = ?)"; - String[] args = new String[]{String.valueOf(attachmentId.getRowId()), - String.valueOf(attachmentId.getUniqueId()), - String.valueOf(dataHash)}; - - return database.update(TABLE_NAME, contentValues, selection, args); - } - - private static void updateAttachmentDataHash(@NonNull SQLiteDatabase database, - @NonNull String oldHash, - @NonNull DataInfo newData) - { - if (oldHash == null) return; - - ContentValues contentValues = new ContentValues(); - contentValues.put(DATA, newData.file.getAbsolutePath()); - contentValues.put(DATA_RANDOM, newData.random); - contentValues.put(DATA_HASH, newData.hash); - database.update(TABLE_NAME, - contentValues, - DATA_HASH + " = ?", - new String[]{oldHash}); - } - - public void markAttachmentUploaded(long messageId, Attachment attachment) { - ContentValues values = new ContentValues(1); - SQLiteDatabase database = databaseHelper.getSignalWritableDatabase(); - - values.put(TRANSFER_STATE, TRANSFER_PROGRESS_DONE); - database.update(TABLE_NAME, values, PART_ID_WHERE, ((DatabaseAttachment)attachment).getAttachmentId().toStrings()); - - notifyConversationListeners(SignalDatabase.messages().getThreadIdForMessage(messageId)); - } - - public void setTransferState(long messageId, @NonNull AttachmentId attachmentId, int transferState) { - final ContentValues values = new ContentValues(1); - final SQLiteDatabase database = databaseHelper.getSignalWritableDatabase(); - - values.put(TRANSFER_STATE, transferState); - database.update(TABLE_NAME, values, PART_ID_WHERE, attachmentId.toStrings()); - notifyConversationListeners(SignalDatabase.messages().getThreadIdForMessage(messageId)); - } - - /** - * Returns (pack_id, pack_key) pairs that are referenced in attachments but not in the stickers - * database. - */ - public @Nullable Cursor getUnavailableStickerPacks() { - String query = "SELECT DISTINCT " + STICKER_PACK_ID + ", " + STICKER_PACK_KEY + - " FROM " + TABLE_NAME + - " WHERE " + - STICKER_PACK_ID + " NOT NULL AND " + - STICKER_PACK_KEY + " NOT NULL AND " + - STICKER_PACK_ID + " NOT IN (" + - "SELECT DISTINCT " + StickerTable.PACK_ID + " FROM " + StickerTable.TABLE_NAME + - ")"; - - return databaseHelper.getSignalReadableDatabase().rawQuery(query, null); - } - - public boolean hasStickerAttachments() { - String selection = STICKER_PACK_ID + " NOT NULL"; - - try (Cursor cursor = databaseHelper.getSignalReadableDatabase().query(TABLE_NAME, null, selection, null, null, null, null, "1")) { - return cursor != null && cursor.moveToFirst(); - } - } - - @SuppressWarnings("WeakerAccess") - private @Nullable InputStream getDataStream(AttachmentId attachmentId, String dataType, long offset) - throws FileNotFoundException - { - DataInfo dataInfo = getAttachmentDataFileInfo(attachmentId, dataType); - - if (dataInfo == null) { - return null; - } - - try { - return ModernDecryptingPartInputStream.createFor(attachmentSecret, dataInfo.random, dataInfo.file, offset); - } catch (FileNotFoundException e) { - Log.w(TAG, e); - throw e; - } catch (IOException e) { - Log.w(TAG, e); - return null; - } - } - - @VisibleForTesting - @Nullable DataInfo getAttachmentDataFileInfo(@NonNull AttachmentId attachmentId, @NonNull String dataType) - { - SQLiteDatabase database = databaseHelper.getSignalReadableDatabase(); - - try (Cursor cursor = database.query(TABLE_NAME, new String[] { dataType, SIZE, DATA_RANDOM, DATA_HASH, TRANSFORM_PROPERTIES }, PART_ID_WHERE, attachmentId.toStrings(), null, null, null)) { - if (cursor != null && cursor.moveToFirst()) { - if (cursor.isNull(cursor.getColumnIndexOrThrow(dataType))) { - return null; - } - - return new DataInfo(new File(cursor.getString(cursor.getColumnIndexOrThrow(dataType))), - cursor.getLong(cursor.getColumnIndexOrThrow(SIZE)), - cursor.getBlob(cursor.getColumnIndexOrThrow(DATA_RANDOM)), - cursor.getString(cursor.getColumnIndexOrThrow(DATA_HASH)), - TransformProperties.parse(CursorUtil.requireString(cursor, TRANSFORM_PROPERTIES))); - } else { - return null; - } - } - - } - - private @NonNull DataInfo storeAttachmentStream(@NonNull InputStream in) throws MmsException { - try { - return storeAttachmentStream(newFile(), in); - } catch (IOException e) { - throw new MmsException(e); - } - } - - public File newFile() throws IOException { - return newFile(context); - } - - private File newTransferFile() throws IOException { - File partsDirectory = context.getDir(DIRECTORY, Context.MODE_PRIVATE); - return PartFileProtector.protect(() -> File.createTempFile("transfer", ".mms", partsDirectory)); - } - - public static File newFile(Context context) throws IOException { - File partsDirectory = context.getDir(DIRECTORY, Context.MODE_PRIVATE); - return PartFileProtector.protect(() -> File.createTempFile("part", ".mms", partsDirectory)); - } - - /** - * Reads the entire stream and saves to disk. If you need to deduplicate attachments, call {@link #deduplicateAttachment(DataInfo, AttachmentId, TransformProperties)} - * afterwards and use the {@link DataInfo} returned by it instead. - */ - private @NonNull DataInfo storeAttachmentStream(@NonNull File destination, @NonNull InputStream in) throws MmsException { - try { - File tempFile = newFile(); - MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); - DigestInputStream digestInputStream = new DigestInputStream(in, messageDigest); - Pair out = ModernEncryptingPartOutputStream.createFor(attachmentSecret, tempFile, false); - long length = StreamUtil.copy(digestInputStream, out.second); - String hash = Base64.encodeWithPadding(digestInputStream.getMessageDigest().digest()); - - if (!tempFile.renameTo(destination)) { - Log.w(TAG, "Couldn't rename " + tempFile.getPath() + " to " + destination.getPath()); - tempFile.delete(); - throw new IllegalStateException("Couldn't rename " + tempFile.getPath() + " to " + destination.getPath()); - } - - return new DataInfo(destination, length, out.first, hash, null); - } catch (IOException | NoSuchAlgorithmException e) { - throw new MmsException(e); - } - } - - private @NonNull DataInfo deduplicateAttachment(@NonNull DataInfo dataInfo, - @Nullable AttachmentId attachmentId, - @NonNull TransformProperties transformProperties) - { - SQLiteDatabase db = databaseHelper.getSignalWritableDatabase(); - - if (!db.inTransaction()) { - throw new IllegalStateException("Must be in a transaction!"); - } - - List sharedDataInfos = findDuplicateDataFileInfos(db, dataInfo.hash, attachmentId); - for (DataInfo sharedDataInfo : sharedDataInfos) { - if (dataInfo.file.equals(sharedDataInfo.file)) { - continue; - } - - boolean isUsedElsewhere = isAttachmentFileUsedByOtherAttachments(attachmentId, dataInfo); - boolean isSameQuality = transformProperties.sentMediaQuality == sharedDataInfo.transformProperties.sentMediaQuality; - - Log.i(TAG, "[deduplicateAttachment] Potential duplicate data file found. usedElsewhere: " + isUsedElsewhere + " sameQuality: " + isSameQuality + " otherFile: " + sharedDataInfo.file.getAbsolutePath()); - - if (!isSameQuality) { - continue; - } - - if (!isUsedElsewhere) { - if (dataInfo.file.delete()) { - Log.i(TAG, "[deduplicateAttachment] Deleted original file. " + dataInfo.file); - } else { - Log.w(TAG, "[deduplicateAttachment] Original file could not be deleted."); - } - } - - return sharedDataInfo; - } - - Log.i(TAG, "[deduplicateAttachment] No acceptable matching attachment data found. " + dataInfo.file.getAbsolutePath()); - return dataInfo; - } - - private static @NonNull List findDuplicateDataFileInfos(@NonNull SQLiteDatabase database, - @NonNull String hash, - @Nullable AttachmentId excludedAttachmentId) - { - if (!database.inTransaction()) { - throw new IllegalArgumentException("Must be in a transaction!"); - } - - Pair selectorArgs = buildSharedFileSelectorArgs(hash, excludedAttachmentId); - return CursorExtensionsKt.readToList(database.query(TABLE_NAME, - new String[] { DATA, DATA_RANDOM, SIZE, TRANSFORM_PROPERTIES }, - selectorArgs.first, - selectorArgs.second, - null, - null, - null, - null), - cursor -> new DataInfo(new File(CursorUtil.requireString(cursor, DATA)), - CursorUtil.requireLong(cursor, SIZE), - CursorUtil.requireBlob(cursor, DATA_RANDOM), - hash, - TransformProperties.parse(CursorUtil.requireString(cursor, TRANSFORM_PROPERTIES)))); - } - - private static Pair buildSharedFileSelectorArgs(@NonNull String newHash, - @Nullable AttachmentId attachmentId) - { - final String selector; - final String[] selection; - - if (attachmentId == null) { - selector = DATA_HASH + " = ?"; - selection = new String[]{newHash}; - } else { - selector = PART_ID_WHERE_NOT + " AND " + DATA_HASH + " = ?"; - selection = new String[]{Long.toString(attachmentId.getRowId()), - Long.toString(attachmentId.getUniqueId()), - newHash}; - } - - return Pair.create(selector, selection); - } - - public List getAttachments(@NonNull Cursor cursor) { - try { - if (cursor.getColumnIndex(AttachmentTable.ATTACHMENT_JSON_ALIAS) != -1) { - if (cursor.isNull(cursor.getColumnIndexOrThrow(ATTACHMENT_JSON_ALIAS))) { - return new LinkedList<>(); - } - - List result = new LinkedList<>(); - JSONArray array = new JSONArray(cursor.getString(cursor.getColumnIndexOrThrow(ATTACHMENT_JSON_ALIAS))); - - for (int i=0;i= 0 - ? new StickerLocator(object.getString(STICKER_PACK_ID), - object.getString(STICKER_PACK_KEY), - object.getInt(STICKER_ID), - object.getString(STICKER_EMOJI)) - : null, - MediaUtil.isAudioType(contentType) ? null : BlurHash.parseOrNull(object.getString(VISUAL_HASH)), - MediaUtil.isAudioType(contentType) ? AudioHash.parseOrNull(object.getString(VISUAL_HASH)) : null, - TransformProperties.parse(object.getString(TRANSFORM_PROPERTIES)), - object.getInt(DISPLAY_ORDER), - object.getLong(UPLOAD_TIMESTAMP))); - } - } - - return result; - } else { - return Collections.singletonList(getAttachment(cursor)); - } - } catch (JSONException e) { - throw new AssertionError(e); - } - } - - private @NonNull DatabaseAttachment getAttachment(@NonNull Cursor cursor) { - String contentType = cursor.getString(cursor.getColumnIndexOrThrow(CONTENT_TYPE)); - return new DatabaseAttachment(new AttachmentId(cursor.getLong(cursor.getColumnIndexOrThrow(ROW_ID)), - cursor.getLong(cursor.getColumnIndexOrThrow(UNIQUE_ID))), - cursor.getLong(cursor.getColumnIndexOrThrow(MMS_ID)), - !cursor.isNull(cursor.getColumnIndexOrThrow(DATA)), - MediaUtil.isImageType(contentType) || MediaUtil.isVideoType(contentType), - contentType, - cursor.getInt(cursor.getColumnIndexOrThrow(TRANSFER_STATE)), - cursor.getLong(cursor.getColumnIndexOrThrow(SIZE)), - cursor.getString(cursor.getColumnIndexOrThrow(FILE_NAME)), - cursor.getInt(cursor.getColumnIndexOrThrow(CDN_NUMBER)), - cursor.getString(cursor.getColumnIndexOrThrow(CONTENT_LOCATION)), - cursor.getString(cursor.getColumnIndexOrThrow(CONTENT_DISPOSITION)), - cursor.getString(cursor.getColumnIndexOrThrow(NAME)), - cursor.getBlob(cursor.getColumnIndexOrThrow(DIGEST)), - cursor.getBlob(cursor.getColumnIndexOrThrow(MAC_DIGEST)), - cursor.getInt(cursor.getColumnIndexOrThrow(INCREMENTAL_MAC_CHUNK_SIZE)), - cursor.getString(cursor.getColumnIndexOrThrow(FAST_PREFLIGHT_ID)), - cursor.getInt(cursor.getColumnIndexOrThrow(VOICE_NOTE)) == 1, - cursor.getInt(cursor.getColumnIndexOrThrow(BORDERLESS)) == 1, - cursor.getInt(cursor.getColumnIndexOrThrow(VIDEO_GIF)) == 1, - cursor.getInt(cursor.getColumnIndexOrThrow(WIDTH)), - cursor.getInt(cursor.getColumnIndexOrThrow(HEIGHT)), - cursor.getInt(cursor.getColumnIndexOrThrow(QUOTE)) == 1, - cursor.getString(cursor.getColumnIndexOrThrow(CAPTION)), - cursor.getInt(cursor.getColumnIndexOrThrow(STICKER_ID)) >= 0 - ? new StickerLocator(CursorUtil.requireString(cursor, STICKER_PACK_ID), - CursorUtil.requireString(cursor, STICKER_PACK_KEY), - CursorUtil.requireInt(cursor, STICKER_ID), - CursorUtil.requireString(cursor, STICKER_EMOJI)) - : null, - MediaUtil.isAudioType(contentType) ? null : BlurHash.parseOrNull(cursor.getString(cursor.getColumnIndexOrThrow(VISUAL_HASH))), - MediaUtil.isAudioType(contentType) ? AudioHash.parseOrNull(cursor.getString(cursor.getColumnIndexOrThrow(VISUAL_HASH))) : null, - TransformProperties.parse(cursor.getString(cursor.getColumnIndexOrThrow(TRANSFORM_PROPERTIES))), - cursor.getInt(cursor.getColumnIndexOrThrow(DISPLAY_ORDER)), - cursor.getLong(cursor.getColumnIndexOrThrow(UPLOAD_TIMESTAMP))); - } - - private AttachmentId insertAttachment(long mmsId, Attachment attachment, boolean quote) - throws MmsException - { - Log.d(TAG, "Inserting attachment for mms id: " + mmsId); - SQLiteDatabase database = databaseHelper.getSignalWritableDatabase(); - - AttachmentId attachmentId = null; - boolean notifyPacks = false; - - database.beginTransaction(); - try { - DataInfo dataInfo = null; - long uniqueId = System.currentTimeMillis(); - - if (attachment.getUri() != null) { - DataInfo storeDataInfo = storeAttachmentStream(PartAuthority.getAttachmentStream(context, attachment.getUri())); - Log.d(TAG, "Wrote part to file: " + storeDataInfo.file.getAbsolutePath()); - dataInfo = deduplicateAttachment(storeDataInfo, attachmentId, attachment.getTransformProperties()); - } - - Attachment template = attachment; - boolean useTemplateUpload = false; - - if (dataInfo != null && dataInfo.hash != null) { - List possibleTemplates = findTemplateAttachments(dataInfo.hash); - - for (Attachment possibleTemplate : possibleTemplates) { - useTemplateUpload = possibleTemplate.getUploadTimestamp() > attachment.getUploadTimestamp() && - possibleTemplate.getTransferState() == TRANSFER_PROGRESS_DONE && - possibleTemplate.getTransformProperties().shouldSkipTransform() && - possibleTemplate.getDigest() != null && - !attachment.getTransformProperties().isVideoEdited() && - possibleTemplate.getTransformProperties().sentMediaQuality == attachment.getTransformProperties().getSentMediaQuality(); - - if (useTemplateUpload) { - Log.i(TAG, "Found a duplicate attachment upon insertion. Using it as a template."); - template = possibleTemplate; - break; - } - } - } - - ContentValues contentValues = new ContentValues(); - contentValues.put(MMS_ID, mmsId); - contentValues.put(CONTENT_TYPE, template.getContentType()); - contentValues.put(TRANSFER_STATE, attachment.getTransferState()); - contentValues.put(UNIQUE_ID, uniqueId); - contentValues.put(CDN_NUMBER, useTemplateUpload ? template.getCdnNumber() : attachment.getCdnNumber()); - contentValues.put(CONTENT_LOCATION, useTemplateUpload ? template.getLocation() : attachment.getLocation()); - contentValues.put(DIGEST, useTemplateUpload ? template.getDigest() : attachment.getDigest()); - contentValues.put(MAC_DIGEST, useTemplateUpload ? template.getIncrementalDigest() : attachment.getIncrementalDigest()); - contentValues.put(INCREMENTAL_MAC_CHUNK_SIZE, useTemplateUpload ? template.getIncrementalMacChunkSize() : attachment.getIncrementalMacChunkSize()); - contentValues.put(CONTENT_DISPOSITION, useTemplateUpload ? template.getKey() : attachment.getKey()); - contentValues.put(NAME, useTemplateUpload ? template.getRelay() : attachment.getRelay()); - contentValues.put(FILE_NAME, StorageUtil.getCleanFileName(attachment.getFileName())); - contentValues.put(SIZE, template.getSize()); - contentValues.put(FAST_PREFLIGHT_ID, attachment.getFastPreflightId()); - contentValues.put(VOICE_NOTE, attachment.isVoiceNote() ? 1 : 0); - contentValues.put(BORDERLESS, attachment.isBorderless() ? 1 : 0); - contentValues.put(VIDEO_GIF, attachment.isVideoGif() ? 1 : 0); - contentValues.put(WIDTH, template.getWidth()); - contentValues.put(HEIGHT, template.getHeight()); - contentValues.put(QUOTE, quote); - contentValues.put(CAPTION, attachment.getCaption()); - contentValues.put(UPLOAD_TIMESTAMP, useTemplateUpload ? template.getUploadTimestamp() : attachment.getUploadTimestamp()); - if (attachment.getTransformProperties().isVideoEdited()) { - contentValues.putNull(VISUAL_HASH); - contentValues.put(TRANSFORM_PROPERTIES, attachment.getTransformProperties().serialize()); - } else { - contentValues.put(VISUAL_HASH, getVisualHashStringOrNull(template)); - contentValues.put(TRANSFORM_PROPERTIES, (useTemplateUpload ? template : attachment).getTransformProperties().serialize()); - } - - if (attachment.isSticker()) { - contentValues.put(STICKER_PACK_ID, attachment.getSticker().getPackId()); - contentValues.put(STICKER_PACK_KEY, attachment.getSticker().getPackKey()); - contentValues.put(STICKER_ID, attachment.getSticker().getStickerId()); - contentValues.put(STICKER_EMOJI, attachment.getSticker().getEmoji()); - } - - if (dataInfo != null) { - contentValues.put(DATA, dataInfo.file.getAbsolutePath()); - contentValues.put(SIZE, dataInfo.length); - contentValues.put(DATA_RANDOM, dataInfo.random); - if (attachment.getTransformProperties().isVideoEdited()) { - contentValues.putNull(DATA_HASH); - } else { - contentValues.put(DATA_HASH, dataInfo.hash); - } - } - - long rowId = database.insert(TABLE_NAME, null, contentValues); - - attachmentId = new AttachmentId(rowId, uniqueId); - notifyPacks = attachment.isSticker() && !hasStickerAttachments(); - - database.setTransactionSuccessful(); - } catch (IOException e) { - throw new MmsException(e); - } finally { - database.endTransaction(); - } - - if (notifyPacks) { - notifyStickerPackListeners(); - } - - notifyAttachmentListeners(); - - return attachmentId; - } - - private @NonNull List findTemplateAttachments(@NonNull String dataHash) { - String selection = DATA_HASH + " = ?"; - String[] args = new String[] { dataHash }; - - return CursorExtensionsKt.readToList(databaseHelper.getSignalReadableDatabase().query(TABLE_NAME, null, selection, args, null, null, null), this::getAttachment); - } - - @WorkerThread - public void writeAudioHash(@NonNull AttachmentId attachmentId, @Nullable AudioWaveFormData audioWaveForm) { - Log.i(TAG, "updating part audio wave form for #" + attachmentId); - - SQLiteDatabase database = databaseHelper.getSignalWritableDatabase(); - ContentValues values = new ContentValues(1); - - if (audioWaveForm != null) { - values.put(VISUAL_HASH, new AudioHash(audioWaveForm).getHash()); - } else { - values.putNull(VISUAL_HASH); - } - - database.update(TABLE_NAME, values, PART_ID_WHERE, attachmentId.toStrings()); - } - - - @RequiresApi(23) - public @Nullable MediaDataSource mediaDataSourceFor(@NonNull AttachmentId attachmentId, Boolean allowReadingFromTempFile) { - DataInfo dataInfo = getAttachmentDataFileInfo(attachmentId, DATA); - - if (dataInfo != null) { - return EncryptedMediaDataSource.createFor(attachmentSecret, dataInfo.file, dataInfo.random, dataInfo.length); - } - - if (allowReadingFromTempFile) { - Log.d(TAG, "Completed data file not found for video attachment, checking for in-progress files."); - - SQLiteDatabase database = databaseHelper.getSignalReadableDatabase(); - - File transferFile = getTransferFile(database, attachmentId); - - if (transferFile != null) { - return EncryptedMediaDataSource.createForDiskBlob(attachmentSecret, transferFile); - } - } - - Log.w(TAG, "No data file found for video attachment!"); - - return null; - } - - public void duplicateAttachmentsForMessage(long destinationMessageId, long sourceMessageId, Collection excludedIds) { - SQLiteDatabaseExtensionsKt.withinTransaction(getWritableDatabase(), db -> { - db.execSQL("CREATE TEMPORARY TABLE tmp_part AS SELECT * FROM " + TABLE_NAME + " WHERE " + MMS_ID + " = ?", SqlUtil.buildArgs(sourceMessageId)); - List queries = SqlUtil.buildCollectionQuery(ROW_ID, excludedIds); - for (SqlUtil.Query query : queries) { - db.delete("tmp_part", query.getWhere(), query.getWhereArgs()); - } - db.execSQL("UPDATE tmp_part SET " + ROW_ID + " = NULL, " + MMS_ID + " = ?", SqlUtil.buildArgs(destinationMessageId)); - db.execSQL("INSERT INTO " + TABLE_NAME + " SELECT * FROM tmp_part"); - db.execSQL("DROP TABLE tmp_part"); - return 0; - }); - } - - @VisibleForTesting - static class DataInfo { - final File file; - final long length; - final byte[] random; - final String hash; - final TransformProperties transformProperties; - - private DataInfo(File file, long length, byte[] random, String hash, TransformProperties transformProperties) { - this.file = file; - this.length = length; - this.random = random; - this.hash = hash; - this.transformProperties = transformProperties; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - final DataInfo dataInfo = (DataInfo) o; - return length == dataInfo.length && - Objects.equals(file, dataInfo.file) && - Arrays.equals(random, dataInfo.random) && - Objects.equals(hash, dataInfo.hash) && - Objects.equals(transformProperties, dataInfo.transformProperties); - } - - @Override - public int hashCode() { - int result = Objects.hash(file, length, hash, transformProperties); - result = 31 * result + Arrays.hashCode(random); - return result; - } - } - - private static final class DataUsageResult { - private final boolean hasStrongReference; - private final List removableWeakReferences; - - private static final DataUsageResult IN_USE = new DataUsageResult(true, Collections.emptyList()); - private static final DataUsageResult NOT_IN_USE = new DataUsageResult(false, Collections.emptyList()); - - DataUsageResult(@NonNull List removableWeakReferences) { - this(false, removableWeakReferences); - } - - private DataUsageResult(boolean hasStrongReference, @NonNull List removableWeakReferences) { - if (hasStrongReference && removableWeakReferences.size() > 0) { - throw new AssertionError(); - } - this.hasStrongReference = hasStrongReference; - this.removableWeakReferences = removableWeakReferences; - } - - boolean hasStrongReference() { - return hasStrongReference; - } - - /** - * Entries in here can be removed from the database. - *

- * Only possible to be non-empty when {@link #hasStrongReference} is false. - */ - @NonNull List getRemovableWeakReferences() { - return removableWeakReferences; - } - } - - public static final class TransformProperties implements Parcelable { - - private static final int DEFAULT_MEDIA_QUALITY = SentMediaQuality.STANDARD.getCode(); - - @JsonProperty private final boolean skipTransform; - @JsonProperty private final boolean videoTrim; - @JsonProperty private final long videoTrimStartTimeUs; - @JsonProperty private final long videoTrimEndTimeUs; - @JsonProperty private final int sentMediaQuality; - - @JsonCreator - public TransformProperties(@JsonProperty("skipTransform") boolean skipTransform, - @JsonProperty("videoTrim") boolean videoTrim, - @JsonProperty("videoTrimStartTimeUs") long videoTrimStartTimeUs, - @JsonProperty("videoTrimEndTimeUs") long videoTrimEndTimeUs, - @JsonProperty("sentMediaQuality") int sentMediaQuality) - { - this.skipTransform = skipTransform; - this.videoTrim = videoTrim; - this.videoTrimStartTimeUs = videoTrimStartTimeUs; - this.videoTrimEndTimeUs = videoTrimEndTimeUs; - this.sentMediaQuality = sentMediaQuality; - } - - protected TransformProperties(Parcel in) { - skipTransform = in.readByte() != 0; - videoTrim = in.readByte() != 0; - videoTrimStartTimeUs = in.readLong(); - videoTrimEndTimeUs = in.readLong(); - sentMediaQuality = in.readInt(); - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeByte((byte) (skipTransform ? 1 : 0)); - dest.writeByte((byte) (videoTrim ? 1 : 0)); - dest.writeLong(videoTrimStartTimeUs); - dest.writeLong(videoTrimEndTimeUs); - dest.writeInt(sentMediaQuality); - } - - @Override - public int describeContents() { - return 0; - } - - public static final Creator CREATOR = new Creator<>() { - @Override - public TransformProperties createFromParcel(Parcel in) { - return new TransformProperties(in); - } - - @Override - public TransformProperties[] newArray(int size) { - return new TransformProperties[size]; - } - }; - - public static @NonNull TransformProperties empty() { - return new TransformProperties(false, false, 0, 0, DEFAULT_MEDIA_QUALITY); - } - - public static @NonNull TransformProperties forSkipTransform() { - return new TransformProperties(true, false, 0, 0, DEFAULT_MEDIA_QUALITY); - } - - public static @NonNull TransformProperties forVideoTrim(long videoTrimStartTimeUs, long videoTrimEndTimeUs) { - return new TransformProperties(false, true, videoTrimStartTimeUs, videoTrimEndTimeUs, DEFAULT_MEDIA_QUALITY); - } - - public static @NonNull TransformProperties forSentMediaQuality(@NonNull Optional currentProperties, @NonNull SentMediaQuality sentMediaQuality) { - TransformProperties existing = currentProperties.orElse(empty()); - return new TransformProperties(existing.skipTransform, existing.videoTrim, existing.videoTrimStartTimeUs, existing.videoTrimEndTimeUs, sentMediaQuality.getCode()); - } - - public boolean shouldSkipTransform() { - return skipTransform; - } - - public boolean isVideoEdited() { - return isVideoTrim(); - } - - public boolean isVideoTrim() { - return videoTrim; - } - - public long getVideoTrimStartTimeUs() { - return videoTrimStartTimeUs; - } - - public long getVideoTrimEndTimeUs() { - return videoTrimEndTimeUs; - } - - public int getSentMediaQuality() { - return sentMediaQuality; - } - - @NonNull TransformProperties withSkipTransform() { - return new TransformProperties(true, false, 0, 0, sentMediaQuality); - } - - public @NonNull String serialize() { - return JsonUtil.toJson(this); - } - - static @NonNull TransformProperties parse(@Nullable String serialized) { - if (serialized == null) { - return empty(); - } - - try { - return JsonUtil.fromJson(serialized, TransformProperties.class); - } catch (IOException e) { - Log.w(TAG, "Failed to parse TransformProperties!", e); - return empty(); - } - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - final TransformProperties that = (TransformProperties) o; - return skipTransform == that.skipTransform && videoTrim == that.videoTrim && videoTrimStartTimeUs == that.videoTrimStartTimeUs && videoTrimEndTimeUs == that.videoTrimEndTimeUs && sentMediaQuality == that.sentMediaQuality; - } - - @Override - public int hashCode() { - return Objects.hash(skipTransform, videoTrim, videoTrimStartTimeUs, videoTrimEndTimeUs, sentMediaQuality); - } - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt new file mode 100644 index 0000000000..d906038e3f --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt @@ -0,0 +1,1647 @@ +/* + * Copyright (C) 2011 Whisper Systems + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.thoughtcrime.securesms.database + +import android.content.ContentValues +import android.content.Context +import android.database.Cursor +import android.media.MediaDataSource +import android.os.Parcelable +import android.text.TextUtils +import androidx.annotation.RequiresApi +import androidx.annotation.VisibleForTesting +import androidx.annotation.WorkerThread +import androidx.core.content.contentValuesOf +import com.bumptech.glide.Glide +import com.fasterxml.jackson.annotation.JsonProperty +import kotlinx.parcelize.IgnoredOnParcel +import kotlinx.parcelize.Parcelize +import org.json.JSONArray +import org.json.JSONException +import org.signal.core.util.Base64.encodeWithPadding +import org.signal.core.util.SqlUtil.buildArgs +import org.signal.core.util.SqlUtil.buildCollectionQuery +import org.signal.core.util.SqlUtil.buildSingleCollectionQuery +import org.signal.core.util.StreamUtil +import org.signal.core.util.ThreadUtil +import org.signal.core.util.delete +import org.signal.core.util.exists +import org.signal.core.util.forEach +import org.signal.core.util.groupBy +import org.signal.core.util.isNull +import org.signal.core.util.logging.Log +import org.signal.core.util.readToList +import org.signal.core.util.readToSingleObject +import org.signal.core.util.requireBlob +import org.signal.core.util.requireBoolean +import org.signal.core.util.requireInt +import org.signal.core.util.requireLong +import org.signal.core.util.requireNonNullBlob +import org.signal.core.util.requireNonNullString +import org.signal.core.util.requireString +import org.signal.core.util.select +import org.signal.core.util.update +import org.signal.core.util.withinTransaction +import org.thoughtcrime.securesms.attachments.Attachment +import org.thoughtcrime.securesms.attachments.AttachmentId +import org.thoughtcrime.securesms.attachments.DatabaseAttachment +import org.thoughtcrime.securesms.audio.AudioHash +import org.thoughtcrime.securesms.blurhash.BlurHash +import org.thoughtcrime.securesms.crypto.AttachmentSecret +import org.thoughtcrime.securesms.crypto.ModernDecryptingPartInputStream +import org.thoughtcrime.securesms.crypto.ModernEncryptingPartOutputStream +import org.thoughtcrime.securesms.database.SignalDatabase.Companion.messages +import org.thoughtcrime.securesms.database.SignalDatabase.Companion.stickers +import org.thoughtcrime.securesms.database.SignalDatabase.Companion.threads +import org.thoughtcrime.securesms.database.model.databaseprotos.AudioWaveFormData +import org.thoughtcrime.securesms.dependencies.ApplicationDependencies +import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob +import org.thoughtcrime.securesms.jobs.GenerateAudioWaveFormJob +import org.thoughtcrime.securesms.mms.MediaStream +import org.thoughtcrime.securesms.mms.MmsException +import org.thoughtcrime.securesms.mms.PartAuthority +import org.thoughtcrime.securesms.mms.SentMediaQuality +import org.thoughtcrime.securesms.stickers.StickerLocator +import org.thoughtcrime.securesms.util.FileUtils +import org.thoughtcrime.securesms.util.JsonUtils.SaneJSONObject +import org.thoughtcrime.securesms.util.MediaUtil +import org.thoughtcrime.securesms.util.StorageUtil +import org.thoughtcrime.securesms.video.EncryptedMediaDataSource +import org.whispersystems.signalservice.internal.util.JsonUtil +import java.io.File +import java.io.FileNotFoundException +import java.io.IOException +import java.io.InputStream +import java.security.DigestInputStream +import java.security.MessageDigest +import java.security.NoSuchAlgorithmException +import java.util.LinkedList +import java.util.Optional + +class AttachmentTable( + context: Context, + databaseHelper: SignalDatabase, + private val attachmentSecret: AttachmentSecret +) : DatabaseTable(context, databaseHelper) { + + companion object { + val TAG = Log.tag(AttachmentTable::class.java) + + const val TABLE_NAME = "attachment" + const val ID = "_id" + const val MESSAGE_ID = "message_id" + const val CONTENT_TYPE = "content_type" + const val REMOTE_KEY = "remote_key" + const val REMOTE_LOCATION = "remote_location" + const val REMOTE_DIGEST = "remote_digest" + const val REMOTE_INCREMENTAL_DIGEST = "remote_incremental_digest" + const val REMOTE_INCREMENTAL_DIGEST_CHUNK_SIZE = "remote_incremental_digest_chunk_size" + const val CDN_NUMBER = "cdn_number" + const val TRANSFER_STATE = "transfer_state" + const val TRANSFER_FILE = "transfer_file" + const val DATA_FILE = "data_file" + const val DATA_SIZE = "data_size" + const val DATA_RANDOM = "data_random" + const val DATA_HASH = "data_hash" + const val FILE_NAME = "file_name" + const val FAST_PREFLIGHT_ID = "fast_preflight_id" + const val VOICE_NOTE = "voice_note" + const val BORDERLESS = "borderless" + const val VIDEO_GIF = "video_gif" + const val QUOTE = "quote" + const val WIDTH = "width" + const val HEIGHT = "height" + const val CAPTION = "caption" + const val STICKER_PACK_ID = "sticker_pack_id" + const val STICKER_PACK_KEY = "sticker_pack_key" + const val STICKER_ID = "sticker_id" + const val STICKER_EMOJI = "sticker_emoji" + const val BLUR_HASH = "blur_hash" + const val TRANSFORM_PROPERTIES = "transform_properties" + const val DISPLAY_ORDER = "display_order" + const val UPLOAD_TIMESTAMP = "upload_timestamp" + + const val ATTACHMENT_JSON_ALIAS = "attachment_json" + + private const val DIRECTORY = "parts" + + const val TRANSFER_PROGRESS_DONE = 0 + const val TRANSFER_PROGRESS_STARTED = 1 + const val TRANSFER_PROGRESS_PENDING = 2 + const val TRANSFER_PROGRESS_FAILED = 3 + const val TRANSFER_PROGRESS_PERMANENT_FAILURE = 4 + const val PREUPLOAD_MESSAGE_ID: Long = -8675309 + + private val PROJECTION = arrayOf( + ID, + MESSAGE_ID, + CONTENT_TYPE, + REMOTE_KEY, + REMOTE_LOCATION, + REMOTE_DIGEST, + REMOTE_INCREMENTAL_DIGEST, + REMOTE_INCREMENTAL_DIGEST_CHUNK_SIZE, + CDN_NUMBER, + TRANSFER_STATE, + TRANSFER_FILE, + DATA_FILE, + DATA_SIZE, + DATA_RANDOM, + DATA_HASH, + FILE_NAME, + FAST_PREFLIGHT_ID, + VOICE_NOTE, + BORDERLESS, + VIDEO_GIF, + QUOTE, + WIDTH, + HEIGHT, + CAPTION, + STICKER_PACK_ID, + STICKER_PACK_KEY, + STICKER_ID, + STICKER_EMOJI, + BLUR_HASH, + TRANSFORM_PROPERTIES, + DISPLAY_ORDER, + UPLOAD_TIMESTAMP + ) + + const val CREATE_TABLE = """ + CREATE TABLE $TABLE_NAME ( + $ID INTEGER PRIMARY KEY AUTOINCREMENT, + $MESSAGE_ID INTEGER, + $CONTENT_TYPE TEXT, + $REMOTE_KEY TEXT, + $REMOTE_LOCATION TEXT, + $REMOTE_DIGEST BLOB, + $REMOTE_INCREMENTAL_DIGEST BLOB, + $REMOTE_INCREMENTAL_DIGEST_CHUNK_SIZE INTEGER DEFAULT 0, + $CDN_NUMBER INTEGER DEFAULT 0, + $TRANSFER_STATE INTEGER, + $TRANSFER_FILE TEXT DEFAULT NULL, + $DATA_FILE TEXT, + $DATA_SIZE INTEGER, + $DATA_RANDOM BLOB, + $DATA_HASH TEXT DEFAULT NULL, + $FILE_NAME TEXT, + $FAST_PREFLIGHT_ID TEXT, + $VOICE_NOTE INTEGER DEFAULT 0, + $BORDERLESS INTEGER DEFAULT 0, + $VIDEO_GIF INTEGER DEFAULT 0, + $QUOTE INTEGER DEFAULT 0, + $WIDTH INTEGER DEFAULT 0, + $HEIGHT INTEGER DEFAULT 0, + $CAPTION TEXT DEFAULT NULL, + $STICKER_PACK_ID TEXT DEFAULT NULL, + $STICKER_PACK_KEY DEFAULT NULL, + $STICKER_ID INTEGER DEFAULT -1, + $STICKER_EMOJI STRING DEFAULT NULL, + $BLUR_HASH TEXT DEFAULT NULL, + $TRANSFORM_PROPERTIES TEXT DEFAULT NULL, + $DISPLAY_ORDER INTEGER DEFAULT 0, + $UPLOAD_TIMESTAMP INTEGER DEFAULT 0 + ) + """ + + @JvmField + val CREATE_INDEXS = arrayOf( + "CREATE INDEX IF NOT EXISTS attachment_message_id_index ON $TABLE_NAME ($MESSAGE_ID);", + "CREATE INDEX IF NOT EXISTS attachment_transfer_state_index ON $TABLE_NAME ($TRANSFER_STATE);", + "CREATE INDEX IF NOT EXISTS attachment_sticker_pack_id_index ON $TABLE_NAME ($STICKER_PACK_ID);", + "CREATE INDEX IF NOT EXISTS attachment_data_hash_index ON $TABLE_NAME ($DATA_HASH);", + "CREATE INDEX IF NOT EXISTS attachment_data_index ON $TABLE_NAME ($DATA_FILE);" + ) + + @JvmStatic + @JvmOverloads + @Throws(IOException::class) + fun newFile(context: Context): File { + val partsDirectory = context.getDir(DIRECTORY, Context.MODE_PRIVATE) + return PartFileProtector.protect { File.createTempFile("part", ".mms", partsDirectory) } + } + } + + @Throws(IOException::class) + fun getAttachmentStream(attachmentId: AttachmentId, offset: Long): InputStream { + return try { + getDataStream(attachmentId, DATA_FILE, offset) + } catch (e: FileNotFoundException) { + throw IOException("No stream for: $attachmentId", e) + } ?: throw IOException("No stream for: $attachmentId") + } + + fun getAttachment(attachmentId: AttachmentId): DatabaseAttachment? { + return readableDatabase + .select(*PROJECTION) + .from(TABLE_NAME) + .where("$ID = ?", attachmentId.id) + .run() + .readToList { it.readAttachments() } + .flatten() + .firstOrNull() + } + + fun getAttachmentsForMessage(mmsId: Long): List { + return readableDatabase + .select(*PROJECTION) + .from(TABLE_NAME) + .where("$MESSAGE_ID = ?", mmsId) + .orderBy("$ID ASC") + .run() + .readToList { it.readAttachments() } + .flatten() + } + + fun getAttachmentsForMessages(mmsIds: Collection): Map> { + if (mmsIds.isEmpty()) { + return emptyMap() + } + + val query = buildSingleCollectionQuery(MESSAGE_ID, mmsIds) + + return readableDatabase + .select(*PROJECTION) + .from(TABLE_NAME) + .where(query.where, query.whereArgs) + .orderBy("$ID ASC") + .run() + .groupBy { cursor -> + val attachment = cursor.readAttachment() + attachment.mmsId to attachment + } + } + + fun hasAttachment(id: AttachmentId): Boolean { + return readableDatabase + .exists(TABLE_NAME) + .where("$ID = ?", id.id) + .run() + } + + fun getPendingAttachments(): List { + return readableDatabase + .select(*PROJECTION) + .from(TABLE_NAME) + .where("$TRANSFER_STATE = ?", TRANSFER_PROGRESS_STARTED.toString()) + .run() + .readToList { it.readAttachments() } + .flatten() + } + + fun deleteAttachmentsForMessage(mmsId: Long): Boolean { + Log.d(TAG, "[deleteAttachmentsForMessage] mmsId: $mmsId") + + return writableDatabase.withinTransaction { db -> + db.select(DATA_FILE, CONTENT_TYPE, ID) + .from(TABLE_NAME) + .where("$MESSAGE_ID = ?", mmsId) + .run() + .forEach { cursor -> + val attachmentId = AttachmentId(cursor.requireLong(ID)) + + ApplicationDependencies.getJobManager().cancelAllInQueue(AttachmentDownloadJob.constructQueueString(attachmentId)) + + deleteAttachmentOnDisk( + data = cursor.requireString(DATA_FILE), + contentType = cursor.requireString(CONTENT_TYPE), + attachmentId = attachmentId + ) + } + + val deleteCount = db.delete(TABLE_NAME) + .where("$MESSAGE_ID = ?", mmsId) + .run() + + notifyAttachmentListeners() + + deleteCount > 0 + } + } + + /** + * Deletes all attachments with an ID of [PREUPLOAD_MESSAGE_ID]. These represent + * attachments that were pre-uploaded and haven't been assigned to a message. This should only be + * done when you *know* that all attachments *should* be assigned a real mmsId. For instance, when + * the app starts. Otherwise you could delete attachments that are legitimately being + * pre-uploaded. + */ + fun deleteAbandonedPreuploadedAttachments(): Int { + var count = 0 + + writableDatabase + .select(ID) + .from(TABLE_NAME) + .where("$MESSAGE_ID = ?", PREUPLOAD_MESSAGE_ID) + .run() + .forEach { cursor -> + val id = AttachmentId(cursor.requireLong(ID)) + deleteAttachment(id) + count++ + } + + return count + } + + fun deleteAttachmentFilesForViewOnceMessage(messageId: Long) { + Log.d(TAG, "[deleteAttachmentFilesForViewOnceMessage] messageId: $messageId") + + writableDatabase.withinTransaction { db -> + db.select(DATA_FILE, CONTENT_TYPE, ID) + .from(TABLE_NAME) + .where("$MESSAGE_ID = ?", messageId) + .run() + .forEach { cursor -> + deleteAttachmentOnDisk( + data = cursor.requireString(DATA_FILE), + contentType = cursor.requireString(CONTENT_TYPE), + attachmentId = AttachmentId(cursor.requireLong(ID)) + ) + } + + db.update(TABLE_NAME) + .values( + DATA_FILE to null, + DATA_RANDOM to null, + DATA_HASH to null, + FILE_NAME to null, + CAPTION to null, + DATA_SIZE to 0, + WIDTH to 0, + HEIGHT to 0, + TRANSFER_STATE to TRANSFER_PROGRESS_DONE, + BLUR_HASH to null, + CONTENT_TYPE to MediaUtil.VIEW_ONCE + ) + .where("$MESSAGE_ID = ?", messageId) + .run() + + notifyAttachmentListeners() + + val threadId = messages.getThreadIdForMessage(messageId) + if (threadId > 0) { + notifyConversationListeners(threadId) + } + } + } + + fun deleteAttachment(id: AttachmentId) { + Log.d(TAG, "[deleteAttachment] attachmentId: $id") + + writableDatabase.withinTransaction { db -> + db.select(DATA_FILE, CONTENT_TYPE) + .from(TABLE_NAME) + .where("$ID = ?", id.id) + .run() + .use { cursor -> + if (!cursor.moveToFirst()) { + Log.w(TAG, "Tried to delete an attachment, but it didn't exist.") + return@withinTransaction + } + + val data = cursor.requireString(DATA_FILE) + val contentType = cursor.requireString(CONTENT_TYPE) + + deleteAttachmentOnDisk( + data = data, + contentType = contentType, + attachmentId = id + ) + + db.delete(TABLE_NAME) + .where("$ID = ?", id.id) + .run() + + deleteAttachmentOnDisk(data, contentType, id) + notifyAttachmentListeners() + } + } + } + + fun trimAllAbandonedAttachments() { + val deleteCount = writableDatabase + .delete(TABLE_NAME) + .where("$MESSAGE_ID != $PREUPLOAD_MESSAGE_ID AND $MESSAGE_ID NOT IN (SELECT ${MessageTable.ID} FROM ${MessageTable.TABLE_NAME})") + .run() + + if (deleteCount > 0) { + Log.i(TAG, "Trimmed $deleteCount abandoned attachments.") + } + } + + fun deleteAbandonedAttachmentFiles(): Int { + val diskFiles = context.getDir(DIRECTORY, Context.MODE_PRIVATE).listFiles() ?: return 0 + + val filesOnDisk: Set = diskFiles + .filter { file: File -> !PartFileProtector.isProtected(file) } + .map { file: File -> file.absolutePath } + .toSet() + + val filesInDb: Set = readableDatabase + .select(DATA_FILE) + .from(TABLE_NAME) + .run() + .readToList { it.requireString(DATA_FILE) } + .filterNotNull() + .toSet() + stickers.allStickerFiles + + val onDiskButNotInDatabase: Set = filesOnDisk - filesInDb + + for (filePath in onDiskButNotInDatabase) { + val success = File(filePath).delete() + if (!success) { + Log.w(TAG, "[deleteAbandonedAttachmentFiles] Failed to delete attachment file. $filePath") + } + } + + return onDiskButNotInDatabase.size + } + + fun deleteAllAttachments() { + Log.d(TAG, "[deleteAllAttachments]") + + writableDatabase + .delete(TABLE_NAME) + .run() + + FileUtils.deleteDirectoryContents(context.getDir(DIRECTORY, Context.MODE_PRIVATE)) + + notifyAttachmentListeners() + } + + fun setTransferState(messageId: Long, attachmentId: AttachmentId, transferState: Int) { + writableDatabase + .update(TABLE_NAME) + .values(TRANSFER_STATE to transferState) + .where("$ID = ?", attachmentId.id) + .run() + + val threadId = messages.getThreadIdForMessage(messageId) + notifyConversationListeners(threadId) + } + + @Throws(MmsException::class) + fun setTransferProgressFailed(attachmentId: AttachmentId, mmsId: Long) { + writableDatabase + .update(TABLE_NAME) + .values(TRANSFER_STATE to TRANSFER_PROGRESS_FAILED) + .where("$ID = ? AND $TRANSFER_STATE < $TRANSFER_PROGRESS_PERMANENT_FAILURE", attachmentId.id) + .run() + + notifyConversationListeners(messages.getThreadIdForMessage(mmsId)) + } + + @Throws(MmsException::class) + fun setTransferProgressPermanentFailure(attachmentId: AttachmentId, mmsId: Long) { + writableDatabase + .update(TABLE_NAME) + .values(TRANSFER_STATE to TRANSFER_PROGRESS_PERMANENT_FAILURE) + .where("$ID = ?", attachmentId.id) + .run() + + notifyConversationListeners(messages.getThreadIdForMessage(mmsId)) + } + + @Throws(MmsException::class) + fun insertAttachmentsForPlaceholder(mmsId: Long, attachmentId: AttachmentId, inputStream: InputStream) { + val placeholder = getAttachment(attachmentId) + val oldInfo = getAttachmentDataFileInfo(attachmentId, DATA_FILE) + var dataInfo = storeAttachmentStream(inputStream) + val transferFile = getTransferFile(databaseHelper.signalReadableDatabase, attachmentId) + + val updated = writableDatabase.withinTransaction { db -> + dataInfo = deduplicateAttachment(dataInfo, attachmentId, placeholder?.transformProperties ?: TransformProperties.empty()) + + if (oldInfo != null) { + updateAttachmentDataHash(db, oldInfo.hash, dataInfo) + } + + val values = ContentValues() + values.put(DATA_FILE, dataInfo.file.absolutePath) + values.put(DATA_SIZE, dataInfo.length) + values.put(DATA_RANDOM, dataInfo.random) + values.put(DATA_HASH, dataInfo.hash) + + val visualHashString = placeholder.getVisualHashStringOrNull() + if (visualHashString != null) { + values.put(BLUR_HASH, visualHashString) + } + + values.put(TRANSFER_STATE, TRANSFER_PROGRESS_DONE) + values.put(TRANSFER_FILE, null as String?) + values.put(TRANSFORM_PROPERTIES, TransformProperties.forSkipTransform().serialize()) + + val updateCount = db.update(TABLE_NAME) + .values(values) + .where("$ID = ?", attachmentId.id) + .run() + + updateCount > 0 + } + + if (updated) { + val threadId = messages.getThreadIdForMessage(mmsId) + + if (!messages.isStory(mmsId)) { + threads.updateSnippetUriSilently(threadId, PartAuthority.getAttachmentDataUri(attachmentId)) + } + + notifyConversationListeners(threadId) + notifyConversationListListeners() + notifyAttachmentListeners() + } else { + if (!dataInfo.file.delete()) { + Log.w(TAG, "Failed to delete unused attachment") + } + } + + if (transferFile != null) { + if (!transferFile.delete()) { + Log.w(TAG, "Unable to delete transfer file.") + } + } + + if (placeholder != null && MediaUtil.isAudio(placeholder)) { + GenerateAudioWaveFormJob.enqueue(placeholder.attachmentId) + } + } + + @Throws(MmsException::class) + fun copyAttachmentData(sourceId: AttachmentId, destinationId: AttachmentId) { + val sourceAttachment = getAttachment(sourceId) ?: throw MmsException("Cannot find attachment for source!") + val sourceDataInfo = getAttachmentDataFileInfo(sourceId, DATA_FILE) ?: throw MmsException("No attachment data found for source!") + + writableDatabase + .update(TABLE_NAME) + .values( + DATA_FILE to sourceDataInfo.file.absolutePath, + DATA_HASH to sourceDataInfo.hash, + DATA_SIZE to sourceDataInfo.length, + DATA_RANDOM to sourceDataInfo.random, + TRANSFER_STATE to sourceAttachment.transferState, + CDN_NUMBER to sourceAttachment.cdnNumber, + REMOTE_LOCATION to sourceAttachment.remoteLocation, + REMOTE_DIGEST to sourceAttachment.remoteDigest, + REMOTE_INCREMENTAL_DIGEST to sourceAttachment.incrementalDigest, + REMOTE_INCREMENTAL_DIGEST_CHUNK_SIZE to sourceAttachment.incrementalMacChunkSize, + REMOTE_KEY to sourceAttachment.remoteKey, + DATA_SIZE to sourceAttachment.size, + FAST_PREFLIGHT_ID to sourceAttachment.fastPreflightId, + WIDTH to sourceAttachment.width, + HEIGHT to sourceAttachment.height, + CONTENT_TYPE to sourceAttachment.contentType, + BLUR_HASH to sourceAttachment.getVisualHashStringOrNull(), + TRANSFORM_PROPERTIES to sourceAttachment.transformProperties?.serialize() + ) + .where("$ID = ?", destinationId.id) + .run() + } + + fun updateAttachmentCaption(id: AttachmentId, caption: String?) { + writableDatabase + .update(TABLE_NAME) + .values(CAPTION to caption) + .where("$ID = ?", id.id) + .run() + } + + fun updateDisplayOrder(orderMap: Map) { + writableDatabase.withinTransaction { db -> + for ((key, value) in orderMap) { + db.update(TABLE_NAME) + .values(DISPLAY_ORDER to value) + .where("$ID = ?", key.id) + .run() + } + } + } + + fun updateAttachmentAfterUpload(id: AttachmentId, attachment: Attachment, uploadTimestamp: Long) { + val dataInfo = getAttachmentDataFileInfo(id, DATA_FILE) + val values = contentValuesOf( + TRANSFER_STATE to TRANSFER_PROGRESS_DONE, + CDN_NUMBER to attachment.cdnNumber, + REMOTE_LOCATION to attachment.remoteLocation, + REMOTE_DIGEST to attachment.remoteDigest, + REMOTE_INCREMENTAL_DIGEST to attachment.incrementalDigest, + REMOTE_INCREMENTAL_DIGEST_CHUNK_SIZE to attachment.incrementalMacChunkSize, + REMOTE_KEY to attachment.remoteKey, + DATA_SIZE to attachment.size, + FAST_PREFLIGHT_ID to attachment.fastPreflightId, + BLUR_HASH to attachment.getVisualHashStringOrNull(), + UPLOAD_TIMESTAMP to uploadTimestamp + ) + + if (dataInfo?.hash != null) { + updateAttachmentAndMatchingHashes(writableDatabase, id, dataInfo.hash, values) + } else { + writableDatabase + .update(TABLE_NAME) + .values(values) + .where("$ID = ?", id.id) + .run() + } + } + + @Throws(MmsException::class) + fun insertAttachmentForPreUpload(attachment: Attachment): DatabaseAttachment { + val result = insertAttachmentsForMessage(PREUPLOAD_MESSAGE_ID, listOf(attachment), emptyList()) + + if (result.values.isEmpty()) { + throw MmsException("Bad attachment result!") + } + + return getAttachment(result.values.iterator().next()) ?: throw MmsException("Failed to retrieve attachment we just inserted!") + } + + fun updateMessageId(attachmentIds: Collection, mmsId: Long, isStory: Boolean) { + writableDatabase.withinTransaction { db -> + val values = ContentValues(2).apply { + put(MESSAGE_ID, mmsId) + if (!isStory) { + putNull(CAPTION) + } + } + + var updatedCount = 0 + var attachmentIdSize = 0 + for (attachmentId in attachmentIds) { + attachmentIdSize++ + updatedCount += db + .update(TABLE_NAME) + .values(values) + .where("$ID = ?", attachmentId.id) + .run() + } + + Log.d(TAG, "[updateMessageId] Updated $updatedCount out of $attachmentIdSize ids.") + } + } + + @Throws(MmsException::class) + fun insertAttachmentsForMessage(mmsId: Long, attachments: List, quoteAttachment: List): Map { + if (attachments.isEmpty() && quoteAttachment.isEmpty()) { + return emptyMap() + } + + Log.d(TAG, "insertParts(${attachments.size})") + + val insertedAttachments: MutableMap = mutableMapOf() + for (attachment in attachments) { + val attachmentId = insertAttachment(mmsId, attachment, attachment.quote) + insertedAttachments[attachment] = attachmentId + Log.i(TAG, "Inserted attachment at ID: $attachmentId") + } + + try { + for (attachment in quoteAttachment) { + val attachmentId = insertAttachment(mmsId, attachment, true) + insertedAttachments[attachment] = attachmentId + Log.i(TAG, "Inserted quoted attachment at ID: $attachmentId") + } + } catch (e: MmsException) { + Log.w(TAG, "Failed to insert quote attachment! messageId: $mmsId") + } + + return insertedAttachments + } + + /** + * @param onlyModifyThisAttachment If false and more than one attachment shares this file and quality, they will all + * be updated. If true, then guarantees not to affect other attachments. + */ + @Throws(MmsException::class, IOException::class) + fun updateAttachmentData( + databaseAttachment: DatabaseAttachment, + mediaStream: MediaStream, + onlyModifyThisAttachment: Boolean + ) { + val attachmentId = databaseAttachment.attachmentId + val oldDataInfo = getAttachmentDataFileInfo(attachmentId, DATA_FILE) ?: throw MmsException("No attachment data found!") + var destination = oldDataInfo.file + val isSingleUseOfData = onlyModifyThisAttachment || oldDataInfo.hash == null + + if (isSingleUseOfData && fileReferencedByMoreThanOneAttachment(destination)) { + Log.i(TAG, "Creating a new file as this one is used by more than one attachment") + destination = newFile(context) + } + + var dataInfo: DataInfo = storeAttachmentStream(destination, mediaStream.stream) + + writableDatabase.withinTransaction { db -> + dataInfo = deduplicateAttachment(dataInfo, attachmentId, databaseAttachment.transformProperties) + + val contentValues = contentValuesOf( + DATA_SIZE to dataInfo.length, + CONTENT_TYPE to mediaStream.mimeType, + WIDTH to mediaStream.width, + HEIGHT to mediaStream.height, + DATA_FILE to dataInfo.file.absolutePath, + DATA_RANDOM to dataInfo.random, + DATA_HASH to dataInfo.hash + ) + + val updateCount = updateAttachmentAndMatchingHashes( + db = db, + attachmentId = attachmentId, + dataHash = if (isSingleUseOfData) dataInfo.hash else oldDataInfo.hash, + contentValues = contentValues + ) + + Log.i(TAG, "[updateAttachmentData] Updated $updateCount rows.") + } + } + + fun duplicateAttachmentsForMessage(destinationMessageId: Long, sourceMessageId: Long, excludedIds: Collection) { + writableDatabase.withinTransaction { db -> + db.execSQL("CREATE TEMPORARY TABLE tmp_part AS SELECT * FROM $TABLE_NAME WHERE $MESSAGE_ID = ?", buildArgs(sourceMessageId)) + + val queries = buildCollectionQuery(ID, excludedIds) + for (query in queries) { + db.delete("tmp_part", query.where, query.whereArgs) + } + + db.execSQL("UPDATE tmp_part SET $ID = NULL, $MESSAGE_ID = ?", buildArgs(destinationMessageId)) + db.execSQL("INSERT INTO $TABLE_NAME SELECT * FROM tmp_part") + db.execSQL("DROP TABLE tmp_part") + } + } + + @Throws(IOException::class) + fun getOrCreateTransferFile(attachmentId: AttachmentId): File { + val existing = getTransferFile(writableDatabase, attachmentId) + if (existing != null) { + return existing + } + + val transferFile = newTransferFile() + + writableDatabase + .update(TABLE_NAME) + .values(TRANSFER_FILE to transferFile.absolutePath) + .where("$ID = ?", attachmentId.id) + .run() + + return transferFile + } + + @VisibleForTesting + fun getAttachmentDataFileInfo(attachmentId: AttachmentId, dataType: String): DataInfo? { + return readableDatabase + .select(dataType, DATA_SIZE, DATA_RANDOM, DATA_HASH, TRANSFORM_PROPERTIES) + .from(TABLE_NAME) + .where("$ID = ?", attachmentId.id) + .run() + .readToSingleObject { cursor -> + if (cursor.isNull(dataType)) { + null + } else { + DataInfo( + file = File(cursor.getString(cursor.getColumnIndexOrThrow(dataType))), + length = cursor.requireLong(DATA_SIZE), + random = cursor.requireNonNullBlob(DATA_RANDOM), + hash = cursor.requireString(DATA_HASH), + transformProperties = TransformProperties.parse(cursor.requireString(TRANSFORM_PROPERTIES)) + ) + } + } + } + + fun markAttachmentAsTransformed(attachmentId: AttachmentId, withFastStart: Boolean) { + writableDatabase.withinTransaction { db -> + try { + var transformProperties = getTransformProperties(attachmentId) + if (transformProperties == null) { + Log.w(TAG, "Failed to get transformation properties, attachment no longer exists.") + return@withinTransaction + } + + transformProperties = transformProperties.withSkipTransform() + if (withFastStart) { + transformProperties = transformProperties.withMp4FastStart() + } + + updateAttachmentTransformProperties(attachmentId, transformProperties) + } catch (e: Exception) { + Log.w(TAG, "Could not mark attachment as transformed.", e) + } + } + } + + @WorkerThread + fun writeAudioHash(attachmentId: AttachmentId, audioWaveForm: AudioWaveFormData?) { + Log.i(TAG, "updating part audio wave form for $attachmentId") + writableDatabase + .update(TABLE_NAME) + .values(BLUR_HASH to audioWaveForm?.let { AudioHash(it).hash }) + .where("$ID = ?", attachmentId.id) + .run() + } + + @RequiresApi(23) + fun mediaDataSourceFor(attachmentId: AttachmentId, allowReadingFromTempFile: Boolean): MediaDataSource? { + val dataInfo = getAttachmentDataFileInfo(attachmentId, DATA_FILE) + if (dataInfo != null) { + return EncryptedMediaDataSource.createFor(attachmentSecret, dataInfo.file, dataInfo.random, dataInfo.length) + } + + if (allowReadingFromTempFile) { + Log.d(TAG, "Completed data file not found for video attachment, checking for in-progress files.") + val transferFile = getTransferFile(readableDatabase, attachmentId) + if (transferFile != null) { + return EncryptedMediaDataSource.createForDiskBlob(attachmentSecret, transferFile) + } + } + + Log.w(TAG, "No data file found for video attachment!") + return null + } + + /** + * @return null if we fail to find the given attachmentId + */ + fun getTransformProperties(attachmentId: AttachmentId): TransformProperties? { + return readableDatabase + .select(TRANSFORM_PROPERTIES) + .from(TABLE_NAME) + .where("$ID = ?", attachmentId.id) + .run() + .readToSingleObject { + TransformProperties.parse(it.requireString(TRANSFORM_PROPERTIES)) + } + } + + fun markAttachmentUploaded(messageId: Long, attachment: Attachment) { + writableDatabase + .update(TABLE_NAME) + .values(TRANSFER_STATE to TRANSFER_PROGRESS_DONE) + .where("$ID = ?", (attachment as DatabaseAttachment).attachmentId.id) + .run() + + val threadId = messages.getThreadIdForMessage(messageId) + notifyConversationListeners(threadId) + } + + fun getAttachments(cursor: Cursor): List { + return try { + if (cursor.getColumnIndex(ATTACHMENT_JSON_ALIAS) != -1) { + if (cursor.isNull(ATTACHMENT_JSON_ALIAS)) { + return LinkedList() + } + + val result: MutableList = mutableListOf() + val array = JSONArray(cursor.requireString(ATTACHMENT_JSON_ALIAS)) + + for (i in 0 until array.length()) { + val jsonObject = SaneJSONObject(array.getJSONObject(i)) + + if (!jsonObject.isNull(ID)) { + val contentType = jsonObject.getString(CONTENT_TYPE) + + result += DatabaseAttachment( + attachmentId = AttachmentId(jsonObject.getLong(ID)), + mmsId = jsonObject.getLong(MESSAGE_ID), + hasData = !TextUtils.isEmpty(jsonObject.getString(DATA_FILE)), + hasThumbnail = MediaUtil.isImageType(contentType) || MediaUtil.isVideoType(contentType), + contentType = contentType, + transferProgress = jsonObject.getInt(TRANSFER_STATE), + size = jsonObject.getLong(DATA_SIZE), + fileName = jsonObject.getString(FILE_NAME), + cdnNumber = jsonObject.getInt(CDN_NUMBER), + location = jsonObject.getString(REMOTE_LOCATION), + key = jsonObject.getString(REMOTE_KEY), + digest = null, + incrementalDigest = null, + incrementalMacChunkSize = 0, + fastPreflightId = jsonObject.getString(FAST_PREFLIGHT_ID), + voiceNote = jsonObject.getInt(VOICE_NOTE) == 1, + borderless = jsonObject.getInt(BORDERLESS) == 1, + videoGif = jsonObject.getInt(VIDEO_GIF) == 1, + width = jsonObject.getInt(WIDTH), + height = jsonObject.getInt(HEIGHT), + quote = jsonObject.getInt(QUOTE) == 1, + caption = jsonObject.getString(CAPTION), + stickerLocator = if (jsonObject.getInt(STICKER_ID) >= 0) { + StickerLocator( + jsonObject.getString(STICKER_PACK_ID)!!, + jsonObject.getString(STICKER_PACK_KEY)!!, + jsonObject.getInt(STICKER_ID), + jsonObject.getString(STICKER_EMOJI) + ) + } else { + null + }, + blurHash = if (MediaUtil.isAudioType(contentType)) null else BlurHash.parseOrNull(jsonObject.getString(BLUR_HASH)), + audioHash = if (MediaUtil.isAudioType(contentType)) AudioHash.parseOrNull(jsonObject.getString(BLUR_HASH)) else null, + transformProperties = TransformProperties.parse(jsonObject.getString(TRANSFORM_PROPERTIES)), + displayOrder = jsonObject.getInt(DISPLAY_ORDER), + uploadTimestamp = jsonObject.getLong(UPLOAD_TIMESTAMP) + ) + } + } + + result + } else { + listOf(getAttachment(cursor)) + } + } catch (e: JSONException) { + throw AssertionError(e) + } + } + + fun hasStickerAttachments(): Boolean { + return readableDatabase + .exists(TABLE_NAME) + .where("$STICKER_PACK_ID NOT NULL") + .run() + } + + fun containsStickerPackId(stickerPackId: String): Boolean { + return readableDatabase.exists(TABLE_NAME) + .where("$STICKER_PACK_ID = ?", stickerPackId) + .run() + } + + fun getUnavailableStickerPacks(): Cursor { + val query = """ + SELECT DISTINCT $STICKER_PACK_ID, $STICKER_PACK_KEY + FROM $TABLE_NAME + WHERE + $STICKER_PACK_ID NOT NULL AND + $STICKER_PACK_KEY NOT NULL AND + $STICKER_PACK_ID NOT IN (SELECT DISTINCT ${StickerTable.PACK_ID} FROM ${StickerTable.TABLE_NAME}) + """ + + return readableDatabase.rawQuery(query, null) + } + + private fun deleteAttachmentOnDisk( + data: String?, + contentType: String?, + attachmentId: AttachmentId + ) { + check(writableDatabase.inTransaction()) { "Must be in a transaction!" } + + val dataUsage = getAttachmentFileUsages(data, attachmentId) + if (dataUsage.hasStrongReference) { + Log.i(TAG, "[deleteAttachmentOnDisk] Attachment in use. Skipping deletion. $data $attachmentId") + return + } + + Log.i(TAG, "[deleteAttachmentOnDisk] No other strong uses of this attachment. Safe to delete. $data $attachmentId") + if (!data.isNullOrBlank()) { + if (File(data).delete()) { + Log.i(TAG, "[deleteAttachmentOnDisk] Deleted attachment file. $data $attachmentId") + + if (dataUsage.removableWeakReferences.isNotEmpty()) { + Log.i(TAG, "[deleteAttachmentOnDisk] Deleting ${dataUsage.removableWeakReferences.size} weak references for $data") + + var deletedCount = 0 + for (weakReference in dataUsage.removableWeakReferences) { + Log.i(TAG, "[deleteAttachmentOnDisk] Clearing weak reference for $data $weakReference") + + deletedCount += writableDatabase + .update(TABLE_NAME) + .values( + DATA_FILE to null, + DATA_RANDOM to null, + DATA_HASH to null + ) + .where("$ID = ?", weakReference.id) + .run() + } + + val logMessage = "[deleteAttachmentOnDisk] Cleared $deletedCount/${dataUsage.removableWeakReferences.size} weak references for $data" + if (deletedCount != dataUsage.removableWeakReferences.size) { + Log.w(TAG, logMessage) + } else { + Log.i(TAG, logMessage) + } + } + } else { + Log.w(TAG, "[deleteAttachmentOnDisk] Failed to delete attachment. $data $attachmentId") + } + } + + if (MediaUtil.isImageType(contentType) || MediaUtil.isVideoType(contentType)) { + Glide.get(context).clearDiskCache() + ThreadUtil.runOnMain { Glide.get(context).clearMemory() } + } + } + + private fun getAttachmentFileUsages(data: String?, attachmentId: AttachmentId): DataUsageResult { + check(writableDatabase.inTransaction()) { "Must be in a transaction!" } + + if (data == null) { + return DataUsageResult.NOT_IN_USE + } + + val quoteRows: MutableList = mutableListOf() + + readableDatabase + .select(ID, QUOTE) + .from(TABLE_NAME) + .where("$DATA_FILE = ? AND $ID != ?", data, attachmentId.id) + .run() + .forEach { cursor -> + if (cursor.requireBoolean(QUOTE)) { + quoteRows += AttachmentId(cursor.requireLong(ID)) + } else { + return DataUsageResult.IN_USE + } + } + + return DataUsageResult(quoteRows) + } + + /** + * Check if data file is in use by another attachment row with a different hash. Rows with the same data and hash + * will be fixed in a later call to [updateAttachmentAndMatchingHashes]. + */ + private fun isAttachmentFileUsedByOtherAttachments(attachmentId: AttachmentId?, dataInfo: DataInfo): Boolean { + return if (attachmentId == null || dataInfo.hash == null) { + false + } else { + readableDatabase + .exists(TABLE_NAME) + .where("$DATA_FILE = ? AND $DATA_HASH != ?", dataInfo.file.absolutePath, dataInfo.hash) + .run() + } + } + + private fun updateAttachmentDataHash( + db: SQLiteDatabase, + oldHash: String?, + newData: DataInfo + ) { + if (oldHash == null) { + return + } + + db.update(TABLE_NAME) + .values( + DATA_FILE to newData.file.absolutePath, + DATA_RANDOM to newData.random, + DATA_HASH to newData.hash + ) + .where("$DATA_HASH = ?", oldHash) + .run() + } + + private fun updateAttachmentTransformProperties(attachmentId: AttachmentId, transformProperties: TransformProperties) { + val dataInfo = getAttachmentDataFileInfo(attachmentId, DATA_FILE) + if (dataInfo == null) { + Log.w(TAG, "[updateAttachmentTransformProperties] No data info found!") + return + } + + val contentValues = contentValuesOf(TRANSFORM_PROPERTIES to transformProperties.serialize()) + val updateCount = updateAttachmentAndMatchingHashes(databaseHelper.signalWritableDatabase, attachmentId, dataInfo.hash, contentValues) + Log.i(TAG, "[updateAttachmentTransformProperties] Updated $updateCount rows.") + } + + private fun updateAttachmentAndMatchingHashes( + db: SQLiteDatabase, + attachmentId: AttachmentId, + dataHash: String?, + contentValues: ContentValues + ): Int { + return db + .update(TABLE_NAME) + .values(contentValues) + .where("$ID = ? OR ($DATA_HASH NOT NULL AND $DATA_HASH = ?)", attachmentId.id, dataHash.toString()) + .run() + } + + /** + * Returns true if the file referenced by two or more attachments. + * Returns false if the file is referenced by zero or one attachments. + */ + private fun fileReferencedByMoreThanOneAttachment(file: File): Boolean { + return readableDatabase + .select("1") + .from(TABLE_NAME) + .where("$DATA_FILE = ?", file.absolutePath) + .limit(2) + .run() + .use { cursor -> + cursor.moveToNext() && cursor.moveToNext() + } + } + + @Throws(FileNotFoundException::class) + private fun getDataStream(attachmentId: AttachmentId, dataType: String, offset: Long): InputStream? { + val dataInfo = getAttachmentDataFileInfo(attachmentId, dataType) ?: return null + + return try { + if (dataInfo.random.size == 32) { + ModernDecryptingPartInputStream.createFor(attachmentSecret, dataInfo.random, dataInfo.file, offset) + } else { + return null + } + } catch (e: FileNotFoundException) { + Log.w(TAG, e) + throw e + } catch (e: IOException) { + Log.w(TAG, e) + null + } + } + + @Throws(MmsException::class) + private fun storeAttachmentStream(inputStream: InputStream): DataInfo { + return try { + storeAttachmentStream(newFile(context), inputStream) + } catch (e: IOException) { + throw MmsException(e) + } + } + + @Throws(IOException::class) + private fun newTransferFile(): File { + val partsDirectory = context.getDir(DIRECTORY, Context.MODE_PRIVATE) + return PartFileProtector.protect { + File.createTempFile("transfer", ".mms", partsDirectory) + } + } + + /** + * Reads the entire stream and saves to disk. If you need to deduplicate attachments, call [deduplicateAttachment] + * afterwards and use the [DataInfo] returned by it instead. + */ + @Throws(MmsException::class, IllegalStateException::class) + private fun storeAttachmentStream(destination: File, inputStream: InputStream): DataInfo { + return try { + val tempFile = newFile(context) + val messageDigest = MessageDigest.getInstance("SHA-256") + val digestInputStream = DigestInputStream(inputStream, messageDigest) + val out = ModernEncryptingPartOutputStream.createFor(attachmentSecret, tempFile, false) + val length = StreamUtil.copy(digestInputStream, out.second) + val hash = encodeWithPadding(digestInputStream.messageDigest.digest()) + + if (!tempFile.renameTo(destination)) { + Log.w(TAG, "Couldn't rename ${tempFile.path} to ${destination.path}") + tempFile.delete() + throw IllegalStateException("Couldn't rename ${tempFile.path} to ${destination.path}") + } + + DataInfo( + file = destination, + length = length, + random = out.first, + hash = hash, + transformProperties = null + ) + } catch (e: IOException) { + throw MmsException(e) + } catch (e: NoSuchAlgorithmException) { + throw MmsException(e) + } + } + + private fun deduplicateAttachment( + dataInfo: DataInfo, + attachmentId: AttachmentId?, + transformProperties: TransformProperties? + ): DataInfo { + check(writableDatabase.inTransaction()) { "Must be in a transaction!" } + + val sharedDataInfos = findDuplicateDataFileInfos(writableDatabase, dataInfo.hash, attachmentId) + + for (sharedDataInfo in sharedDataInfos) { + if (dataInfo.file == sharedDataInfo.file) { + continue + } + + val isUsedElsewhere = isAttachmentFileUsedByOtherAttachments(attachmentId, dataInfo) + val isSameQuality = transformProperties?.sentMediaQuality == sharedDataInfo.transformProperties?.sentMediaQuality + + Log.i(TAG, "[deduplicateAttachment] Potential duplicate data file found. usedElsewhere: " + isUsedElsewhere + " sameQuality: " + isSameQuality + " otherFile: " + sharedDataInfo.file.absolutePath) + + if (!isSameQuality) { + continue + } + + if (!isUsedElsewhere) { + if (dataInfo.file.delete()) { + Log.i(TAG, "[deduplicateAttachment] Deleted original file. ${dataInfo.file}") + } else { + Log.w(TAG, "[deduplicateAttachment] Original file could not be deleted.") + } + } + + return sharedDataInfo + } + + Log.i(TAG, "[deduplicateAttachment] No acceptable matching attachment data found. ${dataInfo.file.absolutePath}") + return dataInfo + } + + private fun findDuplicateDataFileInfos( + database: SQLiteDatabase, + hash: String?, + excludedAttachmentId: AttachmentId? + ): List { + check(database.inTransaction()) { "Must be in a transaction!" } + + if (hash == null) { + return emptyList() + } + + val selectorArgs: Pair> = buildSharedFileSelectorArgs(hash, excludedAttachmentId) + + return database + .select(DATA_FILE, DATA_RANDOM, DATA_SIZE, TRANSFORM_PROPERTIES) + .from(TABLE_NAME) + .where(selectorArgs.first, selectorArgs.second) + .run() + .readToList { cursor -> + DataInfo( + file = File(cursor.requireNonNullString(DATA_FILE)), + length = cursor.requireLong(DATA_SIZE), + random = cursor.requireNonNullBlob(DATA_RANDOM), + hash = hash, + transformProperties = TransformProperties.parse(cursor.requireString(TRANSFORM_PROPERTIES)) + ) + } + } + + private fun buildSharedFileSelectorArgs(newHash: String, attachmentId: AttachmentId?): Pair> { + return if (attachmentId == null) { + "$DATA_HASH = ?" to arrayOf(newHash) + } else { + "$ID != ? AND $DATA_HASH = ?" to arrayOf( + attachmentId.id.toString(), + newHash + ) + } + } + + @Throws(MmsException::class) + private fun insertAttachment(mmsId: Long, attachment: Attachment, quote: Boolean): AttachmentId { + Log.d(TAG, "Inserting attachment for mms id: $mmsId") + + var notifyPacks = false + + val attachmentId: AttachmentId = writableDatabase.withinTransaction { db -> + try { + var dataInfo: DataInfo? = null + + if (attachment.uri != null) { + val storeDataInfo = storeAttachmentStream(PartAuthority.getAttachmentStream(context, attachment.uri!!)) + Log.d(TAG, "Wrote part to file: ${storeDataInfo.file.absolutePath}") + + dataInfo = deduplicateAttachment(storeDataInfo, null, attachment.transformProperties) + } + + var template = attachment + var useTemplateUpload = false + + if (dataInfo != null) { + val possibleTemplates = findTemplateAttachments(dataInfo.hash) + + for (possibleTemplate in possibleTemplates) { + useTemplateUpload = possibleTemplate.uploadTimestamp > attachment.uploadTimestamp && + possibleTemplate.transferState == TRANSFER_PROGRESS_DONE && + possibleTemplate.transformProperties?.shouldSkipTransform() == true && possibleTemplate.remoteDigest != null && + attachment.transformProperties?.videoEdited == false && possibleTemplate.transformProperties?.sentMediaQuality == attachment.transformProperties?.sentMediaQuality + + if (useTemplateUpload) { + Log.i(TAG, "Found a duplicate attachment upon insertion. Using it as a template.") + template = possibleTemplate + break + } + } + } + + val contentValues = ContentValues() + contentValues.put(MESSAGE_ID, mmsId) + contentValues.put(CONTENT_TYPE, template.contentType) + contentValues.put(TRANSFER_STATE, attachment.transferState) + contentValues.put(CDN_NUMBER, if (useTemplateUpload) template.cdnNumber else attachment.cdnNumber) + contentValues.put(REMOTE_LOCATION, if (useTemplateUpload) template.remoteLocation else attachment.remoteLocation) + contentValues.put(REMOTE_DIGEST, if (useTemplateUpload) template.remoteDigest else attachment.remoteDigest) + contentValues.put(REMOTE_INCREMENTAL_DIGEST, if (useTemplateUpload) template.incrementalDigest else attachment.incrementalDigest) + contentValues.put(REMOTE_INCREMENTAL_DIGEST_CHUNK_SIZE, if (useTemplateUpload) template.incrementalMacChunkSize else attachment.incrementalMacChunkSize) + contentValues.put(REMOTE_KEY, if (useTemplateUpload) template.remoteKey else attachment.remoteKey) + contentValues.put(FILE_NAME, StorageUtil.getCleanFileName(attachment.fileName)) + contentValues.put(DATA_SIZE, template.size) + contentValues.put(FAST_PREFLIGHT_ID, attachment.fastPreflightId) + contentValues.put(VOICE_NOTE, if (attachment.voiceNote) 1 else 0) + contentValues.put(BORDERLESS, if (attachment.borderless) 1 else 0) + contentValues.put(VIDEO_GIF, if (attachment.videoGif) 1 else 0) + contentValues.put(WIDTH, template.width) + contentValues.put(HEIGHT, template.height) + contentValues.put(QUOTE, quote) + contentValues.put(CAPTION, attachment.caption) + contentValues.put(UPLOAD_TIMESTAMP, if (useTemplateUpload) template.uploadTimestamp else attachment.uploadTimestamp) + + if (attachment.transformProperties?.videoEdited == true) { + contentValues.putNull(BLUR_HASH) + contentValues.put(TRANSFORM_PROPERTIES, attachment.transformProperties?.serialize()) + } else { + contentValues.put(BLUR_HASH, template.getVisualHashStringOrNull()) + contentValues.put(TRANSFORM_PROPERTIES, (if (useTemplateUpload) template else attachment).transformProperties?.serialize()) + } + + attachment.stickerLocator?.let { sticker -> + contentValues.put(STICKER_PACK_ID, sticker.packId) + contentValues.put(STICKER_PACK_KEY, sticker.packKey) + contentValues.put(STICKER_ID, sticker.stickerId) + contentValues.put(STICKER_EMOJI, sticker.emoji) + } + + if (dataInfo != null) { + contentValues.put(DATA_FILE, dataInfo.file.absolutePath) + contentValues.put(DATA_SIZE, dataInfo.length) + contentValues.put(DATA_RANDOM, dataInfo.random) + + if (attachment.transformProperties?.videoEdited == true) { + contentValues.putNull(DATA_HASH) + } else { + contentValues.put(DATA_HASH, dataInfo.hash) + } + } + + notifyPacks = attachment.isSticker && !hasStickerAttachments() + + val rowId = db.insert(TABLE_NAME, null, contentValues) + AttachmentId(rowId) + } catch (e: IOException) { + throw MmsException(e) + } + } + + if (notifyPacks) { + notifyStickerPackListeners() + } + + notifyAttachmentListeners() + return attachmentId + } + + private fun findTemplateAttachments(dataHash: String?): List { + if (dataHash == null) { + return emptyList() + } + + return readableDatabase + .select(*PROJECTION) + .from(TABLE_NAME) + .where("$DATA_HASH = ?", dataHash) + .run() + .readToList { it.readAttachment() } + } + + private fun getTransferFile(db: SQLiteDatabase, attachmentId: AttachmentId): File? { + return db + .select(TRANSFER_FILE) + .from(TABLE_NAME) + .where("$ID = ?", attachmentId.id) + .limit(1) + .run() + .readToSingleObject { cursor -> + cursor.requireString(TRANSFER_FILE)?.let { File(it) } + } + } + + private fun getAttachment(cursor: Cursor): DatabaseAttachment { + val contentType = cursor.requireString(CONTENT_TYPE) + + return DatabaseAttachment( + attachmentId = AttachmentId(cursor.requireLong(ID)), + mmsId = cursor.requireLong(MESSAGE_ID), + hasData = !cursor.isNull(DATA_FILE), + hasThumbnail = MediaUtil.isImageType(contentType) || MediaUtil.isVideoType(contentType), + contentType = contentType, + transferProgress = cursor.requireInt(TRANSFER_STATE), + size = cursor.requireLong(DATA_SIZE), + fileName = cursor.requireString(FILE_NAME), + cdnNumber = cursor.requireInt(CDN_NUMBER), + location = cursor.requireString(REMOTE_LOCATION), + key = cursor.requireString(REMOTE_KEY), + digest = cursor.requireBlob(REMOTE_DIGEST), + incrementalDigest = cursor.requireBlob(REMOTE_INCREMENTAL_DIGEST), + incrementalMacChunkSize = cursor.requireInt(REMOTE_INCREMENTAL_DIGEST_CHUNK_SIZE), + fastPreflightId = cursor.requireString(FAST_PREFLIGHT_ID), + voiceNote = cursor.requireBoolean(VOICE_NOTE), + borderless = cursor.requireBoolean(BORDERLESS), + videoGif = cursor.requireBoolean(VIDEO_GIF), + width = cursor.requireInt(WIDTH), + height = cursor.requireInt(HEIGHT), + quote = cursor.requireBoolean(QUOTE), + caption = cursor.requireString(CAPTION), + stickerLocator = cursor.readStickerLocator(), + blurHash = if (MediaUtil.isAudioType(contentType)) null else BlurHash.parseOrNull(cursor.requireString(BLUR_HASH)), + audioHash = if (MediaUtil.isAudioType(contentType)) AudioHash.parseOrNull(cursor.requireString(BLUR_HASH)) else null, + transformProperties = TransformProperties.parse(cursor.requireString(TRANSFORM_PROPERTIES)), + displayOrder = cursor.requireInt(DISPLAY_ORDER), + uploadTimestamp = cursor.requireLong(UPLOAD_TIMESTAMP) + ) + } + + private fun Cursor.readAttachments(): List { + return getAttachments(this) + } + + private fun Cursor.readAttachment(): DatabaseAttachment { + return getAttachment(this) + } + + private fun Cursor.readStickerLocator(): StickerLocator? { + return if (this.requireInt(STICKER_ID) >= 0) { + StickerLocator( + packId = this.requireNonNullString(STICKER_PACK_ID), + packKey = this.requireNonNullString(STICKER_PACK_KEY), + stickerId = this.requireInt(STICKER_ID), + emoji = this.requireString(STICKER_EMOJI) + ) + } else { + null + } + } + + private fun Attachment?.getVisualHashStringOrNull(): String? { + return when { + this == null -> null + this.blurHash != null -> this.blurHash!!.hash + this.audioHash != null -> this.audioHash!!.hash + else -> null + } + } + + @VisibleForTesting + class DataInfo( + val file: File, + val length: Long, + val random: ByteArray, + val hash: String?, + val transformProperties: TransformProperties? + ) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as DataInfo + + if (file != other.file) return false + if (length != other.length) return false + if (!random.contentEquals(other.random)) return false + if (hash != other.hash) return false + return transformProperties == other.transformProperties + } + + override fun hashCode(): Int { + var result = file.hashCode() + result = 31 * result + length.hashCode() + result = 31 * result + random.contentHashCode() + result = 31 * result + (hash?.hashCode() ?: 0) + result = 31 * result + (transformProperties?.hashCode() ?: 0) + return result + } + } + + /** + * @param removableWeakReferences Entries in here can be removed from the database. Only possible to be non-empty when [hasStrongReference] is false. + */ + private class DataUsageResult private constructor(val hasStrongReference: Boolean, val removableWeakReferences: List) { + constructor(removableWeakReferences: List) : this(false, removableWeakReferences) + + init { + if (hasStrongReference && removableWeakReferences.isNotEmpty()) { + throw IllegalStateException("There's a strong reference and removable weak references!") + } + } + + companion object { + val IN_USE = DataUsageResult(true, emptyList()) + val NOT_IN_USE = DataUsageResult(false, emptyList()) + } + } + + @Parcelize + data class TransformProperties( + @JsonProperty("skipTransform") + @JvmField + val skipTransform: Boolean, + + @JsonProperty("videoTrim") + @JvmField + val videoTrim: Boolean, + + @JsonProperty("videoTrimStartTimeUs") + @JvmField + val videoTrimStartTimeUs: Long, + + @JsonProperty("videoTrimEndTimeUs") + @JvmField + val videoTrimEndTimeUs: Long, + + @JsonProperty("sentMediaQuality") + @JvmField + val sentMediaQuality: Int, + + @field:JsonProperty("mp4Faststart") + @param:JsonProperty("mp4Faststart") + @JvmField + val mp4FastStart: Boolean + ) : Parcelable { + fun shouldSkipTransform(): Boolean { + return skipTransform + } + + @IgnoredOnParcel + @JsonProperty("videoEdited") + val videoEdited: Boolean = videoTrim + + fun withSkipTransform(): TransformProperties { + return this.copy( + skipTransform = true, + videoTrim = false, + videoTrimStartTimeUs = 0, + videoTrimEndTimeUs = 0, + mp4FastStart = false + ) + } + + fun withMp4FastStart(): TransformProperties { + return this.copy(mp4FastStart = true) + } + + fun serialize(): String { + return JsonUtil.toJson(this) + } + + companion object { + private val DEFAULT_MEDIA_QUALITY = SentMediaQuality.STANDARD.code + + @JvmStatic + fun empty(): TransformProperties { + return TransformProperties( + skipTransform = false, + videoTrim = false, + videoTrimStartTimeUs = 0, + videoTrimEndTimeUs = 0, + sentMediaQuality = DEFAULT_MEDIA_QUALITY, + mp4FastStart = false + ) + } + + fun forSkipTransform(): TransformProperties { + return TransformProperties( + skipTransform = true, + videoTrim = false, + videoTrimStartTimeUs = 0, + videoTrimEndTimeUs = 0, + sentMediaQuality = DEFAULT_MEDIA_QUALITY, + mp4FastStart = false + ) + } + + fun forVideoTrim(videoTrimStartTimeUs: Long, videoTrimEndTimeUs: Long): TransformProperties { + return TransformProperties( + skipTransform = false, + videoTrim = true, + videoTrimStartTimeUs = videoTrimStartTimeUs, + videoTrimEndTimeUs = videoTrimEndTimeUs, + sentMediaQuality = DEFAULT_MEDIA_QUALITY, + mp4FastStart = false + ) + } + + @JvmStatic + fun forSentMediaQuality(currentProperties: Optional, sentMediaQuality: SentMediaQuality): TransformProperties { + val existing = currentProperties.orElse(empty()) + return existing.copy(sentMediaQuality = sentMediaQuality.code) + } + + @JvmStatic + fun parse(serialized: String?): TransformProperties { + return if (serialized == null) { + empty() + } else { + try { + JsonUtil.fromJson(serialized, TransformProperties::class.java) + } catch (e: IOException) { + Log.w(TAG, "Failed to parse TransformProperties!", e) + empty() + } + } + } + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/CallTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/CallTable.kt index ab2f5bb3b0..483d254458 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/CallTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/CallTable.kt @@ -49,16 +49,16 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl private val TIME_WINDOW = TimeUnit.HOURS.toMillis(4) const val TABLE_NAME = "call" - private const val ID = "_id" - private const val CALL_ID = "call_id" - private const val MESSAGE_ID = "message_id" + const val ID = "_id" + const val CALL_ID = "call_id" + const val MESSAGE_ID = "message_id" const val PEER = "peer" const val TYPE = "type" - private const val DIRECTION = "direction" + const val DIRECTION = "direction" const val EVENT = "event" const val TIMESTAMP = "timestamp" - private const val RINGER = "ringer" - private const val DELETION_TIMESTAMP = "deletion_timestamp" + const val RINGER = "ringer" + const val DELETION_TIMESTAMP = "deletion_timestamp" //language=sql val CREATE_TABLE = """ @@ -365,13 +365,12 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl fun acceptIncomingGroupCall(call: Call) { checkIsGroupOrAdHocCall(call) - check(call.direction == Direction.INCOMING) val newEvent = when (call.event) { - Event.RINGING, Event.MISSED, Event.DECLINED -> Event.ACCEPTED + Event.RINGING, Event.MISSED, Event.MISSED_NOTIFICATION_PROFILE, Event.DECLINED -> Event.ACCEPTED Event.GENERIC_GROUP_CALL -> Event.JOINED else -> { - Log.d(TAG, "Call in state ${call.event} cannot be transitioned by ACCEPTED") + Log.d(TAG, "[acceptIncomingGroupCall] Call in state ${call.event} cannot be transitioned by ACCEPTED") return } } @@ -379,6 +378,57 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl writableDatabase .update(TABLE_NAME) .values(EVENT to Event.serialize(newEvent)) + .where("$CALL_ID = ?", call.callId) + .run() + + ApplicationDependencies.getMessageNotifier().updateNotification(context) + ApplicationDependencies.getDatabaseObserver().notifyCallUpdateObservers() + Log.d(TAG, "[acceptIncomingGroupCall] Transitioned group call ${call.callId} from ${call.event} to $newEvent") + } + + fun acceptOutgoingGroupCall(call: Call) { + checkIsGroupOrAdHocCall(call) + + val newEvent = when (call.event) { + Event.GENERIC_GROUP_CALL, Event.JOINED -> Event.OUTGOING_RING + Event.RINGING, Event.MISSED, Event.MISSED_NOTIFICATION_PROFILE, Event.DECLINED, Event.ACCEPTED -> { + Log.w(TAG, "[acceptOutgoingGroupCall] This shouldn't have been an outgoing ring because the call already existed!") + Event.ACCEPTED + } + else -> { + Log.d(TAG, "[acceptOutgoingGroupCall] Call in state ${call.event} cannot be transitioned by ACCEPTED") + return + } + } + + writableDatabase + .update(TABLE_NAME) + .values(EVENT to Event.serialize(newEvent), DIRECTION to Direction.serialize(Direction.OUTGOING)) + .where("$CALL_ID = ?", call.callId) + .run() + + ApplicationDependencies.getMessageNotifier().updateNotification(context) + ApplicationDependencies.getDatabaseObserver().notifyCallUpdateObservers() + Log.d(TAG, "[acceptOutgoingGroupCall] Transitioned group call ${call.callId} from ${call.event} to $newEvent") + } + + fun declineIncomingGroupCall(call: Call) { + checkIsGroupOrAdHocCall(call) + check(call.direction == Direction.INCOMING) + + val newEvent = when (call.event) { + Event.GENERIC_GROUP_CALL, Event.RINGING, Event.MISSED, Event.MISSED_NOTIFICATION_PROFILE -> Event.DECLINED + Event.JOINED -> Event.ACCEPTED + else -> { + Log.d(TAG, "Call in state ${call.event} cannot be transitioned by DECLINED") + return + } + } + + writableDatabase + .update(TABLE_NAME) + .values(EVENT to Event.serialize(newEvent)) + .where("$CALL_ID = ?", call.callId) .run() ApplicationDependencies.getMessageNotifier().updateNotification(context) @@ -429,6 +479,46 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl ApplicationDependencies.getDatabaseObserver().notifyCallUpdateObservers() } + fun insertDeclinedGroupCall( + callId: Long, + recipientId: RecipientId, + timestamp: Long + ) { + val recipient = Recipient.resolved(recipientId) + val type = if (recipient.isCallLink) Type.AD_HOC_CALL else Type.GROUP_CALL + + writableDatabase.withinTransaction { db -> + val messageId: MessageId? = if (type == Type.GROUP_CALL) { + SignalDatabase.messages.insertGroupCall( + groupRecipientId = recipientId, + sender = Recipient.self().id, + timestamp, + "", + emptyList(), + false + ) + } else { + null + } + + db + .insertInto(TABLE_NAME) + .values( + CALL_ID to callId, + MESSAGE_ID to messageId?.id, + PEER to recipientId.toLong(), + EVENT to Event.serialize(Event.DECLINED), + TYPE to Type.serialize(type), + DIRECTION to Direction.serialize(Direction.INCOMING), + TIMESTAMP to timestamp, + RINGER to null + ) + .run(SQLiteDatabase.CONFLICT_ABORT) + } + + ApplicationDependencies.getDatabaseObserver().notifyCallUpdateObservers() + } + fun insertOrUpdateGroupCallFromExternalEvent( groupRecipientId: RecipientId, sender: RecipientId, @@ -622,15 +712,17 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl handleGroupRingState(ringId, groupRecipientId, ringerRecipient, dateReceived, ringState) } + @JvmOverloads fun insertOrUpdateGroupCallFromRingState( ringId: Long, groupRecipientId: RecipientId, ringerAci: ACI, dateReceived: Long, - ringState: RingUpdate + ringState: RingUpdate, + dueToNotificationProfile: Boolean = false ) { val ringerRecipient = Recipient.externalPush(ringerAci) - handleGroupRingState(ringId, groupRecipientId, ringerRecipient.id, dateReceived, ringState) + handleGroupRingState(ringId, groupRecipientId, ringerRecipient.id, dateReceived, ringState, dueToNotificationProfile) } fun isRingCancelled(ringId: Long, groupRecipientId: RecipientId): Boolean { @@ -643,7 +735,8 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl groupRecipientId: RecipientId, ringerRecipient: RecipientId, dateReceived: Long, - ringState: RingUpdate + ringState: RingUpdate, + dueToNotificationProfile: Boolean = false ) { writableDatabase.withinTransaction { Log.d(TAG, "Processing group ring state update for $ringId in state $ringState") @@ -666,13 +759,25 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl RingUpdate.EXPIRED_REQUEST, RingUpdate.CANCELLED_BY_RINGER -> { when (call.event) { - Event.GENERIC_GROUP_CALL, Event.RINGING -> updateEventFromRingState(ringId, Event.MISSED, ringerRecipient) + Event.GENERIC_GROUP_CALL, Event.RINGING -> updateEventFromRingState(ringId, if (dueToNotificationProfile) Event.MISSED_NOTIFICATION_PROFILE else Event.MISSED, ringerRecipient) + Event.JOINED -> updateEventFromRingState(ringId, Event.ACCEPTED, ringerRecipient) Event.OUTGOING_RING -> Log.w(TAG, "Received an expiration or cancellation while in OUTGOING_RING state. Ignoring.") else -> Unit } } - RingUpdate.BUSY_LOCALLY, RingUpdate.BUSY_ON_ANOTHER_DEVICE -> { + RingUpdate.BUSY_LOCALLY -> { + when (call.event) { + Event.JOINED -> updateEventFromRingState(ringId, Event.ACCEPTED) + Event.GENERIC_GROUP_CALL, Event.RINGING -> updateEventFromRingState(ringId, Event.MISSED) + else -> { + updateEventFromRingState(ringId, call.event, ringerRecipient) + Log.w(TAG, "Received a busy event we can't process. Updating ringer only.") + } + } + } + + RingUpdate.BUSY_ON_ANOTHER_DEVICE -> { when (call.event) { Event.JOINED -> updateEventFromRingState(ringId, Event.ACCEPTED) Event.GENERIC_GROUP_CALL, Event.RINGING -> updateEventFromRingState(ringId, Event.MISSED) @@ -686,7 +791,8 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl RingUpdate.DECLINED_ON_ANOTHER_DEVICE -> { when (call.event) { - Event.RINGING, Event.MISSED -> updateEventFromRingState(ringId, Event.DECLINED) + Event.RINGING, Event.MISSED, Event.MISSED_NOTIFICATION_PROFILE, Event.GENERIC_GROUP_CALL -> updateEventFromRingState(ringId, Event.DECLINED) + Event.JOINED -> updateEventFromRingState(ringId, Event.ACCEPTED) Event.OUTGOING_RING -> Log.w(TAG, "Received DECLINED_ON_ANOTHER_DEVICE while in OUTGOING_RING state.") else -> Unit } @@ -695,7 +801,7 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl } else { val event: Event = when (ringState) { RingUpdate.REQUESTED -> Event.RINGING - RingUpdate.EXPIRED_REQUEST -> Event.MISSED + RingUpdate.EXPIRED_REQUEST -> if (dueToNotificationProfile) Event.MISSED_NOTIFICATION_PROFILE else Event.MISSED RingUpdate.ACCEPTED_ON_ANOTHER_DEVICE -> { Log.w(TAG, "Missed original ring request for $ringId") Event.ACCEPTED @@ -859,7 +965,7 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl fun deleteAllNonAdHocCallEventsExcept(callRowIds: Set, missedOnly: Boolean) { val callFilter = if (missedOnly) { - "$EVENT = ${Event.serialize(Event.MISSED)} AND $DELETION_TIMESTAMP = 0" + "($EVENT = ${Event.serialize(Event.MISSED)} OR $EVENT = ${Event.serialize(Event.MISSED_NOTIFICATION_PROFILE)}) AND $DELETION_TIMESTAMP = 0" } else { "$DELETION_TIMESTAMP = 0" } @@ -960,7 +1066,7 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl private fun getCallsCursor(isCount: Boolean, offset: Int, limit: Int, searchTerm: String?, filter: CallLogFilter): Cursor { val filterClause: SqlUtil.Query = when (filter) { CallLogFilter.ALL -> SqlUtil.buildQuery("$DELETION_TIMESTAMP = 0") - CallLogFilter.MISSED -> SqlUtil.buildQuery("$EVENT = ${Event.serialize(Event.MISSED)} AND $DELETION_TIMESTAMP = 0") + CallLogFilter.MISSED -> SqlUtil.buildQuery("($EVENT = ${Event.serialize(Event.MISSED)} OR $EVENT = ${Event.serialize(Event.MISSED_NOTIFICATION_PROFILE)}) AND $DELETION_TIMESTAMP = 0") CallLogFilter.AD_HOC -> SqlUtil.buildQuery("$TYPE = ${Type.serialize(Type.AD_HOC_CALL)} AND $DELETION_TIMESTAMP = 0") } @@ -999,6 +1105,16 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl "p.$ID, p.$TIMESTAMP, $EVENT, $DIRECTION, $PEER, p.$TYPE, $CALL_ID, $MESSAGE_ID, $RINGER, children, in_period, ${MessageTable.BODY}," } + val eventTypeSubQuery = """ + ($TABLE_NAME.$EVENT = c.$EVENT AND ($TABLE_NAME.$EVENT = ${Event.serialize(Event.MISSED)} OR $TABLE_NAME.$EVENT = ${Event.serialize(Event.MISSED_NOTIFICATION_PROFILE)})) OR + ( + $TABLE_NAME.$EVENT != ${Event.serialize(Event.MISSED)} AND + c.$EVENT != ${Event.serialize(Event.MISSED)} AND + $TABLE_NAME.$EVENT != ${Event.serialize(Event.MISSED_NOTIFICATION_PROFILE)} AND + c.$EVENT != ${Event.serialize(Event.MISSED_NOTIFICATION_PROFILE)} + ) + """ + //language=sql val statement = """ SELECT $projection @@ -1026,10 +1142,7 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl AND $TABLE_NAME.$PEER = c.$PEER AND $TABLE_NAME.$TIMESTAMP - $TIME_WINDOW <= c.$TIMESTAMP AND $TABLE_NAME.$TIMESTAMP >= c.$TIMESTAMP - AND ( - ($TABLE_NAME.$EVENT = c.$EVENT AND $TABLE_NAME.$EVENT = ${Event.serialize(Event.MISSED)}) OR - ($TABLE_NAME.$EVENT != ${Event.serialize(Event.MISSED)} AND c.$EVENT != ${Event.serialize(Event.MISSED)}) - ) + AND ($eventTypeSubQuery) AND ${filterClause.where} ORDER BY $TIMESTAMP DESC @@ -1044,10 +1157,7 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl AND $TABLE_NAME.$PEER = c.$PEER AND c.$TIMESTAMP - $TIME_WINDOW <= $TABLE_NAME.$TIMESTAMP AND c.$TIMESTAMP >= $TABLE_NAME.$TIMESTAMP - AND ( - ($TABLE_NAME.$EVENT = c.$EVENT AND $TABLE_NAME.$EVENT = ${Event.serialize(Event.MISSED)}) OR - ($TABLE_NAME.$EVENT != ${Event.serialize(Event.MISSED)} AND c.$EVENT != ${Event.serialize(Event.MISSED)}) - ) + AND ($eventTypeSubQuery) AND ${filterClause.where} ) as children, ( @@ -1092,6 +1202,25 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl ) } + fun getLatestRingingCalls(): List { + return readableDatabase.select() + .from(TABLE_NAME) + .where("$EVENT = ?", Event.serialize(Event.RINGING)) + .limit(10) + .orderBy(TIMESTAMP) + .run() + .readToList { + Call.deserialize(it) + } + } + + fun markRingingCallsAsMissed() { + writableDatabase.update(TABLE_NAME) + .values(EVENT to Event.serialize(Event.MISSED)) + .where("$EVENT = ?", Event.serialize(Event.RINGING)) + .run() + } + fun getCallsCount(searchTerm: String?, filter: CallLogFilter): Int { return getCallsCursor(true, 0, 0, searchTerm, filter).use { it.moveToFirst() @@ -1156,7 +1285,7 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl return MessageTypes.GROUP_CALL_TYPE } - return if (direction == Direction.INCOMING && event == Event.MISSED) { + return if (direction == Direction.INCOMING && event.isMissedCall()) { if (type == Type.VIDEO_CALL) MessageTypes.MISSED_VIDEO_CALL_TYPE else MessageTypes.MISSED_AUDIO_CALL_TYPE } else if (direction == Direction.INCOMING) { if (type == Type.VIDEO_CALL) MessageTypes.INCOMING_VIDEO_CALL_TYPE else MessageTypes.INCOMING_AUDIO_CALL_TYPE @@ -1273,6 +1402,13 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl */ MISSED(3), + /** + * 1:1 and Group/Ad-Hoc Calls. + * + * Call was auto-declined due to a notification profile. + */ + MISSED_NOTIFICATION_PROFILE(10), + /** * 1:1 and Group/Ad-Hoc Calls. */ @@ -1303,7 +1439,11 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl /** * Group Calls: If you are ringing a group. */ - OUTGOING_RING(9); + OUTGOING_RING(9); // Next is 11 + + fun isMissedCall(): Boolean { + return this == MISSED || this == MISSED_NOTIFICATION_PROFILE + } companion object Serializer : IntSerializer { override fun serialize(data: Event): Int = data.code diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/DatabaseTable.java b/app/src/main/java/org/thoughtcrime/securesms/database/DatabaseTable.java index 1193485875..25afaa5eb2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/DatabaseTable.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/DatabaseTable.java @@ -79,11 +79,11 @@ public void reset(SignalDatabase databaseHelper) { this.databaseHelper = databaseHelper; } - protected SQLiteDatabase getReadableDatabase() { + public SQLiteDatabase getReadableDatabase() { return databaseHelper.getSignalReadableDatabase(); } - protected SQLiteDatabase getWritableDatabase() { + public SQLiteDatabase getWritableDatabase() { return databaseHelper.getSignalWritableDatabase(); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/DistributionListTables.kt b/app/src/main/java/org/thoughtcrime/securesms/database/DistributionListTables.kt index 2379a49869..04fe2c431d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/DistributionListTables.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/DistributionListTables.kt @@ -88,9 +88,9 @@ class DistributionListTables constructor(context: Context?, databaseHelper: Sign val CREATE_TABLE = """ CREATE TABLE $TABLE_NAME ( $ID INTEGER PRIMARY KEY AUTOINCREMENT, - $NAME TEXT UNIQUE NOT NULL, + $NAME TEXT NOT NULL, $DISTRIBUTION_ID TEXT UNIQUE NOT NULL, - $RECIPIENT_ID INTEGER UNIQUE REFERENCES ${RecipientTable.TABLE_NAME} (${RecipientTable.ID}), + $RECIPIENT_ID INTEGER UNIQUE REFERENCES ${RecipientTable.TABLE_NAME} (${RecipientTable.ID}) ON DELETE CASCADE, $ALLOWS_REPLIES INTEGER DEFAULT 1, $DELETION_TIMESTAMP INTEGER DEFAULT 0, $IS_UNKNOWN INTEGER DEFAULT 0, @@ -106,7 +106,7 @@ class DistributionListTables constructor(context: Context?, databaseHelper: Sign val LIST_UI_PROJECTION = arrayOf(ID, NAME, RECIPIENT_ID, ALLOWS_REPLIES, IS_UNKNOWN, PRIVACY_MODE, SEARCH_NAME) } - private object MembershipTable { + object MembershipTable { const val TABLE_NAME = "distribution_list_member" const val ID = "_id" @@ -118,7 +118,7 @@ class DistributionListTables constructor(context: Context?, databaseHelper: Sign CREATE TABLE $TABLE_NAME ( $ID INTEGER PRIMARY KEY AUTOINCREMENT, $LIST_ID INTEGER NOT NULL REFERENCES ${ListTable.TABLE_NAME} (${ListTable.ID}) ON DELETE CASCADE, - $RECIPIENT_ID INTEGER NOT NULL REFERENCES ${RecipientTable.TABLE_NAME} (${RecipientTable.ID}), + $RECIPIENT_ID INTEGER NOT NULL REFERENCES ${RecipientTable.TABLE_NAME} (${RecipientTable.ID}) ON DELETE CASCADE, $PRIVACY_MODE INTEGER DEFAULT 0 ) """ diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/GroupReceiptTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/GroupReceiptTable.kt index 80afa68642..ee1bf1fa09 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/GroupReceiptTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/GroupReceiptTable.kt @@ -2,9 +2,11 @@ package org.thoughtcrime.securesms.database import android.content.ContentValues import android.content.Context +import android.database.Cursor import androidx.core.content.contentValuesOf import org.signal.core.util.SqlUtil import org.signal.core.util.delete +import org.signal.core.util.forEach import org.signal.core.util.readToList import org.signal.core.util.requireBoolean import org.signal.core.util.requireInt @@ -21,9 +23,9 @@ class GroupReceiptTable(context: Context?, databaseHelper: SignalDatabase?) : Da private const val ID = "_id" const val MMS_ID = "mms_id" const val RECIPIENT_ID = "address" - private const val STATUS = "status" - private const val TIMESTAMP = "timestamp" - private const val UNIDENTIFIED = "unidentified" + const val STATUS = "status" + const val TIMESTAMP = "timestamp" + const val UNIDENTIFIED = "unidentified" const val STATUS_UNKNOWN = -1 const val STATUS_UNDELIVERED = 0 const val STATUS_DELIVERED = 1 @@ -127,14 +129,32 @@ class GroupReceiptTable(context: Context?, databaseHelper: SignalDatabase?) : Da .from(TABLE_NAME) .where("$MMS_ID = ?", mmsId) .run() - .readToList { cursor -> - GroupReceiptInfo( - recipientId = RecipientId.from(cursor.requireLong(RECIPIENT_ID)), - status = cursor.requireInt(STATUS), - timestamp = cursor.requireLong(TIMESTAMP), - isUnidentified = cursor.requireBoolean(UNIDENTIFIED) - ) - } + .readToList { it.toGroupReceiptInfo() } + } + + fun getGroupReceiptInfoForMessages(ids: Set): Map> { + if (ids.isEmpty()) { + return emptyMap() + } + + val messageIdsToGroupReceipts: MutableMap> = mutableMapOf() + + val args: List> = ids.map { SqlUtil.buildArgs(it) } + + SqlUtil.buildCustomCollectionQuery("$MMS_ID = ?", args).forEach { query -> + readableDatabase + .select() + .from(TABLE_NAME) + .where(query.where, query.whereArgs) + .run() + .forEach { cursor -> + val messageId = cursor.requireLong(MMS_ID) + val receipts = messageIdsToGroupReceipts.getOrPut(messageId) { mutableListOf() } + receipts += cursor.toGroupReceiptInfo() + } + } + + return messageIdsToGroupReceipts } fun deleteRowsForMessage(mmsId: Long) { @@ -163,6 +183,15 @@ class GroupReceiptTable(context: Context?, databaseHelper: SignalDatabase?) : Da .run() } + private fun Cursor.toGroupReceiptInfo(): GroupReceiptInfo { + return GroupReceiptInfo( + recipientId = RecipientId.from(this.requireLong(RECIPIENT_ID)), + status = this.requireInt(STATUS), + timestamp = this.requireLong(TIMESTAMP), + isUnidentified = this.requireBoolean(UNIDENTIFIED) + ) + } + data class GroupReceiptInfo( val recipientId: RecipientId, val status: Int, diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/GroupTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/GroupTable.kt index 1538866627..96a396cf70 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/GroupTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/GroupTable.kt @@ -7,7 +7,6 @@ import android.text.TextUtils import androidx.annotation.WorkerThread import androidx.core.content.contentValuesOf import org.intellij.lang.annotations.Language -import org.signal.core.util.SetUtil import org.signal.core.util.SqlUtil import org.signal.core.util.SqlUtil.appendArg import org.signal.core.util.SqlUtil.buildArgs @@ -36,14 +35,12 @@ import org.signal.storageservice.protos.groups.local.DecryptedGroup import org.thoughtcrime.securesms.contacts.paged.ContactSearchSortOrder import org.thoughtcrime.securesms.contacts.paged.collections.ContactSearchIterator import org.thoughtcrime.securesms.crypto.SenderKeyUtil -import org.thoughtcrime.securesms.database.SignalDatabase.Companion.messages import org.thoughtcrime.securesms.database.SignalDatabase.Companion.recipients import org.thoughtcrime.securesms.database.model.GroupRecord import org.thoughtcrime.securesms.dependencies.ApplicationDependencies import org.thoughtcrime.securesms.groups.BadGroupIdException import org.thoughtcrime.securesms.groups.GroupId import org.thoughtcrime.securesms.groups.GroupId.Push -import org.thoughtcrime.securesms.groups.GroupMigrationMembershipChange import org.thoughtcrime.securesms.groups.v2.processing.GroupsV2StateProcessor import org.thoughtcrime.securesms.jobs.RequestGroupV2InfoJob import org.thoughtcrime.securesms.keyvalue.SignalStore @@ -60,6 +57,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPoin import org.whispersystems.signalservice.api.push.DistributionId import org.whispersystems.signalservice.api.push.ServiceId import org.whispersystems.signalservice.api.push.ServiceId.ACI +import org.whispersystems.signalservice.api.push.ServiceId.PNI import java.io.Closeable import java.security.SecureRandom import java.util.Optional @@ -786,57 +784,6 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) : DatabaseT notifyConversationListListeners() } - /** - * Migrates a V1 group to a V2 group. - * - * @param decryptedGroup The state that represents the group on the server. This will be used to - * determine if we need to save our old membership list and stuff. - */ - fun migrateToV2( - threadId: Long, - groupIdV1: GroupId.V1, - decryptedGroup: DecryptedGroup - ): GroupId.V2 { - val groupIdV2 = groupIdV1.deriveV2MigrationGroupId() - val groupMasterKey = groupIdV1.deriveV2MigrationMasterKey() - - writableDatabase.withinTransaction { db -> - val record = getGroup(groupIdV1).get() - - val newMembers: MutableList = decryptedGroup.members.toAciList().toRecipientIds() - val pendingMembers: List = DecryptedGroupUtil.pendingToServiceIdList(decryptedGroup.pendingMembers).toRecipientIds() - newMembers.addAll(pendingMembers) - - val droppedMembers: List = SetUtil.difference(record.members, newMembers).toList() - val unmigratedMembers: List = pendingMembers + droppedMembers - - val updated: Int = db.update(TABLE_NAME) - .values( - GROUP_ID to groupIdV2.toString(), - V2_MASTER_KEY to groupMasterKey.serialize(), - DISTRIBUTION_ID to DistributionId.create().toString(), - EXPECTED_V2_ID to null, - UNMIGRATED_V1_MEMBERS to if (unmigratedMembers.isEmpty()) null else unmigratedMembers.serialize() - ) - .where("$GROUP_ID = ?", groupIdV1) - .run() - - if (updated != 1) { - throw AssertionError() - } - - recipients.updateGroupId(groupIdV1, groupIdV2) - update(groupMasterKey, decryptedGroup) - messages.insertGroupV1MigrationEvents( - record.recipientId, - threadId, - GroupMigrationMembershipChange(pendingMembers, droppedMembers) - ) - } - - return groupIdV2 - } - fun update(groupMasterKey: GroupMasterKey, decryptedGroup: DecryptedGroup) { update(GroupId.v2(groupMasterKey), decryptedGroup) } @@ -883,6 +830,14 @@ class GroupTable(context: Context?, databaseHelper: SignalDatabase?) : DatabaseT Log.i(TAG, removed.size.toString() + " members were removed from group " + groupId + ". Rotating the DistributionId " + distributionId) SenderKeyUtil.rotateOurKey(distributionId) } + + change.promotePendingPniAciMembers.forEach { member -> + recipients.getAndPossiblyMergePnpVerified( + aci = ACI.parseOrNull(member.aciBytes), + pni = PNI.parseOrNull(member.pniBytes), + e164 = null + ) + } } writableDatabase.withinTransaction { database -> diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MediaTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/MediaTable.kt index 63ad86da12..cc137bcbea 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MediaTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MediaTable.kt @@ -20,18 +20,17 @@ class MediaTable internal constructor(context: Context?, databaseHelper: SignalD private const val THREAD_RECIPIENT_ID = "THREAD_RECIPIENT_ID" private val BASE_MEDIA_QUERY = """ SELECT - ${AttachmentTable.TABLE_NAME}.${AttachmentTable.ROW_ID} AS ${AttachmentTable.ROW_ID}, + ${AttachmentTable.TABLE_NAME}.${AttachmentTable.ID} AS ${AttachmentTable.ID}, ${AttachmentTable.TABLE_NAME}.${AttachmentTable.CONTENT_TYPE}, - ${AttachmentTable.TABLE_NAME}.${AttachmentTable.UNIQUE_ID}, - ${AttachmentTable.TABLE_NAME}.${AttachmentTable.MMS_ID}, + ${AttachmentTable.TABLE_NAME}.${AttachmentTable.MESSAGE_ID}, ${AttachmentTable.TABLE_NAME}.${AttachmentTable.TRANSFER_STATE}, - ${AttachmentTable.TABLE_NAME}.${AttachmentTable.SIZE}, + ${AttachmentTable.TABLE_NAME}.${AttachmentTable.DATA_SIZE}, ${AttachmentTable.TABLE_NAME}.${AttachmentTable.FILE_NAME}, - ${AttachmentTable.TABLE_NAME}.${AttachmentTable.DATA}, + ${AttachmentTable.TABLE_NAME}.${AttachmentTable.DATA_FILE}, ${AttachmentTable.TABLE_NAME}.${AttachmentTable.CDN_NUMBER}, - ${AttachmentTable.TABLE_NAME}.${AttachmentTable.CONTENT_LOCATION}, - ${AttachmentTable.TABLE_NAME}.${AttachmentTable.CONTENT_DISPOSITION}, - ${AttachmentTable.TABLE_NAME}.${AttachmentTable.DIGEST}, + ${AttachmentTable.TABLE_NAME}.${AttachmentTable.REMOTE_LOCATION}, + ${AttachmentTable.TABLE_NAME}.${AttachmentTable.REMOTE_KEY}, + ${AttachmentTable.TABLE_NAME}.${AttachmentTable.REMOTE_DIGEST}, ${AttachmentTable.TABLE_NAME}.${AttachmentTable.FAST_PREFLIGHT_ID}, ${AttachmentTable.TABLE_NAME}.${AttachmentTable.VOICE_NOTE}, ${AttachmentTable.TABLE_NAME}.${AttachmentTable.BORDERLESS}, @@ -43,14 +42,13 @@ class MediaTable internal constructor(context: Context?, databaseHelper: SignalD ${AttachmentTable.TABLE_NAME}.${AttachmentTable.STICKER_PACK_KEY}, ${AttachmentTable.TABLE_NAME}.${AttachmentTable.STICKER_ID}, ${AttachmentTable.TABLE_NAME}.${AttachmentTable.STICKER_EMOJI}, - ${AttachmentTable.TABLE_NAME}.${AttachmentTable.VISUAL_HASH}, + ${AttachmentTable.TABLE_NAME}.${AttachmentTable.BLUR_HASH}, ${AttachmentTable.TABLE_NAME}.${AttachmentTable.TRANSFORM_PROPERTIES}, ${AttachmentTable.TABLE_NAME}.${AttachmentTable.DISPLAY_ORDER}, ${AttachmentTable.TABLE_NAME}.${AttachmentTable.CAPTION}, - ${AttachmentTable.TABLE_NAME}.${AttachmentTable.NAME}, ${AttachmentTable.TABLE_NAME}.${AttachmentTable.UPLOAD_TIMESTAMP}, - ${AttachmentTable.TABLE_NAME}.${AttachmentTable.MAC_DIGEST}, - ${AttachmentTable.TABLE_NAME}.${AttachmentTable.INCREMENTAL_MAC_CHUNK_SIZE}, + ${AttachmentTable.TABLE_NAME}.${AttachmentTable.REMOTE_INCREMENTAL_DIGEST}, + ${AttachmentTable.TABLE_NAME}.${AttachmentTable.REMOTE_INCREMENTAL_DIGEST_CHUNK_SIZE}, ${MessageTable.TABLE_NAME}.${MessageTable.TYPE}, ${MessageTable.TABLE_NAME}.${MessageTable.DATE_SENT}, ${MessageTable.TABLE_NAME}.${MessageTable.DATE_RECEIVED}, @@ -60,10 +58,10 @@ class MediaTable internal constructor(context: Context?, databaseHelper: SignalD ${ThreadTable.TABLE_NAME}.${ThreadTable.RECIPIENT_ID} as $THREAD_RECIPIENT_ID FROM ${AttachmentTable.TABLE_NAME} - LEFT JOIN ${MessageTable.TABLE_NAME} ON ${AttachmentTable.TABLE_NAME}.${AttachmentTable.MMS_ID} = ${MessageTable.TABLE_NAME}.${MessageTable.ID} + LEFT JOIN ${MessageTable.TABLE_NAME} ON ${AttachmentTable.TABLE_NAME}.${AttachmentTable.MESSAGE_ID} = ${MessageTable.TABLE_NAME}.${MessageTable.ID} LEFT JOIN ${ThreadTable.TABLE_NAME} ON ${ThreadTable.TABLE_NAME}.${ThreadTable.ID} = ${MessageTable.TABLE_NAME}.${MessageTable.THREAD_ID} WHERE - ${AttachmentTable.MMS_ID} IN ( + ${AttachmentTable.MESSAGE_ID} IN ( SELECT ${MessageTable.ID} FROM ${MessageTable.TABLE_NAME} WHERE ${MessageTable.THREAD_ID} __EQUALITY__ ? @@ -86,20 +84,20 @@ class MediaTable internal constructor(context: Context?, databaseHelper: SignalD private val UNIQUE_MEDIA_QUERY = """ SELECT - MAX(${AttachmentTable.SIZE}) as ${AttachmentTable.SIZE}, + MAX(${AttachmentTable.DATA_SIZE}) as ${AttachmentTable.DATA_SIZE}, ${AttachmentTable.CONTENT_TYPE} FROM ${AttachmentTable.TABLE_NAME} WHERE ${AttachmentTable.STICKER_PACK_ID} IS NULL AND ${AttachmentTable.TRANSFER_STATE} = ${AttachmentTable.TRANSFER_PROGRESS_DONE} - GROUP BY ${AttachmentTable.DATA} + GROUP BY ${AttachmentTable.DATA_FILE} """ private val GALLERY_MEDIA_QUERY = String.format( BASE_MEDIA_QUERY, """ - ${AttachmentTable.DATA} IS NOT NULL AND + ${AttachmentTable.DATA_FILE} IS NOT NULL AND ${AttachmentTable.CONTENT_TYPE} NOT LIKE 'image/svg%' AND (${AttachmentTable.CONTENT_TYPE} LIKE 'image/%' OR ${AttachmentTable.CONTENT_TYPE} LIKE 'video/%') """ @@ -108,7 +106,7 @@ class MediaTable internal constructor(context: Context?, databaseHelper: SignalD private val GALLERY_MEDIA_QUERY_INCLUDING_TEMP_VIDEOS = String.format( BASE_MEDIA_QUERY, """ - (${AttachmentTable.DATA} IS NOT NULL OR (${AttachmentTable.CONTENT_TYPE} LIKE 'video/%' AND ${AttachmentTable.MAC_DIGEST} IS NOT NULL)) AND + (${AttachmentTable.DATA_FILE} IS NOT NULL OR (${AttachmentTable.CONTENT_TYPE} LIKE 'video/%' AND ${AttachmentTable.REMOTE_INCREMENTAL_DIGEST} IS NOT NULL)) AND ${AttachmentTable.CONTENT_TYPE} NOT LIKE 'image/svg%' AND (${AttachmentTable.CONTENT_TYPE} LIKE 'image/%' OR ${AttachmentTable.CONTENT_TYPE} LIKE 'video/%') """ @@ -117,17 +115,17 @@ class MediaTable internal constructor(context: Context?, databaseHelper: SignalD private val AUDIO_MEDIA_QUERY = String.format( BASE_MEDIA_QUERY, """ - ${AttachmentTable.DATA} IS NOT NULL AND + ${AttachmentTable.DATA_FILE} IS NOT NULL AND ${AttachmentTable.CONTENT_TYPE} LIKE 'audio/%' """ ) - private val ALL_MEDIA_QUERY = String.format(BASE_MEDIA_QUERY, "${AttachmentTable.DATA} IS NOT NULL AND ${AttachmentTable.CONTENT_TYPE} NOT LIKE 'text/x-signal-plain'") + private val ALL_MEDIA_QUERY = String.format(BASE_MEDIA_QUERY, "${AttachmentTable.DATA_FILE} IS NOT NULL AND ${AttachmentTable.CONTENT_TYPE} NOT LIKE 'text/x-signal-plain'") private val DOCUMENT_MEDIA_QUERY = String.format( BASE_MEDIA_QUERY, """ - ${AttachmentTable.DATA} IS NOT NULL AND + ${AttachmentTable.DATA_FILE} IS NOT NULL AND ( ${AttachmentTable.CONTENT_TYPE} LIKE 'image/svg%' OR ( @@ -185,7 +183,7 @@ class MediaTable internal constructor(context: Context?, databaseHelper: SignalD readableDatabase.rawQuery(UNIQUE_MEDIA_QUERY, null).use { cursor -> while (cursor.moveToNext()) { - val size: Int = cursor.requireInt(AttachmentTable.SIZE) + val size: Int = cursor.requireInt(AttachmentTable.DATA_SIZE) val type: String = cursor.requireNonNullString(AttachmentTable.CONTENT_TYPE) when (MediaUtil.getSlideTypeFromContentType(type)) { @@ -253,21 +251,21 @@ class MediaTable internal constructor(context: Context?, databaseHelper: SignalD enum class Sorting(order: String) { Newest( """ - ${AttachmentTable.TABLE_NAME}.${AttachmentTable.MMS_ID} DESC, + ${AttachmentTable.TABLE_NAME}.${AttachmentTable.MESSAGE_ID} DESC, ${AttachmentTable.TABLE_NAME}.${AttachmentTable.DISPLAY_ORDER} DESC, - ${AttachmentTable.TABLE_NAME}.${AttachmentTable.ROW_ID} DESC + ${AttachmentTable.TABLE_NAME}.${AttachmentTable.ID} DESC """ ), Oldest( """ - ${AttachmentTable.TABLE_NAME}.${AttachmentTable.MMS_ID} ASC, + ${AttachmentTable.TABLE_NAME}.${AttachmentTable.MESSAGE_ID} ASC, ${AttachmentTable.TABLE_NAME}.${AttachmentTable.DISPLAY_ORDER} DESC, - ${AttachmentTable.TABLE_NAME}.${AttachmentTable.ROW_ID} ASC + ${AttachmentTable.TABLE_NAME}.${AttachmentTable.ID} ASC """ ), Largest( """ - ${AttachmentTable.TABLE_NAME}.${AttachmentTable.SIZE} DESC, + ${AttachmentTable.TABLE_NAME}.${AttachmentTable.DATA_SIZE} DESC, ${AttachmentTable.TABLE_NAME}.${AttachmentTable.DISPLAY_ORDER} DESC """ ); diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MessageSendLogTables.kt b/app/src/main/java/org/thoughtcrime/securesms/database/MessageSendLogTables.kt index 16e3d7096f..b0e7817e62 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MessageSendLogTables.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MessageSendLogTables.kt @@ -92,13 +92,13 @@ class MessageSendLogTables constructor(context: Context?, databaseHelper: Signal """ CREATE TRIGGER msl_message_delete AFTER DELETE ON ${MessageTable.TABLE_NAME} BEGIN - DELETE FROM $TABLE_NAME WHERE $ID IN (SELECT ${MslMessageTable.PAYLOAD_ID} FROM ${MslMessageTable.TABLE_NAME} WHERE ${MslMessageTable.MESSAGE_ID} = old.${MessageTable.ID}); + DELETE FROM $TABLE_NAME WHERE $ID IN (SELECT ${MslMessageTable.PAYLOAD_ID} FROM ${MslMessageTable.TABLE_NAME} WHERE ${MslMessageTable.MESSAGE_ID} = old.${MessageTable.ID}); END """, """ CREATE TRIGGER msl_attachment_delete AFTER DELETE ON ${AttachmentTable.TABLE_NAME} BEGIN - DELETE FROM $TABLE_NAME WHERE $ID IN (SELECT ${MslMessageTable.PAYLOAD_ID} FROM ${MslMessageTable.TABLE_NAME} WHERE ${MslMessageTable.MESSAGE_ID} = old.${AttachmentTable.MMS_ID}); + DELETE FROM $TABLE_NAME WHERE $ID IN (SELECT ${MslMessageTable.PAYLOAD_ID} FROM ${MslMessageTable.TABLE_NAME} WHERE ${MslMessageTable.TABLE_NAME}.${MslMessageTable.MESSAGE_ID} = old.${AttachmentTable.MESSAGE_ID}); END """ ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt index ab4333022d..0deb72fa5f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt @@ -207,6 +207,9 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat const val ORIGINAL_MESSAGE_ID = "original_message_id" const val REVISION_NUMBER = "revision_number" + const val QUOTE_NOT_PRESENT_ID = 0L + const val QUOTE_TARGET_MISSING_ID = -1L + const val CREATE_TABLE = """ CREATE TABLE $TABLE_NAME ( $ID INTEGER PRIMARY KEY AUTOINCREMENT, @@ -348,15 +351,14 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat """ json_group_array( json_object( - '${AttachmentTable.ROW_ID}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.ROW_ID}, - '${AttachmentTable.UNIQUE_ID}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.UNIQUE_ID}, - '${AttachmentTable.MMS_ID}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.MMS_ID}, - '${AttachmentTable.SIZE}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.SIZE}, + '${AttachmentTable.ID}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.ID}, + '${AttachmentTable.MESSAGE_ID}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.MESSAGE_ID}, + '${AttachmentTable.DATA_SIZE}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.DATA_SIZE}, '${AttachmentTable.FILE_NAME}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.FILE_NAME}, - '${AttachmentTable.DATA}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.DATA}, + '${AttachmentTable.DATA_FILE}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.DATA_FILE}, '${AttachmentTable.CONTENT_TYPE}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.CONTENT_TYPE}, '${AttachmentTable.CDN_NUMBER}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.CDN_NUMBER}, - '${AttachmentTable.CONTENT_LOCATION}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.CONTENT_LOCATION}, + '${AttachmentTable.REMOTE_LOCATION}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.REMOTE_LOCATION}, '${AttachmentTable.FAST_PREFLIGHT_ID}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.FAST_PREFLIGHT_ID}, '${AttachmentTable.VOICE_NOTE}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.VOICE_NOTE}, '${AttachmentTable.BORDERLESS}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.BORDERLESS}, @@ -364,15 +366,14 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat '${AttachmentTable.WIDTH}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.WIDTH}, '${AttachmentTable.HEIGHT}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.HEIGHT}, '${AttachmentTable.QUOTE}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.QUOTE}, - '${AttachmentTable.CONTENT_DISPOSITION}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.CONTENT_DISPOSITION}, - '${AttachmentTable.NAME}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.NAME}, + '${AttachmentTable.REMOTE_KEY}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.REMOTE_KEY}, '${AttachmentTable.TRANSFER_STATE}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.TRANSFER_STATE}, '${AttachmentTable.CAPTION}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.CAPTION}, '${AttachmentTable.STICKER_PACK_ID}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.STICKER_PACK_ID}, '${AttachmentTable.STICKER_PACK_KEY}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.STICKER_PACK_KEY}, '${AttachmentTable.STICKER_ID}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.STICKER_ID}, '${AttachmentTable.STICKER_EMOJI}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.STICKER_EMOJI}, - '${AttachmentTable.VISUAL_HASH}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.VISUAL_HASH}, + '${AttachmentTable.BLUR_HASH}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.BLUR_HASH}, '${AttachmentTable.TRANSFORM_PROPERTIES}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.TRANSFORM_PROPERTIES}, '${AttachmentTable.DISPLAY_ORDER}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.DISPLAY_ORDER}, '${AttachmentTable.UPLOAD_TIMESTAMP}', ${AttachmentTable.TABLE_NAME}.${AttachmentTable.UPLOAD_TIMESTAMP} @@ -398,17 +399,19 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat $PARENT_STORY_ID <= 0 AND $SCHEDULED_DATE = -1 AND $LATEST_REVISION_ID IS NULL AND + $TYPE & ${MessageTypes.KEY_EXCHANGE_IDENTITY_DEFAULT_BIT} = 0 AND + $TYPE & ${MessageTypes.KEY_EXCHANGE_IDENTITY_VERIFIED_BIT} = 0 AND $TYPE NOT IN ( ${MessageTypes.PROFILE_CHANGE_TYPE}, - ${MessageTypes.GV1_MIGRATION_TYPE}, - ${MessageTypes.CHANGE_NUMBER_TYPE}, - ${MessageTypes.BOOST_REQUEST_TYPE}, - ${MessageTypes.SMS_EXPORT_TYPE} - ) - ORDER BY $DATE_RECEIVED DESC LIMIT 1 + ${MessageTypes.GV1_MIGRATION_TYPE}, + ${MessageTypes.CHANGE_NUMBER_TYPE}, + ${MessageTypes.BOOST_REQUEST_TYPE}, + ${MessageTypes.SMS_EXPORT_TYPE} + ) + ORDER BY $DATE_RECEIVED DESC LIMIT 1 """ - private val IS_CALL_TYPE_CLAUSE = """( + const val IS_CALL_TYPE_CLAUSE = """( ($TYPE = ${MessageTypes.INCOMING_AUDIO_CALL_TYPE}) OR ($TYPE = ${MessageTypes.INCOMING_VIDEO_CALL_TYPE}) @@ -1775,7 +1778,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat return threads.getOrCreateThreadIdFor(recipient) } - private fun rawQueryWithAttachments(where: String, arguments: Array?, reverse: Boolean = false, limit: Long = 0): Cursor { + fun rawQueryWithAttachments(where: String, arguments: Array?, reverse: Boolean = false, limit: Long = 0): Cursor { return rawQueryWithAttachments(MMS_PROJECTION_WITH_ATTACHMENTS, where, arguments, reverse, limit) } @@ -1785,7 +1788,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat SELECT ${Util.join(projection, ",")} FROM - $TABLE_NAME LEFT OUTER JOIN ${AttachmentTable.TABLE_NAME} ON ($TABLE_NAME.$ID = ${AttachmentTable.TABLE_NAME}.${AttachmentTable.MMS_ID}) + $TABLE_NAME LEFT OUTER JOIN ${AttachmentTable.TABLE_NAME} ON ($TABLE_NAME.$ID = ${AttachmentTable.TABLE_NAME}.${AttachmentTable.MESSAGE_ID}) WHERE $where GROUP BY @@ -2249,18 +2252,25 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat * Trims data related to expired messages. Only intended to be run after a backup restore. */ fun trimEntriesForExpiredMessages() { + val messageDeleteCount = writableDatabase + .delete(TABLE_NAME) + .where("$EXPIRE_STARTED > 0 AND $EXPIRES_IN > 0 AND ($EXPIRE_STARTED + $EXPIRES_IN) < ${System.currentTimeMillis()}") + .run() + + Log.d(TAG, "Deleted $messageDeleteCount expired messages after backup.") + writableDatabase .delete(GroupReceiptTable.TABLE_NAME) .where("${GroupReceiptTable.MMS_ID} NOT IN (SELECT $ID FROM $TABLE_NAME)") .run() readableDatabase - .select(AttachmentTable.ROW_ID, AttachmentTable.UNIQUE_ID) + .select(AttachmentTable.ID) .from(AttachmentTable.TABLE_NAME) - .where("${AttachmentTable.MMS_ID} NOT IN (SELECT $ID FROM $TABLE_NAME)") + .where("${AttachmentTable.MESSAGE_ID} NOT IN (SELECT $ID FROM $TABLE_NAME)") .run() .forEach { cursor -> - attachments.deleteAttachment(AttachmentId(cursor.requireLong(AttachmentTable.ROW_ID), cursor.requireLong(AttachmentTable.UNIQUE_ID))) + attachments.deleteAttachment(AttachmentId(cursor.requireLong(AttachmentTable.ID))) } mentions.deleteAbandonedMentions() @@ -2277,23 +2287,6 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat } } - fun getNotification(messageId: Long): Optional { - return readableDatabase - .select(FROM_RECIPIENT_ID, MMS_CONTENT_LOCATION, MMS_TRANSACTION_ID, SMS_SUBSCRIPTION_ID) - .from(TABLE_NAME) - .where("$ID = ?", messageId) - .run() - .readToSingleObject { cursor -> - MmsNotificationInfo( - from = RecipientId.from(cursor.requireLong(FROM_RECIPIENT_ID)), - contentLocation = cursor.requireNonNullString(MMS_CONTENT_LOCATION), - transactionId = cursor.requireNonNullString(MMS_TRANSACTION_ID), - subscriptionId = cursor.requireInt(SMS_SUBSCRIPTION_ID) - ) - } - .toOptional() - } - @Throws(MmsException::class, NoSuchMessageException::class) fun getOutgoingMessage(messageId: Long): OutgoingMessage { return rawQueryWithAttachments(RAW_ID_WHERE, arrayOf(messageId.toString())).readToSingleObject { cursor -> @@ -2318,10 +2311,10 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat val quoteText = cursor.requireString(QUOTE_BODY) val quoteType = cursor.requireInt(QUOTE_TYPE) val quoteMissing = cursor.requireBoolean(QUOTE_MISSING) - val quoteAttachments: List = associatedAttachments.filter { it.isQuote }.toList() + val quoteAttachments: List = associatedAttachments.filter { it.quote }.toList() val quoteMentions: List = parseQuoteMentions(cursor) val quoteBodyRanges: BodyRangeList? = parseQuoteBodyRanges(cursor) - val quote: QuoteModel? = if (quoteId > 0 && quoteAuthor > 0 && (!TextUtils.isEmpty(quoteText) || quoteAttachments.isNotEmpty())) { + val quote: QuoteModel? = if (quoteId != QUOTE_NOT_PRESENT_ID && quoteAuthor > 0 && (!TextUtils.isEmpty(quoteText) || quoteAttachments.isNotEmpty())) { QuoteModel(quoteId, RecipientId.from(quoteAuthor), quoteText ?: "", quoteMissing, quoteAttachments, quoteMentions, QuoteModel.Type.fromCode(quoteType), quoteBodyRanges) } else { null @@ -2332,7 +2325,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat val previews: List = getLinkPreviews(cursor, associatedAttachments) val previewAttachments: Set = previews.filter { it.thumbnail.isPresent }.map { it.thumbnail.get() }.toSet() val attachments: List = associatedAttachments - .filterNot { it.isQuote } + .filterNot { it.quote } .filterNot { contactAttachments.contains(it) } .filterNot { previewAttachments.contains(it) } .sortedWith(DisplayOrderComparator()) @@ -2557,7 +2550,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat } if (retrieved.attachments.isEmpty() && editedMessage?.id != null && attachments.getAttachmentsForMessage(editedMessage.id).isNotEmpty()) { - val linkPreviewAttachmentIds = editedMessage.linkPreviews.mapNotNull { it.attachmentId?.rowId }.toSet() + val linkPreviewAttachmentIds = editedMessage.linkPreviews.mapNotNull { it.attachmentId?.id }.toSet() attachments.duplicateAttachmentsForMessage(messageId, editedMessage.id, linkPreviewAttachmentIds) } @@ -2930,8 +2923,8 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat .where("$ID_WHERE OR $LATEST_REVISION_ID = ?", message.messageToEdit, message.messageToEdit) .run() - val textAttachments = (editedMessage as? MmsMessageRecord)?.slideDeck?.asAttachments()?.filter { it.contentType == MediaUtil.LONG_TEXT }?.mapNotNull { (it as? DatabaseAttachment)?.attachmentId?.rowId } ?: emptyList() - val linkPreviewAttachments = (editedMessage as? MmsMessageRecord)?.linkPreviews?.mapNotNull { it.attachmentId?.rowId } ?: emptyList() + val textAttachments = (editedMessage as? MmsMessageRecord)?.slideDeck?.asAttachments()?.filter { it.contentType == MediaUtil.LONG_TEXT }?.mapNotNull { (it as? DatabaseAttachment)?.attachmentId?.id } ?: emptyList() + val linkPreviewAttachments = (editedMessage as? MmsMessageRecord)?.linkPreviews?.mapNotNull { it.attachmentId?.id } ?: emptyList() val excludeIds = HashSet() excludeIds += textAttachments excludeIds += linkPreviewAttachments @@ -3108,26 +3101,16 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat return rowsDeleted } - fun deleteMessage(messageId: Long): Boolean { - val threadId = getThreadIdForMessage(messageId) - return deleteMessage(messageId, threadId) - } + fun deleteExpiringMessage(messageId: Long): Boolean = + deleteMessage(messageId, isExpiring = true) - fun deleteExpiringMessage(messageId: Long): Boolean { - val threadId = getThreadIdForMessage(messageId) - return deleteMessage(messageId, threadId, notify = true, isExpiring = true) - } + fun deleteMessage(messageId: Long): Boolean = + deleteMessage(messageId, isExpiring = false) - fun deleteMessage(messageId: Long, notify: Boolean): Boolean { - val threadId = getThreadIdForMessage(messageId) - return deleteMessage(messageId, threadId, notify, isExpiring = false) - } + private fun deleteMessage(messageId: Long, threadId: Long = getThreadIdForMessage(messageId)): Boolean = + deleteMessage(messageId, isExpiring = false, threadId) - fun deleteMessage(messageId: Long, threadId: Long): Boolean { - return deleteMessage(messageId, threadId, notify = true, isExpiring = false) - } - - private fun deleteMessage(messageId: Long, threadId: Long, notify: Boolean, isExpiring: Boolean): Boolean { + private fun deleteMessage(messageId: Long, isExpiring: Boolean = false, threadId: Long = getThreadIdForMessage(messageId), notify: Boolean = true, updateThread: Boolean = true): Boolean { Log.d(TAG, "deleteMessage($messageId)") attachments.deleteAttachmentsForMessage(messageId) @@ -3141,7 +3124,12 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat calls.updateCallEventDeletionTimestamps() threads.setLastScrolled(threadId, 0) - val threadDeleted = threads.update(threadId, false) + + val threadDeleted = if (updateThread) { + threads.update(threadId, false) + } else { + false + } if (notify) { notifyConversationListeners(threadId) @@ -3316,9 +3304,9 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat val fileSize: Long = readableDatabase.rawQuery( """ SELECT - SUM(${AttachmentTable.TABLE_NAME}.${AttachmentTable.SIZE}) AS s + SUM(${AttachmentTable.TABLE_NAME}.${AttachmentTable.DATA_SIZE}) AS s FROM - $TABLE_NAME INNER JOIN ${AttachmentTable.TABLE_NAME} ON $TABLE_NAME.$ID = ${AttachmentTable.TABLE_NAME}.${AttachmentTable.MMS_ID} + $TABLE_NAME INNER JOIN ${AttachmentTable.TABLE_NAME} ON $TABLE_NAME.$ID = ${AttachmentTable.TABLE_NAME}.${AttachmentTable.MESSAGE_ID} WHERE ${getInsecureMessageClause()} AND $EXPORTED < ${MessageExportStatus.EXPORTED.serialize()} """, @@ -3348,7 +3336,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat OptimizeMessageSearchIndexJob.enqueue() } - fun deleteThreads(threadIds: Set) { + private fun deleteThreads(threadIds: Set) { Log.d(TAG, "deleteThreads(count: ${threadIds.size})") writableDatabase.withinTransaction { db -> @@ -3358,7 +3346,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat .where(query.where, query.whereArgs) .run() .forEach { cursor -> - deleteMessage(cursor.requireLong(ID), false) + deleteMessage(cursor.requireLong(ID), notify = false, updateThread = false) } } } @@ -3426,10 +3414,10 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat $VIEW_ONCE, $DATE_RECEIVED FROM - $TABLE_NAME INNER JOIN ${AttachmentTable.TABLE_NAME} ON $TABLE_NAME.$ID = ${AttachmentTable.TABLE_NAME}.${AttachmentTable.MMS_ID} + $TABLE_NAME INNER JOIN ${AttachmentTable.TABLE_NAME} ON $TABLE_NAME.$ID = ${AttachmentTable.TABLE_NAME}.${AttachmentTable.MESSAGE_ID} WHERE $VIEW_ONCE > 0 AND - (${AttachmentTable.DATA} NOT NULL OR ${AttachmentTable.TRANSFER_STATE} != ?) + (${AttachmentTable.DATA_FILE} NOT NULL OR ${AttachmentTable.TRANSFER_STATE} != ?) """ val args = buildArgs(AttachmentTable.TRANSFER_PROGRESS_DONE) @@ -5006,6 +4994,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat val latestRevisionId: MessageId? = cursor.requireLong(LATEST_REVISION_ID).let { if (it == 0L) null else MessageId(it) } val originalMessageId: MessageId? = cursor.requireLong(ORIGINAL_MESSAGE_ID).let { if (it == 0L) null else MessageId(it) } val editCount = cursor.requireInt(REVISION_NUMBER) + val isRead = cursor.requireBoolean(READ) if (!TextSecurePreferences.isReadReceiptsEnabled(context)) { hasReadReceipt = false @@ -5092,7 +5081,8 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat scheduledDate, latestRevisionId, originalMessageId, - editCount + editCount, + isRead ) } @@ -5130,10 +5120,10 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat val bodyRanges = parseQuoteBodyRanges(cursor) val attachments = attachments.getAttachments(cursor) - val quoteAttachments: List = attachments.filter { it.isQuote } + val quoteAttachments: List = attachments.filter { it.quote } val quoteDeck = SlideDeck(quoteAttachments) - return if (quoteId > 0 && quoteAuthor > 0) { + return if (quoteId != QUOTE_NOT_PRESENT_ID && quoteAuthor > 0) { if (quoteText != null && (quoteMentions.isNotEmpty() || bodyRanges != null)) { val updated: UpdatedBodyAndMentions = MentionUtil.updateBodyAndMentionsWithDisplayNames(context, quoteText, quoteMentions) val styledText = SpannableString(updated.body) @@ -5181,7 +5171,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat @JvmStatic fun buildSlideDeck(attachments: List): SlideDeck { val messageAttachments = attachments - .filterNot { it.isQuote } + .filterNot { it.quote } .sortedWith(DisplayOrderComparator()) return SlideDeck(messageAttachments) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/NotificationProfileTables.kt b/app/src/main/java/org/thoughtcrime/securesms/database/NotificationProfileTables.kt index 136a3989c1..422445c8ee 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/NotificationProfileTables.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/NotificationProfileTables.kt @@ -111,6 +111,7 @@ class NotificationProfileDatabase(context: Context, databaseHelper: SignalDataba put(NotificationProfileTable.EMOJI, emoji) put(NotificationProfileTable.COLOR, color.serialize()) put(NotificationProfileTable.CREATED_AT, createdAt) + put(NotificationProfileTable.ALLOW_ALL_CALLS, 1) } val profileId = db.insert(NotificationProfileTable.TABLE_NAME, null, profileValues) @@ -134,7 +135,8 @@ class NotificationProfileDatabase(context: Context, databaseHelper: SignalDataba name = name, emoji = emoji, createdAt = createdAt, - schedule = getProfileSchedule(profileId) + schedule = getProfileSchedule(profileId), + allowAllCalls = true ) ) } finally { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/ReactionTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/ReactionTable.kt index 851b0b0a74..0d1b0e5363 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/ReactionTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/ReactionTable.kt @@ -22,10 +22,10 @@ class ReactionTable(context: Context, databaseHelper: SignalDatabase) : Database private const val ID = "_id" const val MESSAGE_ID = "message_id" - private const val AUTHOR_ID = "author_id" - private const val EMOJI = "emoji" - private const val DATE_SENT = "date_sent" - private const val DATE_RECEIVED = "date_received" + const val AUTHOR_ID = "author_id" + const val EMOJI = "emoji" + const val DATE_SENT = "date_sent" + const val DATE_RECEIVED = "date_received" @JvmField val CREATE_TABLE = """ diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt index 487fea690f..9d04f0fca7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt @@ -17,10 +17,7 @@ import org.signal.core.util.SqlUtil import org.signal.core.util.delete import org.signal.core.util.exists import org.signal.core.util.logging.Log -import org.signal.core.util.optionalBlob -import org.signal.core.util.optionalBoolean -import org.signal.core.util.optionalInt -import org.signal.core.util.optionalLong +import org.signal.core.util.nullIfBlank import org.signal.core.util.optionalString import org.signal.core.util.or import org.signal.core.util.orNull @@ -29,7 +26,6 @@ import org.signal.core.util.readToSet import org.signal.core.util.readToSingleBoolean import org.signal.core.util.readToSingleLong import org.signal.core.util.requireBlob -import org.signal.core.util.requireBoolean import org.signal.core.util.requireInt import org.signal.core.util.requireLong import org.signal.core.util.requireNonNullString @@ -39,11 +35,9 @@ import org.signal.core.util.update import org.signal.core.util.withinTransaction import org.signal.libsignal.protocol.IdentityKey import org.signal.libsignal.protocol.InvalidKeyException -import org.signal.libsignal.zkgroup.InvalidInputException import org.signal.libsignal.zkgroup.profiles.ExpiringProfileKeyCredential import org.signal.libsignal.zkgroup.profiles.ProfileKey import org.signal.storageservice.protos.groups.local.DecryptedGroup -import org.thoughtcrime.securesms.badges.Badges import org.thoughtcrime.securesms.badges.Badges.toDatabaseBadge import org.thoughtcrime.securesms.badges.models.Badge import org.thoughtcrime.securesms.color.MaterialColor @@ -56,6 +50,7 @@ import org.thoughtcrime.securesms.crypto.ProfileKeyUtil import org.thoughtcrime.securesms.database.GroupTable.LegacyGroupInsertException import org.thoughtcrime.securesms.database.GroupTable.ShowAsStoryState import org.thoughtcrime.securesms.database.IdentityTable.VerifiedStatus +import org.thoughtcrime.securesms.database.RecipientTableCursorUtil.getRecipientExtras import org.thoughtcrime.securesms.database.SignalDatabase.Companion.groups import org.thoughtcrime.securesms.database.SignalDatabase.Companion.identities import org.thoughtcrime.securesms.database.SignalDatabase.Companion.runPostSuccessfulTransaction @@ -82,7 +77,6 @@ import org.thoughtcrime.securesms.groups.v2.processing.GroupsV2StateProcessor import org.thoughtcrime.securesms.jobs.RequestGroupV2InfoJob import org.thoughtcrime.securesms.jobs.RetrieveProfileJob import org.thoughtcrime.securesms.keyvalue.SignalStore -import org.thoughtcrime.securesms.profiles.AvatarHelper import org.thoughtcrime.securesms.profiles.ProfileName import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.RecipientId @@ -91,12 +85,10 @@ import org.thoughtcrime.securesms.storage.StorageRecordUpdate import org.thoughtcrime.securesms.storage.StorageSyncHelper import org.thoughtcrime.securesms.storage.StorageSyncModels import org.thoughtcrime.securesms.util.FeatureFlags -import org.thoughtcrime.securesms.util.GroupUtil import org.thoughtcrime.securesms.util.IdentityUtil import org.thoughtcrime.securesms.util.ProfileUtil import org.thoughtcrime.securesms.util.Util import org.thoughtcrime.securesms.wallpaper.ChatWallpaper -import org.thoughtcrime.securesms.wallpaper.ChatWallpaperFactory import org.thoughtcrime.securesms.wallpaper.WallpaperStorage import org.whispersystems.signalservice.api.profiles.SignalServiceProfile import org.whispersystems.signalservice.api.push.ServiceId @@ -111,7 +103,6 @@ import org.whispersystems.signalservice.api.storage.StorageId import org.whispersystems.signalservice.internal.storage.protos.GroupV2Record import java.io.Closeable import java.io.IOException -import java.util.Arrays import java.util.Collections import java.util.LinkedList import java.util.Objects @@ -121,9 +112,9 @@ import kotlin.math.max open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTable(context, databaseHelper) { - companion object { - private val TAG = Log.tag(RecipientTable::class.java) + val TAG = Log.tag(RecipientTable::class.java) + companion object { private val UNREGISTERED_LIFESPAN: Long = TimeUnit.DAYS.toMillis(30) const val TABLE_NAME = "recipient" @@ -184,13 +175,12 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da const val BADGES = "badges" const val NEEDS_PNI_SIGNATURE = "needs_pni_signature" const val REPORTING_TOKEN = "reporting_token" - // MOLLY: Add new fields to clearFieldsForDeletion + const val PHONE_NUMBER_SHARING = "phone_number_sharing" const val SEARCH_PROFILE_NAME = "search_signal_profile" const val SORT_NAME = "sort_name" const val IDENTITY_STATUS = "identity_status" const val IDENTITY_KEY = "identity_key" - // MOLLY: Add new fields to clearFieldsForDeletion @JvmField val CREATE_TABLE = @@ -251,7 +241,8 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da $CUSTOM_CHAT_COLORS_ID INTEGER DEFAULT 0, $BADGES BLOB DEFAULT NULL, $NEEDS_PNI_SIGNATURE INTEGER DEFAULT 0, - $REPORTING_TOKEN BLOB DEFAULT NULL + $REPORTING_TOKEN BLOB DEFAULT NULL, + $PHONE_NUMBER_SHARING INTEGER DEFAULT ${PhoneNumberSharingState.UNKNOWN.id} ) """ @@ -310,7 +301,8 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da CUSTOM_CHAT_COLORS_ID, BADGES, NEEDS_PNI_SIGNATURE, - REPORTING_TOKEN + REPORTING_TOKEN, + PHONE_NUMBER_SHARING ) private val ID_PROJECTION = arrayOf(ID) @@ -385,6 +377,20 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da /** Used as a placeholder recipient for self during migrations when self isn't yet available. */ public const val PLACEHOLDER_SELF_ID = -2L + + @JvmStatic + fun maskCapabilitiesToLong(capabilities: SignalServiceProfile.Capabilities): Long { + var value: Long = 0 + value = Bitmask.update(value, Capabilities.GROUPS_V1_MIGRATION, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isGv1Migration).serialize().toLong()) + value = Bitmask.update(value, Capabilities.SENDER_KEY, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isSenderKey).serialize().toLong()) + value = Bitmask.update(value, Capabilities.ANNOUNCEMENT_GROUPS, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isAnnouncementGroup).serialize().toLong()) + value = Bitmask.update(value, Capabilities.CHANGE_NUMBER, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isChangeNumber).serialize().toLong()) + value = Bitmask.update(value, Capabilities.STORIES, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isStories).serialize().toLong()) + value = Bitmask.update(value, Capabilities.GIFT_BADGES, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isGiftBadges).serialize().toLong()) + value = Bitmask.update(value, Capabilities.PNP, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isPnp).serialize().toLong()) + value = Bitmask.update(value, Capabilities.PAYMENT_ACTIVATION, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isPaymentActivation).serialize().toLong()) + return value + } } fun getByE164(e164: String): Optional { @@ -453,25 +459,16 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da Log.d(TAG, "[getAndPossiblyMerge] Requires a transaction.") val db = writableDatabase - var transactionSuccessful = false lateinit var result: ProcessPnpTupleResult - db.beginTransaction() - try { + db.withinTransaction { result = processPnpTuple(e164 = e164, pni = pni, aci = aci, pniVerified = pniVerified, changeSelf = changeSelf) if (result.operations.isNotEmpty() || result.requiredInsert) { - val pniString = if (pni == null) "null" else if (aci == null && e164 == null) pni.toString() else "" - val e164String = if (e164 == null) "null" else if (aci == null) e164 else "" - Log.i(TAG, "[getAndPossiblyMerge] ($aci, $pniString, $e164String) BreadCrumbs: ${result.breadCrumbs}, Operations: ${result.operations}, RequiredInsert: ${result.requiredInsert}, FinalId: ${result.finalId}") + Log.i(TAG, "[getAndPossiblyMerge] ($aci, $pni, $e164) BreadCrumbs: ${result.breadCrumbs}, Operations: ${result.operations}, RequiredInsert: ${result.requiredInsert}, FinalId: ${result.finalId}") } - db.setTransactionSuccessful() - transactionSuccessful = true - } finally { - db.endTransaction() - - if (transactionSuccessful) { + db.runPostSuccessfulTransaction { if (result.affectedIds.isNotEmpty()) { result.affectedIds.forEach { ApplicationDependencies.getDatabaseObserver().notifyRecipientChanged(it) } RetrieveProfileJob.enqueue(result.affectedIds) @@ -646,6 +643,15 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da } } + fun getAll(): RecipientIterator { + val cursor = readableDatabase + .select() + .from(TABLE_NAME) + .run() + + return RecipientIterator(context, cursor) + } + /** * Only call once to create initial release channel recipient. */ @@ -675,26 +681,48 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da return RecipientReader(cursor) } + fun getRecords(ids: Collection): Map { + val queries = SqlUtil.buildCollectionQuery( + column = ID, + values = ids.map { it.serialize() } + ) + + val foundRecords = queries.flatMap { query -> + readableDatabase.query(TABLE_NAME, null, query.where, query.whereArgs, null, null, null).readToList { cursor -> + RecipientTableCursorUtil.getRecord(context, cursor) + } + } + + val foundIds = foundRecords.map { record -> record.id } + val remappedRecords = ids.filterNot { it in foundIds }.map(::findRemappedIdRecord) + + return (foundRecords + remappedRecords).associateBy { it.id } + } + fun getRecord(id: RecipientId): RecipientRecord { val query = "$ID = ?" val args = arrayOf(id.serialize()) readableDatabase.query(TABLE_NAME, RECIPIENT_PROJECTION, query, args, null, null, null).use { cursor -> return if (cursor != null && cursor.moveToNext()) { - getRecord(context, cursor) + RecipientTableCursorUtil.getRecord(context, cursor) } else { - val remapped = RemappedRecords.getInstance().getRecipient(id) - - if (remapped.isPresent) { - Log.w(TAG, "Missing recipient for $id, but found it in the remapped records as ${remapped.get()}") - getRecord(remapped.get()) - } else { - throw MissingRecipientException(id) - } + findRemappedIdRecord(id) } } } + private fun findRemappedIdRecord(id: RecipientId): RecipientRecord { + val remapped = RemappedRecords.getInstance().getRecipient(id) + + return if (remapped.isPresent) { + Log.w(TAG, "Missing recipient for $id, but found it in the remapped records as ${remapped.get()}") + getRecord(remapped.get()) + } else { + throw MissingRecipientException(id) + } + } + fun getRecordForSync(id: RecipientId): RecipientRecord? { val query = "$TABLE_NAME.$ID = ?" val args = arrayOf(id.serialize()) @@ -1086,7 +1114,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da readableDatabase.query(table, columns, query, args, "$TABLE_NAME.$ID", null, null).use { cursor -> while (cursor != null && cursor.moveToNext()) { - out.add(getRecord(context, cursor)) + out.add(RecipientTableCursorUtil.getRecord(context, cursor)) } } @@ -1457,18 +1485,8 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da } fun setCapabilities(id: RecipientId, capabilities: SignalServiceProfile.Capabilities) { - var value: Long = 0 - value = Bitmask.update(value, Capabilities.GROUPS_V1_MIGRATION, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isGv1Migration).serialize().toLong()) - value = Bitmask.update(value, Capabilities.SENDER_KEY, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isSenderKey).serialize().toLong()) - value = Bitmask.update(value, Capabilities.ANNOUNCEMENT_GROUPS, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isAnnouncementGroup).serialize().toLong()) - value = Bitmask.update(value, Capabilities.CHANGE_NUMBER, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isChangeNumber).serialize().toLong()) - value = Bitmask.update(value, Capabilities.STORIES, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isStories).serialize().toLong()) - value = Bitmask.update(value, Capabilities.GIFT_BADGES, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isGiftBadges).serialize().toLong()) - value = Bitmask.update(value, Capabilities.PNP, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isPnp).serialize().toLong()) - value = Bitmask.update(value, Capabilities.PAYMENT_ACTIVATION, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isPaymentActivation).serialize().toLong()) - val values = ContentValues(1).apply { - put(CAPABILITIES, value) + put(CAPABILITIES, maskCapabilitiesToLong(capabilities)) } if (update(id, values)) { @@ -1692,9 +1710,9 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da fun setProfileName(id: RecipientId, profileName: ProfileName) { val contentValues = ContentValues(1).apply { - put(PROFILE_GIVEN_NAME, profileName.givenName) - put(PROFILE_FAMILY_NAME, profileName.familyName) - put(PROFILE_JOINED_NAME, profileName.toString()) + put(PROFILE_GIVEN_NAME, profileName.givenName.nullIfBlank()) + put(PROFILE_FAMILY_NAME, profileName.familyName.nullIfBlank()) + put(PROFILE_JOINED_NAME, profileName.toString().nullIfBlank()) } if (update(id, contentValues)) { rotateStorageId(id) @@ -1787,6 +1805,15 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da } } + fun setPhoneNumberSharing(id: RecipientId, phoneNumberSharing: PhoneNumberSharingState) { + val contentValues = contentValuesOf( + PHONE_NUMBER_SHARING to phoneNumberSharing.id + ) + if (update(id, contentValues)) { + ApplicationDependencies.getDatabaseObserver().notifyRecipientChanged(id) + } + } + fun resetAllWallpaper() { val database = writableDatabase val selection = SqlUtil.buildArgs(ID, WALLPAPER_URI) @@ -2051,41 +2078,6 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da } } - fun clearFieldsForDeletion(id: RecipientId) { - val values = ContentValues().apply { - put(MUTE_UNTIL, 0) - putNull(AVATAR_COLOR) - putNull(NOTIFICATION_CHANNEL) - putNull(MESSAGE_RINGTONE) - put(MESSAGE_VIBRATE, VibrateState.DEFAULT.id) - putNull(CALL_RINGTONE) - put(CALL_VIBRATE, VibrateState.DEFAULT.id) - put(LAST_PROFILE_FETCH, 0) - put(PROFILE_SHARING, 0) - put(SYSTEM_INFO_PENDING, 0) - putNull(SYSTEM_GIVEN_NAME) - putNull(SYSTEM_FAMILY_NAME) - putNull(SYSTEM_JOINED_NAME) - putNull(SYSTEM_PHOTO_URI) - putNull(SYSTEM_PHONE_LABEL) - putNull(SYSTEM_CONTACT_URI) - putNull(LAST_SESSION_RESET) - putNull(WALLPAPER) - putNull(WALLPAPER_URI) - putNull(ABOUT) - putNull(ABOUT_EMOJI) - putNull(EXTRAS) - putNull(CHAT_COLORS) - put(CUSTOM_CHAT_COLORS_ID, 0) - putNull(BADGES) - } - if (update(id, values)) { - rotateStorageId(id) - Recipient.live(id).refresh() - StorageSyncHelper.scheduleSyncForDataChange() - } - } - fun getAllE164s(): Set { val results: MutableSet = HashSet() readableDatabase.query(TABLE_NAME, arrayOf(E164), null, null, null, null, null).use { cursor -> @@ -3139,7 +3131,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da .where("$ID LIKE ? OR $ACI_COLUMN LIKE ? OR $PNI_COLUMN LIKE ?", "%$query%", "%$query%", "%$query%") .run() .readToList { cursor -> - getRecord(context, cursor) + RecipientTableCursorUtil.getRecord(context, cursor) } } @@ -3301,7 +3293,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da ( $SORT_NAME GLOB ? OR $USERNAME GLOB ? OR - $E164 GLOB ? OR + ${ContactSearchSelection.E164_SEARCH} OR $EMAIL GLOB ? ) """ @@ -3322,7 +3314,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da ( $SORT_NAME GLOB ? OR $USERNAME GLOB ? OR - $E164 GLOB ? OR + ${ContactSearchSelection.E164_SEARCH} OR $EMAIL GLOB ? )) """ @@ -3343,7 +3335,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da AND ( $SORT_NAME GLOB ? OR $USERNAME GLOB ? OR - $E164 GLOB ? OR + ${ContactSearchSelection.E164_SEARCH} OR $EMAIL GLOB ? ) """ @@ -3423,19 +3415,12 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da } fun markProfilesFetched(ids: Collection, time: Long) { - val db = writableDatabase - db.beginTransaction() - try { - val values = ContentValues(1).apply { - put(LAST_PROFILE_FETCH, time) - } + writableDatabase.withinTransaction { db -> + val values = contentValuesOf(LAST_PROFILE_FETCH to time) - for (id in ids) { - db.update(TABLE_NAME, values, ID_WHERE, arrayOf(id.serialize())) + SqlUtil.buildCollectionQuery(ID, ids).forEach { query -> + db.update(TABLE_NAME, values, query.where, query.whereArgs) } - db.setTransactionSuccessful() - } finally { - db.endTransaction() } } @@ -3612,7 +3597,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da .run() .use { cursor -> return if (cursor.moveToFirst()) { - readCapabilities(cursor) + RecipientTableCursorUtil.readCapabilities(cursor) } else { null } @@ -4072,196 +4057,6 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da RecipientId.clearCache() } - fun getRecord(context: Context, cursor: Cursor): RecipientRecord { - return getRecord(context, cursor, ID) - } - - fun getRecord(context: Context, cursor: Cursor, idColumnName: String): RecipientRecord { - val profileKeyString = cursor.requireString(PROFILE_KEY) - val expiringProfileKeyCredentialString = cursor.requireString(EXPIRING_PROFILE_KEY_CREDENTIAL) - var profileKey: ByteArray? = null - var expiringProfileKeyCredential: ExpiringProfileKeyCredential? = null - - if (profileKeyString != null) { - try { - profileKey = Base64.decode(profileKeyString) - } catch (e: IOException) { - Log.w(TAG, e) - } - - if (expiringProfileKeyCredentialString != null) { - try { - val columnDataBytes = Base64.decode(expiringProfileKeyCredentialString) - val columnData = ExpiringProfileKeyCredentialColumnData.ADAPTER.decode(columnDataBytes) - if (Arrays.equals(columnData.profileKey.toByteArray(), profileKey)) { - expiringProfileKeyCredential = ExpiringProfileKeyCredential(columnData.expiringProfileKeyCredential.toByteArray()) - } else { - Log.i(TAG, "Out of date profile key credential data ignored on read") - } - } catch (e: InvalidInputException) { - Log.w(TAG, "Profile key credential column data could not be read", e) - } catch (e: IOException) { - Log.w(TAG, "Profile key credential column data could not be read", e) - } - } - } - - val serializedWallpaper = cursor.requireBlob(WALLPAPER) - val chatWallpaper: ChatWallpaper? = if (serializedWallpaper != null) { - try { - ChatWallpaperFactory.create(Wallpaper.ADAPTER.decode(serializedWallpaper)) - } catch (e: IOException) { - Log.w(TAG, "Failed to parse wallpaper.", e) - null - } - } else { - null - } - - val customChatColorsId = cursor.requireLong(CUSTOM_CHAT_COLORS_ID) - val serializedChatColors = cursor.requireBlob(CHAT_COLORS) - val chatColors: ChatColors? = if (serializedChatColors != null) { - try { - forChatColor(forLongValue(customChatColorsId), ChatColor.ADAPTER.decode(serializedChatColors)) - } catch (e: IOException) { - Log.w(TAG, "Failed to parse chat colors.", e) - null - } - } else { - null - } - - val recipientId = RecipientId.from(cursor.requireLong(idColumnName)) - val distributionListId: DistributionListId? = DistributionListId.fromNullable(cursor.requireLong(DISTRIBUTION_LIST_ID)) - val avatarColor: AvatarColor = if (distributionListId != null) AvatarColor.UNKNOWN else AvatarColor.deserialize(cursor.requireString(AVATAR_COLOR)) - - return RecipientRecord( - id = recipientId, - aci = ACI.parseOrNull(cursor.requireString(ACI_COLUMN)), - pni = PNI.parsePrefixedOrNull(cursor.requireString(PNI_COLUMN)), - username = cursor.requireString(USERNAME), - e164 = cursor.requireString(E164), - email = cursor.requireString(EMAIL), - groupId = GroupId.parseNullableOrThrow(cursor.requireString(GROUP_ID)), - distributionListId = distributionListId, - recipientType = RecipientType.fromId(cursor.requireInt(TYPE)), - isBlocked = cursor.requireBoolean(BLOCKED), - muteUntil = cursor.requireLong(MUTE_UNTIL), - messageVibrateState = VibrateState.fromId(cursor.requireInt(MESSAGE_VIBRATE)), - callVibrateState = VibrateState.fromId(cursor.requireInt(CALL_VIBRATE)), - messageRingtone = Util.uri(cursor.requireString(MESSAGE_RINGTONE)), - callRingtone = Util.uri(cursor.requireString(CALL_RINGTONE)), - expireMessages = cursor.requireInt(MESSAGE_EXPIRATION_TIME), - registered = RegisteredState.fromId(cursor.requireInt(REGISTERED)), - profileKey = profileKey, - expiringProfileKeyCredential = expiringProfileKeyCredential, - systemProfileName = ProfileName.fromParts(cursor.requireString(SYSTEM_GIVEN_NAME), cursor.requireString(SYSTEM_FAMILY_NAME)), - systemDisplayName = cursor.requireString(SYSTEM_JOINED_NAME), - systemContactPhotoUri = cursor.requireString(SYSTEM_PHOTO_URI), - systemPhoneLabel = cursor.requireString(SYSTEM_PHONE_LABEL), - systemContactUri = cursor.requireString(SYSTEM_CONTACT_URI), - signalProfileName = ProfileName.fromParts(cursor.requireString(PROFILE_GIVEN_NAME), cursor.requireString(PROFILE_FAMILY_NAME)), - signalProfileAvatar = cursor.requireString(PROFILE_AVATAR), - profileAvatarFileDetails = AvatarHelper.getAvatarFileDetails(context, recipientId), - profileSharing = cursor.requireBoolean(PROFILE_SHARING), - lastProfileFetch = cursor.requireLong(LAST_PROFILE_FETCH), - notificationChannel = cursor.requireString(NOTIFICATION_CHANNEL), - unidentifiedAccessMode = UnidentifiedAccessMode.fromMode(cursor.requireInt(SEALED_SENDER_MODE)), - capabilities = readCapabilities(cursor), - storageId = Base64.decodeNullableOrThrow(cursor.requireString(STORAGE_SERVICE_ID)), - mentionSetting = MentionSetting.fromId(cursor.requireInt(MENTION_SETTING)), - wallpaper = chatWallpaper, - chatColors = chatColors, - avatarColor = avatarColor, - about = cursor.requireString(ABOUT), - aboutEmoji = cursor.requireString(ABOUT_EMOJI), - syncExtras = getSyncExtras(cursor), - extras = getExtras(cursor), - hasGroupsInCommon = cursor.requireBoolean(GROUPS_IN_COMMON), - badges = parseBadgeList(cursor.requireBlob(BADGES)), - needsPniSignature = cursor.requireBoolean(NEEDS_PNI_SIGNATURE), - hiddenState = Recipient.HiddenState.deserialize(cursor.requireInt(HIDDEN)), - callLinkRoomId = cursor.requireString(CALL_LINK_ROOM_ID)?.let { CallLinkRoomId.DatabaseSerializer.deserialize(it) } - ) - } - - private fun readCapabilities(cursor: Cursor): RecipientRecord.Capabilities { - val capabilities = cursor.requireLong(CAPABILITIES) - return RecipientRecord.Capabilities( - rawBits = capabilities, - groupsV1MigrationCapability = Recipient.Capability.deserialize(Bitmask.read(capabilities, Capabilities.GROUPS_V1_MIGRATION, Capabilities.BIT_LENGTH).toInt()), - senderKeyCapability = Recipient.Capability.deserialize(Bitmask.read(capabilities, Capabilities.SENDER_KEY, Capabilities.BIT_LENGTH).toInt()), - announcementGroupCapability = Recipient.Capability.deserialize(Bitmask.read(capabilities, Capabilities.ANNOUNCEMENT_GROUPS, Capabilities.BIT_LENGTH).toInt()), - changeNumberCapability = Recipient.Capability.deserialize(Bitmask.read(capabilities, Capabilities.CHANGE_NUMBER, Capabilities.BIT_LENGTH).toInt()), - storiesCapability = Recipient.Capability.deserialize(Bitmask.read(capabilities, Capabilities.STORIES, Capabilities.BIT_LENGTH).toInt()), - giftBadgesCapability = Recipient.Capability.deserialize(Bitmask.read(capabilities, Capabilities.GIFT_BADGES, Capabilities.BIT_LENGTH).toInt()), - pnpCapability = Recipient.Capability.deserialize(Bitmask.read(capabilities, Capabilities.PNP, Capabilities.BIT_LENGTH).toInt()), - paymentActivation = Recipient.Capability.deserialize(Bitmask.read(capabilities, Capabilities.PAYMENT_ACTIVATION, Capabilities.BIT_LENGTH).toInt()) - ) - } - - private fun parseBadgeList(serializedBadgeList: ByteArray?): List { - var badgeList: BadgeList? = null - if (serializedBadgeList != null) { - try { - badgeList = BadgeList.ADAPTER.decode(serializedBadgeList) - } catch (e: IOException) { - Log.w(TAG, e) - } - } - - val badges: List - if (badgeList != null) { - val protoBadges = badgeList.badges - badges = ArrayList(protoBadges.size) - for (protoBadge in protoBadges) { - badges.add(Badges.fromDatabaseBadge(protoBadge)) - } - } else { - badges = emptyList() - } - - return badges - } - - private fun getSyncExtras(cursor: Cursor): RecipientRecord.SyncExtras { - val storageProtoRaw = cursor.optionalString(STORAGE_SERVICE_PROTO).orElse(null) - val storageProto = if (storageProtoRaw != null) Base64.decodeOrThrow(storageProtoRaw) else null - val archived = cursor.optionalBoolean(ThreadTable.ARCHIVED).orElse(false) - val forcedUnread = cursor.optionalInt(ThreadTable.READ).map { status: Int -> status == ThreadTable.ReadStatus.FORCED_UNREAD.serialize() }.orElse(false) - val groupMasterKey = cursor.optionalBlob(GroupTable.V2_MASTER_KEY).map { GroupUtil.requireMasterKey(it) }.orElse(null) - val identityKey = cursor.optionalString(IDENTITY_KEY).map { Base64.decodeOrThrow(it) }.orElse(null) - val identityStatus = cursor.optionalInt(IDENTITY_STATUS).map { VerifiedStatus.forState(it) }.orElse(VerifiedStatus.DEFAULT) - val unregisteredTimestamp = cursor.optionalLong(UNREGISTERED_TIMESTAMP).orElse(0) - val systemNickname = cursor.optionalString(SYSTEM_NICKNAME).orElse(null) - - return RecipientRecord.SyncExtras( - storageProto = storageProto, - groupMasterKey = groupMasterKey, - identityKey = identityKey, - identityStatus = identityStatus, - isArchived = archived, - isForcedUnread = forcedUnread, - unregisteredTimestamp = unregisteredTimestamp, - systemNickname = systemNickname - ) - } - - private fun getExtras(cursor: Cursor): Recipient.Extras? { - return Recipient.Extras.from(getRecipientExtras(cursor)) - } - - private fun getRecipientExtras(cursor: Cursor): RecipientExtras? { - return cursor.optionalBlob(EXTRAS).map { b: ByteArray -> - try { - RecipientExtras.ADAPTER.decode(b) - } catch (e: IOException) { - Log.w(TAG, e) - throw AssertionError(e) - } - }.orElse(null) - } - private fun updateProfileValuesForMerge(values: ContentValues, record: RecipientRecord) { values.apply { put(PROFILE_KEY, if (record.profileKey != null) Base64.encodeWithPadding(record.profileKey) else null) @@ -4392,6 +4187,28 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da } } + class RecipientIterator( + private val context: Context, + private val cursor: Cursor + ) : Iterator, Closeable { + + override fun hasNext(): Boolean { + return cursor.count != 0 && !cursor.isLast + } + + override fun next(): RecipientRecord { + if (!cursor.moveToNext()) { + throw NoSuchElementException() + } + + return RecipientTableCursorUtil.getRecord(context, cursor) + } + + override fun close() { + cursor.close() + } + } + class MissingRecipientException(id: RecipientId?) : IllegalStateException("Failed to find recipient with ID: $id") private class GetOrInsertResult(val recipientId: RecipientId, val neededInsert: Boolean) @@ -4531,16 +4348,17 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da WHERE ${GroupTable.MembershipTable.TABLE_NAME}.${GroupTable.MembershipTable.RECIPIENT_ID} = $TABLE_NAME.$ID AND ${GroupTable.TABLE_NAME}.${GroupTable.ACTIVE} = 1 AND ${GroupTable.TABLE_NAME}.${GroupTable.MMS} = 0 ) """ + val E164_SEARCH = "(($PHONE_NUMBER_SHARING != ${PhoneNumberSharingState.DISABLED.id} OR $SYSTEM_CONTACT_URI NOT NULL) AND $E164 GLOB ?)" const val FILTER_GROUPS = " AND $GROUP_ID IS NULL" const val FILTER_ID = " AND $ID != ?" const val FILTER_BLOCKED = " AND $BLOCKED = ?" const val FILTER_HIDDEN = " AND $HIDDEN = ?" const val NON_SIGNAL_CONTACT = "$REGISTERED != ? AND $SYSTEM_CONTACT_URI NOT NULL AND ($E164 NOT NULL OR $EMAIL NOT NULL)" - const val QUERY_NON_SIGNAL_CONTACT = "$NON_SIGNAL_CONTACT AND ($E164 GLOB ? OR $EMAIL GLOB ? OR $SYSTEM_JOINED_NAME GLOB ?)" + val QUERY_NON_SIGNAL_CONTACT = "$NON_SIGNAL_CONTACT AND ($E164_SEARCH OR $EMAIL GLOB ? OR $SYSTEM_JOINED_NAME GLOB ?)" const val SIGNAL_CONTACT = "$REGISTERED = ? AND (NULLIF($SYSTEM_JOINED_NAME, '') NOT NULL OR $PROFILE_SHARING = ?) AND ($SORT_NAME NOT NULL OR $USERNAME NOT NULL)" - const val QUERY_SIGNAL_CONTACT = "$SIGNAL_CONTACT AND ($E164 GLOB ? OR $SORT_NAME GLOB ? OR $USERNAME GLOB ?)" + val QUERY_SIGNAL_CONTACT = "$SIGNAL_CONTACT AND ($E164_SEARCH OR $SORT_NAME GLOB ? OR $USERNAME GLOB ?)" val GROUP_MEMBER_CONTACT = "$REGISTERED = ? AND $HAS_GROUP_IN_COMMON AND NOT (NULLIF($SYSTEM_JOINED_NAME, '') NOT NULL OR $PROFILE_SHARING = ?) AND ($SORT_NAME NOT NULL OR $USERNAME NOT NULL)" - val QUERY_GROUP_MEMBER_CONTACT = "$GROUP_MEMBER_CONTACT AND ($E164 GLOB ? OR $SORT_NAME GLOB ? OR $USERNAME GLOB ?)" + val QUERY_GROUP_MEMBER_CONTACT = "$GROUP_MEMBER_CONTACT AND ($E164_SEARCH OR $SORT_NAME GLOB ? OR $USERNAME GLOB ?)" } } @@ -4630,6 +4448,19 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da } } + enum class PhoneNumberSharingState(val id: Int) { + UNKNOWN(0), ENABLED(1), DISABLED(2); + + val enabled + get() = this == ENABLED || this == UNKNOWN + + companion object { + fun fromId(id: Int): PhoneNumberSharingState { + return values()[id] + } + } + } + data class CdsV2Result( val pni: PNI, val aci: ACI? diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTableCursorUtil.kt b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTableCursorUtil.kt new file mode 100644 index 0000000000..502d8d490b --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTableCursorUtil.kt @@ -0,0 +1,248 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.database + +import android.content.Context +import android.database.Cursor +import com.google.protobuf.InvalidProtocolBufferException +import org.signal.core.util.Base64 +import org.signal.core.util.Bitmask +import org.signal.core.util.logging.Log +import org.signal.core.util.optionalBlob +import org.signal.core.util.optionalBoolean +import org.signal.core.util.optionalInt +import org.signal.core.util.optionalLong +import org.signal.core.util.optionalString +import org.signal.core.util.requireBlob +import org.signal.core.util.requireBoolean +import org.signal.core.util.requireInt +import org.signal.core.util.requireLong +import org.signal.core.util.requireString +import org.signal.libsignal.zkgroup.InvalidInputException +import org.signal.libsignal.zkgroup.profiles.ExpiringProfileKeyCredential +import org.thoughtcrime.securesms.badges.Badges +import org.thoughtcrime.securesms.badges.models.Badge +import org.thoughtcrime.securesms.conversation.colors.AvatarColor +import org.thoughtcrime.securesms.conversation.colors.ChatColors +import org.thoughtcrime.securesms.database.IdentityTable.VerifiedStatus +import org.thoughtcrime.securesms.database.RecipientTable.Capabilities +import org.thoughtcrime.securesms.database.RecipientTable.RegisteredState +import org.thoughtcrime.securesms.database.model.DistributionListId +import org.thoughtcrime.securesms.database.model.RecipientRecord +import org.thoughtcrime.securesms.database.model.databaseprotos.BadgeList +import org.thoughtcrime.securesms.database.model.databaseprotos.ChatColor +import org.thoughtcrime.securesms.database.model.databaseprotos.ExpiringProfileKeyCredentialColumnData +import org.thoughtcrime.securesms.database.model.databaseprotos.RecipientExtras +import org.thoughtcrime.securesms.database.model.databaseprotos.Wallpaper +import org.thoughtcrime.securesms.groups.GroupId +import org.thoughtcrime.securesms.profiles.AvatarHelper +import org.thoughtcrime.securesms.profiles.ProfileName +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.GroupUtil +import org.thoughtcrime.securesms.util.Util +import org.thoughtcrime.securesms.wallpaper.ChatWallpaper +import org.thoughtcrime.securesms.wallpaper.ChatWallpaperFactory +import org.whispersystems.signalservice.api.push.ServiceId +import java.io.IOException +import java.util.Arrays + +object RecipientTableCursorUtil { + + private val TAG = Log.tag(RecipientTableCursorUtil::class.java) + + fun getRecord(context: Context, cursor: Cursor): RecipientRecord { + return getRecord(context, cursor, RecipientTable.ID) + } + + fun getRecord(context: Context, cursor: Cursor, idColumnName: String): RecipientRecord { + val profileKeyString = cursor.requireString(RecipientTable.PROFILE_KEY) + val expiringProfileKeyCredentialString = cursor.requireString(RecipientTable.EXPIRING_PROFILE_KEY_CREDENTIAL) + var profileKey: ByteArray? = null + var expiringProfileKeyCredential: ExpiringProfileKeyCredential? = null + + if (profileKeyString != null) { + try { + profileKey = Base64.decode(profileKeyString) + } catch (e: IOException) { + Log.w(TAG, e) + } + + if (expiringProfileKeyCredentialString != null) { + try { + val columnDataBytes = Base64.decode(expiringProfileKeyCredentialString) + val columnData = ExpiringProfileKeyCredentialColumnData.ADAPTER.decode(columnDataBytes) + if (Arrays.equals(columnData.profileKey.toByteArray(), profileKey)) { + expiringProfileKeyCredential = ExpiringProfileKeyCredential(columnData.expiringProfileKeyCredential.toByteArray()) + } else { + Log.i(TAG, "Out of date profile key credential data ignored on read") + } + } catch (e: InvalidInputException) { + Log.w(TAG, "Profile key credential column data could not be read", e) + } catch (e: IOException) { + Log.w(TAG, "Profile key credential column data could not be read", e) + } + } + } + + val serializedWallpaper = cursor.requireBlob(RecipientTable.WALLPAPER) + val chatWallpaper: ChatWallpaper? = if (serializedWallpaper != null) { + try { + ChatWallpaperFactory.create(Wallpaper.ADAPTER.decode(serializedWallpaper)) + } catch (e: InvalidProtocolBufferException) { + Log.w(TAG, "Failed to parse wallpaper.", e) + null + } + } else { + null + } + + val customChatColorsId = cursor.requireLong(RecipientTable.CUSTOM_CHAT_COLORS_ID) + val serializedChatColors = cursor.requireBlob(RecipientTable.CHAT_COLORS) + val chatColors: ChatColors? = if (serializedChatColors != null) { + try { + ChatColors.forChatColor(ChatColors.Id.forLongValue(customChatColorsId), ChatColor.ADAPTER.decode(serializedChatColors)) + } catch (e: InvalidProtocolBufferException) { + Log.w(TAG, "Failed to parse chat colors.", e) + null + } + } else { + null + } + + val recipientId = RecipientId.from(cursor.requireLong(idColumnName)) + val distributionListId: DistributionListId? = DistributionListId.fromNullable(cursor.requireLong(RecipientTable.DISTRIBUTION_LIST_ID)) + val avatarColor: AvatarColor = if (distributionListId != null) AvatarColor.UNKNOWN else AvatarColor.deserialize(cursor.requireString(RecipientTable.AVATAR_COLOR)) + + return RecipientRecord( + id = recipientId, + aci = ServiceId.ACI.parseOrNull(cursor.requireString(RecipientTable.ACI_COLUMN)), + pni = ServiceId.PNI.parsePrefixedOrNull(cursor.requireString(RecipientTable.PNI_COLUMN)), + username = cursor.requireString(RecipientTable.USERNAME), + e164 = cursor.requireString(RecipientTable.E164), + email = cursor.requireString(RecipientTable.EMAIL), + groupId = GroupId.parseNullableOrThrow(cursor.requireString(RecipientTable.GROUP_ID)), + distributionListId = distributionListId, + recipientType = RecipientTable.RecipientType.fromId(cursor.requireInt(RecipientTable.TYPE)), + isBlocked = cursor.requireBoolean(RecipientTable.BLOCKED), + muteUntil = cursor.requireLong(RecipientTable.MUTE_UNTIL), + messageVibrateState = RecipientTable.VibrateState.fromId(cursor.requireInt(RecipientTable.MESSAGE_VIBRATE)), + callVibrateState = RecipientTable.VibrateState.fromId(cursor.requireInt(RecipientTable.CALL_VIBRATE)), + messageRingtone = Util.uri(cursor.requireString(RecipientTable.MESSAGE_RINGTONE)), + callRingtone = Util.uri(cursor.requireString(RecipientTable.CALL_RINGTONE)), + expireMessages = cursor.requireInt(RecipientTable.MESSAGE_EXPIRATION_TIME), + registered = RegisteredState.fromId(cursor.requireInt(RecipientTable.REGISTERED)), + profileKey = profileKey, + expiringProfileKeyCredential = expiringProfileKeyCredential, + systemProfileName = ProfileName.fromParts(cursor.requireString(RecipientTable.SYSTEM_GIVEN_NAME), cursor.requireString(RecipientTable.SYSTEM_FAMILY_NAME)), + systemDisplayName = cursor.requireString(RecipientTable.SYSTEM_JOINED_NAME), + systemContactPhotoUri = cursor.requireString(RecipientTable.SYSTEM_PHOTO_URI), + systemPhoneLabel = cursor.requireString(RecipientTable.SYSTEM_PHONE_LABEL), + systemContactUri = cursor.requireString(RecipientTable.SYSTEM_CONTACT_URI), + signalProfileName = ProfileName.fromParts(cursor.requireString(RecipientTable.PROFILE_GIVEN_NAME), cursor.requireString(RecipientTable.PROFILE_FAMILY_NAME)), + signalProfileAvatar = cursor.requireString(RecipientTable.PROFILE_AVATAR), + profileAvatarFileDetails = AvatarHelper.getAvatarFileDetails(context, recipientId), + profileSharing = cursor.requireBoolean(RecipientTable.PROFILE_SHARING), + lastProfileFetch = cursor.requireLong(RecipientTable.LAST_PROFILE_FETCH), + notificationChannel = cursor.requireString(RecipientTable.NOTIFICATION_CHANNEL), + unidentifiedAccessMode = RecipientTable.UnidentifiedAccessMode.fromMode(cursor.requireInt(RecipientTable.SEALED_SENDER_MODE)), + capabilities = readCapabilities(cursor), + storageId = Base64.decodeNullableOrThrow(cursor.requireString(RecipientTable.STORAGE_SERVICE_ID)), + mentionSetting = RecipientTable.MentionSetting.fromId(cursor.requireInt(RecipientTable.MENTION_SETTING)), + wallpaper = chatWallpaper, + chatColors = chatColors, + avatarColor = avatarColor, + about = cursor.requireString(RecipientTable.ABOUT), + aboutEmoji = cursor.requireString(RecipientTable.ABOUT_EMOJI), + syncExtras = getSyncExtras(cursor), + extras = getExtras(cursor), + hasGroupsInCommon = cursor.requireBoolean(RecipientTable.GROUPS_IN_COMMON), + badges = parseBadgeList(cursor.requireBlob(RecipientTable.BADGES)), + needsPniSignature = cursor.requireBoolean(RecipientTable.NEEDS_PNI_SIGNATURE), + hiddenState = Recipient.HiddenState.deserialize(cursor.requireInt(RecipientTable.HIDDEN)), + callLinkRoomId = cursor.requireString(RecipientTable.CALL_LINK_ROOM_ID)?.let { CallLinkRoomId.DatabaseSerializer.deserialize(it) }, + phoneNumberSharing = cursor.requireInt(RecipientTable.PHONE_NUMBER_SHARING).let { RecipientTable.PhoneNumberSharingState.fromId(it) } + ) + } + + fun readCapabilities(cursor: Cursor): RecipientRecord.Capabilities { + val capabilities = cursor.requireLong(RecipientTable.CAPABILITIES) + return RecipientRecord.Capabilities( + rawBits = capabilities, + groupsV1MigrationCapability = Recipient.Capability.deserialize(Bitmask.read(capabilities, Capabilities.GROUPS_V1_MIGRATION, Capabilities.BIT_LENGTH).toInt()), + senderKeyCapability = Recipient.Capability.deserialize(Bitmask.read(capabilities, Capabilities.SENDER_KEY, Capabilities.BIT_LENGTH).toInt()), + announcementGroupCapability = Recipient.Capability.deserialize(Bitmask.read(capabilities, Capabilities.ANNOUNCEMENT_GROUPS, Capabilities.BIT_LENGTH).toInt()), + changeNumberCapability = Recipient.Capability.deserialize(Bitmask.read(capabilities, Capabilities.CHANGE_NUMBER, Capabilities.BIT_LENGTH).toInt()), + storiesCapability = Recipient.Capability.deserialize(Bitmask.read(capabilities, Capabilities.STORIES, Capabilities.BIT_LENGTH).toInt()), + giftBadgesCapability = Recipient.Capability.deserialize(Bitmask.read(capabilities, Capabilities.GIFT_BADGES, Capabilities.BIT_LENGTH).toInt()), + pnpCapability = Recipient.Capability.deserialize(Bitmask.read(capabilities, Capabilities.PNP, Capabilities.BIT_LENGTH).toInt()), + paymentActivation = Recipient.Capability.deserialize(Bitmask.read(capabilities, Capabilities.PAYMENT_ACTIVATION, Capabilities.BIT_LENGTH).toInt()) + ) + } + + fun parseBadgeList(serializedBadgeList: ByteArray?): List { + var badgeList: BadgeList? = null + if (serializedBadgeList != null) { + try { + badgeList = BadgeList.ADAPTER.decode(serializedBadgeList) + } catch (e: InvalidProtocolBufferException) { + Log.w(TAG, e) + } + } + + val badges: List + if (badgeList != null) { + val protoBadges = badgeList.badges + badges = ArrayList(protoBadges.size) + for (protoBadge in protoBadges) { + badges.add(Badges.fromDatabaseBadge(protoBadge)) + } + } else { + badges = emptyList() + } + + return badges + } + + fun getSyncExtras(cursor: Cursor): RecipientRecord.SyncExtras { + val storageProtoRaw = cursor.optionalString(RecipientTable.STORAGE_SERVICE_PROTO).orElse(null) + val storageProto = if (storageProtoRaw != null) Base64.decodeOrThrow(storageProtoRaw) else null + val archived = cursor.optionalBoolean(ThreadTable.ARCHIVED).orElse(false) + val forcedUnread = cursor.optionalInt(ThreadTable.READ).map { status: Int -> status == ThreadTable.ReadStatus.FORCED_UNREAD.serialize() }.orElse(false) + val groupMasterKey = cursor.optionalBlob(GroupTable.V2_MASTER_KEY).map { GroupUtil.requireMasterKey(it) }.orElse(null) + val identityKey = cursor.optionalString(RecipientTable.IDENTITY_KEY).map { Base64.decodeOrThrow(it) }.orElse(null) + val identityStatus = cursor.optionalInt(RecipientTable.IDENTITY_STATUS).map { VerifiedStatus.forState(it) }.orElse(VerifiedStatus.DEFAULT) + val unregisteredTimestamp = cursor.optionalLong(RecipientTable.UNREGISTERED_TIMESTAMP).orElse(0) + val systemNickname = cursor.optionalString(RecipientTable.SYSTEM_NICKNAME).orElse(null) + + return RecipientRecord.SyncExtras( + storageProto = storageProto, + groupMasterKey = groupMasterKey, + identityKey = identityKey, + identityStatus = identityStatus, + isArchived = archived, + isForcedUnread = forcedUnread, + unregisteredTimestamp = unregisteredTimestamp, + systemNickname = systemNickname + ) + } + + fun getExtras(cursor: Cursor): Recipient.Extras? { + return Recipient.Extras.from(getRecipientExtras(cursor)) + } + + fun getRecipientExtras(cursor: Cursor): RecipientExtras? { + return cursor.optionalBlob(RecipientTable.EXTRAS).map { b: ByteArray -> + try { + RecipientExtras.ADAPTER.decode(b) + } catch (e: InvalidProtocolBufferException) { + Log.w(TAG, e) + throw AssertionError(e) + } + }.orElse(null) + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/SentStorySyncManifest.kt b/app/src/main/java/org/thoughtcrime/securesms/database/SentStorySyncManifest.kt index dfa459d506..d4b214586a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/SentStorySyncManifest.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/SentStorySyncManifest.kt @@ -85,7 +85,7 @@ data class SentStorySyncManifest( } fun fromRecipientsSet(recipients: List): SentStorySyncManifest { - val entries = recipients.toSet().map { recipient -> + val entries = recipients.toSet().filter { it.destinationServiceId != null }.map { recipient -> Entry( recipientId = RecipientId.from(ServiceId.parseOrThrow(recipient.destinationServiceId!!)), allowedToReply = recipient.isAllowedToReply!!, diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/SqlCipherErrorHandler.kt b/app/src/main/java/org/thoughtcrime/securesms/database/SqlCipherErrorHandler.kt index deef3d0e4f..9802ae5d8c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/SqlCipherErrorHandler.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/SqlCipherErrorHandler.kt @@ -11,6 +11,7 @@ import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.crypto.DatabaseSecretProvider import org.thoughtcrime.securesms.dependencies.ApplicationDependencies import java.util.concurrent.CountDownLatch +import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicReference /** @@ -18,39 +19,53 @@ import java.util.concurrent.atomic.AtomicReference */ @Suppress("ClassName") class SqlCipherErrorHandler(private val databaseName: String) : DatabaseErrorHandler { + companion object { + private val TAG = Log.tag(SqlCipherErrorHandler::class.java) + + private val errorHandlingInProgress = AtomicBoolean(false) + } override fun onCorruption(db: SQLiteDatabase, message: String) { - val result: DiagnosticResults = runDiagnostics(ApplicationDependencies.getApplication(), db) - var lines: List = result.logs.split("\n") - lines = listOf("Database '$databaseName' corrupted!", "[sqlite] $message", "Diagnostics results:") + lines - - Log.e(TAG, "Database '$databaseName' corrupted!") - Log.e(TAG, "[sqlite] $message") - Log.e(TAG, "Diagnostic results:\n ${result.logs}") - - if (result is DiagnosticResults.Success) { - if (result.pragma1Passes && result.pragma2Passes) { - var endCount = 0 - while (db.inTransaction() && endCount < 10) { - db.endTransaction() - endCount++ - } + if (errorHandlingInProgress.getAndSet(true)) { + Log.w(TAG, "Error handling already in progress, skipping.") + return + } - attemptToClearFullTextSearchIndex(db) - throw DatabaseCorruptedError_BothChecksPass(lines) - } else if (!result.pragma1Passes && result.pragma2Passes) { - attemptToClearFullTextSearchIndex(db) - throw DatabaseCorruptedError_NormalCheckFailsCipherCheckPasses(lines) - } else if (result.pragma1Passes && !result.pragma2Passes) { - attemptToClearFullTextSearchIndex(db) - throw DatabaseCorruptedError_NormalCheckPassesCipherCheckFails(lines) + try { + val result: DiagnosticResults = runDiagnostics(ApplicationDependencies.getApplication(), db) + var lines: List = result.logs.split("\n") + lines = listOf("Database '$databaseName' corrupted!", "[sqlite] $message", "Diagnostics results:") + lines + + Log.e(TAG, "Database '$databaseName' corrupted!") + Log.e(TAG, "[sqlite] $message") + Log.e(TAG, "Diagnostic results:\n ${result.logs}") + + if (result is DiagnosticResults.Success) { + if (result.pragma1Passes && result.pragma2Passes) { + var endCount = 0 + while (db.inTransaction() && endCount < 10) { + db.endTransaction() + endCount++ + } + + attemptToClearFullTextSearchIndex(db) + throw DatabaseCorruptedError_BothChecksPass(lines) + } else if (!result.pragma1Passes && result.pragma2Passes) { + attemptToClearFullTextSearchIndex(db) + throw DatabaseCorruptedError_NormalCheckFailsCipherCheckPasses(lines) + } else if (result.pragma1Passes && !result.pragma2Passes) { + attemptToClearFullTextSearchIndex(db) + throw DatabaseCorruptedError_NormalCheckPassesCipherCheckFails(lines) + } else { + attemptToClearFullTextSearchIndex(db) + throw DatabaseCorruptedError_BothChecksFail(lines) + } } else { attemptToClearFullTextSearchIndex(db) - throw DatabaseCorruptedError_BothChecksFail(lines) + throw DatabaseCorruptedError_FailedToRunChecks(lines) } - } else { - attemptToClearFullTextSearchIndex(db) - throw DatabaseCorruptedError_FailedToRunChecks(lines) + } finally { + errorHandlingInProgress.set(false) } } @@ -184,8 +199,4 @@ class SqlCipherErrorHandler(private val databaseName: String) : DatabaseErrorHan private class DatabaseCorruptedError_NormalCheckFailsCipherCheckPasses constructor(lines: List) : CustomTraceError(lines) private class DatabaseCorruptedError_NormalCheckPassesCipherCheckFails constructor(lines: List) : CustomTraceError(lines) private class DatabaseCorruptedError_FailedToRunChecks constructor(lines: List) : CustomTraceError(lines) - - companion object { - private val TAG = Log.tag(SqlCipherErrorHandler::class.java) - } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/StorySendTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/StorySendTable.kt index 9bb2054a3e..b098a68d5e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/StorySendTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/StorySendTable.kt @@ -10,6 +10,7 @@ import org.signal.core.util.requireLong import org.signal.core.util.select import org.signal.core.util.toInt import org.signal.core.util.update +import org.signal.core.util.withinTransaction import org.thoughtcrime.securesms.database.model.MessageId import org.thoughtcrime.securesms.recipients.RecipientId import org.whispersystems.signalservice.api.push.DistributionId @@ -259,12 +260,7 @@ class StorySendTable(context: Context, databaseHelper: SignalDatabase) : Databas * 1. For each unique message id in local not present in remote, we can assume that the message can be marked deleted. */ fun applySentStoryManifest(remoteManifest: SentStorySyncManifest, sentTimestamp: Long) { - if (remoteManifest.entries.isEmpty()) { - return - } - - writableDatabase.beginTransaction() - try { + writableDatabase.withinTransaction { val localManifest: SentStorySyncManifest = getLocalManifest(sentTimestamp) val query = """ @@ -291,7 +287,7 @@ class StorySendTable(context: Context, databaseHelper: SignalDatabase) : Databas val remoteRows: Set = remoteManifest.flattenToRows(distributionIdToMessageId) if (localRows == remoteRows) { - return + return@withinTransaction } val remoteOnly: List = remoteRows.filterNot { localRows.contains(it) } @@ -332,10 +328,6 @@ class StorySendTable(context: Context, databaseHelper: SignalDatabase) : Databas SignalDatabase.messages.markAsRemoteDelete(it) SignalDatabase.messages.deleteRemotelyDeletedStory(it) } - - writableDatabase.setTransactionSuccessful() - } finally { - writableDatabase.endTransaction() } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadBodyUtil.java b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadBodyUtil.java index 1f00b92cdb..382f81bcc7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadBodyUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadBodyUtil.java @@ -135,12 +135,16 @@ private ThreadBodyUtil() { } } else { boolean isVideoCall = call.getType() == CallTable.Type.VIDEO_CALL; - boolean isMissed = call.getEvent() == CallTable.Event.MISSED; + boolean isMissed = call.getEvent().isMissedCall(); if (accepted) { return context.getString(isVideoCall ? R.string.MessageRecord_incoming_video_call : R.string.MessageRecord_incoming_voice_call); } else if (isMissed) { - return isVideoCall ? context.getString(R.string.MessageRecord_missed_video_call) : context.getString(R.string.MessageRecord_missed_voice_call); + if (call.getEvent() == CallTable.Event.MISSED_NOTIFICATION_PROFILE) { + return isVideoCall ? context.getString(R.string.MessageRecord_missed_video_call_notification_profile) : context.getString(R.string.MessageRecord_missed_voice_call_notification_profile); + } else { + return isVideoCall ? context.getString(R.string.MessageRecord_missed_video_call) : context.getString(R.string.MessageRecord_missed_voice_call); + } } else { return isVideoCall ? context.getString(R.string.MessageRecord_you_declined_a_video_call) : context.getString(R.string.MessageRecord_you_declined_a_voice_call); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadTable.kt index 02ee936dcf..277f8c96a9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadTable.kt @@ -1805,6 +1805,10 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa ) } + fun clearCache() { + threadIdCache.clear() + } + private fun createQuery(where: String, orderBy: String, offset: Long, limit: Long): String { val projection = COMBINED_THREAD_RECIPIENT_GROUP_PROJECTION.joinToString(separator = ",") @@ -1877,7 +1881,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa open fun getCurrent(): ThreadRecord? { val recipientId = RecipientId.from(cursor.requireLong(RECIPIENT_ID)) - val recipientSettings = recipients.getRecord(context, cursor, RECIPIENT_ID) + val recipientSettings = RecipientTableCursorUtil.getRecord(context, cursor, RECIPIENT_ID) val recipient: Recipient = if (recipientSettings.groupId != null) { GroupTable.Reader(cursor).getCurrent()?.let { group -> diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/documents/IdentityKeyMismatch.java b/app/src/main/java/org/thoughtcrime/securesms/database/documents/IdentityKeyMismatch.java index e9b1157787..c573939380 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/documents/IdentityKeyMismatch.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/documents/IdentityKeyMismatch.java @@ -19,6 +19,7 @@ import org.signal.core.util.logging.Log; import org.signal.libsignal.protocol.IdentityKey; import org.signal.libsignal.protocol.InvalidKeyException; +import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientId; import org.signal.core.util.Base64; @@ -51,11 +52,11 @@ public IdentityKeyMismatch(RecipientId recipientId, IdentityKey identityKey) { } @JsonIgnore - public RecipientId getRecipientId(@NonNull Context context) { + public RecipientId getRecipientId() { if (!TextUtils.isEmpty(recipientId)) { return RecipientId.from(recipientId); } else { - return Recipient.external(context, address).getId(); + return Recipient.external(ApplicationDependencies.getApplication(), address).getId(); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/documents/NetworkFailure.java b/app/src/main/java/org/thoughtcrime/securesms/database/documents/NetworkFailure.java index 119c6c148d..b83ab2a7ac 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/documents/NetworkFailure.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/documents/NetworkFailure.java @@ -8,6 +8,8 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; +import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientId; @@ -30,11 +32,11 @@ public NetworkFailure(@NonNull RecipientId recipientId) { public NetworkFailure() {} @JsonIgnore - public RecipientId getRecipientId(@NonNull Context context) { + public RecipientId getRecipientId() { if (!TextUtils.isEmpty(recipientId)) { return RecipientId.from(recipientId); } else { - return Recipient.external(context, address).getId(); + return Recipient.external(ApplicationDependencies.getApplication(), address).getId(); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt index 26dfd5a1e0..9d9ae5bee2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt @@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.database.helpers import android.app.Application import android.content.Context import net.zetetic.database.sqlcipher.SQLiteDatabase +import org.signal.core.util.areForeignKeyConstraintsEnabled import org.signal.core.util.logging.Log import org.signal.core.util.withinTransaction import org.thoughtcrime.securesms.database.helpers.migration.SignalDatabaseMigration @@ -68,6 +69,10 @@ import org.thoughtcrime.securesms.database.helpers.migration.V207_AddChunkSizeCo import org.thoughtcrime.securesms.database.helpers.migration.V209_ClearRecipientPniFromAciColumn import org.thoughtcrime.securesms.database.helpers.migration.V210_FixPniPossibleColumns import org.thoughtcrime.securesms.database.helpers.migration.V211_ReceiptColumnRenames +import org.thoughtcrime.securesms.database.helpers.migration.V212_RemoveDistributionListUniqueConstraint +import org.thoughtcrime.securesms.database.helpers.migration.V213_FixUsernameInE164Column +import org.thoughtcrime.securesms.database.helpers.migration.V214_PhoneNumberSharingColumn +import org.thoughtcrime.securesms.database.helpers.migration.V215_RemoveAttachmentUniqueId /** * Contains all of the database migrations for [SignalDatabase]. Broken into a separate file for cleanliness. @@ -76,8 +81,6 @@ object SignalDatabaseMigrations { val TAG: String = Log.tag(SignalDatabaseMigrations.javaClass) - const val DATABASE_VERSION = 211 - private val migrations: List> = listOf( 149 to V149_LegacyMigrations, 150 to V150_UrgentMslFlagMigration, @@ -142,23 +145,37 @@ object SignalDatabaseMigrations { // 208 was a bad migration that only manipulated data and did not change schema, replaced by 209 209 to V209_ClearRecipientPniFromAciColumn, 210 to V210_FixPniPossibleColumns, - 211 to V211_ReceiptColumnRenames + 211 to V211_ReceiptColumnRenames, + 212 to V212_RemoveDistributionListUniqueConstraint, + 213 to V213_FixUsernameInE164Column, + 214 to V214_PhoneNumberSharingColumn, + 215 to V215_RemoveAttachmentUniqueId ) + const val DATABASE_VERSION = 215 + @JvmStatic fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { + val initialForeignKeyState = db.areForeignKeyConstraintsEnabled() + for (migrationData in migrations) { val (version, migration) = migrationData if (oldVersion < version) { - Log.i(TAG, "Running migration for version $version: ${migration.javaClass.simpleName}") + Log.i(TAG, "Running migration for version $version: ${migration.javaClass.simpleName}. Foreign keys: ${migration.enableForeignKeys}") + val startTime = System.currentTimeMillis() + + db.setForeignKeyConstraintsEnabled(migration.enableForeignKeys) db.withinTransaction { migration.migrate(context, db, oldVersion, newVersion) db.version = version } - Log.i(TAG, "Successfully completed migration for version $version.") + + Log.i(TAG, "Successfully completed migration for version $version in ${System.currentTimeMillis() - startTime} ms") } } + + db.setForeignKeyConstraintsEnabled(initialForeignKeyState) } @JvmStatic diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/SignalDatabaseMigration.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/SignalDatabaseMigration.kt index d6b23ff370..ef190da45c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/SignalDatabaseMigration.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/SignalDatabaseMigration.kt @@ -7,5 +7,9 @@ import net.zetetic.database.sqlcipher.SQLiteDatabase * Simple interface for allowing database migrations to live outside of [org.thoughtcrime.securesms.database.helpers.SignalDatabaseMigrations]. */ interface SignalDatabaseMigration { + /** True if you want foreign key constraints to be enforced during a migration, otherwise false. Defaults to false. */ + val enableForeignKeys: Boolean + get() = false + fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V166_ThreadAndMessageForeignKeys.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V166_ThreadAndMessageForeignKeys.kt index 6ac6c6839a..81b45533b7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V166_ThreadAndMessageForeignKeys.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V166_ThreadAndMessageForeignKeys.kt @@ -66,7 +66,9 @@ object V166_ThreadAndMessageForeignKeys : SignalDatabaseMigration { Log.w(TAG, "There were $count threads for RecipientId::$recipientId. Merging.", true) val threads: List = getThreadsByRecipientId(db, cursor.requireLong("thread_recipient_id")) - mergeThreads(db, threads) + if (threads.size > 1) { + mergeThreads(db, threads) + } } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V212_RemoveDistributionListUniqueConstraint.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V212_RemoveDistributionListUniqueConstraint.kt new file mode 100644 index 0000000000..ccff4d1af2 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V212_RemoveDistributionListUniqueConstraint.kt @@ -0,0 +1,110 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.database.helpers.migration + +import android.app.Application +import net.zetetic.database.sqlcipher.SQLiteDatabase +import org.signal.core.util.SqlUtil +import org.signal.core.util.Stopwatch +import org.signal.core.util.logging.Log + +/** + * The android app was the only one enforcing unique story names. + * It's been decided dupes are ok, so we get to do the table recreation dance. + */ +object V212_RemoveDistributionListUniqueConstraint : SignalDatabaseMigration { + + private val TAG = Log.tag(V212_RemoveDistributionListUniqueConstraint::class.java) + + override fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { + val stopwatch = Stopwatch("migration") + + rebuildMainTable(db) + stopwatch.split("main-table") + + rebuildMembershipTable(db) + stopwatch.split("members-table") + + val foreignKeyViolations: List = SqlUtil.getForeignKeyViolations(db, "distribution_list") + SqlUtil.getForeignKeyViolations(db, "distribution_list_member") + if (foreignKeyViolations.isNotEmpty()) { + Log.w(TAG, "Foreign key violations!\n${foreignKeyViolations.joinToString(separator = "\n")}") + throw IllegalStateException("Foreign key violations!") + } + stopwatch.split("fk-check") + + stopwatch.stop(TAG) + } + + private fun rebuildMainTable(db: SQLiteDatabase) { + db.execSQL( + """ + CREATE TABLE distribution_list_tmp ( + _id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + distribution_id TEXT UNIQUE NOT NULL, + recipient_id INTEGER UNIQUE REFERENCES recipient (_id) ON DELETE CASCADE, + allows_replies INTEGER DEFAULT 1, + deletion_timestamp INTEGER DEFAULT 0, + is_unknown INTEGER DEFAULT 0, + privacy_mode INTEGER DEFAULT 0 + ) + """ + ) + + db.execSQL( + """ + INSERT INTO distribution_list_tmp + SELECT + _id, + name, + distribution_id, + recipient_id, + allows_replies, + deletion_timestamp, + is_unknown, + privacy_mode + FROM distribution_list + """ + ) + + db.execSQL("DROP TABLE distribution_list") + db.execSQL("ALTER TABLE distribution_list_tmp RENAME TO distribution_list") + } + + private fun rebuildMembershipTable(db: SQLiteDatabase) { + db.execSQL("DROP INDEX distribution_list_member_list_id_recipient_id_privacy_mode_index") + db.execSQL("DROP INDEX distribution_list_member_recipient_id") + + db.execSQL( + """ + CREATE TABLE distribution_list_member_tmp ( + _id INTEGER PRIMARY KEY AUTOINCREMENT, + list_id INTEGER NOT NULL REFERENCES distribution_list (_id) ON DELETE CASCADE, + recipient_id INTEGER NOT NULL REFERENCES recipient (_id) ON DELETE CASCADE, + privacy_mode INTEGER DEFAULT 0 + ) + """ + ) + + db.execSQL( + """ + INSERT INTO distribution_list_member_tmp + SELECT + _id, + list_id, + recipient_id, + privacy_mode + FROM distribution_list_member + """ + ) + + db.execSQL("DROP TABLE distribution_list_member") + db.execSQL("ALTER TABLE distribution_list_member_tmp RENAME TO distribution_list_member") + + db.execSQL("CREATE UNIQUE INDEX distribution_list_member_list_id_recipient_id_privacy_mode_index ON distribution_list_member (list_id, recipient_id, privacy_mode)") + db.execSQL("CREATE INDEX distribution_list_member_recipient_id ON distribution_list_member (recipient_id)") + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V213_FixUsernameInE164Column.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V213_FixUsernameInE164Column.kt new file mode 100644 index 0000000000..260f34ea58 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V213_FixUsernameInE164Column.kt @@ -0,0 +1,56 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.database.helpers.migration + +import android.app.Application +import net.zetetic.database.sqlcipher.SQLiteDatabase +import org.signal.core.util.logging.Log + +/** + * There was a bug where adding a member to a group by username could put that username in the e164 column. + * We have to clean it up and move that value to the username column. + */ +object V213_FixUsernameInE164Column : SignalDatabaseMigration { + + private val TAG = Log.tag(V213_FixUsernameInE164Column::class.java) + + /** We rely on foreign keys to clean up data from bad recipients */ + override val enableForeignKeys: Boolean = true + + override fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { + // In order to avoid unique constraint violations, we run this query once to move over everything that doesn't break any violations... + db.execSQL( + """ + UPDATE + recipient + SET + username = e164, + e164 = NULL + WHERE + e164 GLOB '[a-zA-Z][a-zA-Z0-9][a-zA-Z0-9]*.[0-9][0-9]*' + AND e164 NOT IN (SELECT username FROM recipient WHERE username IS NOT NULL) + """ + ) + + // ...and again to just clear out any remaining bad data. This should only clear data that would otherwise violate the unique constraint. + db.execSQL( + """ + UPDATE + recipient + SET + e164 = NULL + WHERE + e164 GLOB '[a-zA-Z][a-zA-Z0-9][a-zA-Z0-9]*.[0-9][0-9]*' + """ + ) + + // Finally, the above queries may have created recipients that have a username but no ACI. These are invalid entries that need to be trimmed. + // Given that usernames are not public, we'll rely on cascading deletes here to clean things up (foreign keys are enabled for this migration). + db.delete("recipient", "username IS NOT NULL AND aci IS NULL", null).let { deleteCount -> + Log.i(TAG, "Deleted $deleteCount username-only recipients.") + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V214_PhoneNumberSharingColumn.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V214_PhoneNumberSharingColumn.kt new file mode 100644 index 0000000000..fcc18257d5 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V214_PhoneNumberSharingColumn.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.database.helpers.migration + +import android.app.Application +import net.zetetic.database.sqlcipher.SQLiteDatabase + +/** + * Adds a phone_number_sharing column to the recipient table. + */ +@Suppress("ClassName") +object V214_PhoneNumberSharingColumn : SignalDatabaseMigration { + override fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { + db.execSQL("ALTER TABLE recipient ADD COLUMN phone_number_sharing INTEGER DEFAULT 0") + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V215_RemoveAttachmentUniqueId.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V215_RemoveAttachmentUniqueId.kt new file mode 100644 index 0000000000..298d39f8a7 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V215_RemoveAttachmentUniqueId.kt @@ -0,0 +1,121 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.database.helpers.migration + +import android.app.Application +import net.zetetic.database.sqlcipher.SQLiteDatabase +import org.signal.core.util.Stopwatch +import org.signal.core.util.logging.Log + +object V215_RemoveAttachmentUniqueId : SignalDatabaseMigration { + + private val TAG = Log.tag(V215_RemoveAttachmentUniqueId::class.java) + + override fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { + val stopwatch = Stopwatch("migration", decimalPlaces = 2) + + db.execSQL( + """ + CREATE TABLE attachment ( + _id INTEGER PRIMARY KEY AUTOINCREMENT, + message_id INTEGER, + content_type TEXT, + remote_key TEXT, + remote_location TEXT, + remote_digest BLOB, + remote_incremental_digest BLOB, + remote_incremental_digest_chunk_size INTEGER DEFAULT 0, + cdn_number INTEGER DEFAULT 0, + transfer_state INTEGER, + transfer_file TEXT DEFAULT NULL, + data_file TEXT, + data_size INTEGER, + data_random BLOB, + data_hash TEXT DEFAULT NULL, + file_name TEXT, + fast_preflight_id TEXT, + voice_note INTEGER DEFAULT 0, + borderless INTEGER DEFAULT 0, + video_gif INTEGER DEFAULT 0, + quote INTEGER DEFAULT 0, + width INTEGER DEFAULT 0, + height INTEGER DEFAULT 0, + caption TEXT DEFAULT NULL, + sticker_pack_id TEXT DEFAULT NULL, + sticker_pack_key DEFAULT NULL, + sticker_id INTEGER DEFAULT -1, + sticker_emoji STRING DEFAULT NULL, + blur_hash TEXT DEFAULT NULL, + transform_properties TEXT DEFAULT NULL, + display_order INTEGER DEFAULT 0, + upload_timestamp INTEGER DEFAULT 0 + ) + """ + ) + + db.execSQL( + """ + INSERT INTO attachment + SELECT + _id, + mid, + ct, + cd, + cl, + digest, + incremental_mac_digest, + incremental_mac_chunk_size, + cdn_number, + pending_push, + transfer_file, + _data, + data_size, + data_random, + data_hash, + file_name, + fast_preflight_id, + voice_note, + borderless, + video_gif, + quote, + width, + height, + caption, + sticker_pack_id, + sticker_pack_key, + sticker_id, + sticker_emoji, + blur_hash, + transform_properties, + display_order, + upload_timestamp + FROM part + """ + ) + stopwatch.split("copy-data") + + db.execSQL("DROP TABLE part") + stopwatch.split("drop-old") + + db.execSQL("CREATE INDEX IF NOT EXISTS attachment_message_id_index ON attachment (message_id)") + db.execSQL("CREATE INDEX IF NOT EXISTS attachment_transfer_state_index ON attachment (transfer_state)") + db.execSQL("CREATE INDEX IF NOT EXISTS attachment_sticker_pack_id_index ON attachment (sticker_pack_id)") + db.execSQL("CREATE INDEX IF NOT EXISTS attachment_data_hash_index ON attachment (data_hash)") + db.execSQL("CREATE INDEX IF NOT EXISTS attachment_data_index ON attachment (data_file)") + stopwatch.split("create-indexes") + + db.execSQL( + """ + CREATE TRIGGER msl_attachment_delete AFTER DELETE ON attachment + BEGIN + DELETE FROM msl_payload WHERE _id IN (SELECT payload_id FROM msl_message WHERE msl_message.message_id = old.message_id); + END + """ + ) + + stopwatch.stop(TAG) + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/loaders/GroupedThreadMediaLoader.java b/app/src/main/java/org/thoughtcrime/securesms/database/loaders/GroupedThreadMediaLoader.java index b742701abe..4d4d683e56 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/loaders/GroupedThreadMediaLoader.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/loaders/GroupedThreadMediaLoader.java @@ -182,7 +182,7 @@ public static class RoughSizeGroupingMethod implements GroupingMethod { @Override public int groupForRecord(@NonNull MediaTable.MediaRecord mediaRecord) { - long size = mediaRecord.getAttachment().getSize(); + long size = mediaRecord.getAttachment().size; if (size < MB) return SMALL; if (size < 20 * MB) return MEDIUM; diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/loaders/PagingMediaLoader.java b/app/src/main/java/org/thoughtcrime/securesms/database/loaders/PagingMediaLoader.java index a551bb1201..26a64611de 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/loaders/PagingMediaLoader.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/loaders/PagingMediaLoader.java @@ -48,7 +48,7 @@ public PagingMediaLoader(@NonNull Context context, long threadId, @NonNull Uri u Cursor cursor = SignalDatabase.media().getGalleryMediaForThread(threadId, sorting); while (cursor.moveToNext()) { - AttachmentId attachmentId = new AttachmentId(cursor.getLong(cursor.getColumnIndexOrThrow(AttachmentTable.ROW_ID)), cursor.getLong(cursor.getColumnIndexOrThrow(AttachmentTable.UNIQUE_ID))); + AttachmentId attachmentId = new AttachmentId(cursor.getLong(cursor.getColumnIndexOrThrow(AttachmentTable.ID))); Uri attachmentUri = PartAuthority.getAttachmentDataUri(attachmentId); if (attachmentUri.equals(uri)) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/MmsMessageRecord.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/MmsMessageRecord.java index 99266b1880..4d044a5da9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/MmsMessageRecord.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/MmsMessageRecord.java @@ -70,6 +70,7 @@ public class MmsMessageRecord extends MessageRecord { private final CallTable.Call call; private final long scheduledDate; private final MessageId latestRevisionId; + private final boolean isRead; public MmsMessageRecord(long id, Recipient fromRecipient, @@ -109,26 +110,28 @@ public MmsMessageRecord(long id, long scheduledDate, @Nullable MessageId latestRevisionId, @Nullable MessageId originalMessageId, - int revisionNumber) + int revisionNumber, + boolean isRead) { super(id, body, fromRecipient, fromDeviceId, toRecipient, dateSent, dateReceived, dateServer, threadId, Status.STATUS_NONE, hasDeliveryReceipt, mailbox, mismatches, failures, subscriptionId, expiresIn, expireStarted, hasReadReceipt, unidentified, reactions, remoteDelete, notifiedTimestamp, viewed, receiptTimestamp, originalMessageId, revisionNumber); - this.slideDeck = slideDeck; - this.quote = quote; - this.viewOnce = viewOnce; - this.storyType = storyType; - this.parentStoryId = parentStoryId; - this.giftBadge = giftBadge; - this.mentionsSelf = mentionsSelf; - this.messageRanges = messageRanges; - this.payment = payment; - this.call = call; - this.scheduledDate = scheduledDate; - this.latestRevisionId = latestRevisionId; - + this.slideDeck = slideDeck; + this.quote = quote; + this.viewOnce = viewOnce; + this.storyType = storyType; + this.parentStoryId = parentStoryId; + this.giftBadge = giftBadge; + this.mentionsSelf = mentionsSelf; + this.messageRanges = messageRanges; + this.payment = payment; + this.call = call; + this.scheduledDate = scheduledDate; + this.latestRevisionId = latestRevisionId; + this.isRead = isRead; + this.contacts.addAll(contacts); this.linkPreviews.addAll(linkPreviews); } @@ -197,6 +200,10 @@ public boolean isMmsNotification() { return false; } + public boolean isRead() { + return isRead; + } + @Override @WorkerThread public SpannableString getDisplayBody(@NonNull Context context) { @@ -218,8 +225,8 @@ public SpannableString getDisplayBody(@NonNull Context context) { @Override public @Nullable UpdateDescription getUpdateDisplayBody(@NonNull Context context, @Nullable Consumer recipientClickHandler) { if (isCallLog() && call != null && !(call.getType() == CallTable.Type.GROUP_CALL)) { - boolean accepted = call.getEvent() == CallTable.Event.ACCEPTED; - String callDateString = getCallDateString(context); + boolean accepted = call.getEvent() == CallTable.Event.ACCEPTED; + String callDateString = getCallDateString(context); if (call.getDirection() == CallTable.Direction.OUTGOING) { if (call.getType() == CallTable.Type.AUDIO_CALL) { @@ -231,15 +238,27 @@ public SpannableString getDisplayBody(@NonNull Context context) { } } else { boolean isVideoCall = call.getType() == CallTable.Type.VIDEO_CALL; - boolean isMissed = call.getEvent() == CallTable.Event.MISSED; + boolean isMissed = call.getEvent().isMissedCall(); if (accepted) { int updateString = isVideoCall ? R.string.MessageRecord_incoming_video_call : R.string.MessageRecord_incoming_voice_call; int icon = isVideoCall ? R.drawable.ic_update_video_call_incoming_16 : R.drawable.ic_update_audio_call_incoming_16; return staticUpdateDescription(context.getString(R.string.MessageRecord_call_message_with_date, context.getString(updateString), callDateString), icon); } else if (isMissed) { - return isVideoCall ? staticUpdateDescription(context.getString(R.string.MessageRecord_call_message_with_date, context.getString(R.string.MessageRecord_missed_video_call), callDateString), R.drawable.ic_update_video_call_missed_16, ContextCompat.getColor(context, R.color.core_red_shade), ContextCompat.getColor(context, R.color.core_red)) - : staticUpdateDescription(context.getString(R.string.MessageRecord_call_message_with_date, context.getString(R.string.MessageRecord_missed_voice_call), callDateString), R.drawable.ic_update_audio_call_missed_16, ContextCompat.getColor(context, R.color.core_red_shade), ContextCompat.getColor(context, R.color.core_red)); + int icon = isVideoCall ? R.drawable.ic_update_video_call_missed_16 : R.drawable.ic_update_audio_call_missed_16; + int message; + if (call.getEvent() == CallTable.Event.MISSED_NOTIFICATION_PROFILE) { + message = isVideoCall ? R.string.MessageRecord_missed_video_call_notification_profile : R.string.MessageRecord_missed_voice_call_notification_profile; + } else { + message = isVideoCall ? R.string.MessageRecord_missed_video_call : R.string.MessageRecord_missed_voice_call; + } + + return staticUpdateDescription(context.getString(R.string.MessageRecord_call_message_with_date, + context.getString(message), + callDateString), + icon, + ContextCompat.getColor(context, R.color.core_red_shade), + ContextCompat.getColor(context, R.color.core_red)); } else { return isVideoCall ? staticUpdateDescription(context.getString(R.string.MessageRecord_call_message_with_date, context.getString(R.string.MessageRecord_you_declined_a_video_call), callDateString), R.drawable.ic_update_video_call_incoming_16) : staticUpdateDescription(context.getString(R.string.MessageRecord_call_message_with_date, context.getString(R.string.MessageRecord_you_declined_a_voice_call), callDateString), R.drawable.ic_update_audio_call_incoming_16); @@ -280,7 +299,7 @@ public long getScheduledDate() { getType(), getIdentityKeyMismatches(), getNetworkFailures(), getSubscriptionId(), getExpiresIn(), getExpireStarted(), isViewOnce(), hasReadReceipt(), getQuote(), getSharedContacts(), getLinkPreviews(), isUnidentified(), reactions, isRemoteDelete(), mentionsSelf, getNotifiedTimestamp(), isViewed(), getReceiptTimestamp(), getMessageRanges(), getStoryType(), getParentStoryId(), getGiftBadge(), getPayment(), getCall(), getScheduledDate(), getLatestRevisionId(), - getOriginalMessageId(), getRevisionNumber()); + getOriginalMessageId(), getRevisionNumber(), isRead()); } public @NonNull MmsMessageRecord withoutQuote() { @@ -288,13 +307,13 @@ public long getScheduledDate() { getType(), getIdentityKeyMismatches(), getNetworkFailures(), getSubscriptionId(), getExpiresIn(), getExpireStarted(), isViewOnce(), hasReadReceipt(), null, getSharedContacts(), getLinkPreviews(), isUnidentified(), getReactions(), isRemoteDelete(), mentionsSelf, getNotifiedTimestamp(), isViewed(), getReceiptTimestamp(), getMessageRanges(), getStoryType(), getParentStoryId(), getGiftBadge(), getPayment(), getCall(), getScheduledDate(), getLatestRevisionId(), - getOriginalMessageId(), getRevisionNumber()); + getOriginalMessageId(), getRevisionNumber(), isRead()); } public @NonNull MmsMessageRecord withAttachments(@NonNull List attachments) { Map attachmentIdMap = new HashMap<>(); for (DatabaseAttachment attachment : attachments) { - attachmentIdMap.put(attachment.getAttachmentId(), attachment); + attachmentIdMap.put(attachment.attachmentId, attachment); } List contacts = updateContacts(getSharedContacts(), attachmentIdMap); @@ -310,7 +329,7 @@ public long getScheduledDate() { getType(), getIdentityKeyMismatches(), getNetworkFailures(), getSubscriptionId(), getExpiresIn(), getExpireStarted(), isViewOnce(), hasReadReceipt(), quote, contacts, linkPreviews, isUnidentified(), getReactions(), isRemoteDelete(), mentionsSelf, getNotifiedTimestamp(), isViewed(), getReceiptTimestamp(), getMessageRanges(), getStoryType(), getParentStoryId(), getGiftBadge(), getPayment(), getCall(), getScheduledDate(), getLatestRevisionId(), - getOriginalMessageId(), getRevisionNumber()); + getOriginalMessageId(), getRevisionNumber(), isRead()); } public @NonNull MmsMessageRecord withPayment(@NonNull Payment payment) { @@ -318,7 +337,7 @@ public long getScheduledDate() { getType(), getIdentityKeyMismatches(), getNetworkFailures(), getSubscriptionId(), getExpiresIn(), getExpireStarted(), isViewOnce(), hasReadReceipt(), getQuote(), getSharedContacts(), getLinkPreviews(), isUnidentified(), getReactions(), isRemoteDelete(), mentionsSelf, getNotifiedTimestamp(), isViewed(), getReceiptTimestamp(), getMessageRanges(), getStoryType(), getParentStoryId(), getGiftBadge(), payment, getCall(), getScheduledDate(), getLatestRevisionId(), - getOriginalMessageId(), getRevisionNumber()); + getOriginalMessageId(), getRevisionNumber(), isRead()); } @@ -327,7 +346,7 @@ public long getScheduledDate() { getType(), getIdentityKeyMismatches(), getNetworkFailures(), getSubscriptionId(), getExpiresIn(), getExpireStarted(), isViewOnce(), hasReadReceipt(), getQuote(), getSharedContacts(), getLinkPreviews(), isUnidentified(), getReactions(), isRemoteDelete(), mentionsSelf, getNotifiedTimestamp(), isViewed(), getReceiptTimestamp(), getMessageRanges(), getStoryType(), getParentStoryId(), getGiftBadge(), getPayment(), call, getScheduledDate(), getLatestRevisionId(), - getOriginalMessageId(), getRevisionNumber()); + getOriginalMessageId(), getRevisionNumber(), isRead()); } private static @NonNull List updateContacts(@NonNull List contacts, @NonNull Map attachmentIdMap) { @@ -369,7 +388,7 @@ public long getScheduledDate() { return null; } - List quoteAttachments = attachments.stream().filter(Attachment::isQuote).collect(Collectors.toList()); + List quoteAttachments = attachments.stream().filter(a -> a.quote).collect(Collectors.toList()); return quote.withAttachment(new SlideDeck(quoteAttachments)); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/RecipientRecord.kt b/app/src/main/java/org/thoughtcrime/securesms/database/model/RecipientRecord.kt index e279f3479b..7b4cae3c91 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/RecipientRecord.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/RecipientRecord.kt @@ -9,6 +9,7 @@ import org.thoughtcrime.securesms.conversation.colors.ChatColors import org.thoughtcrime.securesms.database.IdentityTable.VerifiedStatus import org.thoughtcrime.securesms.database.RecipientTable import org.thoughtcrime.securesms.database.RecipientTable.MentionSetting +import org.thoughtcrime.securesms.database.RecipientTable.PhoneNumberSharingState import org.thoughtcrime.securesms.database.RecipientTable.RegisteredState import org.thoughtcrime.securesms.database.RecipientTable.UnidentifiedAccessMode import org.thoughtcrime.securesms.database.RecipientTable.VibrateState @@ -76,7 +77,8 @@ data class RecipientRecord( @get:JvmName("needsPniSignature") val needsPniSignature: Boolean, val hiddenState: Recipient.HiddenState, - val callLinkRoomId: CallLinkRoomId? + val callLinkRoomId: CallLinkRoomId?, + val phoneNumberSharing: PhoneNumberSharingState ) { fun e164Only(): Boolean { diff --git a/app/src/main/java/org/thoughtcrime/securesms/events/CallParticipant.kt b/app/src/main/java/org/thoughtcrime/securesms/events/CallParticipant.kt index 5583b3c978..5c37196f64 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/events/CallParticipant.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/events/CallParticipant.kt @@ -16,6 +16,7 @@ data class CallParticipant constructor( val isForwardingVideo: Boolean = true, val isVideoEnabled: Boolean = false, val isMicrophoneEnabled: Boolean = false, + val handRaisedTimestamp: Long = HAND_LOWERED, val lastSpoke: Long = 0, val audioLevel: AudioLevel? = null, val isMediaKeysReceived: Boolean = true, @@ -35,6 +36,9 @@ data class CallParticipant constructor( val isSelf: Boolean get() = recipient.isSelf + val isHandRaised: Boolean + get() = handRaisedTimestamp > 0 + fun getRecipientDisplayName(context: Context): String { return if (recipient.isSelf && isPrimary) { context.getString(R.string.CallParticipant__you) @@ -71,6 +75,10 @@ data class CallParticipant constructor( return copy(isScreenSharing = enable) } + fun withHandRaisedTimestamp(timestamp: Long): CallParticipant { + return copy(handRaisedTimestamp = timestamp) + } + enum class DeviceOrdinal { PRIMARY, SECONDARY } @@ -102,22 +110,28 @@ data class CallParticipant constructor( } companion object { + const val HAND_LOWERED = -1L + @JvmField val EMPTY: CallParticipant = CallParticipant() @JvmStatic + @JvmOverloads fun createLocal( cameraState: CameraState, renderer: BroadcastVideoSink, - microphoneEnabled: Boolean + microphoneEnabled: Boolean, + handRaisedTimestamp: Long, + callParticipantId: CallParticipantId = CallParticipantId(Recipient.self()) ): CallParticipant { return CallParticipant( - callParticipantId = CallParticipantId(Recipient.self()), + callParticipantId = callParticipantId, recipient = Recipient.self(), videoSink = renderer, cameraState = cameraState, isVideoEnabled = cameraState.isEnabled && cameraState.cameraCount > 0, - isMicrophoneEnabled = microphoneEnabled + isMicrophoneEnabled = microphoneEnabled, + handRaisedTimestamp = handRaisedTimestamp ) } @@ -130,6 +144,7 @@ data class CallParticipant constructor( isForwardingVideo: Boolean, audioEnabled: Boolean, videoEnabled: Boolean, + handRaisedTimestamp: Long, lastSpoke: Long, mediaKeysReceived: Boolean, addedToCallTime: Long, @@ -144,6 +159,7 @@ data class CallParticipant constructor( isForwardingVideo = isForwardingVideo, isVideoEnabled = videoEnabled, isMicrophoneEnabled = audioEnabled, + handRaisedTimestamp = handRaisedTimestamp, lastSpoke = lastSpoke, isMediaKeysReceived = mediaKeysReceived, addedToCallTime = addedToCallTime, diff --git a/app/src/main/java/org/thoughtcrime/securesms/events/GroupCallRaiseHandEvent.kt b/app/src/main/java/org/thoughtcrime/securesms/events/GroupCallRaiseHandEvent.kt new file mode 100644 index 0000000000..3356117f08 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/events/GroupCallRaiseHandEvent.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.events + +import org.thoughtcrime.securesms.recipients.Recipient +import java.util.concurrent.TimeUnit + +data class GroupCallRaiseHandEvent(val sender: Recipient, val timestamp: Long) { + fun getCollapseTimestamp(): Long { + return timestamp + TimeUnit.SECONDS.toMillis(LIFESPAN_SECONDS) + } + + companion object { + const val LIFESPAN_SECONDS = 4L + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/events/GroupCallReactionEvent.kt b/app/src/main/java/org/thoughtcrime/securesms/events/GroupCallReactionEvent.kt new file mode 100644 index 0000000000..6de2582a4c --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/events/GroupCallReactionEvent.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.events + +import org.thoughtcrime.securesms.recipients.Recipient +import java.util.concurrent.TimeUnit + +/** + * This is a data class to represent a reaction coming in over the wire in the format we need (mapped to a [Recipient]) in a way that can be easily + * compared across Rx streams. + */ +data class GroupCallReactionEvent(val sender: Recipient, val reaction: String, val timestamp: Long) { + fun getExpirationTimestamp(): Long { + return timestamp + TimeUnit.SECONDS.toMillis(LIFESPAN_SECONDS) + } + + companion object { + const val LIFESPAN_SECONDS = 4L + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/events/WebRtcViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/events/WebRtcViewModel.kt index e7d2e71591..9e9180db2f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/events/WebRtcViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/events/WebRtcViewModel.kt @@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.events import com.annimon.stream.OptionalLong import org.thoughtcrime.securesms.components.webrtc.BroadcastVideoSink +import org.thoughtcrime.securesms.events.CallParticipant.Companion.HAND_LOWERED import org.thoughtcrime.securesms.events.CallParticipant.Companion.createLocal import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.RecipientId @@ -106,10 +107,11 @@ class WebRtcViewModel(state: WebRtcServiceState) { val availableDevices: Set = state.localDeviceState.availableDevices val bluetoothPermissionDenied: Boolean = state.localDeviceState.bluetoothPermissionDenied - val localParticipant: CallParticipant = createLocal( + val localParticipant: CallParticipant = state.callInfoState.localParticipant ?: createLocal( state.localDeviceState.cameraState, (if (state.videoState.localSink != null) state.videoState.localSink else BroadcastVideoSink())!!, - state.localDeviceState.isMicrophoneEnabled + state.localDeviceState.isMicrophoneEnabled, + HAND_LOWERED ) val isCellularConnection: Boolean = when (state.localDeviceState.networkConnectionType) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/addmembers/AddMembersActivity.java b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/addmembers/AddMembersActivity.java index d13f678f9a..652e894bf2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/addmembers/AddMembersActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/addmembers/AddMembersActivity.java @@ -141,7 +141,7 @@ private void displayAlertMessage(@NonNull AddMembersViewModel.AddMemberDialogMes Recipient recipient = Util.firstNonNull(state.getRecipient(), Recipient.UNKNOWN); String message = getResources().getQuantityString(R.plurals.AddMembersActivity__add_d_members_to_s, state.getSelectionCount(), - recipient.getDisplayName(this), state.getGroupTitle(), state.getSelectionCount()); + recipient.getDisplayNameOrUsername(this), state.getGroupTitle(), state.getSelectionCount()); new MaterialAlertDialogBuilder(this) .setMessage(message) diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/addtogroup/AddToGroupViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/addtogroup/AddToGroupViewModel.java index 40229da773..5303a7006d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/addtogroup/AddToGroupViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/addtogroup/AddToGroupViewModel.java @@ -47,7 +47,7 @@ void onContinueWithSelection(@NonNull List groupRecipientIds) { SignalExecutors.BOUNDED.execute(() -> { Recipient recipient = Recipient.resolved(recipientId); Recipient groupRecipient = Recipient.resolved(groupRecipientIds.get(0)); - String recipientName = recipient.getDisplayName(context); + String recipientName = recipient.getDisplayNameOrUsername(context); String groupName = groupRecipient.getDisplayName(context); if (groupRecipient.getGroupId().get().isV1() && !recipient.hasE164()) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/managegroup/dialogs/GroupInviteSentDialog.java b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/managegroup/dialogs/GroupInviteSentDialog.java index 11914bc7b0..3661bb7cbe 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/managegroup/dialogs/GroupInviteSentDialog.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/managegroup/dialogs/GroupInviteSentDialog.java @@ -36,7 +36,7 @@ private GroupInviteSentDialog() { // }) .setPositiveButton(android.R.string.ok, null); if (size == 1) { - builder.setMessage(context.getString(R.string.GroupManagement_invite_single_user, recipients.get(0).getDisplayName(context))); + builder.setMessage(context.getString(R.string.GroupManagement_invite_single_user, recipients.get(0).getDisplayNameOrUsername(context))); } else { builder.setMessage(R.string.GroupManagement_invite_multiple_users) .setView(R.layout.dialog_multiple_group_invites_sent); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentCompressionJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentCompressionJob.java index 6faa30de6b..838626f9db 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentCompressionJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentCompressionJob.java @@ -7,11 +7,15 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.WorkerThread; - import androidx.media3.common.MimeTypes; +import com.google.common.io.ByteStreams; + import org.greenrobot.eventbus.EventBus; import org.signal.core.util.logging.Log; +import org.signal.libsignal.media.Mp4Sanitizer; +import org.signal.libsignal.media.ParseException; +import org.signal.libsignal.media.SanitizedMetadata; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.attachments.AttachmentId; @@ -23,9 +27,10 @@ import org.thoughtcrime.securesms.database.AttachmentTable; import org.thoughtcrime.securesms.database.SignalDatabase; import org.thoughtcrime.securesms.events.PartProgressEvent; -import org.thoughtcrime.securesms.jobmanager.JsonJobData; import org.thoughtcrime.securesms.jobmanager.Job; +import org.thoughtcrime.securesms.jobmanager.JsonJobData; import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; +import org.thoughtcrime.securesms.jobmanager.persistence.JobSpec; import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader; import org.thoughtcrime.securesms.mms.MediaConstraints; import org.thoughtcrime.securesms.mms.MediaStream; @@ -37,7 +42,6 @@ import org.thoughtcrime.securesms.util.FeatureFlags; import org.thoughtcrime.securesms.util.ImageCompressionUtil; import org.thoughtcrime.securesms.util.MediaUtil; -import org.thoughtcrime.securesms.util.MemoryFileDescriptor; import org.thoughtcrime.securesms.util.MemoryFileDescriptor.MemoryFileException; import org.thoughtcrime.securesms.video.InMemoryTranscoder; import org.thoughtcrime.securesms.video.StreamingTranscoder; @@ -49,7 +53,9 @@ import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; +import java.io.SequenceInputStream; import java.util.Objects; import java.util.concurrent.TimeUnit; @@ -60,9 +66,8 @@ public final class AttachmentCompressionJob extends BaseJob { @SuppressWarnings("unused") private static final String TAG = Log.tag(AttachmentCompressionJob.class); - private static final String KEY_ROW_ID = "row_id"; - private static final String KEY_UNIQUE_ID = "unique_id"; - private static final String KEY_MMS = "mms"; + private static final String KEY_ATTACHMENT_ID = "row_id"; + private static final String KEY_MMS = "mms"; private static final String KEY_MMS_SUBSCRIPTION_ID = "mms_subscription_id"; private final AttachmentId attachmentId; @@ -73,7 +78,7 @@ public static AttachmentCompressionJob fromAttachment(@NonNull DatabaseAttachmen boolean mms, int mmsSubscriptionId) { - return new AttachmentCompressionJob(databaseAttachment.getAttachmentId(), + return new AttachmentCompressionJob(databaseAttachment.attachmentId, MediaUtil.isVideo(databaseAttachment) && MediaConstraints.isVideoTranscodeAvailable(), mms, mmsSubscriptionId); @@ -108,8 +113,7 @@ private AttachmentCompressionJob(@NonNull Parameters parameters, @Override public @Nullable byte [] serialize() { - return new JsonJobData.Builder().putLong(KEY_ROW_ID, attachmentId.getRowId()) - .putLong(KEY_UNIQUE_ID, attachmentId.getUniqueId()) + return new JsonJobData.Builder().putLong(KEY_ATTACHMENT_ID, attachmentId.id) .putBoolean(KEY_MMS, mms) .putInt(KEY_MMS_SUBSCRIPTION_ID, mmsSubscriptionId) .serialize(); @@ -125,6 +129,21 @@ protected boolean shouldTrace() { return true; } + @Override + public void onAdded() { + Log.i(TAG, "onAdded() " + attachmentId.toString()); + + final AttachmentTable database = SignalDatabase.attachments(); + final DatabaseAttachment attachment = database.getAttachment(attachmentId); + final boolean pending = attachment != null && attachment.transferState != AttachmentTable.TRANSFER_PROGRESS_DONE + && attachment.transferState != AttachmentTable.TRANSFER_PROGRESS_PERMANENT_FAILURE; + + if (pending) { + Log.i(TAG, "onAdded() Marking attachment progress as 'started'"); + database.setTransferState(attachment.mmsId, attachmentId, AttachmentTable.TRANSFER_PROGRESS_STARTED); + } + } + @Override public void onRun() throws Exception { Log.d(TAG, "Running for: " + attachmentId); @@ -136,19 +155,39 @@ public void onRun() throws Exception { throw new UndeliverableMessageException("Cannot find the specified attachment."); } - if (databaseAttachment.getTransformProperties().shouldSkipTransform()) { + AttachmentTable.TransformProperties transformProperties = databaseAttachment.transformProperties; + + if (transformProperties == null) { + Log.i(TAG, "TransformProperties were null! Using empty TransformProperties."); + transformProperties = AttachmentTable.TransformProperties.empty(); + } + + if (transformProperties.shouldSkipTransform()) { Log.i(TAG, "Skipping at the direction of the TransformProperties."); return; } MediaConstraints mediaConstraints = mms ? MediaConstraints.getMmsMediaConstraints(mmsSubscriptionId) - : MediaConstraints.getPushMediaConstraints(SentMediaQuality.fromCode(databaseAttachment.getTransformProperties().getSentMediaQuality())); + : MediaConstraints.getPushMediaConstraints(SentMediaQuality.fromCode(transformProperties.sentMediaQuality)); compress(database, mediaConstraints, databaseAttachment); } @Override - public void onFailure() { } + public void onFailure() { + AttachmentTable database = SignalDatabase.attachments(); + DatabaseAttachment databaseAttachment = database.getAttachment(attachmentId); + if (databaseAttachment == null) { + Log.i(TAG, "Could not find attachment in DB for compression job upon failure."); + return; + } + + try { + database.setTransferProgressFailed(attachmentId, databaseAttachment.mmsId); + } catch (MmsException e) { + Log.w(TAG, "Error marking attachment as failed upon failed compression.", e); + } + } @Override protected boolean onShouldRetry(@NonNull Exception exception) { @@ -174,10 +213,10 @@ private void compress(@NonNull AttachmentTable attachmentDatabase, try (MediaStream converted = compressImage(context, attachment, constraints)) { attachmentDatabase.updateAttachmentData(attachment, converted, false); } - attachmentDatabase.markAttachmentAsTransformed(attachmentId); + attachmentDatabase.markAttachmentAsTransformed(attachmentId, false); } else if (constraints.isSatisfied(context, attachment)) { Log.i(TAG, "Not compressing."); - attachmentDatabase.markAttachmentAsTransformed(attachmentId); + attachmentDatabase.markAttachmentAsTransformed(attachmentId, false); } else { throw new UndeliverableMessageException("Size constraints could not be met!"); } @@ -194,12 +233,12 @@ private void compress(@NonNull AttachmentTable attachmentDatabase, @NonNull TranscoderCancelationSignal cancelationSignal) throws UndeliverableMessageException { - AttachmentTable.TransformProperties transformProperties = attachment.getTransformProperties(); + AttachmentTable.TransformProperties transformProperties = attachment.transformProperties; boolean allowSkipOnFailure = false; if (!MediaConstraints.isVideoTranscodeAvailable()) { - if (transformProperties.isVideoEdited()) { + if (transformProperties.getVideoEdited()) { throw new UndeliverableMessageException("Video edited, but transcode is not available"); } return attachment; @@ -210,27 +249,28 @@ private void compress(@NonNull AttachmentTable attachmentDatabase, notification.setIndeterminate(true); } - try (MediaDataSource dataSource = attachmentDatabase.mediaDataSourceFor(attachment.getAttachmentId(), false)) { + try (MediaDataSource dataSource = attachmentDatabase.mediaDataSourceFor(attachment.attachmentId, false)) { if (dataSource == null) { throw new UndeliverableMessageException("Cannot get media data source for attachment."); } - allowSkipOnFailure = !transformProperties.isVideoEdited(); + allowSkipOnFailure = !transformProperties.getVideoEdited(); TranscoderOptions options = null; - if (transformProperties.isVideoTrim()) { - options = new TranscoderOptions(transformProperties.getVideoTrimStartTimeUs(), transformProperties.getVideoTrimEndTimeUs()); + if (transformProperties.videoTrim) { + options = new TranscoderOptions(transformProperties.videoTrimStartTimeUs, transformProperties.videoTrimEndTimeUs); } - if (FeatureFlags.useStreamingVideoMuxer() || !MemoryFileDescriptor.supported()) { + if (FeatureFlags.useStreamingVideoMuxer()) { StreamingTranscoder transcoder = new StreamingTranscoder(dataSource, options, constraints.getCompressedVideoMaxSize(context)); if (transcoder.isTranscodeRequired()) { Log.i(TAG, "Compressing with streaming muxer"); AttachmentSecret attachmentSecret = AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret(); - File file = SignalDatabase.attachments().newFile(); + File file = SignalDatabase.attachments().newFile(context); file.deleteOnExit(); + boolean faststart = false; try { try (OutputStream outputStream = ModernEncryptingPartOutputStream.createFor(attachmentSecret, file, true).second) { transcoder.transcode(percent -> { @@ -244,8 +284,28 @@ private void compress(@NonNull AttachmentTable attachmentDatabase, }, outputStream, cancelationSignal); } - try (MediaStream mediaStream = new MediaStream(ModernDecryptingPartInputStream.createFor(attachmentSecret, file, 0), MimeTypes.VIDEO_MP4, 0, 0)) { - attachmentDatabase.updateAttachmentData(attachment, mediaStream, true); + eventBus.postSticky(new PartProgressEvent(attachment, + PartProgressEvent.Type.COMPRESSION, + 100, + 100)); + + InputStream transcodedFileStream = ModernDecryptingPartInputStream.createFor(attachmentSecret, file, 0); + SanitizedMetadata metadata = null; + try { + metadata = Mp4Sanitizer.sanitize(transcodedFileStream, file.length()); + } catch (ParseException e) { + Log.e(TAG, "Could not parse MP4 file.", e); + } + + if (metadata != null && metadata.getSanitizedMetadata() != null) { + try (MediaStream mediaStream = new MediaStream(new SequenceInputStream(new ByteArrayInputStream(metadata.getSanitizedMetadata()), ByteStreams.limit(ModernDecryptingPartInputStream.createFor(attachmentSecret, file, metadata.getDataOffset()), metadata.getDataLength())), MimeTypes.VIDEO_MP4, 0, 0, true)) { + attachmentDatabase.updateAttachmentData(attachment, mediaStream, true); + faststart = true; + } + } else { + try (MediaStream mediaStream = new MediaStream(ModernDecryptingPartInputStream.createFor(attachmentSecret, file, 0), MimeTypes.VIDEO_MP4, 0, 0)) { + attachmentDatabase.updateAttachmentData(attachment, mediaStream, true); + } } } finally { if (!file.delete()) { @@ -253,9 +313,9 @@ private void compress(@NonNull AttachmentTable attachmentDatabase, } } - attachmentDatabase.markAttachmentAsTransformed(attachment.getAttachmentId()); + attachmentDatabase.markAttachmentAsTransformed(attachment.attachmentId, faststart); - return Objects.requireNonNull(attachmentDatabase.getAttachment(attachment.getAttachmentId())); + return Objects.requireNonNull(attachmentDatabase.getAttachment(attachment.attachmentId)); } else { Log.i(TAG, "Transcode was not required"); } @@ -274,11 +334,14 @@ private void compress(@NonNull AttachmentTable attachmentDatabase, percent)); }, cancelationSignal)) { attachmentDatabase.updateAttachmentData(attachment, mediaStream, true); + attachmentDatabase.markAttachmentAsTransformed(attachment.attachmentId, mediaStream.getFaststart()); } - attachmentDatabase.markAttachmentAsTransformed(attachment.getAttachmentId()); - - return Objects.requireNonNull(attachmentDatabase.getAttachment(attachment.getAttachmentId())); + eventBus.postSticky(new PartProgressEvent(attachment, + PartProgressEvent.Type.COMPRESSION, + 100, + 100)); + return Objects.requireNonNull(attachmentDatabase.getAttachment(attachment.attachmentId)); } else { Log.i(TAG, "Transcode was not required (in-memory transcoder)"); } @@ -286,7 +349,7 @@ private void compress(@NonNull AttachmentTable attachmentDatabase, } } } catch (VideoSourceException | EncodingException | MemoryFileException e) { - if (attachment.getSize() > constraints.getVideoMaxSize(context)) { + if (attachment.size > constraints.getVideoMaxSize(context)) { throw new UndeliverableMessageException("Duration not found, attachment too large to skip transcode", e); } else { if (allowSkipOnFailure) { @@ -322,7 +385,7 @@ private static MediaStream compressImage(@NonNull Context context, try { for (int size : mediaConstraints.getImageDimensionTargets(context)) { result = ImageCompressionUtil.compressWithinConstraints(context, - attachment.getContentType(), + attachment.contentType, new DecryptableStreamUriLoader.DecryptableUri(uri), size, mediaConstraints.getImageMaxSize(context), @@ -345,13 +408,28 @@ private static MediaStream compressImage(@NonNull Context context, result.getHeight()); } + public static boolean jobSpecMatchesAttachmentId(@NonNull JobSpec jobSpec, @NonNull AttachmentId attachmentId) { + if (!KEY.equals(jobSpec.getFactoryKey())) { + return false; + } + + final byte[] serializedData = jobSpec.getSerializedData(); + if (serializedData == null) { + return false; + } + + JsonJobData data = JsonJobData.deserialize(serializedData); + final AttachmentId parsed = new AttachmentId(data.getLong(KEY_ATTACHMENT_ID)); + return attachmentId.equals(parsed); + } + public static final class Factory implements Job.Factory { @Override public @NonNull AttachmentCompressionJob create(@NonNull Parameters parameters, @Nullable byte[] serializedData) { JsonJobData data = JsonJobData.deserialize(serializedData); return new AttachmentCompressionJob(parameters, - new AttachmentId(data.getLong(KEY_ROW_ID), data.getLong(KEY_UNIQUE_ID)), + new AttachmentId(data.getLong(KEY_ATTACHMENT_ID)), data.getBoolean(KEY_MMS), data.getInt(KEY_MMS_SUBSCRIPTION_ID)); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.java index c4414daee5..56d3049ba4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.java @@ -28,6 +28,7 @@ import org.thoughtcrime.securesms.jobmanager.JobLogger; import org.thoughtcrime.securesms.jobmanager.JsonJobData; import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; +import org.thoughtcrime.securesms.jobmanager.persistence.JobSpec; import org.thoughtcrime.securesms.mms.MmsException; import org.thoughtcrime.securesms.notifications.v2.ConversationId; import org.thoughtcrime.securesms.releasechannel.ReleaseChannel; @@ -64,13 +65,11 @@ public final class AttachmentDownloadJob extends BaseJob { private static final String TAG = Log.tag(AttachmentDownloadJob.class); private static final String KEY_MESSAGE_ID = "message_id"; - private static final String KEY_PART_ROW_ID = "part_row_id"; - private static final String KEY_PAR_UNIQUE_ID = "part_unique_id"; + private static final String KEY_ATTACHMENT_ID = "part_row_id"; private static final String KEY_MANUAL = "part_manual"; - private final long messageId; - private final long partRowId; - private final long partUniqueId; + private final long messageId; + private final long attachmentId; private final boolean manual; public AttachmentDownloadJob(long messageId, AttachmentId attachmentId, boolean manual) { @@ -89,16 +88,14 @@ private AttachmentDownloadJob(@NonNull Job.Parameters parameters, long messageId super(parameters); this.messageId = messageId; - this.partRowId = attachmentId.getRowId(); - this.partUniqueId = attachmentId.getUniqueId(); + this.attachmentId = attachmentId.id; this.manual = manual; } @Override public @Nullable byte[] serialize() { return new JsonJobData.Builder().putLong(KEY_MESSAGE_ID, messageId) - .putLong(KEY_PART_ROW_ID, partRowId) - .putLong(KEY_PAR_UNIQUE_ID, partUniqueId) + .putLong(KEY_ATTACHMENT_ID, attachmentId) .putBoolean(KEY_MANUAL, manual) .serialize(); } @@ -109,18 +106,18 @@ private AttachmentDownloadJob(@NonNull Job.Parameters parameters, long messageId } public static String constructQueueString(AttachmentId attachmentId) { - return "AttachmentDownloadJob" + attachmentId.getRowId() + "-" + attachmentId.getUniqueId(); + return "AttachmentDownloadJob-" + attachmentId.id; } @Override public void onAdded() { - Log.i(TAG, "onAdded() messageId: " + messageId + " partRowId: " + partRowId + " partUniqueId: " + partUniqueId + " manual: " + manual); + Log.i(TAG, "onAdded() messageId: " + messageId + " attachmentId: " + attachmentId + " manual: " + manual); final AttachmentTable database = SignalDatabase.attachments(); - final AttachmentId attachmentId = new AttachmentId(partRowId, partUniqueId); + final AttachmentId attachmentId = new AttachmentId(this.attachmentId); final DatabaseAttachment attachment = database.getAttachment(attachmentId); - final boolean pending = attachment != null && attachment.getTransferState() != AttachmentTable.TRANSFER_PROGRESS_DONE - && attachment.getTransferState() != AttachmentTable.TRANSFER_PROGRESS_PERMANENT_FAILURE; + final boolean pending = attachment != null && attachment.transferState != AttachmentTable.TRANSFER_PROGRESS_DONE + && attachment.transferState != AttachmentTable.TRANSFER_PROGRESS_PERMANENT_FAILURE; if (pending && (manual || AttachmentUtil.isAutoDownloadPermitted(context, attachment))) { Log.i(TAG, "onAdded() Marking attachment progress as 'started'"); @@ -138,10 +135,10 @@ public void onRun() throws Exception { } public void doWork() throws IOException, RetryLaterException { - Log.i(TAG, "onRun() messageId: " + messageId + " partRowId: " + partRowId + " partUniqueId: " + partUniqueId + " manual: " + manual); + Log.i(TAG, "onRun() messageId: " + messageId + " attachmentId: " + attachmentId + " manual: " + manual); final AttachmentTable database = SignalDatabase.attachments(); - final AttachmentId attachmentId = new AttachmentId(partRowId, partUniqueId); + final AttachmentId attachmentId = new AttachmentId(this.attachmentId); final DatabaseAttachment attachment = database.getAttachment(attachmentId); if (attachment == null) { @@ -168,7 +165,7 @@ public void doWork() throws IOException, RetryLaterException { Log.i(TAG, "Downloading push part " + attachmentId); database.setTransferState(messageId, attachmentId, AttachmentTable.TRANSFER_PROGRESS_STARTED); - if (attachment.getCdnNumber() != ReleaseChannel.CDN_NUMBER) { + if (attachment.cdnNumber != ReleaseChannel.CDN_NUMBER) { retrieveAttachment(messageId, attachmentId, attachment); } else { retrieveUrlAttachment(messageId, attachmentId, attachment); @@ -177,9 +174,9 @@ public void doWork() throws IOException, RetryLaterException { @Override public void onFailure() { - Log.w(TAG, JobLogger.format(this, "onFailure() messageId: " + messageId + " partRowId: " + partRowId + " partUniqueId: " + partUniqueId + " manual: " + manual)); + Log.w(TAG, JobLogger.format(this, "onFailure() messageId: " + messageId + " attachmentId: " + attachmentId + " manual: " + manual)); - final AttachmentId attachmentId = new AttachmentId(partRowId, partUniqueId); + final AttachmentId attachmentId = new AttachmentId(this.attachmentId); markFailed(messageId, attachmentId); } @@ -200,7 +197,7 @@ private void retrieveAttachment(long messageId, File attachmentFile = database.getOrCreateTransferFile(attachmentId); try { - if (attachment.getSize() > maxReceiveSize) { + if (attachment.size > maxReceiveSize) { throw new MmsException("Attachment too large, failing download"); } SignalServiceMessageReceiver messageReceiver = ApplicationDependencies.getSignalServiceMessageReceiver(); @@ -243,38 +240,38 @@ public boolean shouldCancel() { } private SignalServiceAttachmentPointer createAttachmentPointer(Attachment attachment) throws InvalidPartException { - if (TextUtils.isEmpty(attachment.getLocation())) { + if (TextUtils.isEmpty(attachment.remoteLocation)) { throw new InvalidPartException("empty content id"); } - if (TextUtils.isEmpty(attachment.getKey())) { + if (TextUtils.isEmpty(attachment.remoteKey)) { throw new InvalidPartException("empty encrypted key"); } try { - final SignalServiceAttachmentRemoteId remoteId = SignalServiceAttachmentRemoteId.from(attachment.getLocation()); - final byte[] key = Base64.decode(attachment.getKey()); + final SignalServiceAttachmentRemoteId remoteId = SignalServiceAttachmentRemoteId.from(attachment.remoteLocation); + final byte[] key = Base64.decode(attachment.remoteKey); - if (attachment.getDigest() != null) { - Log.i(TAG, "Downloading attachment with digest: " + Hex.toString(attachment.getDigest())); + if (attachment.remoteDigest != null) { + Log.i(TAG, "Downloading attachment with digest: " + Hex.toString(attachment.remoteDigest)); } else { Log.i(TAG, "Downloading attachment with no digest..."); } - return new SignalServiceAttachmentPointer(attachment.getCdnNumber(), remoteId, null, key, - Optional.of(Util.toIntExact(attachment.getSize())), + return new SignalServiceAttachmentPointer(attachment.cdnNumber, remoteId, null, key, + Optional.of(Util.toIntExact(attachment.size)), Optional.empty(), 0, 0, - Optional.ofNullable(attachment.getDigest()), + Optional.ofNullable(attachment.remoteDigest), Optional.ofNullable(attachment.getIncrementalDigest()), - attachment.getIncrementalMacChunkSize(), - Optional.ofNullable(attachment.getFileName()), - attachment.isVoiceNote(), - attachment.isBorderless(), - attachment.isVideoGif(), + attachment.incrementalMacChunkSize, + Optional.ofNullable(attachment.fileName), + attachment.voiceNote, + attachment.borderless, + attachment.videoGif, Optional.empty(), - Optional.ofNullable(attachment.getBlurHash()).map(BlurHash::getHash), - attachment.getUploadTimestamp()); + Optional.ofNullable(attachment.blurHash).map(BlurHash::getHash), + attachment.uploadTimestamp); } catch (IOException | ArithmeticException e) { Log.w(TAG, e); throw new InvalidPartException(e); @@ -286,7 +283,7 @@ private void retrieveUrlAttachment(long messageId, final Attachment attachment) throws IOException { - try (Response response = S3.getObject(Objects.requireNonNull(attachment.getFileName()))) { + try (Response response = S3.getObject(Objects.requireNonNull(attachment.fileName))) { ResponseBody body = response.body(); if (body != null) { if (body.contentLength() > FeatureFlags.maxAttachmentReceiveSizeBytes()) { @@ -318,6 +315,21 @@ private void markPermanentlyFailed(long messageId, AttachmentId attachmentId) { } } + public static boolean jobSpecMatchesAttachmentId(@NonNull JobSpec jobSpec, @NonNull AttachmentId attachmentId) { + if (!KEY.equals(jobSpec.getFactoryKey())) { + return false; + } + + final byte[] serializedData = jobSpec.getSerializedData(); + if (serializedData == null) { + return false; + } + + JsonJobData data = JsonJobData.deserialize(serializedData); + final AttachmentId parsed = new AttachmentId(data.getLong(KEY_ATTACHMENT_ID)); + return attachmentId.equals(parsed); + } + @VisibleForTesting static class InvalidPartException extends Exception { InvalidPartException(String s) {super(s);} @@ -331,7 +343,7 @@ public static final class Factory implements Job.Factory return new AttachmentDownloadJob(parameters, data.getLong(KEY_MESSAGE_ID), - new AttachmentId(data.getLong(KEY_PART_ROW_ID), data.getLong(KEY_PAR_UNIQUE_ID)), + new AttachmentId(data.getLong(KEY_ATTACHMENT_ID)), data.getBoolean(KEY_MANUAL)); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentMarkUploadedJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentMarkUploadedJob.java index 6e22100931..1c3b206857 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentMarkUploadedJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentMarkUploadedJob.java @@ -24,9 +24,8 @@ public final class AttachmentMarkUploadedJob extends BaseJob { @SuppressWarnings("unused") private static final String TAG = Log.tag(AttachmentMarkUploadedJob.class); - private static final String KEY_ROW_ID = "row_id"; - private static final String KEY_UNIQUE_ID = "unique_id"; - private static final String KEY_MESSAGE_ID = "message_id"; + private static final String KEY_ATTACHMENT_ID = "row_id"; + private static final String KEY_MESSAGE_ID = "message_id"; private final AttachmentId attachmentId; private final long messageId; @@ -48,8 +47,7 @@ private AttachmentMarkUploadedJob(@NonNull Parameters parameters, long messageId @Override public @Nullable byte[] serialize() { - return new JsonJobData.Builder().putLong(KEY_ROW_ID, attachmentId.getRowId()) - .putLong(KEY_UNIQUE_ID, attachmentId.getUniqueId()) + return new JsonJobData.Builder().putLong(KEY_ATTACHMENT_ID, attachmentId.id) .putLong(KEY_MESSAGE_ID, messageId) .serialize(); } @@ -93,7 +91,7 @@ public static final class Factory implements Job.Factory 0) { @@ -152,8 +184,17 @@ class AttachmentUploadJob private constructor( } override fun onFailure() { - if (isCanceled) { - SignalDatabase.attachments.deleteAttachment(attachmentId) + val database = SignalDatabase.attachments + val databaseAttachment = database.getAttachment(attachmentId) + if (databaseAttachment == null) { + Log.i(TAG, "Could not find attachment in DB for upload job upon failure/cancellation.") + return + } + + try { + database.setTransferProgressFailed(attachmentId, databaseAttachment.mmsId) + } catch (e: MmsException) { + Log.w(TAG, "Error marking attachment as failed upon failed/canceled upload.", e) } } @@ -174,9 +215,10 @@ class AttachmentUploadJob private constructor( .withContentType(attachment.contentType) .withLength(attachment.size) .withFileName(attachment.fileName) - .withVoiceNote(attachment.isVoiceNote) - .withBorderless(attachment.isBorderless) - .withGif(attachment.isVideoGif) + .withVoiceNote(attachment.voiceNote) + .withBorderless(attachment.borderless) + .withGif(attachment.videoGif) + .withFaststart(attachment.transformProperties?.mp4FastStart ?: false) .withWidth(attachment.width) .withHeight(attachment.height) .withUploadTimestamp(System.currentTimeMillis()) @@ -254,7 +296,7 @@ class AttachmentUploadJob private constructor( val data = AttachmentUploadJobData.ADAPTER.decode(serializedData!!) return AttachmentUploadJob( parameters = parameters, - attachmentId = AttachmentId(data.attachmentRowId, data.attachmentUniqueId), + attachmentId = AttachmentId(data.attachmentId), data.uploadSpec ) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/CallSyncEventJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/CallSyncEventJob.kt index 5df5f5c27e..493f140230 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/CallSyncEventJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/CallSyncEventJob.kt @@ -46,6 +46,21 @@ class CallSyncEventJob private constructor( ) } + @JvmStatic + fun createForNotAccepted(conversationRecipientId: RecipientId, callId: Long, isIncoming: Boolean): CallSyncEventJob { + return CallSyncEventJob( + getParameters(), + listOf( + CallSyncEventJobRecord( + recipientId = conversationRecipientId.toLong(), + callId = callId, + direction = CallTable.Direction.serialize(if (isIncoming) CallTable.Direction.INCOMING else CallTable.Direction.OUTGOING), + event = CallTable.Event.serialize(CallTable.Event.NOT_ACCEPTED) + ) + ) + ) + } + private fun createForDelete(calls: List): CallSyncEventJob { return CallSyncEventJob( getParameters(), @@ -129,6 +144,13 @@ class CallSyncEventJob private constructor( isVideoCall = callType != CallTable.Type.AUDIO_CALL ) + CallTable.Event.NOT_ACCEPTED -> CallEventSyncMessageUtil.createNotAcceptedSyncMessage( + remotePeer = RemotePeer(callSyncEvent.deserializeRecipientId(), CallId(callSyncEvent.callId)), + timestamp = syncTimestamp, + isOutgoing = callSyncEvent.deserializeDirection() == CallTable.Direction.OUTGOING, + isVideoCall = callType != CallTable.Type.AUDIO_CALL + ) + CallTable.Event.DELETE -> CallEventSyncMessageUtil.createDeleteCallEvent( remotePeer = RemotePeer(callSyncEvent.deserializeRecipientId(), CallId(callSyncEvent.callId)), timestamp = syncTimestamp, diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/GenerateAudioWaveFormJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/GenerateAudioWaveFormJob.kt index 41a1e3b5ca..ae0b3cea19 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/GenerateAudioWaveFormJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/GenerateAudioWaveFormJob.kt @@ -22,8 +22,7 @@ class GenerateAudioWaveFormJob private constructor(private val attachmentId: Att companion object { private val TAG = Log.tag(GenerateAudioWaveFormJob::class.java) - private const val KEY_PART_ROW_ID = "part_row_id" - private const val KEY_PAR_UNIQUE_ID = "part_unique_id" + private const val KEY_ATTACHMENT_ID = "part_row_id" const val KEY = "GenerateAudioWaveFormJob" @@ -48,8 +47,7 @@ class GenerateAudioWaveFormJob private constructor(private val attachmentId: Att override fun serialize(): ByteArray? { return JsonJobData.Builder() - .putLong(KEY_PART_ROW_ID, attachmentId.rowId) - .putLong(KEY_PAR_UNIQUE_ID, attachmentId.uniqueId) + .putLong(KEY_ATTACHMENT_ID, attachmentId.id) .serialize() } @@ -89,7 +87,7 @@ class GenerateAudioWaveFormJob private constructor(private val attachmentId: Att class Factory : Job.Factory { override fun create(parameters: Parameters, serializedData: ByteArray?): GenerateAudioWaveFormJob { val data = JsonJobData.deserialize(serializedData) - return GenerateAudioWaveFormJob(AttachmentId(data.getLong(KEY_PART_ROW_ID), data.getLong(KEY_PAR_UNIQUE_ID)), parameters) + return GenerateAudioWaveFormJob(AttachmentId(data.getLong(KEY_ATTACHMENT_ID)), parameters) } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/GroupRingCleanupJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/GroupRingCleanupJob.kt new file mode 100644 index 0000000000..5f6139e458 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/GroupRingCleanupJob.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.jobs + +import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.dependencies.ApplicationDependencies +import org.thoughtcrime.securesms.jobmanager.Job +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint +import java.util.concurrent.TimeUnit + +/** + * Cleans database of stale group rings which can occur if the device or application + * crashes while an incoming ring is happening. + */ +class GroupRingCleanupJob private constructor(parameters: Parameters) : BaseJob(parameters) { + companion object { + const val KEY = "GroupRingCleanupJob" + + @JvmStatic + fun enqueue() { + ApplicationDependencies.getJobManager().add( + GroupRingCleanupJob( + Parameters.Builder() + .addConstraint(NetworkConstraint.KEY) + .setLifespan(TimeUnit.HOURS.toMillis(1)) + .setMaxInstancesForFactory(1) + .setQueue(KEY) + .build() + ) + ) + } + } + + override fun serialize(): ByteArray? = null + + override fun getFactoryKey(): String = KEY + + override fun onFailure() = Unit + + override fun onRun() { + SignalDatabase.calls.getLatestRingingCalls().forEach { + ApplicationDependencies.getSignalCallManager().peekGroupCall(it.peer) + } + + SignalDatabase.calls.markRingingCallsAsMissed() + } + + override fun onShouldRetry(e: Exception): Boolean = false + + class Factory : Job.Factory { + override fun create(parameters: Parameters, serializedData: ByteArray?): GroupRingCleanupJob { + return GroupRingCleanupJob(parameters) + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/IndividualSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/IndividualSendJob.java index 1e5d7020b0..c9da62233a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/IndividualSendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/IndividualSendJob.java @@ -166,6 +166,9 @@ public void onPushSend() markAttachmentsUploaded(messageId, message); database.markUnidentified(messageId, unidentified); + // For scheduled messages, which may not have updated the thread with it's snippet yet + SignalDatabase.threads().updateSilently(threadId, false); + if (recipient.isSelf()) { SignalDatabase.messages().incrementDeliveryReceiptCount(message.getSentTimeMillis(), recipient.getId(), System.currentTimeMillis()); SignalDatabase.messages().incrementReadReceiptCount(message.getSentTimeMillis(), recipient.getId(), System.currentTimeMillis()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java index f0ae7acc22..77dd9a6184 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java @@ -68,6 +68,7 @@ import org.thoughtcrime.securesms.migrations.StickerDayByDayMigrationJob; import org.thoughtcrime.securesms.migrations.StickerMyDailyLifeMigrationJob; import org.thoughtcrime.securesms.migrations.StorageCapabilityMigrationJob; +import org.thoughtcrime.securesms.migrations.StorageFixLocalUnknownMigrationJob; import org.thoughtcrime.securesms.migrations.StorageServiceMigrationJob; import org.thoughtcrime.securesms.migrations.StorageServiceSystemNameMigrationJob; import org.thoughtcrime.securesms.migrations.StoryViewedReceiptsStateMigrationJob; @@ -122,6 +123,7 @@ public static Map getJobFactories(@NonNull Application appl put(GroupCallUpdateSendJob.KEY, new GroupCallUpdateSendJob.Factory()); put(GroupCallPeekJob.KEY, new GroupCallPeekJob.Factory()); put(GroupCallPeekWorkerJob.KEY, new GroupCallPeekWorkerJob.Factory()); + put(GroupRingCleanupJob.KEY, new GroupRingCleanupJob.Factory()); put(GroupV2UpdateSelfProfileKeyJob.KEY, new GroupV2UpdateSelfProfileKeyJob.Factory()); put(IndividualSendJob.KEY, new IndividualSendJob.Factory()); put(LeaveGroupV2Job.KEY, new LeaveGroupV2Job.Factory()); @@ -149,7 +151,6 @@ public static Map getJobFactories(@NonNull Application appl put(MultiDeviceVerifiedUpdateJob.KEY, new MultiDeviceVerifiedUpdateJob.Factory()); put(MultiDeviceViewOnceOpenJob.KEY, new MultiDeviceViewOnceOpenJob.Factory()); put(MultiDeviceViewedUpdateJob.KEY, new MultiDeviceViewedUpdateJob.Factory()); - put(NewRegistrationUsernameSyncJob.KEY, new NewRegistrationUsernameSyncJob.Factory()); put(NullMessageSendJob.KEY, new NullMessageSendJob.Factory()); put(OptimizeMessageSearchIndexJob.KEY, new OptimizeMessageSearchIndexJob.Factory()); put(PaymentLedgerUpdateJob.KEY, new PaymentLedgerUpdateJob.Factory()); @@ -171,6 +172,7 @@ public static Map getJobFactories(@NonNull Application appl put(PushProcessMessageJob.KEY, new PushProcessMessageJob.Factory()); put(ReactionSendJob.KEY, new ReactionSendJob.Factory()); put(RebuildMessageSearchIndexJob.KEY, new RebuildMessageSearchIndexJob.Factory()); + put(ReclaimUsernameAndLinkJob.KEY, new ReclaimUsernameAndLinkJob.Factory()); put(RefreshAttributesJob.KEY, new RefreshAttributesJob.Factory()); put(RefreshCallLinkDetailsJob.KEY, new RefreshCallLinkDetailsJob.Factory()); put(RefreshSvrCredentialsJob.KEY, new RefreshSvrCredentialsJob.Factory()); @@ -253,6 +255,7 @@ public static Map getJobFactories(@NonNull Application appl put(StickerDayByDayMigrationJob.KEY, new StickerDayByDayMigrationJob.Factory()); put(StickerMyDailyLifeMigrationJob.KEY, new StickerMyDailyLifeMigrationJob.Factory()); put(StorageCapabilityMigrationJob.KEY, new StorageCapabilityMigrationJob.Factory()); + put(StorageFixLocalUnknownMigrationJob.KEY, new StorageFixLocalUnknownMigrationJob.Factory()); put(StorageServiceMigrationJob.KEY, new StorageServiceMigrationJob.Factory()); put(StorageServiceSystemNameMigrationJob.KEY, new StorageServiceSystemNameMigrationJob.Factory()); put(StoryViewedReceiptsStateMigrationJob.KEY, new StoryViewedReceiptsStateMigrationJob.Factory()); @@ -300,6 +303,7 @@ public static Map getJobFactories(@NonNull Application appl put("SmsReceiveJob", new FailingJob.Factory()); put("StoryReadStateMigrationJob", new PassingMigrationJob.Factory()); put("GroupV1MigrationJob", new FailingJob.Factory()); + put("NewRegistrationUsernameSyncJob", new FailingJob.Factory()); }}; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/LegacyAttachmentUploadJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/LegacyAttachmentUploadJob.java index 050a089100..fe03637f24 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/LegacyAttachmentUploadJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/LegacyAttachmentUploadJob.java @@ -59,9 +59,8 @@ public final class LegacyAttachmentUploadJob extends BaseJob { private static final long UPLOAD_REUSE_THRESHOLD = TimeUnit.DAYS.toMillis(3); - private static final String KEY_ROW_ID = "row_id"; - private static final String KEY_UNIQUE_ID = "unique_id"; - private static final String KEY_FORCE_V2 = "force_v2"; + private static final String KEY_ROW_ID = "row_id"; + private static final String KEY_FORCE_V2 = "force_v2"; /** * Foreground notification shows while uploading attachments above this. @@ -80,8 +79,7 @@ private LegacyAttachmentUploadJob(@NonNull Job.Parameters parameters, @NonNull A @Override public @Nullable byte[] serialize() { - return new JsonJobData.Builder().putLong(KEY_ROW_ID, attachmentId.getRowId()) - .putLong(KEY_UNIQUE_ID, attachmentId.getUniqueId()) + return new JsonJobData.Builder().putLong(KEY_ROW_ID, attachmentId.id) .putBoolean(KEY_FORCE_V2, forceV2) .serialize(); } @@ -125,22 +123,22 @@ public void onRun() throws Exception { throw new InvalidAttachmentException("Cannot find the specified attachment."); } - long timeSinceUpload = System.currentTimeMillis() - databaseAttachment.getUploadTimestamp(); - if (timeSinceUpload < UPLOAD_REUSE_THRESHOLD && !TextUtils.isEmpty(databaseAttachment.getLocation())) { + long timeSinceUpload = System.currentTimeMillis() - databaseAttachment.uploadTimestamp; + if (timeSinceUpload < UPLOAD_REUSE_THRESHOLD && !TextUtils.isEmpty(databaseAttachment.remoteLocation)) { Log.i(TAG, "We can re-use an already-uploaded file. It was uploaded " + timeSinceUpload + " ms ago. Skipping."); return; - } else if (databaseAttachment.getUploadTimestamp() > 0) { + } else if (databaseAttachment.uploadTimestamp > 0) { Log.i(TAG, "This file was previously-uploaded, but too long ago to be re-used. Age: " + timeSinceUpload + " ms"); } - Log.i(TAG, "Uploading attachment for message " + databaseAttachment.getMmsId() + " with ID " + databaseAttachment.getAttachmentId()); + Log.i(TAG, "Uploading attachment for message " + databaseAttachment.mmsId + " with ID " + databaseAttachment.attachmentId); try (NotificationController notification = getNotificationForAttachment(databaseAttachment)) { try (SignalServiceAttachmentStream localAttachment = getAttachmentFor(databaseAttachment, notification, resumableUploadSpec)) { SignalServiceAttachmentPointer remoteAttachment = messageSender.uploadAttachment(localAttachment); - Attachment attachment = PointerAttachment.forPointer(Optional.of(remoteAttachment), null, databaseAttachment.getFastPreflightId()).get(); + Attachment attachment = PointerAttachment.forPointer(Optional.of(remoteAttachment), null, databaseAttachment.fastPreflightId).get(); - database.updateAttachmentAfterUpload(databaseAttachment.getAttachmentId(), attachment, remoteAttachment.getUploadTimestamp()); + database.updateAttachmentAfterUpload(databaseAttachment.attachmentId, attachment, remoteAttachment.getUploadTimestamp()); } } catch (NonSuccessfulResumableUploadResponseCodeException e) { if (e.getCode() == 400) { @@ -151,7 +149,7 @@ public void onRun() throws Exception { } private @Nullable NotificationController getNotificationForAttachment(@NonNull Attachment attachment) { - if (attachment.getSize() >= FOREGROUND_LIMIT) { + if (attachment.size >= FOREGROUND_LIMIT) { try { return ForegroundServiceUtil.startGenericTaskWhenCapable(context, context.getString(R.string.AttachmentUploadJob_uploading_media)); } catch (UnableToStartException e) { @@ -178,7 +176,7 @@ protected boolean onShouldRetry(@NonNull Exception exception) { } private @NonNull SignalServiceAttachmentStream getAttachmentFor(Attachment attachment, @Nullable NotificationController notification, @Nullable ResumableUploadSpec resumableUploadSpec) throws InvalidAttachmentException { - if (attachment.getUri() == null || attachment.getSize() == 0) { + if (attachment.getUri() == null || attachment.size == 0) { throw new InvalidAttachmentException(new IOException("Assertion failed, outgoing attachment has no data!")); } @@ -186,16 +184,17 @@ protected boolean onShouldRetry(@NonNull Exception exception) { InputStream is = PartAuthority.getAttachmentStream(context, attachment.getUri()); SignalServiceAttachment.Builder builder = SignalServiceAttachment.newStreamBuilder() .withStream(is) - .withContentType(attachment.getContentType()) - .withLength(attachment.getSize()) - .withFileName(attachment.getFileName()) - .withVoiceNote(attachment.isVoiceNote()) - .withBorderless(attachment.isBorderless()) - .withGif(attachment.isVideoGif()) - .withWidth(attachment.getWidth()) - .withHeight(attachment.getHeight()) + .withContentType(attachment.contentType) + .withLength(attachment.size) + .withFileName(attachment.fileName) + .withVoiceNote(attachment.voiceNote) + .withBorderless(attachment.borderless) + .withGif(attachment.videoGif) + .withFaststart(attachment.transformProperties.mp4FastStart) + .withWidth(attachment.width) + .withHeight(attachment.height) .withUploadTimestamp(System.currentTimeMillis()) - .withCaption(attachment.getCaption()) + .withCaption(attachment.caption) .withCancelationSignal(this::isCanceled) .withResumableUploadSpec(resumableUploadSpec) .withListener(new SignalServiceAttachment.ProgressListener() { @@ -212,9 +211,9 @@ public boolean shouldCancel() { return isCanceled(); } }); - if (MediaUtil.isImageType(attachment.getContentType())) { + if (MediaUtil.isImageType(attachment.contentType)) { return builder.withBlurHash(getImageBlurHash(attachment)).build(); - } else if (MediaUtil.isVideoType(attachment.getContentType())) { + } else if (MediaUtil.isVideoType(attachment.contentType)) { return builder.withBlurHash(getVideoBlurHash(attachment)).build(); } else { return builder.build(); @@ -225,7 +224,7 @@ public boolean shouldCancel() { } private @Nullable String getImageBlurHash(@NonNull Attachment attachment) throws IOException { - if (attachment.getBlurHash() != null) return attachment.getBlurHash().getHash(); + if (attachment.blurHash != null) return attachment.blurHash.getHash(); if (attachment.getUri() == null) return null; try (InputStream inputStream = PartAuthority.getAttachmentStream(context, attachment.getUri())) { @@ -234,8 +233,8 @@ public boolean shouldCancel() { } private @Nullable String getVideoBlurHash(@NonNull Attachment attachment) throws IOException { - if (attachment.getBlurHash() != null) { - return attachment.getBlurHash().getHash(); + if (attachment.blurHash != null) { + return attachment.blurHash.getHash(); } Bitmap bitmap = MediaUtil.getVideoThumbnail(context, Objects.requireNonNull(attachment.getUri()), 1000); @@ -269,7 +268,7 @@ public static final class Factory implements Job.Factory { - override fun create(parameters: Parameters, serializedData: ByteArray?): NewRegistrationUsernameSyncJob { - return NewRegistrationUsernameSyncJob(parameters) - } - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PreKeysSyncJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/PreKeysSyncJob.kt index c93b799707..3f5f60bc67 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PreKeysSyncJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PreKeysSyncJob.kt @@ -146,7 +146,6 @@ class PreKeysSyncJob private constructor(parameters: Parameters) : BaseJob(param accountManager.setPreKeys( PreKeyUpload( serviceIdType = serviceIdType, - identityKey = protocolStore.identityKeyPair.publicKey, signedPreKey = signedPreKeyToUpload, oneTimeEcPreKeys = oneTimeEcPreKeysToUpload, lastResortKyberPreKey = lastResortKyberPreKeyToUpload, diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushDistributionListSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushDistributionListSendJob.java index b698aef5aa..c1a5ed9d56 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushDistributionListSendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushDistributionListSendJob.java @@ -106,7 +106,7 @@ public static void enqueue(@NonNull Context context, if (!message.getStoryType().isTextStory()) { DatabaseAttachment storyAttachment = (DatabaseAttachment) message.getAttachments().get(0); - SignalDatabase.attachments().updateAttachmentCaption(storyAttachment.getAttachmentId(), message.getBody()); + SignalDatabase.attachments().updateAttachmentCaption(storyAttachment.attachmentId, message.getBody()); } Set attachmentUploadIds = enqueueCompressingAndUploadAttachmentsChains(jobManager, message); @@ -169,9 +169,9 @@ public void onPushSend() if (Util.hasItems(filterRecipientIds)) { targets = new ArrayList<>(filterRecipientIds.size() + existingNetworkFailures.size()); targets.addAll(filterRecipientIds.stream().map(Recipient::resolved).collect(Collectors.toList())); - targets.addAll(existingNetworkFailures.stream().map(nf -> nf.getRecipientId(context)).distinct().map(Recipient::resolved).collect(Collectors.toList())); + targets.addAll(existingNetworkFailures.stream().map(NetworkFailure::getRecipientId).distinct().map(Recipient::resolved).collect(Collectors.toList())); } else if (!existingNetworkFailures.isEmpty()) { - targets = Stream.of(existingNetworkFailures).map(nf -> nf.getRecipientId(context)).distinct().map(Recipient::resolved).toList(); + targets = Stream.of(existingNetworkFailures).map(NetworkFailure::getRecipientId).distinct().map(Recipient::resolved).toList(); } else { Stories.SendData data = Stories.getRecipientsToSendTo(messageId, message.getSentTimeMillis(), message.getStoryType().isStoryWithReplies()); targets = data.getTargets(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java index 688920f09f..9d174a1ac8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java @@ -221,9 +221,9 @@ public void onPushSend() if (Util.hasItems(filterRecipients)) { target = new ArrayList<>(filterRecipients.size() + existingNetworkFailures.size()); target.addAll(Stream.of(filterRecipients).map(Recipient::resolved).toList()); - target.addAll(Stream.of(existingNetworkFailures).map(nf -> nf.getRecipientId(context)).distinct().map(Recipient::resolved).toList()); + target.addAll(Stream.of(existingNetworkFailures).map(NetworkFailure::getRecipientId).distinct().map(Recipient::resolved).toList()); } else if (!existingNetworkFailures.isEmpty()) { - target = Stream.of(existingNetworkFailures).map(nf -> nf.getRecipientId(context)).distinct().map(Recipient::resolved).toList(); + target = Stream.of(existingNetworkFailures).map(NetworkFailure::getRecipientId).distinct().map(Recipient::resolved).toList(); } else { GroupRecipientResult result = getGroupMessageRecipients(groupRecipient.requireGroupId(), messageId); @@ -421,8 +421,8 @@ static void processGroupMessageResults(@NonNull Context context, List successes = Stream.of(results).filter(result -> result.getSuccess() != null).toList(); List> successUnidentifiedStatus = Stream.of(successes).map(result -> new Pair<>(accessList.requireIdByAddress(result.getAddress()), result.getSuccess().isUnidentified())).toList(); Set successIds = Stream.of(successUnidentifiedStatus).map(Pair::first).collect(Collectors.toSet()); - Set resolvedNetworkFailures = Stream.of(existingNetworkFailures).filter(failure -> successIds.contains(failure.getRecipientId(context))).collect(Collectors.toSet()); - Set resolvedIdentityFailures = Stream.of(existingIdentityMismatches).filter(failure -> successIds.contains(failure.getRecipientId(context))).collect(Collectors.toSet()); + Set resolvedNetworkFailures = Stream.of(existingNetworkFailures).filter(failure -> successIds.contains(failure.getRecipientId())).collect(Collectors.toSet()); + Set resolvedIdentityFailures = Stream.of(existingIdentityMismatches).filter(failure -> successIds.contains(failure.getRecipientId())).collect(Collectors.toSet()); List unregisteredRecipients = Stream.of(results).filter(SendMessageResult::isUnregisteredFailure).map(result -> RecipientId.from(result.getAddress())).toList(); List invalidPreKeyRecipients = Stream.of(results).filter(SendMessageResult::isInvalidPreKeyFailure).map(result -> RecipientId.from(result.getAddress())).toList(); Set skippedRecipients = new HashSet<>(); @@ -442,12 +442,12 @@ static void processGroupMessageResults(@NonNull Context context, } existingNetworkFailures.removeAll(resolvedNetworkFailures); - existingNetworkFailures.removeIf(it -> skippedRecipients.contains(it.getRecipientId(context))); + existingNetworkFailures.removeIf(it -> skippedRecipients.contains(it.getRecipientId())); existingNetworkFailures.addAll(networkFailures); database.setNetworkFailures(messageId, existingNetworkFailures); existingIdentityMismatches.removeAll(resolvedIdentityFailures); - existingIdentityMismatches.removeIf(it -> skippedRecipients.contains(it.getRecipientId(context))); + existingIdentityMismatches.removeIf(it -> skippedRecipients.contains(it.getRecipientId())); existingIdentityMismatches.addAll(identityMismatches); database.setMismatchedIdentities(messageId, existingIdentityMismatches); @@ -485,7 +485,7 @@ static void processGroupMessageResults(@NonNull Context context, notifyMediaMessageDeliveryFailed(context, messageId); Set mismatchRecipientIds = Stream.of(existingIdentityMismatches) - .map(mismatch -> mismatch.getRecipientId(context)) + .map(mismatch -> mismatch.getRecipientId()) .collect(Collectors.toSet()); RetrieveProfileJob.enqueue(mismatchRecipientIds); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java index f2801becd0..f13cfa2390 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java @@ -197,19 +197,20 @@ protected Optional getProfileKey(@NonNull Recipient recipient) { protected SignalServiceAttachment getAttachmentFor(Attachment attachment) { try { - if (attachment.getUri() == null || attachment.getSize() == 0) throw new IOException("Assertion failed, outgoing attachment has no data!"); + if (attachment.getUri() == null || attachment.size == 0) throw new IOException("Assertion failed, outgoing attachment has no data!"); InputStream is = PartAuthority.getAttachmentStream(context, attachment.getUri()); return SignalServiceAttachment.newStreamBuilder() .withStream(is) - .withContentType(attachment.getContentType()) - .withLength(attachment.getSize()) - .withFileName(attachment.getFileName()) - .withVoiceNote(attachment.isVoiceNote()) - .withBorderless(attachment.isBorderless()) - .withGif(attachment.isVideoGif()) - .withWidth(attachment.getWidth()) - .withHeight(attachment.getHeight()) - .withCaption(attachment.getCaption()) + .withContentType(attachment.contentType) + .withLength(attachment.size) + .withFileName(attachment.fileName) + .withVoiceNote(attachment.voiceNote) + .withBorderless(attachment.borderless) + .withGif(attachment.videoGif) + .withFaststart(attachment.transformProperties.mp4FastStart) + .withWidth(attachment.width) + .withHeight(attachment.height) + .withCaption(attachment.caption) .withListener(new SignalServiceAttachment.ProgressListener() { @Override public void onAttachmentProgress(long total, long progress) { @@ -245,7 +246,7 @@ protected static Set enqueueCompressingAndUploadAttachmentsChains(@NonNu .toList()); return new HashSet<>(Stream.of(attachments).map(a -> { - AttachmentUploadJob attachmentUploadJob = new AttachmentUploadJob(((DatabaseAttachment) a).getAttachmentId()); + AttachmentUploadJob attachmentUploadJob = new AttachmentUploadJob(((DatabaseAttachment) a).attachmentId); jobManager.startChain(AttachmentCompressionJob.fromAttachment((DatabaseAttachment) a, false, -1)) .then(attachmentUploadJob) @@ -261,22 +262,22 @@ protected static Set enqueueCompressingAndUploadAttachmentsChains(@NonNu } protected @Nullable SignalServiceAttachment getAttachmentPointerFor(Attachment attachment) { - if (TextUtils.isEmpty(attachment.getLocation())) { + if (TextUtils.isEmpty(attachment.remoteLocation)) { Log.w(TAG, "empty content id"); return null; } - if (TextUtils.isEmpty(attachment.getKey())) { + if (TextUtils.isEmpty(attachment.remoteKey)) { Log.w(TAG, "empty encrypted key"); return null; } try { - final SignalServiceAttachmentRemoteId remoteId = SignalServiceAttachmentRemoteId.from(attachment.getLocation()); - final byte[] key = Base64.decode(attachment.getKey()); + final SignalServiceAttachmentRemoteId remoteId = SignalServiceAttachmentRemoteId.from(attachment.remoteLocation); + final byte[] key = Base64.decode(attachment.remoteKey); - int width = attachment.getWidth(); - int height = attachment.getHeight(); + int width = attachment.width; + int height = attachment.height; if ((width == 0 || height == 0) && MediaUtil.hasVideoThumbnail(context, attachment.getUri())) { Bitmap thumbnail = MediaUtil.getVideoThumbnail(context, attachment.getUri(), 1000); @@ -287,24 +288,24 @@ protected static Set enqueueCompressingAndUploadAttachmentsChains(@NonNu } } - return new SignalServiceAttachmentPointer(attachment.getCdnNumber(), + return new SignalServiceAttachmentPointer(attachment.cdnNumber, remoteId, - attachment.getContentType(), + attachment.contentType, key, - Optional.of(Util.toIntExact(attachment.getSize())), + Optional.of(Util.toIntExact(attachment.size)), Optional.empty(), width, height, - Optional.ofNullable(attachment.getDigest()), + Optional.ofNullable(attachment.remoteDigest), Optional.ofNullable(attachment.getIncrementalDigest()), - attachment.getIncrementalMacChunkSize(), - Optional.ofNullable(attachment.getFileName()), - attachment.isVoiceNote(), - attachment.isBorderless(), - attachment.isVideoGif(), - Optional.ofNullable(attachment.getCaption()), - Optional.ofNullable(attachment.getBlurHash()).map(BlurHash::getHash), - attachment.getUploadTimestamp()); + attachment.incrementalMacChunkSize, + Optional.ofNullable(attachment.fileName), + attachment.voiceNote, + attachment.borderless, + attachment.videoGif, + Optional.ofNullable(attachment.caption), + Optional.ofNullable(attachment.blurHash).map(BlurHash::getHash), + attachment.uploadTimestamp); } catch (IOException | ArithmeticException e) { Log.w(TAG, e); return null; @@ -352,7 +353,7 @@ protected Optional getQuoteFor(OutgoingMessage m Optional localQuoteAttachment = message.getOutgoingQuote() .getAttachments() .stream() - .filter(a -> !MediaUtil.isViewOnceType(a.getContentType())) + .filter(a -> !MediaUtil.isViewOnceType(a.contentType)) .findFirst(); if (localQuoteAttachment.isPresent()) { @@ -362,13 +363,13 @@ protected Optional getQuoteFor(OutgoingMessage m SignalServiceAttachment thumbnail = null; try { - if (MediaUtil.isImageType(attachment.getContentType()) && attachment.getUri() != null) { - thumbnailData = ImageCompressionUtil.compress(context, attachment.getContentType(), new DecryptableUri(attachment.getUri()), 100, 50); - } else if (Build.VERSION.SDK_INT >= 23 && MediaUtil.isVideoType(attachment.getContentType()) && attachment.getUri() != null) { + if (MediaUtil.isImageType(attachment.contentType) && attachment.getUri() != null) { + thumbnailData = ImageCompressionUtil.compress(context, attachment.contentType, new DecryptableUri(attachment.getUri()), 100, 50); + } else if (Build.VERSION.SDK_INT >= 23 && MediaUtil.isVideoType(attachment.contentType) && attachment.getUri() != null) { Bitmap bitmap = MediaUtil.getVideoThumbnail(context, attachment.getUri(), 1000); if (bitmap != null) { - thumbnailData = ImageCompressionUtil.compress(context, attachment.getContentType(), new DecryptableUri(attachment.getUri()), 100, 50); + thumbnailData = ImageCompressionUtil.compress(context, attachment.contentType, new DecryptableUri(attachment.getUri()), 100, 50); } } @@ -384,8 +385,8 @@ protected Optional getQuoteFor(OutgoingMessage m thumbnail = builder.build(); } - quoteAttachments.add(new SignalServiceDataMessage.Quote.QuotedAttachment(attachment.isVideoGif() ? MediaUtil.IMAGE_GIF : attachment.getContentType(), - attachment.getFileName(), + quoteAttachments.add(new SignalServiceDataMessage.Quote.QuotedAttachment(attachment.videoGif ? MediaUtil.IMAGE_GIF : attachment.contentType, + attachment.fileName, thumbnail)); } catch (BitmapDecodingException e) { Log.w(TAG, e); @@ -411,10 +412,10 @@ protected Optional getStickerFor(OutgoingMessa } try { - byte[] packId = Hex.fromStringCondensed(stickerAttachment.getSticker().getPackId()); - byte[] packKey = Hex.fromStringCondensed(stickerAttachment.getSticker().getPackKey()); - int stickerId = stickerAttachment.getSticker().getStickerId(); - StickerRecord record = SignalDatabase.stickers().getSticker(stickerAttachment.getSticker().getPackId(), stickerId, false); + byte[] packId = Hex.fromStringCondensed(stickerAttachment.stickerLocator.packId); + byte[] packKey = Hex.fromStringCondensed(stickerAttachment.stickerLocator.packKey); + int stickerId = stickerAttachment.stickerLocator.stickerId; + StickerRecord record = SignalDatabase.stickers().getSticker(stickerAttachment.stickerLocator.packId, stickerId, false); String emoji = record != null ? record.getEmoji() : null; SignalServiceAttachment attachment = getAttachmentPointerFor(stickerAttachment); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/ReclaimUsernameAndLinkJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/ReclaimUsernameAndLinkJob.kt new file mode 100644 index 0000000000..b9d02a3f2a --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/ReclaimUsernameAndLinkJob.kt @@ -0,0 +1,62 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.jobs + +import org.thoughtcrime.securesms.jobmanager.Job +import org.thoughtcrime.securesms.jobmanager.impl.BackoffUtil +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint +import org.thoughtcrime.securesms.profiles.manage.UsernameRepository +import org.thoughtcrime.securesms.util.FeatureFlags +import kotlin.time.Duration.Companion.days + +/** + * A job to attempt to reclaim a previously-owned username and link after the user re-registers. + * + * There's some nuance here in the scheduling -- we need it to after either the account record + * has been fetched, meaning either [StorageAccountRestoreJob] or [StorageSyncJob] has been run + * first. We manage this creating chains where the job is constructed and putting it in the same + * queue as the storage sync job just in case it managed to get enqueued some other way in the + * future. + * + * Also worth noting that [StorageAccountRestoreJob] also attempts to reclaim the username first, + * but we enqueue this as a fallback since [StorageAccountRestoreJob] has always been a best-effort + * thing that relies on future [StorageSyncJob]'s to clean up any failures. + */ +class ReclaimUsernameAndLinkJob private constructor(parameters: Parameters) : Job(parameters) { + companion object { + const val KEY = "UsernameAndLinkRestoreJob" + } + + constructor() : this( + Parameters.Builder() + .setQueue(StorageSyncJob.QUEUE_KEY) + .addConstraint(NetworkConstraint.KEY) + .setMaxAttempts(Parameters.UNLIMITED) + .setLifespan(30.days.inWholeMilliseconds) + .setMaxInstancesForFactory(1) + .build() + ) + + override fun serialize(): ByteArray? = null + + override fun getFactoryKey(): String = KEY + + override fun run(): Result { + return when (UsernameRepository.reclaimUsernameIfNecessary()) { + UsernameRepository.UsernameReclaimResult.SUCCESS -> Result.success() + UsernameRepository.UsernameReclaimResult.PERMANENT_ERROR -> Result.success() + UsernameRepository.UsernameReclaimResult.NETWORK_ERROR -> Result.retry(BackoffUtil.exponentialBackoff(runAttempt + 1, FeatureFlags.getDefaultMaxBackoff())) + } + } + + override fun onFailure() = Unit + + class Factory : Job.Factory { + override fun create(parameters: Parameters, serializedData: ByteArray?): ReclaimUsernameAndLinkJob { + return ReclaimUsernameAndLinkJob(parameters) + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshOwnProfileJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshOwnProfileJob.java index 32b097eec2..45be7f4146 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshOwnProfileJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshOwnProfileJob.java @@ -20,6 +20,7 @@ import org.thoughtcrime.securesms.keyvalue.AccountValues; import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.profiles.ProfileName; +import org.thoughtcrime.securesms.profiles.manage.UsernameRepository; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.storage.StorageSyncHelper; import org.thoughtcrime.securesms.util.FeatureFlags; @@ -127,6 +128,7 @@ protected void onRun() throws Exception { setProfileAvatar(profile.getAvatar()); setProfileCapabilities(profile.getCapabilities()); ensureUnidentifiedAccessCorrect(profile.getUnidentifiedAccess(), profile.isUnrestrictedUnidentifiedAccess()); + ensurePhoneNumberSharingIsCorrect(profile.getPhoneNumberSharing()); profileAndCredential.getExpiringProfileKeyCredential() .ifPresent(expiringProfileKeyCredential -> setExpiringProfileKeyCredential(self, ProfileKeyUtil.getSelfProfileKey(), expiringProfileKeyCredential)); @@ -235,7 +237,46 @@ private void ensureUnidentifiedAccessCorrect(@Nullable String unidentifiedAccess } } - static void checkUsernameIsInSync() { + /** + * Checks to make sure that our phone number sharing setting matches what's on our profile. If there's a mismatch, we first sync with storage service + * (to limit race conditions between devices) and then upload our profile. + */ + private void ensurePhoneNumberSharingIsCorrect(@Nullable String phoneNumberSharingCiphertext) { + if (phoneNumberSharingCiphertext == null) { + Log.w(TAG, "No phone number sharing is set remotely! Syncing with storage service, then uploading our profile."); + syncWithStorageServiceThenUploadProfile(); + return; + } + + ProfileKey profileKey = ProfileKeyUtil.getSelfProfileKey(); + ProfileCipher cipher = new ProfileCipher(profileKey); + + try { + RecipientTable.PhoneNumberSharingState remotePhoneNumberSharing = cipher.decryptBoolean(Base64.decode(phoneNumberSharingCiphertext)) + .map(value -> value ? RecipientTable.PhoneNumberSharingState.ENABLED : RecipientTable.PhoneNumberSharingState.DISABLED) + .orElse(RecipientTable.PhoneNumberSharingState.UNKNOWN); + + if (remotePhoneNumberSharing == RecipientTable.PhoneNumberSharingState.UNKNOWN || remotePhoneNumberSharing.getEnabled() != SignalStore.phoneNumberPrivacy().isPhoneNumberSharingEnabled()) { + Log.w(TAG, "Phone number sharing setting did not match! Syncing with storage service, then uploading our profile."); + syncWithStorageServiceThenUploadProfile(); + } + } catch (IOException e) { + Log.w(TAG, "Failed to decode phone number sharing! Syncing with storage service, then uploading our profile.", e); + syncWithStorageServiceThenUploadProfile(); + } catch (InvalidCiphertextException e) { + Log.w(TAG, "Failed to decrypt phone number sharing! Syncing with storage service, then uploading our profile.", e); + syncWithStorageServiceThenUploadProfile(); + } + } + + private void syncWithStorageServiceThenUploadProfile() { + ApplicationDependencies.getJobManager() + .startChain(new StorageSyncJob()) + .then(new ProfileUploadJob()) + .enqueue(); + } + + private static void checkUsernameIsInSync() { boolean validated = false; try { @@ -247,9 +288,10 @@ static void checkUsernameIsInSync() { if (TextUtils.isEmpty(localUsernameHash) && TextUtils.isEmpty(remoteUsernameHash)) { Log.d(TAG, "Local and remote username hash are both empty. Considering validated."); + UsernameRepository.onUsernameConsistencyValidated(); } else if (!Objects.equals(localUsernameHash, remoteUsernameHash)) { Log.w(TAG, "Local username hash does not match server username hash. Local hash: " + (TextUtils.isEmpty(localUsername) ? "empty" : "present") + ", Remote hash: " + (TextUtils.isEmpty(remoteUsernameHash) ? "empty" : "present")); - SignalStore.account().setUsernameSyncState(AccountValues.UsernameSyncState.USERNAME_AND_LINK_CORRUPTED); + UsernameRepository.onUsernameMismatchDetected(); return; } else { Log.d(TAG, "Username validated."); @@ -258,7 +300,8 @@ static void checkUsernameIsInSync() { Log.w(TAG, "Failed perform synchronization check during username phase.", e); } catch (BaseUsernameException e) { Log.w(TAG, "Our local username data is invalid!", e); - SignalStore.account().setUsernameSyncState(AccountValues.UsernameSyncState.USERNAME_AND_LINK_CORRUPTED); + UsernameRepository.onUsernameMismatchDetected(); + return; } try { @@ -271,9 +314,7 @@ static void checkUsernameIsInSync() { if (!remoteUsername.getUsername().equals(SignalStore.account().getUsername())) { Log.w(TAG, "The remote username decrypted ok, but the decrypted username did not match our local username!"); - SignalStore.account().setUsernameSyncState(AccountValues.UsernameSyncState.LINK_CORRUPTED); - SignalStore.account().setUsernameLink(null); - StorageSyncHelper.scheduleSyncForDataChange(); + UsernameRepository.onUsernameLinkMismatchDetected(); } else { Log.d(TAG, "Username link validated."); } @@ -284,13 +325,11 @@ static void checkUsernameIsInSync() { Log.w(TAG, "Failed perform synchronization check during the username link phase.", e); } catch (BaseUsernameException e) { Log.w(TAG, "Failed to decrypt username link using the remote encrypted username and our local entropy!", e); - SignalStore.account().setUsernameSyncState(AccountValues.UsernameSyncState.LINK_CORRUPTED); - SignalStore.account().setUsernameLink(null); - StorageSyncHelper.scheduleSyncForDataChange(); + UsernameRepository.onUsernameLinkMismatchDetected(); } if (validated) { - SignalStore.account().setUsernameSyncState(AccountValues.UsernameSyncState.IN_SYNC); + UsernameRepository.onUsernameConsistencyValidated(); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.java deleted file mode 100644 index 0d5c26e343..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.java +++ /dev/null @@ -1,536 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - -import android.app.Application; -import android.content.Context; -import android.text.TextUtils; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.WorkerThread; - -import com.annimon.stream.Collectors; -import com.annimon.stream.Stream; - -import org.signal.core.util.ListUtil; -import org.signal.core.util.SetUtil; -import org.signal.core.util.Stopwatch; -import org.signal.core.util.concurrent.SignalExecutors; -import org.signal.core.util.logging.Log; -import org.signal.libsignal.protocol.IdentityKey; -import org.signal.libsignal.protocol.InvalidKeyException; -import org.signal.libsignal.protocol.util.Pair; -import org.signal.libsignal.zkgroup.profiles.ExpiringProfileKeyCredential; -import org.signal.libsignal.zkgroup.profiles.ProfileKey; -import org.thoughtcrime.securesms.crypto.ProfileKeyUtil; -import org.thoughtcrime.securesms.database.GroupTable; -import org.thoughtcrime.securesms.database.RecipientTable; -import org.thoughtcrime.securesms.database.RecipientTable.UnidentifiedAccessMode; -import org.thoughtcrime.securesms.database.SignalDatabase; -import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; -import org.thoughtcrime.securesms.jobmanager.JsonJobData; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.JobManager; -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; -import org.thoughtcrime.securesms.keyvalue.SignalStore; -import org.thoughtcrime.securesms.notifications.v2.ConversationId; -import org.thoughtcrime.securesms.profiles.ProfileName; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.recipients.RecipientId; -import org.thoughtcrime.securesms.recipients.RecipientUtil; -import org.thoughtcrime.securesms.transport.RetryLaterException; -import org.signal.core.util.Base64; -import org.thoughtcrime.securesms.util.IdentityUtil; -import org.thoughtcrime.securesms.util.ProfileUtil; -import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.signalservice.api.crypto.InvalidCiphertextException; -import org.whispersystems.signalservice.api.crypto.ProfileCipher; -import org.whispersystems.signalservice.api.profiles.ProfileAndCredential; -import org.whispersystems.signalservice.api.profiles.SignalServiceProfile; -import org.whispersystems.signalservice.api.services.ProfileService; -import org.whispersystems.signalservice.api.util.ExpiringProfileCredentialUtil; -import org.whispersystems.signalservice.internal.ServiceResponse; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Set; -import java.util.concurrent.TimeUnit; - -import io.reactivex.rxjava3.core.Observable; -import io.reactivex.rxjava3.schedulers.Schedulers; - -/** - * Retrieves a users profile and sets the appropriate local fields. - */ -public class RetrieveProfileJob extends BaseJob { - - public static final String KEY = "RetrieveProfileJob"; - - private static final String TAG = Log.tag(RetrieveProfileJob.class); - - private static final String KEY_RECIPIENTS = "recipients"; - private static final String DEDUPE_KEY_RETRIEVE_AVATAR = KEY + "_RETRIEVE_PROFILE_AVATAR"; - - private final Set recipientIds; - - /** - * Identical to {@link #enqueue(Set)})}, but run on a background thread for convenience. - */ - public static void enqueueAsync(@NonNull RecipientId recipientId) { - SignalExecutors.BOUNDED_IO.execute(() -> ApplicationDependencies.getJobManager().add(forRecipient(recipientId))); - } - - /** - * Submits the necessary job to refresh the profile of the requested recipient. Works for any - * RecipientId, including individuals, groups, or yourself. - *

- * Identical to {@link #enqueue(Set)})} - */ - @WorkerThread - public static void enqueue(@NonNull RecipientId recipientId) { - ApplicationDependencies.getJobManager().add(forRecipient(recipientId)); - } - - /** - * Submits the necessary jobs to refresh the profiles of the requested recipients. Works for any - * RecipientIds, including individuals, groups, or yourself. - */ - @WorkerThread - public static void enqueue(@NonNull Set recipientIds) { - JobManager jobManager = ApplicationDependencies.getJobManager(); - - for (Job job : forRecipients(recipientIds)) { - jobManager.add(job); - } - } - - /** - * Works for any RecipientId, whether it's an individual, group, or yourself. - */ - @WorkerThread - public static @NonNull Job forRecipient(@NonNull RecipientId recipientId) { - Recipient recipient = Recipient.resolved(recipientId); - - if (recipient.isSelf()) { - return new RefreshOwnProfileJob(); - } else if (recipient.isGroup()) { - List recipients = SignalDatabase.groups().getGroupMemberIds(recipient.requireGroupId(), GroupTable.MemberSet.FULL_MEMBERS_EXCLUDING_SELF); - - return new RetrieveProfileJob(new HashSet<>(recipients)); - } else { - return new RetrieveProfileJob(Collections.singleton(recipientId)); - } - } - - /** - * Works for any RecipientId, whether it's an individual, group, or yourself. - * - * @return A list of length 2 or less. Two iff you are in the recipients. - */ - @WorkerThread - public static @NonNull List forRecipients(@NonNull Set recipientIds) { - Context context = ApplicationDependencies.getApplication(); - Set combined = new HashSet<>(recipientIds.size()); - boolean includeSelf = false; - - for (RecipientId recipientId : recipientIds) { - Recipient recipient = Recipient.resolved(recipientId); - - if (recipient.isSelf()) { - includeSelf = true; - } else if (recipient.isGroup()) { - List recipients = SignalDatabase.groups().getGroupMembers(recipient.requireGroupId(), GroupTable.MemberSet.FULL_MEMBERS_EXCLUDING_SELF); - combined.addAll(Stream.of(recipients).map(Recipient::getId).toList()); - } else { - combined.add(recipientId); - } - } - - List jobs = new ArrayList<>(2); - - if (includeSelf) { - jobs.add(new RefreshOwnProfileJob()); - } - - if (combined.size() > 0) { - jobs.add(new RetrieveProfileJob(combined)); - } - - return jobs; - } - - /** - * Will fetch some profiles to ensure we're decently up-to-date if we haven't done so within a - * certain time period. - */ - public static void enqueueRoutineFetchIfNecessary(Application application) { - if (!SignalStore.registrationValues().isRegistrationComplete() || - !SignalStore.account().isRegistered() || - SignalStore.account().getAci() == null) - { - Log.i(TAG, "Registration not complete. Skipping."); - return; - } - - long timeSinceRefresh = System.currentTimeMillis() - SignalStore.misc().getLastProfileRefreshTime(); - if (timeSinceRefresh < TimeUnit.HOURS.toMillis(12)) { - Log.i(TAG, "Too soon to refresh. Did the last refresh " + timeSinceRefresh + " ms ago."); - return; - } - - SignalExecutors.BOUNDED.execute(() -> { - RecipientTable db = SignalDatabase.recipients(); - long current = System.currentTimeMillis(); - - List ids = db.getRecipientsForRoutineProfileFetch(current - TimeUnit.DAYS.toMillis(30), - current - TimeUnit.DAYS.toMillis(1), - 50); - - ids.add(Recipient.self().getId()); - - if (ids.size() > 0) { - Log.i(TAG, "Optimistically refreshing " + ids.size() + " eligible recipient(s)."); - enqueue(new HashSet<>(ids)); - } else { - Log.i(TAG, "No recipients to refresh."); - } - - SignalStore.misc().setLastProfileRefreshTime(System.currentTimeMillis()); - }); - } - - public RetrieveProfileJob(@NonNull Set recipientIds) { - this(new Job.Parameters.Builder().addConstraint(NetworkConstraint.KEY) - .setMaxAttempts(3) - .build(), - recipientIds); - } - - private RetrieveProfileJob(@NonNull Job.Parameters parameters, @NonNull Set recipientIds) { - super(parameters); - this.recipientIds = recipientIds; - } - - @Override - public @Nullable byte[] serialize() { - return new JsonJobData.Builder().putStringListAsArray(KEY_RECIPIENTS, Stream.of(recipientIds) - .map(RecipientId::serialize) - .toList()) - .serialize(); - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - protected boolean shouldTrace() { - return true; - } - - @Override - public void onRun() throws IOException, RetryLaterException { - if (!SignalStore.account().isRegistered()) { - Log.w(TAG, "Unregistered. Skipping."); - return; - } - - Stopwatch stopwatch = new Stopwatch("RetrieveProfile"); - RecipientTable recipientTable = SignalDatabase.recipients(); - - RecipientUtil.ensureUuidsAreAvailable(context, Stream.of(Recipient.resolvedList(recipientIds)) - .filter(r -> r.getRegistered() != RecipientTable.RegisteredState.NOT_REGISTERED) - .toList()); - - List recipients = Recipient.resolvedList(recipientIds); - stopwatch.split("resolve-ensure"); - - List>>> requests = Stream.of(recipients) - .filter(Recipient::hasServiceId) - .map(r -> ProfileUtil.retrieveProfile(context, r, getRequestType(r)).toObservable()) - .toList(); - stopwatch.split("requests"); - - OperationState operationState = Observable.mergeDelayError(requests, 16, 1) - .observeOn(Schedulers.io(), true) - .scan(new OperationState(), (state, pair) -> { - Recipient recipient = pair.first(); - ProfileService.ProfileResponseProcessor processor = new ProfileService.ProfileResponseProcessor(pair.second()); - if (processor.hasResult()) { - state.profiles.add(processor.getResult(recipient)); - } else if (processor.notFound()) { - Log.w(TAG, "Failed to find a profile for " + recipient.getId()); - if (recipient.isRegistered()) { - state.unregistered.add(recipient.getId()); - } - } else if (processor.genericIoError()) { - state.retries.add(recipient.getId()); - } else { - Log.w(TAG, "Failed to retrieve profile for " + recipient.getId(), processor.getError()); - } - return state; - }) - .lastOrError() - .blockingGet(); - - stopwatch.split("responses"); - - Set success = SetUtil.difference(recipientIds, operationState.retries); - - Set newlyRegistered = Stream.of(operationState.profiles) - .map(Pair::first) - .filterNot(Recipient::isRegistered) - .map(Recipient::getId) - .collect(Collectors.toSet()); - - - //noinspection SimplifyStreamApiCallChains - ListUtil.chunk(operationState.profiles, 150).stream().forEach(list -> { - SignalDatabase.runInTransaction((db) -> { - for (Pair profile : list) { - process(profile.first(), profile.second()); - } - return null; - }); - }); - - recipientTable.markProfilesFetched(success, System.currentTimeMillis()); - - if (operationState.unregistered.size() > 0 || newlyRegistered.size() > 0) { - Log.i(TAG, "Marking " + newlyRegistered.size() + " users as registered and " + operationState.unregistered.size() + " users as unregistered."); - recipientTable.bulkUpdatedRegisteredStatus(newlyRegistered, operationState.unregistered); - } - - stopwatch.split("process"); - - for (Pair profile : operationState.profiles) { - setIdentityKey(profile.first(), profile.second().getProfile().getIdentityKey()); - } - - stopwatch.split("identityKeys"); - - long keyCount = Stream.of(operationState.profiles).map(Pair::first).map(Recipient::getProfileKey).withoutNulls().count(); - Log.d(TAG, String.format(Locale.US, "Started with %d recipient(s). Found %d profile(s), and had keys for %d of them. Will retry %d.", recipients.size(), operationState.profiles.size(), keyCount, operationState.retries.size())); - - stopwatch.stop(TAG); - - recipientIds.clear(); - recipientIds.addAll(operationState.retries); - - if (recipientIds.size() > 0) { - throw new RetryLaterException(); - } - } - - @Override - public boolean onShouldRetry(@NonNull Exception e) { - return e instanceof RetryLaterException; - } - - @Override - public void onFailure() {} - - private void process(Recipient recipient, ProfileAndCredential profileAndCredential) { - SignalServiceProfile profile = profileAndCredential.getProfile(); - ProfileKey recipientProfileKey = ProfileKeyUtil.profileKeyOrNull(recipient.getProfileKey()); - - boolean wroteNewProfileName = setProfileName(recipient, profile.getName()); - - setProfileAbout(recipient, profile.getAbout(), profile.getAboutEmoji()); - setProfileAvatar(recipient, profile.getAvatar()); - setProfileCapabilities(recipient, profile.getCapabilities()); - setUnidentifiedAccessMode(recipient, profile.getUnidentifiedAccess(), profile.isUnrestrictedUnidentifiedAccess()); - - if (recipientProfileKey != null) { - profileAndCredential.getExpiringProfileKeyCredential() - .ifPresent(profileKeyCredential -> setExpiringProfileKeyCredential(recipient, recipientProfileKey, profileKeyCredential)); - } - - if (recipient.hasNonUsernameDisplayName(context) || wroteNewProfileName) { - clearUsername(recipient); - } - } - - private void setExpiringProfileKeyCredential(@NonNull Recipient recipient, - @NonNull ProfileKey recipientProfileKey, - @NonNull ExpiringProfileKeyCredential credential) - { - RecipientTable recipientTable = SignalDatabase.recipients(); - recipientTable.setProfileKeyCredential(recipient.getId(), recipientProfileKey, credential); - } - - private static SignalServiceProfile.RequestType getRequestType(@NonNull Recipient recipient) { - return ExpiringProfileCredentialUtil.isValid(recipient.getExpiringProfileKeyCredential()) ? SignalServiceProfile.RequestType.PROFILE - : SignalServiceProfile.RequestType.PROFILE_AND_CREDENTIAL; - } - - private void setIdentityKey(Recipient recipient, String identityKeyValue) { - try { - if (TextUtils.isEmpty(identityKeyValue)) { - Log.w(TAG, "Identity key is missing on profile!"); - return; - } - - IdentityKey identityKey = new IdentityKey(Base64.decode(identityKeyValue), 0); - - if (!ApplicationDependencies.getProtocolStore().aci().identities().getIdentityRecord(recipient.getId()).isPresent()) { - Log.w(TAG, "Still first use for " + recipient.getId()); - return; - } - - IdentityUtil.saveIdentity(recipient.requireServiceId().toString(), identityKey); - } catch (InvalidKeyException | IOException e) { - Log.w(TAG, e); - } - } - - private void setUnidentifiedAccessMode(Recipient recipient, String unidentifiedAccessVerifier, boolean unrestrictedUnidentifiedAccess) { - RecipientTable recipientTable = SignalDatabase.recipients(); - ProfileKey profileKey = ProfileKeyUtil.profileKeyOrNull(recipient.getProfileKey()); - - if (unrestrictedUnidentifiedAccess && unidentifiedAccessVerifier != null) { - if (recipient.getUnidentifiedAccessMode() != UnidentifiedAccessMode.UNRESTRICTED) { - Log.i(TAG, "Marking recipient UD status as unrestricted."); - recipientTable.setUnidentifiedAccessMode(recipient.getId(), UnidentifiedAccessMode.UNRESTRICTED); - } - } else if (profileKey == null || unidentifiedAccessVerifier == null) { - if (recipient.getUnidentifiedAccessMode() != UnidentifiedAccessMode.DISABLED) { - Log.i(TAG, "Marking recipient UD status as disabled."); - recipientTable.setUnidentifiedAccessMode(recipient.getId(), UnidentifiedAccessMode.DISABLED); - } - } else { - ProfileCipher profileCipher = new ProfileCipher(profileKey); - boolean verifiedUnidentifiedAccess; - - try { - verifiedUnidentifiedAccess = profileCipher.verifyUnidentifiedAccess(Base64.decode(unidentifiedAccessVerifier)); - } catch (IOException e) { - Log.w(TAG, e); - verifiedUnidentifiedAccess = false; - } - - UnidentifiedAccessMode mode = verifiedUnidentifiedAccess ? UnidentifiedAccessMode.ENABLED : UnidentifiedAccessMode.DISABLED; - - if (recipient.getUnidentifiedAccessMode() != mode) { - Log.i(TAG, "Marking recipient UD status as " + mode.name() + " after verification."); - recipientTable.setUnidentifiedAccessMode(recipient.getId(), mode); - } - } - } - - private boolean setProfileName(Recipient recipient, String profileName) { - try { - ProfileKey profileKey = ProfileKeyUtil.profileKeyOrNull(recipient.getProfileKey()); - if (profileKey == null) return false; - - String plaintextProfileName = Util.emptyIfNull(ProfileUtil.decryptString(profileKey, profileName)); - - if (TextUtils.isEmpty(plaintextProfileName)) { - Log.w(TAG, "No name set on the profile for " + recipient.getId() + " -- Leaving it alone"); - return false; - } - - ProfileName remoteProfileName = ProfileName.fromSerialized(plaintextProfileName); - ProfileName localProfileName = recipient.getProfileName(); - - if (!remoteProfileName.equals(localProfileName)) { - Log.i(TAG, "Profile name updated. Writing new value."); - SignalDatabase.recipients().setProfileName(recipient.getId(), remoteProfileName); - - String remoteDisplayName = remoteProfileName.toString(); - String localDisplayName = localProfileName.toString(); - - boolean writeChangeEvent = !recipient.isBlocked() && - !recipient.isGroup() && - !recipient.isSelf() && - !localDisplayName.isEmpty() && - !remoteDisplayName.equals(localDisplayName); - if (writeChangeEvent) { - Log.i(TAG, "Writing a profile name change event for " + recipient.getId()); - SignalDatabase.messages().insertProfileNameChangeMessages(recipient, remoteDisplayName, localDisplayName); - } else { - Log.i(TAG, String.format(Locale.US, "Name changed, but wasn't relevant to write an event. blocked: %s, group: %s, self: %s, firstSet: %s, displayChange: %s", - recipient.isBlocked(), recipient.isGroup(), recipient.isSelf(), localDisplayName.isEmpty(), !remoteDisplayName.equals(localDisplayName))); - } - - if (writeChangeEvent || localDisplayName.isEmpty()) { - Long threadId = SignalDatabase.threads().getThreadIdFor(recipient.getId()); - if (threadId != null) { - ApplicationDependencies.getMessageNotifier().updateNotification(context, ConversationId.forConversation(threadId)); - } - } - - return true; - } - } catch (InvalidCiphertextException e) { - Log.w(TAG, "Bad profile key for " + recipient.getId()); - } catch (IOException e) { - Log.w(TAG, e); - } - - return false; - } - - private void setProfileAbout(@NonNull Recipient recipient, @Nullable String encryptedAbout, @Nullable String encryptedEmoji) { - try { - ProfileKey profileKey = ProfileKeyUtil.profileKeyOrNull(recipient.getProfileKey()); - if (profileKey == null) return; - - String plaintextAbout = ProfileUtil.decryptString(profileKey, encryptedAbout); - String plaintextEmoji = ProfileUtil.decryptString(profileKey, encryptedEmoji); - - SignalDatabase.recipients().setAbout(recipient.getId(), plaintextAbout, plaintextEmoji); - } catch (InvalidCiphertextException | IOException e) { - Log.w(TAG, e); - } - } - - private static void setProfileAvatar(Recipient recipient, String profileAvatar) { - if (recipient.getProfileKey() == null) return; - if (!Util.equals(profileAvatar, recipient.getProfileAvatar())) { - SignalDatabase.runPostSuccessfulTransaction(DEDUPE_KEY_RETRIEVE_AVATAR + recipient.getId(), () -> { - SignalExecutors.BOUNDED.execute(() -> { - ApplicationDependencies.getJobManager().add(new RetrieveProfileAvatarJob(recipient, profileAvatar)); - }); - }); - } - } - - private void clearUsername(Recipient recipient) { - SignalDatabase.recipients().setUsername(recipient.getId(), null); - } - - private void setProfileCapabilities(@NonNull Recipient recipient, @Nullable SignalServiceProfile.Capabilities capabilities) { - if (capabilities == null) { - return; - } - - SignalDatabase.recipients().setCapabilities(recipient.getId(), capabilities); - } - - /** - * Collective state as responses are processed as they come in. - */ - private static class OperationState { - final Set retries = new HashSet<>(); - final Set unregistered = new HashSet<>(); - final List> profiles = new ArrayList<>(); - } - - public static final class Factory implements Job.Factory { - - @Override - public @NonNull RetrieveProfileJob create(@NonNull Parameters parameters, @Nullable byte[] serializedData) { - JsonJobData data = JsonJobData.deserialize(serializedData); - - String[] ids = data.getStringArray(KEY_RECIPIENTS); - Set recipientIds = Stream.of(ids).map(RecipientId::from).collect(Collectors.toSet()); - - return new RetrieveProfileJob(parameters, recipientIds); - } - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.kt new file mode 100644 index 0000000000..698b3039aa --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.kt @@ -0,0 +1,565 @@ +package org.thoughtcrime.securesms.jobs + +import androidx.annotation.WorkerThread +import io.reactivex.rxjava3.core.Observable +import io.reactivex.rxjava3.schedulers.Schedulers +import org.signal.core.util.Base64.decode +import org.signal.core.util.Stopwatch +import org.signal.core.util.concurrent.SignalExecutors +import org.signal.core.util.concurrent.safeBlockingGet +import org.signal.core.util.logging.Log +import org.signal.libsignal.protocol.IdentityKey +import org.signal.libsignal.protocol.InvalidKeyException +import org.signal.libsignal.protocol.util.Pair +import org.signal.libsignal.zkgroup.profiles.ExpiringProfileKeyCredential +import org.signal.libsignal.zkgroup.profiles.ProfileKey +import org.thoughtcrime.securesms.badges.Badges +import org.thoughtcrime.securesms.crypto.ProfileKeyUtil +import org.thoughtcrime.securesms.database.GroupTable +import org.thoughtcrime.securesms.database.RecipientTable +import org.thoughtcrime.securesms.database.RecipientTable.Companion.maskCapabilitiesToLong +import org.thoughtcrime.securesms.database.RecipientTable.PhoneNumberSharingState +import org.thoughtcrime.securesms.database.RecipientTable.UnidentifiedAccessMode +import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.database.model.RecipientRecord +import org.thoughtcrime.securesms.dependencies.ApplicationDependencies +import org.thoughtcrime.securesms.jobmanager.Job +import org.thoughtcrime.securesms.jobmanager.JsonJobData +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint +import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.notifications.v2.ConversationId.Companion.forConversation +import org.thoughtcrime.securesms.profiles.ProfileName +import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.recipients.RecipientId +import org.thoughtcrime.securesms.recipients.RecipientUtil +import org.thoughtcrime.securesms.transport.RetryLaterException +import org.thoughtcrime.securesms.util.IdentityUtil +import org.thoughtcrime.securesms.util.ProfileUtil +import org.thoughtcrime.securesms.util.Util +import org.whispersystems.signalservice.api.crypto.InvalidCiphertextException +import org.whispersystems.signalservice.api.crypto.ProfileCipher +import org.whispersystems.signalservice.api.profiles.ProfileAndCredential +import org.whispersystems.signalservice.api.profiles.SignalServiceProfile +import org.whispersystems.signalservice.api.services.ProfileService.ProfileResponseProcessor +import org.whispersystems.signalservice.api.util.ExpiringProfileCredentialUtil +import org.whispersystems.signalservice.internal.ServiceResponse +import java.io.IOException +import java.util.Optional +import java.util.concurrent.TimeUnit + +/** + * Retrieves a users profile and sets the appropriate local fields. + */ +class RetrieveProfileJob private constructor(parameters: Parameters, private val recipientIds: MutableSet) : BaseJob(parameters) { + constructor(recipientIds: Set) : this( + Parameters.Builder() + .addConstraint(NetworkConstraint.KEY) + .setMaxAttempts(3) + .build(), + recipientIds.toMutableSet() + ) + + override fun serialize(): ByteArray? { + return JsonJobData.Builder() + .putStringListAsArray(KEY_RECIPIENTS, recipientIds.map { it.serialize() }) + .serialize() + } + + override fun getFactoryKey(): String = KEY + + override fun shouldTrace(): Boolean = true + + @Throws(IOException::class, RetryLaterException::class) + public override fun onRun() { + if (!SignalStore.account().isRegistered) { + Log.w(TAG, "Unregistered. Skipping.") + return + } + + val stopwatch = Stopwatch("RetrieveProfile") + + RecipientUtil.ensureUuidsAreAvailable( + context, + Recipient.resolvedList(recipientIds).filter { it.registered != RecipientTable.RegisteredState.NOT_REGISTERED } + ) + + val recipients = Recipient.resolvedList(recipientIds) + stopwatch.split("resolve-ensure") + + val requests: List>>> = recipients + .filter { it.hasServiceId() } + .map { ProfileUtil.retrieveProfile(context, it, getRequestType(it)).toObservable() } + stopwatch.split("requests") + + val operationState = Observable.mergeDelayError(requests, 16, 1) + .observeOn(Schedulers.io(), true) + .scan(OperationState()) { state: OperationState, pair: Pair> -> + val recipient = pair.first() + val processor = ProfileResponseProcessor(pair.second()) + + if (processor.hasResult()) { + state.profiles.add(processor.getResult(recipient)) + } else if (processor.notFound()) { + Log.w(TAG, "Failed to find a profile for ${recipient.id}") + if (recipient.isRegistered) { + state.unregistered.add(recipient.id) + } + } else if (processor.genericIoError()) { + state.retries.add(recipient.id) + } else { + Log.w(TAG, "Failed to retrieve profile for ${recipient.id}", processor.error) + } + state + } + .lastOrError() + .safeBlockingGet() + stopwatch.split("responses") + + val localRecords = SignalDatabase.recipients.getRecords(recipientIds) + Log.d(TAG, "Fetched ${localRecords.size} existing records.") + stopwatch.split("disk-fetch") + + val successIds: Set = recipientIds - operationState.retries + val newlyRegisteredIds: Set = operationState.profiles + .map { it.first() } + .filterNot { it.isRegistered } + .map { it.id } + .toSet() + + val updatedProfiles = operationState.profiles + .filter { recipientProfileAndCredentialPair: Pair -> + val recipientToUpdate = recipientProfileAndCredentialPair.first() + val localRecipientRecord = localRecords[recipientToUpdate.id] ?: return@filter true + val remoteProfile = recipientProfileAndCredentialPair.second().profile + val remoteCredential = recipientProfileAndCredentialPair.second().expiringProfileKeyCredential + + return@filter try { + isUpdated(localRecipientRecord, remoteProfile, remoteCredential) + } catch (e: InvalidCiphertextException) { + Log.w(TAG, "Could not compare new and old profiles.", e) + true + } catch (e: IOException) { + Log.w(TAG, "Could not compare new and old profiles.", e) + true + } + } + .toList() + stopwatch.split("filter") + + Log.d(TAG, "Committing updates to " + updatedProfiles.size + " of " + operationState.profiles.size + " retrieved profiles.") + updatedProfiles.chunked(150).forEach { list: List> -> + SignalDatabase.runInTransaction { + for (profile in list) { + process(profile.first(), profile.second()) + } + } + } + stopwatch.split("process") + + SignalDatabase.recipients.markProfilesFetched(successIds, System.currentTimeMillis()) + stopwatch.split("mark-fetched") + + if (operationState.unregistered.isNotEmpty() || newlyRegisteredIds.isNotEmpty()) { + Log.i(TAG, "Marking " + newlyRegisteredIds.size + " users as registered and " + operationState.unregistered.size + " users as unregistered.") + SignalDatabase.recipients.bulkUpdatedRegisteredStatus(newlyRegisteredIds, operationState.unregistered) + } + stopwatch.split("registered-update") + + for (profile in operationState.profiles) { + setIdentityKey(profile.first(), profile.second().profile.identityKey) + } + stopwatch.split("identityKeys") + + val keyCount = operationState.profiles.map { it.first() }.mapNotNull { it.profileKey }.count() + + Log.d(TAG, "Started with ${recipients.size} recipient(s). Found ${operationState.profiles.size} profile(s), and had keys for $keyCount of them. Will retry ${operationState.retries.size}.") + stopwatch.stop(TAG) + + recipientIds.clear() + recipientIds.addAll(operationState.retries) + + if (recipientIds.isNotEmpty()) { + throw RetryLaterException() + } + } + + public override fun onShouldRetry(e: Exception): Boolean { + return e is RetryLaterException + } + + override fun onFailure() {} + + @Throws(InvalidCiphertextException::class, IOException::class) + private fun isUpdated(localRecipientRecord: RecipientRecord, remoteProfile: SignalServiceProfile, remoteExpiringProfileKeyCredential: Optional): Boolean { + if (localRecipientRecord.signalProfileAvatar != remoteProfile.avatar) { + return true + } + + if (localRecipientRecord.badges != remoteProfile.badges.map { Badges.fromServiceBadge(it) }) { + return true + } + + if (localRecipientRecord.capabilities.rawBits != maskCapabilitiesToLong(remoteProfile.capabilities)) { + return true + } + + val profileKey = ProfileKeyUtil.profileKeyOrNull(localRecipientRecord.profileKey) + val accessMode = deriveUnidentifiedAccessMode( + profileKey = profileKey, + unidentifiedAccessVerifier = remoteProfile.unidentifiedAccess, + unrestrictedUnidentifiedAccess = remoteProfile.isUnrestrictedUnidentifiedAccess + ) + + if (localRecipientRecord.unidentifiedAccessMode != accessMode) { + return true + } + + if (profileKey == null) { + return false + } + + val newProfileName = ProfileName.fromSerialized(ProfileUtil.decryptString(profileKey, remoteProfile.name)) + if (localRecipientRecord.signalProfileName != newProfileName) { + return true + } + + if (localRecipientRecord.about != ProfileUtil.decryptString(profileKey, remoteProfile.about)) { + return true + } + + if (remoteExpiringProfileKeyCredential.isPresent && localRecipientRecord.expiringProfileKeyCredential != remoteExpiringProfileKeyCredential.get()) { + return true + } + + val remotePhoneNumberSharing = ProfileUtil.decryptBoolean(profileKey, remoteProfile.phoneNumberSharing) + .map { value: Boolean -> if (value) PhoneNumberSharingState.ENABLED else PhoneNumberSharingState.DISABLED } + .orElse(PhoneNumberSharingState.UNKNOWN) + + if (localRecipientRecord.phoneNumberSharing != remotePhoneNumberSharing) { + return true + } + + return false + } + + private fun process(recipient: Recipient, profileAndCredential: ProfileAndCredential) { + val profile = profileAndCredential.profile + val recipientProfileKey = ProfileKeyUtil.profileKeyOrNull(recipient.profileKey) + val wroteNewProfileName = setProfileName(recipient, profile.name) + + setProfileAbout(recipient, profile.about, profile.aboutEmoji) + setProfileAvatar(recipient, profile.avatar) + setProfileCapabilities(recipient, profile.capabilities) + setUnidentifiedAccessMode(recipient, profile.unidentifiedAccess, profile.isUnrestrictedUnidentifiedAccess) + setPhoneNumberSharingMode(recipient, profile.phoneNumberSharing) + + if (recipientProfileKey != null) { + profileAndCredential.expiringProfileKeyCredential + .ifPresent { profileKeyCredential: ExpiringProfileKeyCredential -> setExpiringProfileKeyCredential(recipient, recipientProfileKey, profileKeyCredential) } + } + + if (recipient.hasNonUsernameDisplayName(context) || wroteNewProfileName) { + clearUsername(recipient) + } + } + + private fun setExpiringProfileKeyCredential( + recipient: Recipient, + recipientProfileKey: ProfileKey, + credential: ExpiringProfileKeyCredential + ) { + SignalDatabase.recipients.setProfileKeyCredential(recipient.id, recipientProfileKey, credential) + } + + private fun setIdentityKey(recipient: Recipient, identityKeyValue: String?) { + try { + if (identityKeyValue.isNullOrBlank()) { + Log.w(TAG, "Identity key is missing on profile!") + return + } + + val identityKey = IdentityKey(decode(identityKeyValue), 0) + if (!ApplicationDependencies.getProtocolStore().aci().identities().getIdentityRecord(recipient.id).isPresent) { + Log.w(TAG, "Still first use for ${recipient.id}") + return + } + + IdentityUtil.saveIdentity(recipient.requireServiceId().toString(), identityKey) + } catch (e: InvalidKeyException) { + Log.w(TAG, e) + } catch (e: IOException) { + Log.w(TAG, e) + } + } + + private fun setUnidentifiedAccessMode(recipient: Recipient, unidentifiedAccessVerifier: String?, unrestrictedUnidentifiedAccess: Boolean) { + val profileKey = ProfileKeyUtil.profileKeyOrNull(recipient.profileKey) + val newMode = deriveUnidentifiedAccessMode(profileKey, unidentifiedAccessVerifier, unrestrictedUnidentifiedAccess) + + if (recipient.unidentifiedAccessMode !== newMode) { + if (newMode === UnidentifiedAccessMode.UNRESTRICTED) { + Log.i(TAG, "Marking recipient UD status as unrestricted.") + } else if (profileKey == null || unidentifiedAccessVerifier == null) { + Log.i(TAG, "Marking recipient UD status as disabled.") + } else { + Log.i(TAG, "Marking recipient UD status as " + newMode.name + " after verification.") + } + + SignalDatabase.recipients.setUnidentifiedAccessMode(recipient.id, newMode) + } + } + + private fun deriveUnidentifiedAccessMode(profileKey: ProfileKey?, unidentifiedAccessVerifier: String?, unrestrictedUnidentifiedAccess: Boolean): UnidentifiedAccessMode { + return if (unrestrictedUnidentifiedAccess && unidentifiedAccessVerifier != null) { + UnidentifiedAccessMode.UNRESTRICTED + } else if (profileKey == null || unidentifiedAccessVerifier == null) { + UnidentifiedAccessMode.DISABLED + } else { + val profileCipher = ProfileCipher(profileKey) + val verifiedUnidentifiedAccess: Boolean = try { + profileCipher.verifyUnidentifiedAccess(decode(unidentifiedAccessVerifier)) + } catch (e: IOException) { + Log.w(TAG, e) + false + } + + if (verifiedUnidentifiedAccess) { + UnidentifiedAccessMode.ENABLED + } else { + UnidentifiedAccessMode.DISABLED + } + } + } + + private fun setProfileName(recipient: Recipient, profileName: String?): Boolean { + try { + val profileKey = ProfileKeyUtil.profileKeyOrNull(recipient.profileKey) ?: return false + val plaintextProfileName = Util.emptyIfNull(ProfileUtil.decryptString(profileKey, profileName)) + + if (plaintextProfileName.isNullOrBlank()) { + Log.w(TAG, "No name set on the profile for ${recipient.id} -- Leaving it alone") + return false + } + + val remoteProfileName = ProfileName.fromSerialized(plaintextProfileName) + val localProfileName = recipient.profileName + + if (remoteProfileName != localProfileName) { + Log.i(TAG, "Profile name updated. Writing new value.") + SignalDatabase.recipients.setProfileName(recipient.id, remoteProfileName) + + val remoteDisplayName = remoteProfileName.toString() + val localDisplayName = localProfileName.toString() + val writeChangeEvent = !recipient.isBlocked && + !recipient.isGroup && + !recipient.isSelf && + localDisplayName.isNotEmpty() && + remoteDisplayName != localDisplayName + + if (writeChangeEvent) { + Log.i(TAG, "Writing a profile name change event for ${recipient.id}") + SignalDatabase.messages.insertProfileNameChangeMessages(recipient, remoteDisplayName, localDisplayName) + } else { + Log.i(TAG, "Name changed, but wasn't relevant to write an event. blocked: ${recipient.isBlocked}, group: ${recipient.isGroup}, self: ${recipient.isSelf}, firstSet: ${localDisplayName.isEmpty()}, displayChange: ${remoteDisplayName != localDisplayName}") + } + + if (writeChangeEvent || localDisplayName.isEmpty()) { + val threadId = SignalDatabase.threads.getThreadIdFor(recipient.id) + if (threadId != null) { + ApplicationDependencies.getMessageNotifier().updateNotification(context, forConversation(threadId)) + } + } + + return true + } + } catch (e: InvalidCiphertextException) { + Log.w(TAG, "Bad profile key for ${recipient.id}") + } catch (e: IOException) { + Log.w(TAG, e) + } + + return false + } + + private fun setProfileAbout(recipient: Recipient, encryptedAbout: String?, encryptedEmoji: String?) { + try { + val profileKey = ProfileKeyUtil.profileKeyOrNull(recipient.profileKey) ?: return + val plaintextAbout = ProfileUtil.decryptString(profileKey, encryptedAbout) + val plaintextEmoji = ProfileUtil.decryptString(profileKey, encryptedEmoji) + + SignalDatabase.recipients.setAbout(recipient.id, plaintextAbout, plaintextEmoji) + } catch (e: InvalidCiphertextException) { + Log.w(TAG, e) + } catch (e: IOException) { + Log.w(TAG, e) + } + } + + private fun clearUsername(recipient: Recipient) { + SignalDatabase.recipients.setUsername(recipient.id, null) + } + + private fun setProfileCapabilities(recipient: Recipient, capabilities: SignalServiceProfile.Capabilities?) { + if (capabilities == null) { + return + } + + SignalDatabase.recipients.setCapabilities(recipient.id, capabilities) + } + + private fun setPhoneNumberSharingMode(recipient: Recipient, phoneNumberSharing: String?) { + val profileKey = ProfileKeyUtil.profileKeyOrNull(recipient.profileKey) ?: return + + try { + val remotePhoneNumberSharing = ProfileUtil.decryptBoolean(profileKey, phoneNumberSharing) + .map { value: Boolean -> if (value) PhoneNumberSharingState.ENABLED else PhoneNumberSharingState.DISABLED } + .orElse(PhoneNumberSharingState.UNKNOWN) + + if (recipient.phoneNumberSharing !== remotePhoneNumberSharing) { + Log.i(TAG, "Updating phone number sharing state for " + recipient.id + " to " + remotePhoneNumberSharing) + SignalDatabase.recipients.setPhoneNumberSharing(recipient.id, remotePhoneNumberSharing) + } + } catch (e: InvalidCiphertextException) { + Log.w(TAG, "Failed to set the phone number sharing setting!", e) + } catch (e: IOException) { + Log.w(TAG, "Failed to set the phone number sharing setting!", e) + } + } + + private fun setProfileAvatar(recipient: Recipient, profileAvatar: String?) { + if (recipient.profileKey == null) { + return + } + + if (profileAvatar != recipient.profileAvatar) { + SignalDatabase.runPostSuccessfulTransaction(DEDUPE_KEY_RETRIEVE_AVATAR + recipient.id) { + SignalExecutors.BOUNDED.execute { + ApplicationDependencies.getJobManager().add(RetrieveProfileAvatarJob(recipient, profileAvatar)) + } + } + } + } + + private fun getRequestType(recipient: Recipient): SignalServiceProfile.RequestType { + return if (ExpiringProfileCredentialUtil.isValid(recipient.expiringProfileKeyCredential)) SignalServiceProfile.RequestType.PROFILE else SignalServiceProfile.RequestType.PROFILE_AND_CREDENTIAL + } + + /** + * Collective state as responses are processed as they come in. + */ + private class OperationState { + val retries: MutableSet = HashSet() + val unregistered: MutableSet = HashSet() + val profiles: MutableList> = ArrayList() + } + + class Factory : Job.Factory { + override fun create(parameters: Parameters, serializedData: ByteArray?): RetrieveProfileJob { + val data = JsonJobData.deserialize(serializedData) + val recipientIds: MutableSet = data.getStringArray(KEY_RECIPIENTS).map { RecipientId.from(it) }.toMutableSet() + + return RetrieveProfileJob(parameters, recipientIds) + } + } + + companion object { + const val KEY = "RetrieveProfileJob" + private val TAG = Log.tag(RetrieveProfileJob::class.java) + private const val KEY_RECIPIENTS = "recipients" + private const val DEDUPE_KEY_RETRIEVE_AVATAR = KEY + "_RETRIEVE_PROFILE_AVATAR" + + /** + * Submits the necessary job to refresh the profile of the requested recipient. Works for any + * RecipientId, including individuals, groups, or yourself. + * + * May not enqueue any jobs in certain circumstances. In particular, if the recipient is a group + * with no other members, then no job will be enqueued. + */ + @JvmStatic + @WorkerThread + fun enqueue(recipientId: RecipientId) { + forRecipients(setOf(recipientId)).firstOrNull()?.let { job -> + ApplicationDependencies.getJobManager().add(job) + } + } + + /** + * Submits the necessary jobs to refresh the profiles of the requested recipients. Works for any + * RecipientIds, including individuals, groups, or yourself. + */ + @JvmStatic + @WorkerThread + fun enqueue(recipientIds: Set) { + val jobManager = ApplicationDependencies.getJobManager() + for (job in forRecipients(recipientIds)) { + jobManager.add(job) + } + } + + /** + * Works for any RecipientId, whether it's an individual, group, or yourself. + * + * @return A list of length 2 or less. Two iff you are in the recipients. Could be empty for groups with no other members. + */ + @JvmStatic + @WorkerThread + fun forRecipients(recipientIds: Set): List { + val combined: MutableSet = HashSet(recipientIds.size) + var includeSelf = false + + for (recipientId in recipientIds) { + val recipient = Recipient.resolved(recipientId) + when { + recipient.isSelf -> includeSelf = true + recipient.isGroup -> combined += SignalDatabase.groups.getGroupMemberIds(recipient.requireGroupId(), GroupTable.MemberSet.FULL_MEMBERS_EXCLUDING_SELF) + else -> combined.add(recipientId) + } + } + + return ArrayList(2).apply { + if (includeSelf) { + add(RefreshOwnProfileJob()) + } + if (combined.size > 0) { + add(RetrieveProfileJob(combined)) + } + } + } + + /** + * Will fetch some profiles to ensure we're decently up-to-date if we haven't done so within a + * certain time period. + */ + @JvmStatic + fun enqueueRoutineFetchIfNecessary() { + if (!SignalStore.registrationValues().isRegistrationComplete || !SignalStore.account().isRegistered || SignalStore.account().aci == null) { + Log.i(TAG, "Registration not complete. Skipping.") + return + } + + val timeSinceRefresh = System.currentTimeMillis() - SignalStore.misc().lastProfileRefreshTime + if (timeSinceRefresh < TimeUnit.HOURS.toMillis(12)) { + Log.i(TAG, "Too soon to refresh. Did the last refresh $timeSinceRefresh ms ago.") + return + } + + SignalExecutors.BOUNDED.execute { + val current = System.currentTimeMillis() + val ids: List = SignalDatabase.recipients.getRecipientsForRoutineProfileFetch( + lastInteractionThreshold = current - TimeUnit.DAYS.toMillis(30), + lastProfileFetchThreshold = current - TimeUnit.DAYS.toMillis(1), + limit = 50 + ) + Recipient.self().id + + if (ids.isNotEmpty()) { + Log.i(TAG, "Optimistically refreshing ${ids.size} eligible recipient(s).") + enqueue(ids.toSet()) + } else { + Log.i(TAG, "No recipients to refresh.") + } + + SignalStore.misc().lastProfileRefreshTime = System.currentTimeMillis() + } + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/SendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/SendJob.java index 8214328b0d..659a71b8ab 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/SendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/SendJob.java @@ -66,7 +66,7 @@ protected static void markAttachmentsUploaded(long messageId, @NonNull OutgoingM protected String buildAttachmentString(@NonNull List attachments) { List strings = attachments.stream().map(attachment -> { if (attachment instanceof DatabaseAttachment) { - return ((DatabaseAttachment) attachment).getAttachmentId().toString(); + return ((DatabaseAttachment) attachment).attachmentId.toString(); } else if (attachment.getUri() != null) { return attachment.getUri().toString(); } else { diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageAccountRestoreJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageAccountRestoreJob.java index abcdca8549..5f9c553843 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageAccountRestoreJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageAccountRestoreJob.java @@ -4,13 +4,17 @@ import androidx.annotation.Nullable; import org.signal.core.util.logging.Log; +import org.signal.libsignal.usernames.BaseUsernameException; +import org.signal.libsignal.usernames.Username; import org.thoughtcrime.securesms.database.SignalDatabase; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobmanager.JobManager; import org.thoughtcrime.securesms.jobmanager.JobTracker; import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; +import org.thoughtcrime.securesms.keyvalue.AccountValues; import org.thoughtcrime.securesms.keyvalue.SignalStore; +import org.thoughtcrime.securesms.profiles.manage.UsernameRepository; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.storage.StorageSyncHelper; import org.whispersystems.signalservice.api.SignalServiceAccountManager; @@ -111,6 +115,16 @@ protected void onRun() throws Exception { SignalDatabase.getRawDatabase().endTransaction(); } + // We will try to reclaim the username here, as early as possible, but the registration flow also enqueues a username restore job, + // so failing here isn't a huge deal + if (SignalStore.account().getUsername() != null) { + Log.i(TAG, "Attempting to reclaim username..."); + UsernameRepository.UsernameReclaimResult result = UsernameRepository.reclaimUsernameIfNecessary(); + Log.i(TAG, "Username reclaim result: " + result.name()); + } else { + Log.i(TAG, "No username to reclaim."); + } + JobManager jobManager = ApplicationDependencies.getJobManager(); if (accountRecord.getAvatarUrlPath().isPresent()) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageSyncJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageSyncJob.java index 1f72b8a56a..4808dc932b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageSyncJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageSyncJob.java @@ -331,9 +331,9 @@ private boolean performSync() throws IOException, RetryLaterException, InvalidKe Log.i(TAG, "Removed " + removedUnregistered + " recipients from storage service that have been unregistered for longer than 30 days."); } - List localStorageIds = getAllLocalStorageIds(self).stream().filter(it -> !it.isUnknown()).collect(Collectors.toList()); + List localStorageIds = getAllLocalStorageIds(self); IdDifferenceResult idDifference = StorageSyncHelper.findIdDifference(remoteManifest.getStorageIds(), localStorageIds); - List remoteInserts = buildLocalStorageRecords(context, self, idDifference.getLocalOnlyIds()); + List remoteInserts = buildLocalStorageRecords(context, self, idDifference.getLocalOnlyIds().stream().filter(it -> !it.isUnknown()).collect(Collectors.toList())); List remoteDeletes = Stream.of(idDifference.getRemoteOnlyIds()).map(StorageId::getRaw).toList(); Log.i(TAG, "ID Difference :: " + idDifference); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/Svr2MirrorJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/Svr2MirrorJob.kt index 7ce1ba6f13..86a34c5bd6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/Svr2MirrorJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/Svr2MirrorJob.kt @@ -82,7 +82,7 @@ class Svr2MirrorJob private constructor(parameters: Parameters, private var seri return when (val response: BackupResponse = session.execute()) { is BackupResponse.Success -> { - Log.i(TAG, "Successfully migrated to SVR2!") + Log.i(TAG, "Successfully migrated to SVR2! $svr2") SignalStore.svr().appendAuthTokenToList(response.authorization.asBasic()) ApplicationDependencies.getJobManager().add(RefreshAttributesJob()) Result.success() diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/AccountValues.kt b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/AccountValues.kt index a7c8738e52..070819cb69 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/AccountValues.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/AccountValues.kt @@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.keyvalue import androidx.annotation.VisibleForTesting import org.signal.core.util.Base64 -import org.signal.core.util.LongSerializer import org.signal.core.util.logging.Log import org.signal.libsignal.protocol.IdentityKey import org.signal.libsignal.protocol.IdentityKeyPair @@ -77,6 +76,7 @@ internal class AccountValues internal constructor(store: KeyValueStore) : Signal private const val KEY_USERNAME_LINK_ENTROPY = "account.username_link_entropy" private const val KEY_USERNAME_LINK_SERVER_ID = "account.username_link_server_id" private const val KEY_USERNAME_SYNC_STATE = "phoneNumberPrivacy.usernameSyncState" + private const val KEY_USERNAME_SYNC_ERROR_COUNT = "phoneNumberPrivacy.usernameErrorCount" } init { @@ -252,6 +252,30 @@ internal class AccountValues internal constructor(store: KeyValueStore) : Signal } } + fun restorePniIdentityKeyFromBackup(publicKey: ByteArray, privateKey: ByteArray) { + synchronized(this) { + Log.i(TAG, "Setting a new PNI identity key pair.") + + store + .beginWrite() + .putBlob(KEY_PNI_IDENTITY_PUBLIC_KEY, publicKey) + .putBlob(KEY_PNI_IDENTITY_PRIVATE_KEY, privateKey) + .commit() + } + } + + fun restoreAciIdentityKeyFromBackup(publicKey: ByteArray, privateKey: ByteArray) { + synchronized(this) { + Log.i(TAG, "Setting a new ACI identity key pair.") + + store + .beginWrite() + .putBlob(KEY_ACI_IDENTITY_PUBLIC_KEY, publicKey) + .putBlob(KEY_ACI_IDENTITY_PRIVATE_KEY, privateKey) + .commit() + } + } + /** Only to be used when restoring an identity public key from an old backup */ fun restoreLegacyIdentityPublicKeyFromBackup(base64: String) { Log.w(TAG, "Restoring legacy identity public key from backup.") @@ -341,6 +365,18 @@ internal class AccountValues internal constructor(store: KeyValueStore) : Signal } } + /** + * Function for testing backup/restore + */ + @Deprecated("debug only") + fun clearRegistrationButKeepCredentials() { + putBoolean(KEY_IS_REGISTERED, false) + + ApplicationDependencies.getIncomingMessageObserver().notifyRegistrationChanged() + + Recipient.self().live().refresh() + } + val deviceName: String? get() = getString(KEY_DEVICE_NAME, null) @@ -394,17 +430,14 @@ internal class AccountValues internal constructor(store: KeyValueStore) : Signal * There are some cases where our username may fall out of sync with the service. In particular, we may get a new value for our username from * storage service but then find that it doesn't match what's on the service. */ - var usernameSyncState: UsernameSyncState by enumValue( - KEY_USERNAME_SYNC_STATE, - UsernameSyncState.IN_SYNC, - object : LongSerializer { - override fun serialize(data: UsernameSyncState): Long { - Log.i(TAG, "Marking username sync state as: $data") - return data.serialize() - } - override fun deserialize(data: Long): UsernameSyncState = UsernameSyncState.deserialize(data) + var usernameSyncState: UsernameSyncState + get() = UsernameSyncState.deserialize(getLong(KEY_USERNAME_SYNC_STATE, UsernameSyncState.IN_SYNC.serialize())) + set(value) { + Log.i(TAG, "Marking username sync state as: $value") + putLong(KEY_USERNAME_SYNC_STATE, value.serialize()) } - ) + + var usernameSyncErrorCount: Int by integerValue(KEY_USERNAME_SYNC_ERROR_COUNT, 0) private fun clearLocalCredentials() { putString(KEY_SERVICE_PASSWORD, Util.getSecret(18)) @@ -498,7 +531,7 @@ internal class AccountValues internal constructor(store: KeyValueStore) : Signal companion object { fun deserialize(value: Long): UsernameSyncState { - return values().firstOrNull { it.value == value } ?: throw IllegalArgumentException("Invalud value: $value") + return values().firstOrNull { it.value == value } ?: throw IllegalArgumentException("Invalid value: $value") } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/BackupValues.kt b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/BackupValues.kt new file mode 100644 index 0000000000..bf6d8f058c --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/BackupValues.kt @@ -0,0 +1,76 @@ +package org.thoughtcrime.securesms.keyvalue + +import com.fasterxml.jackson.annotation.JsonProperty +import org.signal.core.util.logging.Log +import org.whispersystems.signalservice.api.archive.ArchiveServiceCredential +import org.whispersystems.signalservice.internal.util.JsonUtil +import java.io.IOException +import kotlin.time.Duration +import kotlin.time.Duration.Companion.days + +internal class BackupValues(store: KeyValueStore) : SignalStoreValues(store) { + companion object { + val TAG = Log.tag(BackupValues::class.java) + val KEY_CREDENTIALS = "backup.credentials" + } + + override fun onFirstEverAppLaunch() = Unit + override fun getKeysToIncludeInBackup(): List = emptyList() + + /** + * Retrieves the stored credentials, mapped by the day they're valid. The day is represented as + * the unix time (in seconds) of the start of the day. Wrapped in a [ArchiveServiceCredentials] + * type to make it easier to use. See [ArchiveServiceCredentials.getForCurrentTime]. + */ + val credentialsByDay: ArchiveServiceCredentials + get() { + val serialized = store.getString(KEY_CREDENTIALS, null) ?: return ArchiveServiceCredentials() + + return try { + val map = JsonUtil.fromJson(serialized, SerializedCredentials::class.java).credentialsByDay + ArchiveServiceCredentials(map) + } catch (e: IOException) { + Log.w(TAG, "Invalid JSON! Clearing.", e) + putString(KEY_CREDENTIALS, null) + ArchiveServiceCredentials() + } + } + + /** + * Adds the given credentials to the existing list of stored credentials. + */ + fun addCredentials(credentials: List) { + val current: MutableMap = credentialsByDay.toMutableMap() + current.putAll(credentials.associateBy { it.redemptionTime }) + putString(KEY_CREDENTIALS, JsonUtil.toJson(SerializedCredentials(current))) + } + + /** + * Trims out any credentials that are for days older than the given timestamp. + */ + fun clearCredentialsOlderThan(startOfDayInSeconds: Long) { + val current: MutableMap = credentialsByDay.toMutableMap() + val updated = current.filterKeys { it < startOfDayInSeconds } + putString(KEY_CREDENTIALS, JsonUtil.toJson(SerializedCredentials(updated))) + } + + class SerializedCredentials( + @JsonProperty("credentialsByDay") + val credentialsByDay: Map + ) + + /** + * A [Map] wrapper that makes it easier to get the credential for the current time. + */ + class ArchiveServiceCredentials(map: Map) : Map by map { + constructor() : this(mapOf()) + + /** + * Retrieves a credential that is valid for the current time, otherwise null. + */ + fun getForCurrentTime(currentTime: Duration): ArchiveServiceCredential? { + val startOfDayInSeconds: Long = currentTime.inWholeDays.days.inWholeSeconds + return this[startOfDayInSeconds] + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/MiscellaneousValues.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/MiscellaneousValues.java index 0a02334584..578934957a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/MiscellaneousValues.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/MiscellaneousValues.java @@ -38,6 +38,7 @@ public final class MiscellaneousValues extends SignalStoreValues { private static final String LAST_CONSISTENCY_CHECK_TIME = "misc.last_consistency_check_time"; private static final String SERVER_TIME_OFFSET = "misc.server_time_offset"; private static final String LAST_SERVER_TIME_OFFSET_UPDATE = "misc.last_server_time_offset_update"; + private static final String NEEDS_USERNAME_RESTORE = "misc.needs_username_restore"; MiscellaneousValues(@NonNull KeyValueStore store) { super(store); @@ -46,6 +47,7 @@ public final class MiscellaneousValues extends SignalStoreValues { @Override void onFirstEverAppLaunch() { putLong(MESSAGE_REQUEST_ENABLE_TIME, 0); + putBoolean(NEEDS_USERNAME_RESTORE, true); } @Override @@ -326,4 +328,15 @@ public long getLastKnownServerTimeOffset() { public long getLastKnownServerTimeOffsetUpdateTime() { return getLong(LAST_SERVER_TIME_OFFSET_UPDATE, 0); } + + /** + * Whether or not we should attempt to restore the user's username and link. + */ + public boolean needsUsernameRestore() { + return getBoolean(NEEDS_USERNAME_RESTORE, false); + } + + public void setNeedsUsernameRestore(boolean value) { + putBoolean(NEEDS_USERNAME_RESTORE, value); + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/ReleaseChannelValues.kt b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/ReleaseChannelValues.kt index ce86d2c621..f9a500b3ff 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/ReleaseChannelValues.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/ReleaseChannelValues.kt @@ -32,6 +32,10 @@ internal class ReleaseChannelValues(store: KeyValueStore) : SignalStoreValues(st putString(KEY_RELEASE_CHANNEL_RECIPIENT_ID, id.serialize()) } + fun clearReleaseChannelRecipientId() { + putString(KEY_RELEASE_CHANNEL_RECIPIENT_ID, "") + } + var nextScheduledCheck by longValue(KEY_NEXT_SCHEDULED_CHECK, 0) var previousManifestMd5 by blobValue(KEY_PREVIOUS_MANIFEST_MD5, ByteArray(0)) var highestVersionNoteReceived by integerValue(KEY_HIGHEST_VERSION_NOTE_RECEIVED, 0) diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SignalStore.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SignalStore.java index ab45613f12..596c5761d8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SignalStore.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SignalStore.java @@ -44,6 +44,7 @@ public final class SignalStore { private final ReleaseChannelValues releaseChannelValues; private final StoryValues storyValues; private final ApkUpdateValues apkUpdate; + private final BackupValues backupValues; private final PlainTextSharedPrefsDataStore plainTextValues; @@ -89,6 +90,7 @@ private SignalStore(@NonNull KeyValueStore store) { this.releaseChannelValues = new ReleaseChannelValues(store); this.storyValues = new StoryValues(store); this.apkUpdate = new ApkUpdateValues(store); + this.backupValues = new BackupValues(store); this.plainTextValues = new PlainTextSharedPrefsDataStore(ApplicationDependencies.getApplication()); } @@ -270,6 +272,10 @@ public static void onPostBackupRestore() { return getInstance().apkUpdate; } + public static @NonNull BackupValues backup() { + return getInstance().backupValues; + } + public static @NonNull GroupsV2AuthorizationSignalStoreCache groupsV2AciAuthorizationCache() { return GroupsV2AuthorizationSignalStoreCache.createAciCache(getStore()); } @@ -301,4 +307,9 @@ public static void blockUntilAllWritesFinished() { public static void inject(@NonNull KeyValueStore store) { instance = new SignalStore(store); } + + public static void clearAllDataForBackupRestore() { + releaseChannelValues().clearReleaseChannelRecipientId(); + account().clearRegistrationButKeepCredentials(); + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/TooltipValues.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/TooltipValues.java index fbc0e86b31..5a255bb378 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/TooltipValues.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/TooltipValues.java @@ -15,7 +15,7 @@ public class TooltipValues extends SignalStoreValues { private static final String MULTI_FORWARD_DIALOG = "tooltip.multi.forward.dialog"; private static final String BUBBLE_OPT_OUT = "tooltip.bubble.opt.out"; private static final String PROFILE_SETTINGS_QR_CODE = "tooltip.profile_settings_qr_code"; - + private static final String CALLING_SWITCH_CAMERA = "tooltip.calling.switch_camera"; TooltipValues(@NonNull KeyValueStore store) { super(store); @@ -82,4 +82,12 @@ public boolean showProfileSettingsQrCodeTooltop() { public void markProfileSettingsQrCodeTooltipSeen() { putBoolean(PROFILE_SETTINGS_QR_CODE, false); } + + public boolean showCallingSwitchCameraTooltip() { + return getBoolean(CALLING_SWITCH_CAMERA, true); + } + + public void markCallingSwitchCameraTooltipSeen() { + putBoolean(CALLING_SWITCH_CAMERA, false); + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreview.java b/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreview.java index 527752c9ec..67b5340d20 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreview.java +++ b/app/src/main/java/org/thoughtcrime/securesms/linkpreview/LinkPreview.java @@ -45,7 +45,7 @@ public LinkPreview(@NonNull String url, @NonNull String title, @NonNull String d this.description = description; this.date = date; this.thumbnail = Optional.of(thumbnail); - this.attachmentId = thumbnail.getAttachmentId(); + this.attachmentId = thumbnail.attachmentId; } public LinkPreview(@NonNull String url, @NonNull String title, @NonNull String description, long date, @NonNull Optional thumbnail) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/main/MainActivityListHostFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/main/MainActivityListHostFragment.kt index a9cbde2b11..1d7119122d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/main/MainActivityListHostFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/main/MainActivityListHostFragment.kt @@ -15,7 +15,6 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.navigation.NavController import androidx.navigation.NavDestination -import androidx.navigation.Navigator import androidx.navigation.findNavController import androidx.navigation.fragment.FragmentNavigatorExtras import androidx.recyclerview.widget.RecyclerView @@ -45,7 +44,6 @@ import org.thoughtcrime.securesms.util.AvatarUtil import org.thoughtcrime.securesms.util.BottomSheetUtil import org.thoughtcrime.securesms.util.Material3OnScrollHelper import org.thoughtcrime.securesms.util.TopToastPopup -import org.thoughtcrime.securesms.util.TopToastPopup.Companion.show import org.thoughtcrime.securesms.util.Util import org.thoughtcrime.securesms.util.runHideAnimation import org.thoughtcrime.securesms.util.runRevealAnimation @@ -115,19 +113,21 @@ class MainActivityListHostFragment : Fragment(R.layout.main_activity_list_host_f if (state.tab == ConversationListTab.CHATS) { return } else { - val cameraFab = requireView().findViewById(R.id.camera_fab) - val newConvoFab = requireView().findViewById(R.id.fab) - - ViewCompat.setTransitionName(cameraFab, "camera_fab") - ViewCompat.setTransitionName(newConvoFab, "new_convo_fab") + val cameraFab = requireView().findViewById(R.id.camera_fab) + val newConvoFab = requireView().findViewById(R.id.fab) + + val extras = when { + cameraFab != null && newConvoFab != null -> { + ViewCompat.setTransitionName(cameraFab, "camera_fab") + ViewCompat.setTransitionName(newConvoFab, "new_convo_fab") + + FragmentNavigatorExtras( + cameraFab to "camera_fab", + newConvoFab to "new_convo_fab" + ) + } - val extras: Navigator.Extras? = if (cameraFab == null || newConvoFab == null) { - null - } else { - FragmentNavigatorExtras( - cameraFab to "camera_fab", - newConvoFab to "new_convo_fab" - ) + else -> null } val destination = if (state.tab == ConversationListTab.STORIES) { @@ -309,19 +309,23 @@ class MainActivityListHostFragment : Fragment(R.layout.main_activity_list_host_f val activeProfile = NotificationProfiles.getActiveProfile(notificationProfiles) if (activeProfile != null) { if (activeProfile.id != SignalStore.notificationProfileValues().lastProfilePopup) { - requireView().postDelayed({ - SignalStore.notificationProfileValues().lastProfilePopup = activeProfile.id - SignalStore.notificationProfileValues().lastProfilePopupTime = System.currentTimeMillis() - if (previousTopToastPopup?.isShowing == true) { - previousTopToastPopup?.dismiss() - } - var view = requireView() as ViewGroup - val fragment = parentFragmentManager.findFragmentByTag(BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG) - if (fragment != null && fragment.isAdded && fragment.view != null) { - view = fragment.requireView() as ViewGroup - } + view?.postDelayed({ try { - previousTopToastPopup = show(view, R.drawable.ic_moon_16, getString(R.string.ConversationListFragment__s_on, activeProfile.name)) + var fragmentView = view as? ViewGroup ?: return@postDelayed + + SignalStore.notificationProfileValues().lastProfilePopup = activeProfile.id + SignalStore.notificationProfileValues().lastProfilePopupTime = System.currentTimeMillis() + + if (previousTopToastPopup?.isShowing == true) { + previousTopToastPopup?.dismiss() + } + + val fragment = parentFragmentManager.findFragmentByTag(BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG) + if (fragment != null && fragment.isAdded && fragment.view != null) { + fragmentView = fragment.requireView() as ViewGroup + } + + previousTopToastPopup = TopToastPopup.show(fragmentView, R.drawable.ic_moon_16, getString(R.string.ConversationListFragment__s_on, activeProfile.name)) } catch (e: Exception) { Log.w(TAG, "Unable to show toast popup", e) } @@ -331,6 +335,7 @@ class MainActivityListHostFragment : Fragment(R.layout.main_activity_list_host_f } else { notificationProfileStatus.visibility = View.GONE } + if (!SignalStore.notificationProfileValues().hasSeenTooltip && Util.hasItems(notificationProfiles)) { val target: View? = findOverflowMenuButton(_toolbar) if (target != null) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaActions.java b/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaActions.java index 5608d89273..b24d95dcb2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaActions.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaActions.java @@ -101,7 +101,7 @@ protected List doInBackground(Void... params) { attachments.add(new SaveAttachmentTask.Attachment(mediaRecord.getAttachment().getUri(), mediaRecord.getContentType(), mediaRecord.getDate(), - mediaRecord.getAttachment().getFileName())); + mediaRecord.getAttachment().fileName)); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaGalleryAllAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaGalleryAllAdapter.java index 9df8e2f3d5..d3bb27fde0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaGalleryAllAdapter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaGalleryAllAdapter.java @@ -203,7 +203,7 @@ public int getSectionItemCount(int section) { } public void toggleSelection(@NonNull MediaRecord mediaRecord) { - AttachmentId attachmentId = mediaRecord.getAttachment().getAttachmentId(); + AttachmentId attachmentId = mediaRecord.getAttachment().attachmentId; MediaTable.MediaRecord removed = selected.remove(attachmentId); if (removed == null) { selected.put(attachmentId, mediaRecord); @@ -219,7 +219,7 @@ public int getSelectedMediaCount() { public long getSelectedMediaTotalFileSize() { //noinspection ConstantConditions attacment cannot be null if selected return Stream.of(selected.values()) - .collect(Collectors.summingLong(a -> a.getAttachment().getSize())); + .collect(Collectors.summingLong(a -> a.getAttachment().size)); } @NonNull @@ -238,7 +238,7 @@ void selectAllMedia() { int sectionItemCount = media.getSectionItemCount(section); for (int item = 0; item < sectionItemCount; item++) { MediaRecord mediaRecord = media.get(section, item); - selected.put(mediaRecord.getAttachment().getAttachmentId(), mediaRecord); + selected.put(mediaRecord.getAttachment().attachmentId, mediaRecord); } } this.notifyItemRangeChanged(0, getItemCount(), PAYLOAD_SELECTED); @@ -282,7 +282,7 @@ void unbind() { } protected boolean isSelected() { - return selected.containsKey(mediaRecord.getAttachment().getAttachmentId()); + return selected.containsKey(mediaRecord.getAttachment().attachmentId); } protected void updateSelectedView() { @@ -539,11 +539,11 @@ public void bind(@NonNull Context context, @NonNull MediaTable.MediaRecord media throw new AssertionError(); } - isVoiceNote = slide.asAttachment().isVoiceNote(); + isVoiceNote = slide.asAttachment().voiceNote; super.bind(context, mediaRecord, slide); - long mmsId = Objects.requireNonNull(mediaRecord.getAttachment()).getMmsId(); + long mmsId = Objects.requireNonNull(mediaRecord.getAttachment()).mmsId; audioItemListener.unregisterPlaybackStateObserver(audioView.getPlaybackStateObserver()); audioView.setAudio((AudioSlide) slide, new AudioViewCallbacksAdapter(audioItemListener, mmsId), true, true); diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaOverviewPageFragment.java b/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaOverviewPageFragment.java index 92c1c6ee4c..9ef52f7d5b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaOverviewPageFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaOverviewPageFragment.java @@ -247,17 +247,17 @@ private void handleMediaPreviewClick(@NonNull View view, @NonNull MediaTable.Med mediaRecord.getDate(), Objects.requireNonNull(mediaRecord.getAttachment().getUri()), mediaRecord.getContentType(), - mediaRecord.getAttachment().getSize(), - mediaRecord.getAttachment().getCaption(), + mediaRecord.getAttachment().size, + mediaRecord.getAttachment().caption, true, true, threadId == MediaTable.ALL_THREADS, true, sorting, - attachment.isVideoGif(), + attachment.videoGif, new MediaIntentFactory.SharedElementArgs( - attachment.getWidth(), - attachment.getHeight(), + attachment.width, + attachment.height, DimensionUnit.DP.toDp(12), DimensionUnit.DP.toDp(12), DimensionUnit.DP.toDp(12), diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaIntentFactory.kt b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaIntentFactory.kt index 76c68f3ba5..d33c4552c6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaIntentFactory.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaIntentFactory.kt @@ -77,7 +77,7 @@ object MediaIntentFactory { leftIsRecent, allMediaInRail = allMediaInRail, sorting = MediaTable.Sorting.Newest, - isVideoGif = attachment.isVideoGif, + isVideoGif = attachment.videoGif, sharedElementArgs = SharedElementArgs( attachment.width, attachment.height, diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewRepository.kt index 496deaccf9..feb9741bdd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewRepository.kt @@ -47,9 +47,7 @@ class MediaPreviewRepository { val mediaRecords = mutableListOf() var startingRow = -1 while (cursor.moveToNext()) { - if (startingAttachmentId.rowId == cursor.requireLong(AttachmentTable.ROW_ID) && - startingAttachmentId.uniqueId == cursor.requireLong(AttachmentTable.UNIQUE_ID) - ) { + if (startingAttachmentId.id == cursor.requireLong(AttachmentTable.ID)) { startingRow = cursor.position break } diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2Adapter.kt b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2Adapter.kt index 5803925a4d..5ad24e4c72 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2Adapter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2Adapter.kt @@ -31,8 +31,8 @@ class MediaPreviewV2Adapter(fragment: Fragment) : FragmentStateAdapter(fragment) MediaPreviewFragment.DATA_URI to attachment.uri, MediaPreviewFragment.DATA_CONTENT_TYPE to contentType, MediaPreviewFragment.DATA_SIZE to attachment.size, - MediaPreviewFragment.AUTO_PLAY to attachment.isVideoGif, - MediaPreviewFragment.VIDEO_GIF to attachment.isVideoGif + MediaPreviewFragment.AUTO_PLAY to attachment.videoGif, + MediaPreviewFragment.VIDEO_GIF to attachment.videoGif ) val fragment = if (MediaUtil.isVideo(contentType)) { VideoMediaPreviewFragment() diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2Fragment.kt b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2Fragment.kt index 6379b5640e..80130dda44 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2Fragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2Fragment.kt @@ -331,7 +331,7 @@ class MediaPreviewV2Fragment : LoggingFragment(R.layout.fragment_media_preview_v } private fun bindMediaPreviewPlaybackControls(currentItem: MediaTable.MediaRecord, currentFragment: MediaPreviewFragment?) { - val mediaType: MediaPreviewPlayerControlView.MediaMode = if (currentItem.attachment?.isVideoGif == true) { + val mediaType: MediaPreviewPlayerControlView.MediaMode = if (currentItem.attachment?.videoGif == true) { MediaPreviewPlayerControlView.MediaMode.IMAGE } else { MediaPreviewPlayerControlView.MediaMode.fromString(currentItem.contentType) diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2ViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2ViewModel.kt index 3632ace6d3..e41f7a08a8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2ViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2ViewModel.kt @@ -127,8 +127,8 @@ fun MediaTable.MediaRecord.toMedia(): Media? { attachment.height, attachment.size, 0, - attachment.isBorderless, - attachment.isVideoGif, + attachment.borderless, + attachment.videoGif, Optional.empty(), Optional.ofNullable(attachment.caption), Optional.empty() diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaUploadRepository.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaUploadRepository.java index 7bb5a47fde..7dd440a3aa 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaUploadRepository.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaUploadRepository.java @@ -100,7 +100,7 @@ private boolean hasSameTransformProperties(@NonNull Media oldMedia, @NonNull Med return oldProperties == newProperties; } - return !newProperties.isVideoEdited() && oldProperties.getSentMediaQuality() == newProperties.getSentMediaQuality(); + return !newProperties.getVideoEdited() && oldProperties.sentMediaQuality == newProperties.sentMediaQuality; } public void cancelUpload(@NonNull Media media) { @@ -161,6 +161,7 @@ private void cancelUploadInternal(@NonNull Media media) { if (result != null) { Stream.of(result.getJobIds()).forEach(jobManager::cancel); uploadResults.remove(media); + SignalDatabase.attachments().deleteAttachment(result.getAttachmentId()); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/VideoTrimTransform.java b/app/src/main/java/org/thoughtcrime/securesms/mediasend/VideoTrimTransform.java index a2f44f62d2..4bb9cc1307 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/VideoTrimTransform.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/VideoTrimTransform.java @@ -33,6 +33,6 @@ public VideoTrimTransform(@NonNull VideoEditorFragment.Data data) { media.isVideoGif(), media.getBucketId(), media.getCaption(), - Optional.of(new AttachmentTable.TransformProperties(false, data.durationEdited, data.startTimeUs, data.endTimeUs, SentMediaQuality.STANDARD.getCode()))); + Optional.of(new AttachmentTable.TransformProperties(false, data.durationEdited, data.startTimeUs, data.endTimeUs, SentMediaQuality.STANDARD.getCode(), false))); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaSelectionRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaSelectionRepository.kt index 4fde5ae4d0..fa2cd294c0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaSelectionRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaSelectionRepository.kt @@ -109,7 +109,7 @@ class MediaSelectionRepository(context: Context) { val updatedMedia = oldToNewMediaMap.values.toList() for (media in updatedMedia) { - Log.w(TAG, media.uri.toString() + " : " + media.transformProperties.map { t: TransformProperties -> "" + t.isVideoTrim }.orElse("null")) + Log.w(TAG, media.uri.toString() + " : " + media.transformProperties.map { t: TransformProperties -> "" + t.videoTrim }.orElse("null")) } val singleRecipient: Recipient? = singleContact?.let { Recipient.resolved(it.recipientId) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaSelectionViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaSelectionViewModel.kt index ff9444d22a..75c7918234 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaSelectionViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/MediaSelectionViewModel.kt @@ -12,6 +12,7 @@ import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.core.Flowable import io.reactivex.rxjava3.core.Maybe import io.reactivex.rxjava3.core.Observable +import io.reactivex.rxjava3.core.Single import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.Disposable import io.reactivex.rxjava3.kotlin.plusAssign @@ -122,13 +123,17 @@ class MediaSelectionViewModel( addMedia(initialMedia) } - disposables += selectedMediaSubject.map { media -> - Stories.MediaTransform.getSendRequirements(media) - }.subscribeBy { requirements -> - store.update { - it.copy(storySendRequirements = requirements) + disposables += selectedMediaSubject + .flatMapSingle { media -> + Single.fromCallable { + Stories.MediaTransform.getSendRequirements(media) + }.subscribeOn(Schedulers.io()) + } + .subscribeBy { requirements -> + store.update { + it.copy(storySendRequirements = requirements) + } } - } } override fun onCleared() { @@ -493,12 +498,14 @@ class MediaSelectionViewModel( putBoolean(BUNDLE_IS_IMAGE, true) } } + is VideoEditorFragment.Data -> { value.bundle.apply { putParcelable(BUNDLE_URI, key) putBoolean(BUNDLE_IS_IMAGE, false) } } + else -> { throw IllegalStateException() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/megaphone/RemoteMegaphoneRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/megaphone/RemoteMegaphoneRepository.kt index bf8a7e8407..6319db024e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/megaphone/RemoteMegaphoneRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/megaphone/RemoteMegaphoneRepository.kt @@ -54,7 +54,7 @@ object RemoteMegaphoneRepository { private val actions = mapOf( ActionId.SNOOZE.id to snooze, ActionId.FINISH.id to finish, - ActionId.DONATE.id to donate + ActionId.DONATE.id to donate, ) @WorkerThread diff --git a/app/src/main/java/org/thoughtcrime/securesms/messagedetails/MessageDetailsRepository.java b/app/src/main/java/org/thoughtcrime/securesms/messagedetails/MessageDetailsRepository.java index f66e3a7b9f..076a431494 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messagedetails/MessageDetailsRepository.java +++ b/app/src/main/java/org/thoughtcrime/securesms/messagedetails/MessageDetailsRepository.java @@ -138,7 +138,7 @@ public final class MessageDetailsRepository { private @Nullable NetworkFailure getNetworkFailure(MessageRecord messageRecord, Recipient recipient) { if (messageRecord.hasNetworkFailures()) { for (final NetworkFailure failure : messageRecord.getNetworkFailures()) { - if (failure.getRecipientId(context).equals(recipient.getId())) { + if (failure.getRecipientId().equals(recipient.getId())) { return failure; } } @@ -149,7 +149,7 @@ public final class MessageDetailsRepository { private @Nullable IdentityKeyMismatch getKeyMismatchFailure(MessageRecord messageRecord, Recipient recipient) { if (messageRecord.isIdentityMismatchFailure()) { for (final IdentityKeyMismatch mismatch : messageRecord.getIdentityKeyMismatches()) { - if (mismatch.getRecipientId(context).equals(recipient.getId())) { + if (mismatch.getRecipientId().equals(recipient.getId())) { return mismatch; } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/CallMessageProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/messages/CallMessageProcessor.kt index 5b58c50c98..991fb4d208 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/CallMessageProcessor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/CallMessageProcessor.kt @@ -40,9 +40,7 @@ object CallMessageProcessor { callMessage.offer != null -> handleCallOfferMessage(envelope, metadata, callMessage.offer!!, senderRecipient.id, serverDeliveredTimestamp) callMessage.answer != null -> handleCallAnswerMessage(envelope, metadata, callMessage.answer!!, senderRecipient.id) callMessage.iceUpdate.isNotEmpty() -> handleCallIceUpdateMessage(envelope, metadata, callMessage.iceUpdate, senderRecipient.id) - callMessage.hangup != null || callMessage.legacyHangup != null -> { - handleCallHangupMessage(envelope, metadata, callMessage.hangup ?: callMessage.legacyHangup, senderRecipient.id) - } + callMessage.hangup != null -> handleCallHangupMessage(envelope, metadata, callMessage.hangup!!, senderRecipient.id) callMessage.busy != null -> handleCallBusyMessage(envelope, metadata, callMessage.busy!!, senderRecipient.id) callMessage.opaque != null -> handleCallOpaqueMessage(envelope, metadata, callMessage.opaque!!, senderRecipient.requireAci(), serverDeliveredTimestamp) } @@ -51,10 +49,10 @@ object CallMessageProcessor { private fun handleCallOfferMessage(envelope: Envelope, metadata: EnvelopeMetadata, offer: Offer, senderRecipientId: RecipientId, serverDeliveredTimestamp: Long) { log(envelope.timestamp!!, "handleCallOfferMessage...") - val offerId = if (offer.id != null && offer.type != null && ((offer.opaque != null) xor (offer.sdp != null))) { + val offerId = if (offer.id != null && offer.type != null && offer.opaque != null) { offer.id!! } else { - warn(envelope.timestamp!!, "Invalid offer, missing id/type, or invalid combination of opaque/sdp") + warn(envelope.timestamp!!, "Invalid offer, missing id, type, or opaque") return } @@ -64,7 +62,7 @@ object CallMessageProcessor { ApplicationDependencies.getSignalCallManager() .receivedOffer( CallMetadata(remotePeer, metadata.sourceDeviceId), - OfferMetadata(offer.opaque?.toByteArray(), offer.sdp, OfferMessage.Type.fromProto(offer.type!!)), + OfferMetadata(offer.opaque?.toByteArray(), OfferMessage.Type.fromProto(offer.type!!)), ReceivedOfferMetadata( remoteIdentityKey, envelope.serverTimestamp!!, @@ -81,10 +79,10 @@ object CallMessageProcessor { ) { log(envelope.timestamp!!, "handleCallAnswerMessage...") - val answerId = if (answer.id != null && ((answer.opaque != null) xor (answer.sdp != null))) { + val answerId = if (answer.id != null && answer.opaque != null) { answer.id!! } else { - warn(envelope.timestamp!!, "Invalid answer, missing id") + warn(envelope.timestamp!!, "Invalid answer, missing id or opaque") return } @@ -94,7 +92,7 @@ object CallMessageProcessor { ApplicationDependencies.getSignalCallManager() .receivedAnswer( CallMetadata(remotePeer, metadata.sourceDeviceId), - AnswerMetadata(answer.opaque?.toByteArray(), answer.sdp), + AnswerMetadata(answer.opaque?.toByteArray()), ReceivedAnswerMetadata(remoteIdentityKey) ) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/DataMessageProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/messages/DataMessageProcessor.kt index f506ab4f8b..6db419e186 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/DataMessageProcessor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/DataMessageProcessor.kt @@ -223,7 +223,7 @@ object DataMessageProcessor { if (SignalDatabase.recipients.setProfileKey(senderRecipient.id, messageProfileKey)) { log(timestamp, "Profile key on message from " + senderRecipient.id + " didn't match our local store. It has been updated.") SignalDatabase.runPostSuccessfulTransaction { - ApplicationDependencies.getJobManager().add(RetrieveProfileJob.forRecipient(senderRecipient.id)) + RetrieveProfileJob.enqueue(senderRecipient.id) } } } else { @@ -522,7 +522,7 @@ object DataMessageProcessor { } else { val reactionRecord = ReactionRecord(emoji!!, senderRecipientId, message.timestamp!!, System.currentTimeMillis()) SignalDatabase.reactions.addReaction(targetMessageId, reactionRecord) - ApplicationDependencies.getMessageNotifier().updateNotification(context, ConversationId.fromMessageRecord(targetMessage), false) + ApplicationDependencies.getMessageNotifier().updateNotification(context, ConversationId.fromMessageRecord(targetMessage)) } return targetMessageId @@ -542,7 +542,7 @@ object DataMessageProcessor { SignalDatabase.messages.deleteRemotelyDeletedStory(targetMessage.id) } - ApplicationDependencies.getMessageNotifier().updateNotification(context, ConversationId.fromMessageRecord(targetMessage), false) + ApplicationDependencies.getMessageNotifier().updateNotification(context, ConversationId.fromMessageRecord(targetMessage)) MessageId(targetMessage.id) } else if (targetMessage == null) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/IncomingMessageObserver.kt b/app/src/main/java/org/thoughtcrime/securesms/messages/IncomingMessageObserver.kt index 6d39db83be..70f70c2967 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/IncomingMessageObserver.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/IncomingMessageObserver.kt @@ -156,6 +156,13 @@ class IncomingMessageObserver(private val context: Application) { } } + private fun shouldKeepAliveUnidentified(): Boolean { + val timeIdle = lock.withLock { + if (appVisible) 0 else System.currentTimeMillis() - lastInteractionTime + } + return timeIdle <= websocketReadTimeout + } + private fun isConnectionNecessary(): Boolean { if (KeyCachingService.isLocked()) { Log.i(TAG, "Don't connect anymore. App is locked.") @@ -387,7 +394,7 @@ class IncomingMessageObserver(private val context: Application) { decryptionDrained = false } - signalWebSocket.connect() + signalWebSocket.connect(shouldKeepAliveUnidentified()) try { while (isConnectionNecessary()) { try { @@ -438,11 +445,12 @@ class IncomingMessageObserver(private val context: Application) { } } catch (e: WebSocketUnavailableException) { Log.i(TAG, "Pipe unexpectedly unavailable, connecting") - signalWebSocket.connect() + signalWebSocket.connect(shouldKeepAliveUnidentified()) } catch (e: TimeoutException) { Log.w(TAG, "Application level read timeout...") attempts = 0 } + signalWebSocket.setKeepAliveUnidentified(shouldKeepAliveUnidentified()) } if (!appVisible) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/StoryMessageProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/messages/StoryMessageProcessor.kt index 2d85a3cc2f..a7d14b94c7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/StoryMessageProcessor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/StoryMessageProcessor.kt @@ -140,6 +140,10 @@ object StoryMessageProcessor { warn("Incoming text story has color / position mismatch. Defaulting to start and end colors.") linearGradientBuilder.colors(listOf(gradient.colors[0], gradient.colors[gradient.colors.size - 1])) linearGradientBuilder.positions(listOf(0f, 1f)) + } else if (gradient.startColor != null && gradient.endColor != null) { + warn("Incoming text story is using deprecated fields for the gradient. Building a two color gradient with them.") + linearGradientBuilder.colors(listOf(gradient.startColor!!, gradient.endColor!!)) + linearGradientBuilder.positions(listOf(0f, 1f)) } else { warn("Incoming text story did not have a valid linear gradient.") linearGradientBuilder.colors(listOf(Color.BLACK, Color.BLACK)) diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/SyncMessageProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/messages/SyncMessageProcessor.kt index 669b518aaf..5e1d9c73a2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/SyncMessageProcessor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/SyncMessageProcessor.kt @@ -1328,24 +1328,39 @@ object SyncMessageProcessor { when (event) { CallTable.Event.DELETE -> SignalDatabase.calls.deleteGroupCall(call) CallTable.Event.ACCEPTED -> { - if (call.timestamp < timestamp) { + if (call.timestamp > timestamp) { SignalDatabase.calls.setTimestamp(call.callId, recipient.id, timestamp) } - if (callEvent.direction == SyncMessage.CallEvent.Direction.INCOMING) { + if (direction == CallTable.Direction.INCOMING) { SignalDatabase.calls.acceptIncomingGroupCall(call) } else { - warn(envelopeTimestamp, "Invalid direction OUTGOING for event ACCEPTED") + SignalDatabase.calls.acceptOutgoingGroupCall(call) + } + } + CallTable.Event.NOT_ACCEPTED -> { + if (call.timestamp > timestamp) { + SignalDatabase.calls.setTimestamp(call.callId, recipient.id, timestamp) + } + if (callEvent.direction == SyncMessage.CallEvent.Direction.INCOMING) { + SignalDatabase.calls.declineIncomingGroupCall(call) + } else { + warn(envelopeTimestamp, "Invalid direction OUTGOING for event NOT_ACCEPTED") } } - CallTable.Event.NOT_ACCEPTED -> warn("Unsupported event type $event. Ignoring. timestamp: $timestamp type: $type direction: $direction event: $event hasPeer: $hasConversationId") else -> warn("Unsupported event type $event. Ignoring. timestamp: $timestamp type: $type direction: $direction event: $event hasPeer: $hasConversationId") } } else { when (event) { CallTable.Event.DELETE -> SignalDatabase.calls.insertDeletedGroupCallFromSyncEvent(callEvent.id!!, recipient.id, direction, timestamp) CallTable.Event.ACCEPTED -> SignalDatabase.calls.insertAcceptedGroupCall(callEvent.id!!, recipient.id, direction, timestamp) - CallTable.Event.NOT_ACCEPTED -> warn("Unsupported event type $event. Ignoring. timestamp: $timestamp type: $type direction: $direction event: $event hasPeer: $hasConversationId") - else -> warn("Unsupported event type $event. Ignoring. timestamp: $timestamp type: $type direction: $direction event: $event hasPeer: $hasConversationId") + CallTable.Event.NOT_ACCEPTED -> { + if (callEvent.direction == SyncMessage.CallEvent.Direction.INCOMING) { + SignalDatabase.calls.insertDeclinedGroupCall(callEvent.id!!, recipient.id, timestamp) + } else { + warn(envelopeTimestamp, "Invalid direction OUTGOING for event NOT_ACCEPTED for non-existing call") + } + } + else -> warn("Unsupported event type $event. Ignoring. timestamp: $timestamp type: $type direction: $direction event: $event hasPeer: $hasConversationId call: null") } } } @@ -1381,7 +1396,6 @@ object SyncMessageProcessor { ApplicationDependencies.getSignalServiceAccountManager().setPreKeys( PreKeyUpload( serviceIdType = ServiceIdType.PNI, - identityKey = pniProtocolStore.identityKeyPair.publicKey, signedPreKey = signedPreKey, oneTimeEcPreKeys = oneTimePreKeys, lastResortKyberPreKey = null, diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/protocol/BufferedSessionStore.kt b/app/src/main/java/org/thoughtcrime/securesms/messages/protocol/BufferedSessionStore.kt index 9a13ceaa96..377c63f973 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/protocol/BufferedSessionStore.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/protocol/BufferedSessionStore.kt @@ -100,7 +100,7 @@ class BufferedSessionStore(private val selfServiceId: ServiceId) : SignalService error("Should not happen during the intended usage pattern of this class") } - override fun getAllAddressesWithActiveSessions(addressNames: MutableList): Set { + override fun getAllAddressesWithActiveSessions(addressNames: MutableList): Map { error("Should not happen during the intended usage pattern of this class") } diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/protocol/BufferedSignalServiceAccountDataStore.kt b/app/src/main/java/org/thoughtcrime/securesms/messages/protocol/BufferedSignalServiceAccountDataStore.kt index b00bb91527..5da7f56e9a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/protocol/BufferedSignalServiceAccountDataStore.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/protocol/BufferedSignalServiceAccountDataStore.kt @@ -177,7 +177,7 @@ class BufferedSignalServiceAccountDataStore(selfServiceId: ServiceId) : SignalSe sessionStore.archiveSession(address) } - override fun getAllAddressesWithActiveSessions(addressNames: MutableList): Set { + override fun getAllAddressesWithActiveSessions(addressNames: MutableList): Map { return sessionStore.getAllAddressesWithActiveSessions(addressNames) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java b/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java index c4f498118e..17a22719ed 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java +++ b/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java @@ -130,9 +130,11 @@ static final class Version { static final int THREAD_COUNT_DB_MIGRATION = 97; static final int SYNC_KEYS_MIGRATION = 98; static final int SELF_REGISTERTED_STATE = 99; + static final int SVR2_ENCLAVE_UPDATE = 100; + static final int STORAGE_LOCAL_UNKNOWNS_FIX = 101; } - public static final int CURRENT_VERSION = 99; + public static final int CURRENT_VERSION = 101; /** * This *must* be called after the {@link JobManager} has been instantiated, but *before* the call @@ -583,6 +585,14 @@ private static LinkedHashMap getMigrationJobs(@NonNull Co jobs.put(Version.SELF_REGISTERTED_STATE, new SelfRegisteredStateMigrationJob()); } + if (lastSeenVersion < Version.SVR2_ENCLAVE_UPDATE) { + jobs.put(Version.SVR2_ENCLAVE_UPDATE, new Svr2MirrorMigrationJob()); + } + + if (lastSeenVersion < Version.STORAGE_LOCAL_UNKNOWNS_FIX) { + jobs.put(Version.STORAGE_LOCAL_UNKNOWNS_FIX, new StorageFixLocalUnknownMigrationJob()); + } + return jobs; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/migrations/PniAccountInitializationMigrationJob.java b/app/src/main/java/org/thoughtcrime/securesms/migrations/PniAccountInitializationMigrationJob.java index 309047050c..9d5ca9fd22 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/migrations/PniAccountInitializationMigrationJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/migrations/PniAccountInitializationMigrationJob.java @@ -79,7 +79,7 @@ public void performMigration() throws IOException { SignedPreKeyRecord signedPreKey = PreKeyUtil.generateAndStoreSignedPreKey(protocolStore, metadataStore); List oneTimePreKeys = PreKeyUtil.generateAndStoreOneTimeEcPreKeys(protocolStore, metadataStore); - accountManager.setPreKeys(new PreKeyUpload(ServiceIdType.PNI, protocolStore.getIdentityKeyPair().getPublicKey(), signedPreKey, oneTimePreKeys, null, null)); + accountManager.setPreKeys(new PreKeyUpload(ServiceIdType.PNI, signedPreKey, oneTimePreKeys, null, null)); metadataStore.setActiveSignedPreKeyId(signedPreKey.getId()); metadataStore.setSignedPreKeyRegistered(true); } else { diff --git a/app/src/main/java/org/thoughtcrime/securesms/migrations/StorageFixLocalUnknownMigrationJob.kt b/app/src/main/java/org/thoughtcrime/securesms/migrations/StorageFixLocalUnknownMigrationJob.kt new file mode 100644 index 0000000000..488395884a --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/migrations/StorageFixLocalUnknownMigrationJob.kt @@ -0,0 +1,65 @@ +package org.thoughtcrime.securesms.migrations + +import org.signal.core.util.logging.Log +import org.signal.core.util.withinTransaction +import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.dependencies.ApplicationDependencies +import org.thoughtcrime.securesms.jobmanager.Job +import org.thoughtcrime.securesms.jobs.MultiDeviceKeysUpdateJob +import org.thoughtcrime.securesms.jobs.StorageSyncJob +import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.util.TextSecurePreferences + +/** + * Remove local unknown storage ids not in local storage service manifest. + */ +internal class StorageFixLocalUnknownMigrationJob( + parameters: Parameters = Parameters.Builder().build() +) : MigrationJob(parameters) { + + companion object { + val TAG = Log.tag(StorageFixLocalUnknownMigrationJob::class.java) + const val KEY = "StorageFixLocalUnknownMigrationJob" + } + + override fun getFactoryKey(): String = KEY + + override fun isUiBlocking(): Boolean = false + + @Suppress("UsePropertyAccessSyntax") + override fun performMigration() { + val localStorageIds = SignalStore.storageService().getManifest().storageIds.toSet() + val unknownLocalIds = SignalDatabase.unknownStorageIds.getAllUnknownIds().toSet() + val danglingLocalUnknownIds = unknownLocalIds - localStorageIds + + if (danglingLocalUnknownIds.isEmpty()) { + return + } + + Log.w(TAG, "Removing ${danglingLocalUnknownIds.size} dangling unknown ids") + + SignalDatabase.rawDatabase.withinTransaction { + SignalDatabase.unknownStorageIds.delete(danglingLocalUnknownIds) + } + + val jobManager = ApplicationDependencies.getJobManager() + + if (TextSecurePreferences.isMultiDevice(context)) { + Log.i(TAG, "Multi-device.") + jobManager.startChain(StorageSyncJob()) + .then(MultiDeviceKeysUpdateJob()) + .enqueue() + } else { + Log.i(TAG, "Single-device.") + jobManager.add(StorageSyncJob()) + } + } + + override fun shouldRetry(e: Exception): Boolean = false + + class Factory : Job.Factory { + override fun create(parameters: Parameters, serializedData: ByteArray?): StorageFixLocalUnknownMigrationJob { + return StorageFixLocalUnknownMigrationJob(parameters) + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/AttachmentManager.java b/app/src/main/java/org/thoughtcrime/securesms/mms/AttachmentManager.java index 3ae596337b..e7727f719c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/AttachmentManager.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/AttachmentManager.java @@ -35,6 +35,7 @@ import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.WorkerThread; import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; @@ -266,6 +267,8 @@ public ListenableFuture setMedia(@NonNull final GlideRequests glideRequ final SettableFuture result = new SettableFuture<>(); new AsyncTask() { + private boolean areConstraintsSatisfied = false; + @Override protected void onPreExecute() { thumbnail.clear(glideRequests); @@ -275,19 +278,21 @@ protected void onPreExecute() { @Override protected @Nullable Slide doInBackground(Void... params) { + Slide slide; try { if (PartAuthority.isLocalUri(uri)) { - return getManuallyCalculatedSlideInfo(uri, width, height); + slide = getManuallyCalculatedSlideInfo(uri, width, height); } else { Slide result = getContentResolverSlideInfo(uri, width, height); - - if (result == null) return getManuallyCalculatedSlideInfo(uri, width, height); - else return result; + slide = (result == null) ? getManuallyCalculatedSlideInfo(uri, width, height) : result; } } catch (IOException e) { Log.w(TAG, e); return null; } + + this.areConstraintsSatisfied = areConstraintsSatisfied(context, slide, constraints); + return slide; } @Override @@ -298,7 +303,7 @@ protected void onPostExecute(@Nullable final Slide slide) { R.string.ConversationActivity_sorry_there_was_an_error_setting_your_attachment, Toast.LENGTH_SHORT).show(); result.set(false); - } else if (!areConstraintsSatisfied(context, slide, constraints)) { + } else if (!areConstraintsSatisfied) { attachmentViewStub.get().setVisibility(View.GONE); Toast.makeText(context, R.string.ConversationActivity_attachment_exceeds_size_limits, @@ -318,7 +323,7 @@ protected void onPostExecute(@Nullable final Slide slide) { result.set(true); } else { Attachment attachment = slide.asAttachment(); - result.deferTo(thumbnail.setImageResource(glideRequests, slide, false, true, attachment.getWidth(), attachment.getHeight())); + result.deferTo(thumbnail.setImageResource(glideRequests, slide, false, true, attachment.width, attachment.height)); removableMediaView.display(thumbnail, mediaType == SlideFactory.MediaType.IMAGE); } @@ -524,6 +529,7 @@ private static void selectMediaType(Fragment fragment, @NonNull String type, @Nu } } + @WorkerThread private boolean areConstraintsSatisfied(final @NonNull Context context, final @Nullable Slide slide, final @NonNull MediaConstraints constraints) @@ -540,7 +546,7 @@ private void previewImageDraft(final @NonNull Slide slide) { MediaIntentFactory.UNKNOWN_TIMESTAMP, slide.getUri(), slide.getContentType(), - slide.asAttachment().getSize(), + slide.asAttachment().size, slide.getCaption().orElse(null), false, false, diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/GifSlide.java b/app/src/main/java/org/thoughtcrime/securesms/mms/GifSlide.java index c328d7e632..f195b37761 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/GifSlide.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/GifSlide.java @@ -14,7 +14,7 @@ public class GifSlide extends ImageSlide { public GifSlide(Attachment attachment) { super(attachment); - this.borderless = attachment.isBorderless(); + this.borderless = attachment.borderless; } public GifSlide(Context context, Uri uri, long size, int width, int height) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/ImageSlide.java b/app/src/main/java/org/thoughtcrime/securesms/mms/ImageSlide.java index f4272f4b20..0d690d4cad 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/ImageSlide.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/ImageSlide.java @@ -40,7 +40,7 @@ public class ImageSlide extends Slide { public ImageSlide(@NonNull Attachment attachment) { super(attachment); - this.borderless = attachment.isBorderless(); + this.borderless = attachment.borderless; } public ImageSlide(Context context, Uri uri, long size, int width, int height, @Nullable BlurHash blurHash) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/MediaConstraints.java b/app/src/main/java/org/thoughtcrime/securesms/mms/MediaConstraints.java index 07d1841c1f..121ca9ff99 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/MediaConstraints.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/MediaConstraints.java @@ -75,7 +75,7 @@ public long getMaxAttachmentSize() { public boolean isSatisfied(@NonNull Context context, @NonNull Attachment attachment) { try { - long size = attachment.getSize(); + long size = attachment.size; if (size > getMaxAttachmentSize()) { return false; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/MediaStream.java b/app/src/main/java/org/thoughtcrime/securesms/mms/MediaStream.java index 00db66b738..35c8efe04b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/MediaStream.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/MediaStream.java @@ -25,12 +25,22 @@ public class MediaStream implements Closeable { private final String mimeType; private final int width; private final int height; + private final boolean faststart; public MediaStream(InputStream stream, String mimeType, int width, int height) { - this.stream = stream; - this.mimeType = mimeType; - this.width = width; - this.height = height; + this.stream = stream; + this.mimeType = mimeType; + this.width = width; + this.height = height; + this.faststart = false; + } + + public MediaStream(InputStream stream, String mimeType, int width, int height, boolean faststart) { + this.stream = stream; + this.mimeType = mimeType; + this.width = width; + this.height = height; + this.faststart = faststart; } public InputStream getStream() { @@ -49,6 +59,10 @@ public int getHeight() { return height; } + public boolean getFaststart() { + return faststart; + } + @Override public void close() throws IOException { stream.close(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/PartAuthority.java b/app/src/main/java/org/thoughtcrime/securesms/mms/PartAuthority.java index a1461b58e5..74b399786e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/PartAuthority.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/PartAuthority.java @@ -54,7 +54,7 @@ public class PartAuthority { static { uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); - uriMatcher.addURI(AUTHORITY, "part/*/#", PART_ROW); + uriMatcher.addURI(AUTHORITY, "part/#", PART_ROW); uriMatcher.addURI(AUTHORITY, "sticker/#", STICKER_ROW); uriMatcher.addURI(AUTHORITY, "wallpaper/*", WALLPAPER_ROW); uriMatcher.addURI(AUTHORITY, "emoji/*", EMOJI_ROW); @@ -97,7 +97,7 @@ public static InputStream getAttachmentStream(@NonNull Context context, @NonNull case PART_ROW: Attachment attachment = SignalDatabase.attachments().getAttachment(new PartUriParser(uri).getPartId()); - if (attachment != null) return attachment.getFileName(); + if (attachment != null) return attachment.fileName; else return null; case PERSISTENT_ROW: return DeprecatedPersistentBlobProvider.getFileName(context, uri); @@ -115,7 +115,7 @@ public static InputStream getAttachmentStream(@NonNull Context context, @NonNull case PART_ROW: Attachment attachment = SignalDatabase.attachments().getAttachment(new PartUriParser(uri).getPartId()); - if (attachment != null) return attachment.getSize(); + if (attachment != null) return attachment.size; else return null; case PERSISTENT_ROW: return DeprecatedPersistentBlobProvider.getFileSize(context, uri); @@ -133,7 +133,7 @@ public static InputStream getAttachmentStream(@NonNull Context context, @NonNull case PART_ROW: Attachment attachment = SignalDatabase.attachments().getAttachment(new PartUriParser(uri).getPartId()); - if (attachment != null) return attachment.getContentType(); + if (attachment != null) return attachment.contentType; else return null; case PERSISTENT_ROW: return DeprecatedPersistentBlobProvider.getMimeType(context, uri); @@ -151,7 +151,7 @@ public static boolean getAttachmentIsVideoGif(@NonNull Context context, @NonNull case PART_ROW: Attachment attachment = SignalDatabase.attachments().getAttachment(new PartUriParser(uri).getPartId()); - if (attachment != null) return attachment.isVideoGif(); + if (attachment != null) return attachment.videoGif; else return false; default: return false; @@ -174,8 +174,7 @@ public static Uri getAttachmentPublicUri(Uri uri) { } public static Uri getAttachmentDataUri(AttachmentId attachmentId) { - Uri uri = Uri.withAppendedPath(PART_CONTENT_URI, String.valueOf(attachmentId.getUniqueId())); - return ContentUris.withAppendedId(uri, attachmentId.getRowId()); + return ContentUris.withAppendedId(PART_CONTENT_URI, attachmentId.id); } public static Uri getAttachmentThumbnailUri(AttachmentId attachmentId) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/PartUriParser.java b/app/src/main/java/org/thoughtcrime/securesms/mms/PartUriParser.java index ca22393265..a750b0892a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/PartUriParser.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/PartUriParser.java @@ -14,7 +14,7 @@ public PartUriParser(Uri uri) { } public AttachmentId getPartId() { - return new AttachmentId(getId(), getUniqueId()); + return new AttachmentId(getId()); } private long getId() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/Slide.java b/app/src/main/java/org/thoughtcrime/securesms/mms/Slide.java index febdc66704..771b0ebcea 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/Slide.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/Slide.java @@ -46,7 +46,7 @@ public Slide(@NonNull Attachment attachment) { } public String getContentType() { - return attachment.getContentType(); + return attachment.contentType; } @Nullable @@ -69,21 +69,21 @@ public Optional getBody() { @NonNull public Optional getCaption() { - return Optional.ofNullable(attachment.getCaption()); + return Optional.ofNullable(attachment.caption); } @NonNull public Optional getFileName() { - return Optional.ofNullable(attachment.getFileName()); + return Optional.ofNullable(attachment.fileName); } @Nullable public String getFastPreflightId() { - return attachment.getFastPreflightId(); + return attachment.fastPreflightId; } public long getFileSize() { - return attachment.getSize(); + return attachment.size; } public boolean hasImage() { @@ -117,7 +117,7 @@ public boolean isBorderless() { } public boolean isVideoGif() { - return hasVideo() && attachment.isVideoGif(); + return hasVideo() && attachment.videoGif; } public @NonNull String getContentDescription(@NonNull Context context) { return ""; } @@ -136,7 +136,7 @@ public boolean isPendingDownload() { } public int getTransferState() { - return attachment.getTransferState(); + return attachment.transferState; } public @DrawableRes int getPlaceholderRes(Theme theme) { @@ -144,7 +144,7 @@ public int getTransferState() { } public @Nullable BlurHash getPlaceholderBlur() { - return attachment.getBlurHash(); + return attachment.blurHash; } public boolean hasPlaceholder() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/StickerSlide.java b/app/src/main/java/org/thoughtcrime/securesms/mms/StickerSlide.java index 7efe11411d..4dce46d291 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/StickerSlide.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/StickerSlide.java @@ -23,12 +23,12 @@ public class StickerSlide extends Slide { public StickerSlide(@NonNull Attachment attachment) { super(attachment); - this.stickerLocator = Objects.requireNonNull(attachment.getSticker()); + this.stickerLocator = Objects.requireNonNull(attachment.stickerLocator); } public StickerSlide(Context context, Uri uri, long size, @NonNull StickerLocator stickerLocator, @NonNull String contentType) { super(constructAttachmentFromUri(context, uri, contentType, size, WIDTH, HEIGHT, true, null, null, stickerLocator, null, null, false, false, false, false)); - this.stickerLocator = Objects.requireNonNull(attachment.getSticker()); + this.stickerLocator = Objects.requireNonNull(attachment.stickerLocator); } @Override @@ -52,6 +52,6 @@ public boolean isBorderless() { } public @Nullable String getEmoji() { - return stickerLocator.getEmoji(); + return stickerLocator.emoji; } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/net/SignalWebSocketHealthMonitor.java b/app/src/main/java/org/thoughtcrime/securesms/net/SignalWebSocketHealthMonitor.java index 9c7ed91dc1..76a31b5b3a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/net/SignalWebSocketHealthMonitor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/net/SignalWebSocketHealthMonitor.java @@ -110,12 +110,14 @@ private void onStateChange(WebSocketConnectionState connectionState, HealthState } @Override - public void onKeepAliveResponse(long sentTimestamp, boolean isIdentifiedWebSocket) { + public void onKeepAliveResponse(long sentTimestamp, boolean isIdentifiedWebSocket, boolean keepMonitoring) { final long keepAliveTime = System.currentTimeMillis(); executor.execute(() -> { if (isIdentifiedWebSocket) { + identified.needsKeepAlive = keepMonitoring; identified.lastKeepAliveReceived = keepAliveTime; } else { + unidentified.needsKeepAlive = keepMonitoring; unidentified.lastKeepAliveReceived = keepAliveTime; } }); @@ -143,6 +145,13 @@ private static class HealthState { private volatile boolean needsKeepAlive; private volatile long lastKeepAliveReceived; + + /** + * Checks if a keep-alive response has not been received yet based on the provided send time. + */ + private boolean isKeepAlivePending(long sendTime) { + return needsKeepAlive && lastKeepAliveReceived < sendTime; + } } /** @@ -172,9 +181,8 @@ public void run() { sleepUntil(responseRequiredTime); if (shouldKeepRunning && isKeepAliveNecessary()) { - if (identified.lastKeepAliveReceived < keepAliveSendTime || unidentified.lastKeepAliveReceived < keepAliveSendTime) { + if (identified.isKeepAlivePending(keepAliveSendTime)) { Log.w(TAG, "Missed keep alives, identified last: " + identified.lastKeepAliveReceived + - " unidentified last: " + unidentified.lastKeepAliveReceived + " needed by: " + responseRequiredTime); signalWebSocket.forceNewWebSockets(); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/DeleteNotificationReceiver.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/DeleteNotificationReceiver.java index e7fe4e685d..b3b02e2c0e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/DeleteNotificationReceiver.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/DeleteNotificationReceiver.java @@ -23,7 +23,6 @@ public class DeleteNotificationReceiver extends ExportedBroadcastReceiver { public void onReceiveUnlock(final Context context, Intent intent) { if (DELETE_NOTIFICATION_ACTION.equals(intent.getAction())) { MessageNotifier notifier = ApplicationDependencies.getMessageNotifier(); - notifier.clearReminder(context); final long[] ids = intent.getLongArrayExtra(EXTRA_IDS); final boolean[] mms = intent.getBooleanArrayExtra(EXTRA_MMS); diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/MessageNotifier.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/MessageNotifier.java index 8fe32177ba..6d9ac9e58d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/MessageNotifier.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/MessageNotifier.java @@ -24,24 +24,18 @@ public interface MessageNotifier { void notifyStoryDeliveryFailed(@NonNull Context context, @NonNull Recipient recipient, @NonNull ConversationId conversationId); void notifyProofRequired(@NonNull Context context, @NonNull Recipient recipient, @NonNull ConversationId conversationId); void cancelDelayedNotifications(); + boolean clearNotifications(@NonNull Context context); void updateNotification(@NonNull Context context); void updateNotification(@NonNull Context context, @NonNull ConversationId conversationId); - void updateNotification(@NonNull Context context, @NonNull ConversationId conversationId, @NonNull BubbleUtil.BubbleState defaultBubbleState); - void updateNotification(@NonNull Context context, @NonNull ConversationId conversationId, boolean signal); - void updateNotification(@NonNull Context context, @Nullable ConversationId conversationId, boolean signal, int reminderCount, @NonNull BubbleUtil.BubbleState defaultBubbleState); - boolean clearNotifications(@NonNull Context context); - void clearReminder(@NonNull Context context); + void forceBubbleNotification(@NonNull Context context, @NonNull ConversationId conversationId); void addStickyThread(@NonNull ConversationId conversationId, long earliestTimestamp); void removeStickyThread(@NonNull ConversationId conversationId); - class ReminderReceiver extends BroadcastReceiver { - @Override public void onReceive(final Context context, final Intent intent) { SignalExecutors.BOUNDED.execute(() -> { - int reminderCount = intent.getIntExtra("reminder_count", 0); - ApplicationDependencies.getMessageNotifier().updateNotification(context, null, true, reminderCount + 1, BubbleUtil.BubbleState.HIDDEN); + ApplicationDependencies.getMessageNotifier().updateNotification(context); }); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/OptimizedMessageNotifier.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/OptimizedMessageNotifier.java index bf35b0f944..d578ac0b3e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/OptimizedMessageNotifier.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/OptimizedMessageNotifier.java @@ -30,7 +30,6 @@ public class OptimizedMessageNotifier implements MessageNotifier { private static final String DEDUPE_KEY_GENERAL = "MESSAGE_NOTIFIER_DEFAULT"; private static final String DEDUPE_KEY_CHAT = "MESSAGE_NOTIFIER_CHAT_"; - private static final String DEDUPE_KEY_CANCEL_DELAYED = "MESSAGE_NOTIFIER_CANCEL_DELAYED"; @MainThread public OptimizedMessageNotifier(@NonNull Application context) { @@ -81,9 +80,7 @@ public void notifyProofRequired(@NonNull Context context, @NonNull Recipient rec @Override public void cancelDelayedNotifications() { - SignalDatabase.runPostSuccessfulTransaction(DEDUPE_KEY_CANCEL_DELAYED, () -> { - getNotifier().cancelDelayedNotifications(); - }); + getNotifier().cancelDelayedNotifications(); } @Override @@ -100,36 +97,15 @@ public void updateNotification(@NonNull Context context, @NonNull ConversationId }); } - @Override - public void updateNotification(@NonNull Context context, @NonNull ConversationId conversationId, @NonNull BubbleUtil.BubbleState defaultBubbleState) { - SignalDatabase.runPostSuccessfulTransaction(() -> { - runOnLimiter(() -> getNotifier().updateNotification(context, conversationId, defaultBubbleState)); - }); - } - - @Override - public void updateNotification(@NonNull Context context, @NonNull ConversationId conversationId, boolean signal) { - SignalDatabase.runPostSuccessfulTransaction(() -> { - runOnLimiter(() -> getNotifier().updateNotification(context, conversationId, signal)); - }); - } - - @Override - public void updateNotification(@NonNull Context context, @Nullable ConversationId conversationId, boolean signal, int reminderCount, @NonNull BubbleUtil.BubbleState defaultBubbleState) { - SignalDatabase.runPostSuccessfulTransaction(() -> { - runOnLimiter(() -> getNotifier().updateNotification(context, conversationId, signal, reminderCount, defaultBubbleState)); - }); - } - @Override public boolean clearNotifications(@NonNull Context context) { return getNotifier().clearNotifications(context); } @Override - public void clearReminder(@NonNull Context context) { + public void forceBubbleNotification(@NonNull Context context, @NonNull ConversationId conversationId) { SignalDatabase.runPostSuccessfulTransaction(() -> { - getNotifier().clearReminder(context); + runOnLimiter(() -> getNotifier().forceBubbleNotification(context, conversationId)); }); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/profiles/NotificationProfile.kt b/app/src/main/java/org/thoughtcrime/securesms/notifications/profiles/NotificationProfile.kt index 5b567b7d78..822bf7e26e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/profiles/NotificationProfile.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/profiles/NotificationProfile.kt @@ -9,7 +9,7 @@ data class NotificationProfile( val emoji: String, val color: AvatarColor = AvatarColor.A210, val createdAt: Long, - val allowAllCalls: Boolean = false, + val allowAllCalls: Boolean = true, val allowAllMentions: Boolean = false, val schedule: NotificationProfileSchedule, val allowedMembers: Set = emptySet() diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/DefaultMessageNotifier.kt b/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/DefaultMessageNotifier.kt index e7da3a9588..6b99dc24c9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/DefaultMessageNotifier.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/DefaultMessageNotifier.kt @@ -106,7 +106,7 @@ class DefaultMessageNotifier(context: Application) : MessageNotifier { } override fun updateNotification(context: Context) { - updateNotification(context, null, false, 0, BubbleState.HIDDEN) + updateNotification(context, null, BubbleState.HIDDEN) } override fun updateNotification(context: Context, conversationId: ConversationId) { @@ -114,27 +114,17 @@ class DefaultMessageNotifier(context: Application) : MessageNotifier { Log.i(TAG, "Scheduling delayed notification...") executor.enqueue(context, conversationId) } else { - updateNotification(context, conversationId, true) + updateNotification(context, conversationId, BubbleState.HIDDEN) } } - override fun updateNotification(context: Context, conversationId: ConversationId, defaultBubbleState: BubbleState) { - updateNotification(context, conversationId, false, 0, defaultBubbleState) + override fun forceBubbleNotification(context: Context, conversationId: ConversationId) { + updateNotification(context, conversationId, BubbleState.SHOWN) } - override fun updateNotification(context: Context, conversationId: ConversationId, signal: Boolean) { - updateNotification(context, conversationId, signal, 0, BubbleState.HIDDEN) - } - - /** - * @param signal is no longer used - * @param reminderCount is not longer used - */ - override fun updateNotification( + private fun updateNotification( context: Context, conversationId: ConversationId?, - signal: Boolean, - reminderCount: Int, defaultBubbleState: BubbleState ) { NotificationChannels.getInstance().ensureCustomChannelConsistency() @@ -258,10 +248,6 @@ class DefaultMessageNotifier(context: Application) : MessageNotifier { private fun getDisplayedNotificationIds(context: Context) = ServiceUtil.getNotificationManager(context).getDisplayedNotificationIds().getOrDefault(emptySet()) - override fun clearReminder(context: Context) { - // Intentionally left blank - } - override fun addStickyThread(conversationId: ConversationId, earliestTimestamp: Long) { stickyThreads[conversationId] = StickyThread(conversationId, NotificationIds.getNotificationIdForThread(conversationId), earliestTimestamp) } @@ -428,8 +414,8 @@ private class CancelableExecutor { } if (!canceled.get()) { Log.i(TAG, "Not canceled, notifying...") - ApplicationDependencies.getMessageNotifier().updateNotification(context, thread, true) ApplicationDependencies.getMessageNotifier().cancelDelayedNotifications() + ApplicationDependencies.getMessageNotifier().updateNotification(context, thread) } else { Log.w(TAG, "Canceled, not notifying...") } diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/NotificationBuilder.kt b/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/NotificationBuilder.kt index 9d7a190caa..6199956f95 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/NotificationBuilder.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/NotificationBuilder.kt @@ -508,6 +508,6 @@ private fun Bitmap?.toIconCompat(): IconCompat? { private fun ReplyMethod.toLongDescription(): Int { return when (this) { ReplyMethod.GroupMessage -> R.string.MessageNotifier_reply - ReplyMethod.SecureMessage -> R.string.MessageNotifier_signal_message + ReplyMethod.SecureMessage -> R.string.NewConversationActivity__message } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/NotificationThumbnails.kt b/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/NotificationThumbnails.kt index 474bb7a90b..2d4d1dbbae 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/NotificationThumbnails.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/NotificationThumbnails.kt @@ -2,6 +2,8 @@ package org.thoughtcrime.securesms.notifications.v2 import android.content.Context import android.net.Uri +import android.os.Build +import org.signal.core.util.asListContains import org.signal.core.util.concurrent.SignalExecutors import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.database.model.MessageId @@ -10,6 +12,7 @@ import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader import org.thoughtcrime.securesms.mms.Slide import org.thoughtcrime.securesms.providers.BlobProvider import org.thoughtcrime.securesms.util.BitmapDecodingException +import org.thoughtcrime.securesms.util.FeatureFlags import org.thoughtcrime.securesms.util.ImageCompressionUtil import org.thoughtcrime.securesms.util.kb import org.thoughtcrime.securesms.util.mb @@ -33,9 +36,21 @@ object NotificationThumbnails { private val thumbnailCache = LinkedHashMap(MAX_CACHE_SIZE) + /** + * Some devices are hitting weird issues when rendering notification thumbnails. It's only a few specific older models, so rather than try to figure out the + * specifics here, we'll just disable notification thumbnails for them. + */ + private val isBlocklisted by lazy { + FeatureFlags.notificationThumbnailProductBlocklist().asListContains(Build.PRODUCT) + } + fun getWithoutModifying(notificationItem: NotificationItem): NotificationItem.ThumbnailInfo { val thumbnailSlide: Slide? = notificationItem.slideDeck?.thumbnailSlide + if (isBlocklisted) { + return NotificationItem.ThumbnailInfo.NONE + } + if (thumbnailSlide == null || thumbnailSlide.uri == null) { return NotificationItem.ThumbnailInfo.NONE } diff --git a/app/src/main/java/org/thoughtcrime/securesms/payments/MobileCoinLedgerWrapper.java b/app/src/main/java/org/thoughtcrime/securesms/payments/MobileCoinLedgerWrapper.java index c58a936605..f87d7eabb5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/payments/MobileCoinLedgerWrapper.java +++ b/app/src/main/java/org/thoughtcrime/securesms/payments/MobileCoinLedgerWrapper.java @@ -5,6 +5,7 @@ import org.thoughtcrime.securesms.payments.proto.MobileCoinLedger; import org.whispersystems.signalservice.api.payments.Money; +import java.math.BigInteger; import java.util.ArrayList; import java.util.List; @@ -18,8 +19,8 @@ public final class MobileCoinLedgerWrapper { private final Balance balance; public MobileCoinLedgerWrapper(@NonNull MobileCoinLedger ledger) { - Money.MobileCoin fullAmount = Money.picoMobileCoin(ledger.balance); - Money.MobileCoin transferableAmount = Money.picoMobileCoin(ledger.transferableBalance); + Money.MobileCoin fullAmount = ledger.balance.size() > 0 ? Money.picoMobileCoin(new BigInteger(ledger.balance.toByteArray())) : Money.picoMobileCoin(ledger.deprecatedBalance); + Money.MobileCoin transferableAmount = ledger.transferableBalance.size() > 0 ? Money.picoMobileCoin(new BigInteger(ledger.transferableBalance.toByteArray())) : Money.picoMobileCoin(ledger.deprecatedTransferableBalance); this.ledger = ledger; this.balance = new Balance(fullAmount, transferableAmount, ledger.asOfTimeStamp); @@ -54,7 +55,7 @@ public static class OwnedTxo { } public @NonNull Money.MobileCoin getValue() { - return Money.picoMobileCoin(ownedTXO.amount); + return ownedTXO.amount.size() > 0 ? Money.picoMobileCoin(new BigInteger(ownedTXO.amount.toByteArray())) : Money.picoMobileCoin(ownedTXO.deprecatedAmount); } public @NonNull ByteString getKeyImage() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/payments/MobileCoinMainNetConfig.java b/app/src/main/java/org/thoughtcrime/securesms/payments/MobileCoinMainNetConfig.java index 75b9f73005..3831acb4ad 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/payments/MobileCoinMainNetConfig.java +++ b/app/src/main/java/org/thoughtcrime/securesms/payments/MobileCoinMainNetConfig.java @@ -37,7 +37,7 @@ public MobileCoinMainNetConfig(@NonNull SignalServiceAccountManager signalServic @Override @NonNull Uri getFogUri() { - return Uri.parse("fog://service.fog.mob.production.namda.net"); + return Uri.parse("fog://fog.prod.mobilecoinww.com"); } @Override diff --git a/app/src/main/java/org/thoughtcrime/securesms/payments/MobileCoinTestNetConfig.java b/app/src/main/java/org/thoughtcrime/securesms/payments/MobileCoinTestNetConfig.java index f17256d577..1075e6566e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/payments/MobileCoinTestNetConfig.java +++ b/app/src/main/java/org/thoughtcrime/securesms/payments/MobileCoinTestNetConfig.java @@ -36,7 +36,7 @@ public MobileCoinTestNetConfig(@NonNull SignalServiceAccountManager signalServic @Override @NonNull Uri getFogUri() { - return Uri.parse("fog://service.fog.mob.staging.namda.net"); + return Uri.parse("fog://fog.test.mobilecoin.com"); } @Override diff --git a/app/src/main/java/org/thoughtcrime/securesms/payments/Wallet.java b/app/src/main/java/org/thoughtcrime/securesms/payments/Wallet.java index 9bed32be43..23f35aaf6e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/payments/Wallet.java +++ b/app/src/main/java/org/thoughtcrime/securesms/payments/Wallet.java @@ -165,7 +165,7 @@ public Wallet(@NonNull MobileCoinConfig mobileCoinConfig, @NonNull Entropy payme for (OwnedTxOut txOut : accountSnapshot.getAccountActivity().getAllTokenTxOuts(TokenId.MOB)) { final Amount txOutAmount = txOut.getAmount(); MobileCoinLedger.OwnedTXO.Builder txoBuilder = new MobileCoinLedger.OwnedTXO.Builder() - .amount(Uint64Util.bigIntegerToUInt64(txOutAmount.getValue())) + .amount(ByteString.of(txOutAmount.getValue().toByteArray())) .receivedInBlock(getBlock(txOut.getReceivedBlockIndex(), txOut.getReceivedBlockTimestamp())) .keyImage(ByteString.of(txOut.getKeyImage().getData())) .publicKey(ByteString.of(txOut.getPublicKey().getKeyBytes())); @@ -198,8 +198,8 @@ public Wallet(@NonNull MobileCoinConfig mobileCoinConfig, @NonNull Entropy payme builder.spentTxos(spentTxos) .unspentTxos(unspentTxos) - .balance(Uint64Util.bigIntegerToUInt64(totalUnspent)) - .transferableBalance(Uint64Util.bigIntegerToUInt64(accountSnapshot.getTransferableAmount(minimumTxFee).getValue())) + .balance(ByteString.of(totalUnspent.toByteArray())) + .transferableBalance(ByteString.of(accountSnapshot.getTransferableAmount(minimumTxFee).getValue().toByteArray())) .asOfTimeStamp(asOfTimestamp) .highestBlock(new MobileCoinLedger.Block.Builder() .blockNumber(highestBlockIndex.longValue()) diff --git a/app/src/main/java/org/thoughtcrime/securesms/permissions/Permissions.java b/app/src/main/java/org/thoughtcrime/securesms/permissions/Permissions.java index 7a2cfe864d..14c6d052d4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/permissions/Permissions.java +++ b/app/src/main/java/org/thoughtcrime/securesms/permissions/Permissions.java @@ -232,7 +232,7 @@ private static void requestPermissions(@NonNull Fragment fragment, int requestCo } - fragment.requestPermissions(filterNotGranted(fragment.requireContext(), permissions), requestCode); + fragment.requestPermissions(neededPermissions, requestCode); } private static String[] filterNotGranted(@NonNull Context context, String... permissions) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/pin/SvrRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/pin/SvrRepository.kt index 9155454c36..c2e877c968 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/pin/SvrRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/pin/SvrRepository.kt @@ -12,7 +12,7 @@ import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.BuildConfig import org.thoughtcrime.securesms.dependencies.ApplicationDependencies import org.thoughtcrime.securesms.jobmanager.JobTracker -import org.thoughtcrime.securesms.jobs.NewRegistrationUsernameSyncJob +import org.thoughtcrime.securesms.jobs.ReclaimUsernameAndLinkJob import org.thoughtcrime.securesms.jobs.RefreshAttributesJob import org.thoughtcrime.securesms.jobs.ResetSvrGuessCountJob import org.thoughtcrime.securesms.jobs.StorageAccountRestoreJob @@ -39,10 +39,14 @@ object SvrRepository { val TAG = Log.tag(SvrRepository::class.java) + private val svr2Deprecated: SecureValueRecovery = ApplicationDependencies.getSignalServiceAccountManager().getSecureValueRecoveryV2(BuildConfig.SVR2_MRENCLAVE_DEPRECATED) private val svr2: SecureValueRecovery = ApplicationDependencies.getSignalServiceAccountManager().getSecureValueRecoveryV2(BuildConfig.SVR2_MRENCLAVE) - /** An ordered list of SVR implementations. They should be in priority order, with the most important one listed first. */ - private val implementations: List = listOf(svr2) + /** An ordered list of SVR implementations to read from. They should be in priority order, with the most important one listed first. */ + private val readImplementations: List = listOf(svr2, svr2Deprecated) + + /** An ordered list of SVR implementations to write to. They should be in priority order, with the most important one listed first. */ + private val writeImplementations: List = listOf(svr2) /** * A lock that ensures that only one thread at a time is altering the various pieces of SVR state. @@ -70,7 +74,8 @@ object SvrRepository { Log.i(TAG, "restoreMasterKeyPreRegistration()", true) val operations: List RestoreResponse>> = listOf( - svr2 to { restoreMasterKeyPreRegistration(svr2, credentials.svr2, userPin) } + svr2 to { restoreMasterKeyPreRegistration(svr2, credentials.svr2, userPin) }, + svr2Deprecated to { restoreMasterKeyPreRegistration(svr2Deprecated, credentials.svr2, userPin) } ) for ((implementation, operation) in operations) { @@ -122,7 +127,7 @@ object SvrRepository { val stopwatch = Stopwatch("pin-submission") operationLock.withLock { - for (implementation in implementations) { + for (implementation in readImplementations) { when (val response: RestoreResponse = implementation.restoreDataPostRegistration(userPin)) { is RestoreResponse.Success -> { Log.i(TAG, "[restoreMasterKeyPostRegistration] Successfully restored master key. $implementation", true) @@ -147,7 +152,7 @@ object SvrRepository { ApplicationDependencies .getJobManager() .startChain(StorageSyncJob()) - .then(NewRegistrationUsernameSyncJob()) + .then(ReclaimUsernameAndLinkJob()) .enqueueAndBlockUntilCompletion(TimeUnit.SECONDS.toMillis(10)) stopwatch.split("contact-restore") @@ -187,7 +192,7 @@ object SvrRepository { } /** - * Sets the user's PIN the one specified, updating local stores as necessary. + * Sets the user's PIN to the one specified, updating local stores as necessary. * The resulting Single will not throw an error in any expected case, only if there's a runtime exception. */ @WorkerThread @@ -196,7 +201,7 @@ object SvrRepository { return operationLock.withLock { val masterKey: MasterKey = SignalStore.svr().getOrCreateMasterKey() - val responses: List = implementations + val responses: List = writeImplementations .filter { it != svr2 || FeatureFlags.svr2() } .map { it.setPin(userPin, masterKey) } .map { it.execute() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/profiles/edit/pnp/WhoCanFindMeByPhoneNumberRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/profiles/edit/pnp/WhoCanFindMeByPhoneNumberRepository.kt index c9867f8c80..f16fadcfac 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/profiles/edit/pnp/WhoCanFindMeByPhoneNumberRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/profiles/edit/pnp/WhoCanFindMeByPhoneNumberRepository.kt @@ -2,9 +2,11 @@ package org.thoughtcrime.securesms.profiles.edit.pnp import io.reactivex.rxjava3.core.Completable import org.thoughtcrime.securesms.dependencies.ApplicationDependencies +import org.thoughtcrime.securesms.jobs.ProfileUploadJob import org.thoughtcrime.securesms.jobs.RefreshAttributesJob import org.thoughtcrime.securesms.keyvalue.PhoneNumberPrivacyValues import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.storage.StorageSyncHelper /** * Manages the current phone-number listing state. @@ -31,6 +33,8 @@ class WhoCanFindMeByPhoneNumberRepository { } ApplicationDependencies.getJobManager().add(RefreshAttributesJob()) + StorageSyncHelper.scheduleSyncForDataChange() + ApplicationDependencies.getJobManager().add(ProfileUploadJob()) } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/EditProfileViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/EditProfileViewModel.java index 0a78309a40..b27b344985 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/EditProfileViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/EditProfileViewModel.java @@ -69,7 +69,7 @@ public EditProfileViewModel() { SignalExecutors.BOUNDED.execute(() -> { onRecipientChanged(Recipient.self().fresh()); - ApplicationDependencies.getJobManager().add(RetrieveProfileJob.forRecipient(Recipient.self().getId())); + RetrieveProfileJob.enqueue(Recipient.self().getId()); }); Recipient.self().live().observeForever(observer); @@ -104,7 +104,7 @@ public EditProfileViewModel() { } public Single deleteUsername() { - return UsernameRepository.deleteUsername().observeOn(AndroidSchedulers.mainThread()); + return UsernameRepository.deleteUsernameAndLink().observeOn(AndroidSchedulers.mainThread()); } public boolean shouldShowUsername() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameEditFragment.java b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameEditFragment.java index 8557a2953d..39899d9ed0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameEditFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameEditFragment.java @@ -3,14 +3,12 @@ import android.animation.LayoutTransition; import android.content.Intent; import android.content.res.ColorStateList; -import android.graphics.drawable.Drawable; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.inputmethod.EditorInfo; import android.widget.EditText; -import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; @@ -25,25 +23,22 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.textfield.TextInputLayout; -import org.signal.core.util.DimensionUnit; +import org.signal.core.util.EditTextUtil; +import org.signal.core.util.concurrent.LifecycleDisposable; import org.thoughtcrime.securesms.LoggingFragment; import org.thoughtcrime.securesms.PassphraseRequiredActivity; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.contactshare.SimpleTextWatcher; import org.thoughtcrime.securesms.databinding.UsernameEditFragmentBinding; -import org.thoughtcrime.securesms.keyvalue.SignalStore; -import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.util.FragmentResultContract; -import org.signal.core.util.concurrent.LifecycleDisposable; import org.thoughtcrime.securesms.util.UsernameUtil; import org.thoughtcrime.securesms.util.ViewUtil; import org.thoughtcrime.securesms.util.views.CircularProgressMaterialButton; -import java.util.Objects; - public class UsernameEditFragment extends LoggingFragment { - private static final float DISABLED_ALPHA = 0.5f; + private static final float DISABLED_ALPHA = 0.5f; + public static final String IGNORE_TEXT_CHANGE_EVENT = "ignore.text.change.event"; private UsernameEditViewModel viewModel; private UsernameEditFragmentBinding binding; @@ -99,23 +94,32 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat lifecycleDisposable.add(viewModel.getUiState().subscribe(this::onUiStateChanged)); lifecycleDisposable.add(viewModel.getEvents().subscribe(this::onEvent)); + lifecycleDisposable.add(viewModel.getUsernameInputState().subscribe(this::presentUsernameInputState)); binding.usernameSubmitButton.setOnClickListener(v -> viewModel.onUsernameSubmitted()); binding.usernameDeleteButton.setOnClickListener(v -> viewModel.onUsernameDeleted()); binding.usernameDoneButton.setOnClickListener(v -> viewModel.onUsernameSubmitted()); binding.usernameSkipButton.setOnClickListener(v -> viewModel.onUsernameSkipped()); - String username = SignalStore.account().getUsername(); - UsernameState usernameState = username != null ? new UsernameState.Set(username) : UsernameState.NoUsername.INSTANCE; - - binding.usernameText.setText(usernameState.getNickname()); binding.usernameText.addTextChangedListener(new SimpleTextWatcher() { @Override public void onTextChanged(@NonNull String text) { - viewModel.onNicknameUpdated(text); + if (binding.usernameText.getTag() != IGNORE_TEXT_CHANGE_EVENT) { + viewModel.onNicknameUpdated(text); + } + } + }); + + binding.discriminatorText.addTextChangedListener(new SimpleTextWatcher() { + @Override + public void onTextChanged(@NonNull String text) { + if (binding.discriminatorText.getTag() != IGNORE_TEXT_CHANGE_EVENT) { + viewModel.onDiscriminatorUpdated(text); + } } }); - binding.usernameText.setOnEditorActionListener((v, actionId, event) -> { + + binding.discriminatorText.setOnEditorActionListener((v, actionId, event) -> { if (actionId == EditorInfo.IME_ACTION_DONE) { viewModel.onUsernameSubmitted(); return true; @@ -127,24 +131,9 @@ public void onTextChanged(@NonNull String text) { binding.usernameDescription.setLearnMoreVisible(true); binding.usernameDescription.setOnLinkClickListener(this::onLearnMore); - initializeSuffix(); ViewUtil.focusAndShowKeyboard(binding.usernameText); } - private void initializeSuffix() { - TextView suffixTextView = binding.usernameTextWrapper.getSuffixTextView(); - Drawable pipe = Objects.requireNonNull(ContextCompat.getDrawable(requireContext(), R.drawable.pipe_divider)); - - pipe.setBounds(0, 0, (int) DimensionUnit.DP.toPixels(1f), (int) DimensionUnit.DP.toPixels(20f)); - suffixTextView.setCompoundDrawablesRelative(pipe, null, null, null); - - ViewUtil.setLeftMargin(suffixTextView, (int) DimensionUnit.DP.toPixels(16f)); - - binding.usernameTextWrapper.getSuffixTextView().setCompoundDrawablePadding((int) DimensionUnit.DP.toPixels(16f)); - - suffixTextView.setOnClickListener(this::onLearnMore); - } - @Override public void onDestroyView() { super.onDestroyView(); @@ -162,48 +151,33 @@ private void onLearnMore(@Nullable View unused) { private void onUiStateChanged(@NonNull UsernameEditViewModel.State state) { TextInputLayout usernameInputWrapper = binding.usernameTextWrapper; - presentSuffix(state.username); + presentProgressState(state.usernameState); presentButtonState(state.buttonState); - presentSummary(state.username); + presentSummary(state.usernameState); binding.root.setLayoutTransition(ANIMATED_LAYOUT); - switch (state.usernameStatus) { - case NONE: - usernameInputWrapper.setError(null); - break; - case TOO_SHORT: - case TOO_LONG: - usernameInputWrapper.setError(getResources().getString(R.string.UsernameEditFragment_usernames_must_be_between_a_and_b_characters, UsernameUtil.MIN_LENGTH, UsernameUtil.MAX_LENGTH)); - usernameInputWrapper.setErrorTextColor(ColorStateList.valueOf(getResources().getColor(R.color.signal_colorError))); - - break; - case INVALID_CHARACTERS: - usernameInputWrapper.setError(getResources().getString(R.string.UsernameEditFragment_usernames_can_only_include)); - usernameInputWrapper.setErrorTextColor(ColorStateList.valueOf(getResources().getColor(R.color.signal_colorError))); - - break; - case CANNOT_START_WITH_NUMBER: - usernameInputWrapper.setError(getResources().getString(R.string.UsernameEditFragment_usernames_cannot_begin_with_a_number)); - usernameInputWrapper.setErrorTextColor(ColorStateList.valueOf(getResources().getColor(R.color.signal_colorError))); - - break; - case INVALID_GENERIC: - usernameInputWrapper.setError(getResources().getString(R.string.UsernameEditFragment_username_is_invalid)); - usernameInputWrapper.setErrorTextColor(ColorStateList.valueOf(getResources().getColor(R.color.signal_colorError))); - - break; - case TAKEN: - usernameInputWrapper.setError(getResources().getString(R.string.UsernameEditFragment_this_username_is_taken)); - usernameInputWrapper.setErrorTextColor(ColorStateList.valueOf(getResources().getColor(R.color.signal_colorError))); - - break; - } - - CharSequence error = usernameInputWrapper.getError(); + CharSequence error = switch (state.usernameStatus) { + case NONE -> null; + case TOO_SHORT, TOO_LONG -> getString(R.string.UsernameEditFragment_usernames_must_be_between_a_and_b_characters, UsernameUtil.MIN_NICKNAME_LENGTH, UsernameUtil.MAX_NICKNAME_LENGTH); + case INVALID_CHARACTERS -> getString(R.string.UsernameEditFragment_usernames_can_only_include); + case CANNOT_START_WITH_NUMBER -> getString(R.string.UsernameEditFragment_usernames_cannot_begin_with_a_number); + case INVALID_GENERIC -> getString(R.string.UsernameEditFragment_username_is_invalid); + case TAKEN -> getString(R.string.UsernameEditFragment_this_username_is_taken); + case DISCRIMINATOR_HAS_INVALID_CHARACTERS, DISCRIMINATOR_NOT_AVAILABLE -> getString(R.string.UsernameEditFragment__this_username_is_not_available_try_another_number); + case DISCRIMINATOR_TOO_LONG -> getString(R.string.UsernameEditFragment__invalid_username_enter_a_maximum_of_d_digits, UsernameUtil.MAX_DISCRIMINATOR_LENGTH); + case DISCRIMINATOR_TOO_SHORT -> getString(R.string.UsernameEditFragment__invalid_username_enter_a_minimum_of_d_digits, UsernameUtil.MIN_DISCRIMINATOR_LENGTH); + }; + + int colorRes = error != null ? R.color.signal_colorError : R.color.signal_colorPrimary; + int color = ContextCompat.getColor(requireContext(), colorRes); + + binding.usernameTextFocusedStroke.setBackgroundColor(color); + binding.usernameTextWrapper.setHintTextColor(ColorStateList.valueOf(color)); + EditTextUtil.setCursorColor(binding.usernameText, color); + EditTextUtil.setCursorColor(binding.discriminatorText, color); binding.usernameError.setVisibility(error != null ? View.VISIBLE : View.GONE); - binding.usernameError.setText(usernameInputWrapper.getError()); - + binding.usernameError.setText(error); binding.root.setLayoutTransition(STATIC_LAYOUT); } @@ -217,11 +191,9 @@ private void presentButtonState(@NonNull UsernameEditViewModel.ButtonState butto private void presentSummary(@NonNull UsernameState usernameState) { if (usernameState.getUsername() != null) { - binding.summary.setText(usernameState.getUsername()); + binding.summary.setText(usernameState.getUsername().getUsername()); binding.summary.setAlpha(1f); - } else if (usernameState instanceof UsernameState.Loading) { - binding.summary.setAlpha(0.5f); - } else { + } else if (!(usernameState instanceof UsernameState.Loading)) { binding.summary.setText(R.string.UsernameEditFragment__choose_your_username); binding.summary.setAlpha(1f); } @@ -302,9 +274,25 @@ private void presentProfileUpdateButtonState(@NonNull UsernameEditViewModel.Butt } } - private void presentSuffix(@NonNull UsernameState usernameState) { - binding.usernameTextWrapper.setSuffixText(usernameState.getDiscriminator()); + private void presentUsernameInputState(@NonNull UsernameEditStateMachine.State state) { + binding.usernameText.setTag(IGNORE_TEXT_CHANGE_EVENT); + String nickname = state.getNickname(); + if (!binding.usernameText.getText().toString().equals(nickname)) { + binding.usernameText.setText(state.getNickname()); + binding.usernameText.setSelection(binding.usernameText.length()); + } + binding.usernameText.setTag(null); + + binding.discriminatorText.setTag(IGNORE_TEXT_CHANGE_EVENT); + String discriminator = state.getDiscriminator(); + if (!binding.discriminatorText.getText().toString().equals(discriminator)) { + binding.discriminatorText.setText(state.getDiscriminator()); + binding.discriminatorText.setSelection(binding.discriminatorText.length()); + } + binding.discriminatorText.setTag(null); + } + private void presentProgressState(@NonNull UsernameState usernameState) { boolean isInProgress = usernameState.isInProgress(); if (isInProgress) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameEditStateMachine.kt b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameEditStateMachine.kt new file mode 100644 index 0000000000..459a5fcc0b --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameEditStateMachine.kt @@ -0,0 +1,216 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.profiles.manage + +object UsernameEditStateMachine { + + enum class StateModifier { + USER, + SYSTEM + } + + sealed class State { + abstract val nickname: String + abstract val discriminator: String + abstract val stateModifier: StateModifier + + abstract fun onUserChangedNickname(nickname: String): State + abstract fun onUserChangedDiscriminator(discriminator: String): State + abstract fun onSystemChangedNickname(nickname: String): State + abstract fun onSystemChangedDiscriminator(discriminator: String): State + } + + /** + * This state is representative of when the user has not manually changed either field in + * the form, and it is assumed that both values are either blank or system provided. + * + * This can be thought of as our "initial state" and can be pre-populated with username information + * for the local user. + */ + data class NoUserEntry( + override val nickname: String, + override val discriminator: String, + override val stateModifier: StateModifier + ) : State() { + override fun onUserChangedNickname(nickname: String): State { + return if (nickname.isBlank()) { + NoUserEntry( + nickname = "", + discriminator = discriminator, + stateModifier = StateModifier.USER + ) + } else { + UserEnteredNickname( + nickname = nickname, + discriminator = discriminator, + stateModifier = StateModifier.USER + ) + } + } + + override fun onUserChangedDiscriminator(discriminator: String): State { + return if (discriminator.isBlank()) { + NoUserEntry( + nickname = nickname, + discriminator = "", + stateModifier = StateModifier.USER + ) + } else { + UserEnteredDiscriminator( + nickname = nickname, + discriminator = discriminator, + stateModifier = StateModifier.USER + ) + } + } + + override fun onSystemChangedNickname(nickname: String): State { + return copy(nickname = nickname, stateModifier = StateModifier.SYSTEM) + } + + override fun onSystemChangedDiscriminator(discriminator: String): State { + return copy(discriminator = discriminator, stateModifier = StateModifier.SYSTEM) + } + } + + /** + * The user has altered the nickname field with something that is non-empty. + * The user has not altered the discriminator field. + */ + data class UserEnteredNickname( + override val nickname: String, + override val discriminator: String, + override val stateModifier: StateModifier + ) : State() { + override fun onUserChangedNickname(nickname: String): State { + return if (nickname.isBlank()) { + NoUserEntry( + nickname = "", + discriminator = discriminator, + stateModifier = StateModifier.USER + ) + } else { + copy(nickname = nickname, stateModifier = StateModifier.USER) + } + } + + override fun onUserChangedDiscriminator(discriminator: String): State { + return if (discriminator.isBlank()) { + copy(discriminator = "", stateModifier = StateModifier.USER) + } else { + UserEnteredNicknameAndDiscriminator( + nickname = nickname, + discriminator = discriminator, + stateModifier = StateModifier.USER + ) + } + } + + override fun onSystemChangedNickname(nickname: String): State { + return NoUserEntry( + nickname = nickname, + discriminator = discriminator, + stateModifier = StateModifier.SYSTEM + ) + } + + override fun onSystemChangedDiscriminator(discriminator: String): State { + return copy(discriminator = discriminator, stateModifier = StateModifier.SYSTEM) + } + } + + /** + * The user has altered the discriminator field with something that is non-empty. + * The user has not altered the nickname field. + */ + data class UserEnteredDiscriminator( + override val nickname: String, + override val discriminator: String, + override val stateModifier: StateModifier + ) : State() { + override fun onUserChangedNickname(nickname: String): State { + return if (nickname.isBlank()) { + copy(nickname = nickname, stateModifier = StateModifier.USER) + } else if (discriminator.isBlank()) { + UserEnteredNickname( + nickname = nickname, + discriminator = "", + stateModifier = StateModifier.USER + ) + } else { + UserEnteredNicknameAndDiscriminator( + nickname = nickname, + discriminator = discriminator, + stateModifier = StateModifier.USER + ) + } + } + + override fun onUserChangedDiscriminator(discriminator: String): State { + return copy(discriminator = discriminator, stateModifier = StateModifier.USER) + } + + override fun onSystemChangedNickname(nickname: String): State { + return copy(nickname = nickname, stateModifier = StateModifier.SYSTEM) + } + + override fun onSystemChangedDiscriminator(discriminator: String): State { + return NoUserEntry( + nickname = nickname, + discriminator = discriminator, + stateModifier = StateModifier.SYSTEM + ) + } + } + + /** + * The user has altered the nickname field with something that is non-empty. + * The user has altered the discriminator field with something that is non-empty. + */ + data class UserEnteredNicknameAndDiscriminator( + override val nickname: String, + override val discriminator: String, + override val stateModifier: StateModifier + ) : State() { + override fun onUserChangedNickname(nickname: String): State { + return if (nickname.isBlank()) { + UserEnteredDiscriminator( + nickname = "", + discriminator = discriminator, + stateModifier = StateModifier.USER + ) + } else if (discriminator.isBlank()) { + UserEnteredNickname( + nickname = nickname, + discriminator = "", + stateModifier = StateModifier.USER + ) + } else { + copy(nickname = nickname, stateModifier = StateModifier.USER) + } + } + + override fun onUserChangedDiscriminator(discriminator: String): State { + return copy(discriminator = discriminator, stateModifier = StateModifier.USER) + } + + override fun onSystemChangedNickname(nickname: String): State { + return UserEnteredDiscriminator( + nickname = nickname, + discriminator = discriminator, + stateModifier = StateModifier.SYSTEM + ) + } + + override fun onSystemChangedDiscriminator(discriminator: String): State { + return UserEnteredNickname( + nickname = nickname, + discriminator = discriminator, + stateModifier = StateModifier.SYSTEM + ) + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameEditViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameEditViewModel.kt index ac7fffbd69..23939372ed 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameEditViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameEditViewModel.kt @@ -5,18 +5,23 @@ import androidx.lifecycle.ViewModelProvider import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.core.Flowable import io.reactivex.rxjava3.core.Observable +import io.reactivex.rxjava3.core.Single import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.kotlin.plusAssign -import io.reactivex.rxjava3.processors.PublishProcessor +import io.reactivex.rxjava3.kotlin.subscribeBy import io.reactivex.rxjava3.schedulers.Schedulers import io.reactivex.rxjava3.subjects.PublishSubject import org.signal.core.util.Result +import org.signal.core.util.logging.Log +import org.signal.libsignal.usernames.Username import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.profiles.manage.UsernameRepository.UsernameDeleteResult import org.thoughtcrime.securesms.profiles.manage.UsernameRepository.UsernameSetResult import org.thoughtcrime.securesms.util.UsernameUtil.InvalidReason +import org.thoughtcrime.securesms.util.UsernameUtil.checkDiscriminator import org.thoughtcrime.securesms.util.UsernameUtil.checkUsername import org.thoughtcrime.securesms.util.rx.RxStore +import org.whispersystems.signalservice.api.util.Usernames import java.util.concurrent.TimeUnit /** @@ -32,22 +37,34 @@ import java.util.concurrent.TimeUnit */ internal class UsernameEditViewModel private constructor(private val isInRegistration: Boolean) : ViewModel() { private val events: PublishSubject = PublishSubject.create() - private val nicknamePublisher: PublishProcessor = PublishProcessor.create() private val disposables: CompositeDisposable = CompositeDisposable() private val uiState: RxStore = RxStore( defaultValue = State( buttonState = ButtonState.SUBMIT_DISABLED, usernameStatus = UsernameStatus.NONE, - username = SignalStore.account().username?.let { UsernameState.Set(it) } ?: UsernameState.NoUsername + usernameState = SignalStore.account().username?.let { UsernameState.Set(Username(it)) } ?: UsernameState.NoUsername ), scheduler = Schedulers.computation() ) + private val stateMachineStore = RxStore( + defaultValue = UsernameEditStateMachine.NoUserEntry( + nickname = SignalStore.account().username?.split(Usernames.DELIMITER)?.first() ?: "", + discriminator = SignalStore.account().username?.split(Usernames.DELIMITER)?.last() ?: "", + stateModifier = UsernameEditStateMachine.StateModifier.SYSTEM + ), + scheduler = Schedulers.computation() + ) + + val usernameInputState: Flowable = stateMachineStore.stateFlowable.observeOn(AndroidSchedulers.mainThread()) + init { - disposables += nicknamePublisher + disposables += stateMachineStore + .stateFlowable + .filter { it.stateModifier == UsernameEditStateMachine.StateModifier.USER } .debounce(NICKNAME_PUBLISHER_DEBOUNCE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS) - .subscribe { nickname: String -> onNicknameUpdatedDebounced(nickname) } + .subscribeBy(onNext = this::onUsernameStateUpdateDebounced) } override fun onCleared() { @@ -62,30 +79,42 @@ internal class UsernameEditViewModel private constructor(private val isInRegistr return@update State( buttonState = if (isInRegistration) ButtonState.SUBMIT_DISABLED else ButtonState.DELETE, usernameStatus = UsernameStatus.NONE, - username = UsernameState.NoUsername + usernameState = UsernameState.NoUsername ) } - val invalidReason: InvalidReason? = checkUsername(nickname) + State( + buttonState = ButtonState.SUBMIT_DISABLED, + usernameStatus = UsernameStatus.NONE, + usernameState = state.usernameState + ) + } - if (invalidReason != null) { - // We only want to show actual errors after debouncing. But we also don't want to allow users to submit names with errors. - // So we disable submit, but we don't show an error yet. - State( - buttonState = ButtonState.SUBMIT_DISABLED, - usernameStatus = UsernameStatus.NONE, - username = state.username - ) - } else { - State( - buttonState = ButtonState.SUBMIT_DISABLED, + stateMachineStore.update { + it.onUserChangedNickname(nickname) + } + } + + fun onDiscriminatorUpdated(discriminator: String) { + uiState.update { state: State -> + if (discriminator.isBlank() && SignalStore.account().username != null) { + return@update State( + buttonState = if (isInRegistration) ButtonState.SUBMIT_DISABLED else ButtonState.DELETE, usernameStatus = UsernameStatus.NONE, - username = state.username + usernameState = UsernameState.NoUsername ) } + + State( + buttonState = ButtonState.SUBMIT_DISABLED, + usernameStatus = UsernameStatus.NONE, + usernameState = state.usernameState + ) } - nicknamePublisher.onNext(nickname) + stateMachineStore.update { + it.onUserChangedDiscriminator(discriminator) + } } fun onUsernameSkipped() { @@ -94,49 +123,62 @@ internal class UsernameEditViewModel private constructor(private val isInRegistr } fun onUsernameSubmitted() { - val usernameState = uiState.state.username - if (usernameState !is UsernameState.Reserved) { - uiState.update { State(ButtonState.SUBMIT_DISABLED, UsernameStatus.NONE, it.username) } + val editState = stateMachineStore.state + val usernameState = uiState.state.usernameState + val isCaseChange = isCaseChange(editState) + + if (usernameState !is UsernameState.Reserved && usernameState !is UsernameState.CaseChange) { + Log.w(TAG, "Username was submitted, current state is invalid! State: ${usernameState.javaClass.simpleName}") + uiState.update { it.copy(buttonState = ButtonState.SUBMIT_DISABLED, usernameStatus = UsernameStatus.NONE) } return } - if (usernameState.username == SignalStore.account().username) { - uiState.update { State(ButtonState.SUBMIT_DISABLED, UsernameStatus.NONE, it.username) } + if (usernameState.requireUsername().username == SignalStore.account().username) { + Log.d(TAG, "Username was submitted, but was identical to the current username. Ignoring.") + uiState.update { it.copy(buttonState = ButtonState.SUBMIT_DISABLED, usernameStatus = UsernameStatus.NONE) } return } - val invalidReason = checkUsername(usernameState.getNickname()) + val invalidReason: InvalidReason? = checkUsername(usernameState.getNickname()) if (invalidReason != null) { - uiState.update { State(ButtonState.SUBMIT_DISABLED, mapUsernameError(invalidReason), it.username) } + Log.w(TAG, "Username was submitted, but did not pass validity checks. Reason: $invalidReason") + uiState.update { it.copy(buttonState = ButtonState.SUBMIT_DISABLED, usernameStatus = mapNicknameError(invalidReason)) } return } - uiState.update { State(ButtonState.SUBMIT_LOADING, UsernameStatus.NONE, it.username) } + uiState.update { it.copy(buttonState = ButtonState.SUBMIT_LOADING, usernameStatus = UsernameStatus.NONE) } - disposables += UsernameRepository.confirmUsername(usernameState).subscribe { result: UsernameSetResult -> + val usernameConfirmOperation: Single = if (isCaseChange) { + UsernameRepository.updateUsernameDisplayForCurrentLink(usernameState.requireUsername()) + } else { + val reservation = usernameState as UsernameState.Reserved + UsernameRepository.confirmUsernameAndCreateNewLink(reservation.requireUsername()) + } + + disposables += usernameConfirmOperation.subscribe { result: UsernameSetResult -> val nickname = usernameState.getNickname() when (result) { UsernameSetResult.SUCCESS -> { SignalStore.uiHints().markHasSetOrSkippedUsernameCreation() - uiState.update { State(ButtonState.SUBMIT_DISABLED, UsernameStatus.NONE, it.username) } + uiState.update { State(ButtonState.SUBMIT_DISABLED, UsernameStatus.NONE, it.usernameState) } events.onNext(Event.SUBMIT_SUCCESS) } UsernameSetResult.USERNAME_INVALID -> { - uiState.update { State(ButtonState.SUBMIT_DISABLED, UsernameStatus.INVALID_GENERIC, it.username) } + uiState.update { State(ButtonState.SUBMIT_DISABLED, UsernameStatus.INVALID_GENERIC, it.usernameState) } events.onNext(Event.SUBMIT_FAIL_INVALID) nickname?.let { onNicknameUpdated(it) } } UsernameSetResult.CANDIDATE_GENERATION_ERROR, UsernameSetResult.USERNAME_UNAVAILABLE -> { - uiState.update { State(ButtonState.SUBMIT_DISABLED, UsernameStatus.TAKEN, it.username) } + uiState.update { State(ButtonState.SUBMIT_DISABLED, UsernameStatus.TAKEN, it.usernameState) } events.onNext(Event.SUBMIT_FAIL_TAKEN) nickname?.let { onNicknameUpdated(it) } } UsernameSetResult.NETWORK_ERROR -> { - uiState.update { State(ButtonState.SUBMIT, UsernameStatus.NONE, it.username) } + uiState.update { State(ButtonState.SUBMIT, UsernameStatus.NONE, it.usernameState) } events.onNext(Event.NETWORK_FAILURE) } } @@ -144,17 +186,17 @@ internal class UsernameEditViewModel private constructor(private val isInRegistr } fun onUsernameDeleted() { - uiState.update { state: State -> State(ButtonState.DELETE_LOADING, UsernameStatus.NONE, state.username) } + uiState.update { state: State -> State(ButtonState.DELETE_LOADING, UsernameStatus.NONE, state.usernameState) } - disposables += UsernameRepository.deleteUsername().subscribe { result: UsernameDeleteResult -> + disposables += UsernameRepository.deleteUsernameAndLink().subscribe { result: UsernameDeleteResult -> when (result) { UsernameDeleteResult.SUCCESS -> { - uiState.update { state: State -> State(ButtonState.DELETE_DISABLED, UsernameStatus.NONE, state.username) } + uiState.update { state: State -> State(ButtonState.DELETE_DISABLED, UsernameStatus.NONE, state.usernameState) } events.onNext(Event.DELETE_SUCCESS) } UsernameDeleteResult.NETWORK_ERROR -> { - uiState.update { state: State -> State(ButtonState.DELETE, UsernameStatus.NONE, state.username) } + uiState.update { state: State -> State(ButtonState.DELETE, UsernameStatus.NONE, state.usernameState) } events.onNext(Event.NETWORK_FAILURE) } } @@ -169,19 +211,66 @@ internal class UsernameEditViewModel private constructor(private val isInRegistr return events.observeOn(AndroidSchedulers.mainThread()) } + private fun isCaseChange(state: UsernameEditStateMachine.State): Boolean { + if (state is UsernameEditStateMachine.UserEnteredDiscriminator || state is UsernameEditStateMachine.UserEnteredNicknameAndDiscriminator) { + return false + } + + val newLower = state.nickname.lowercase() + val oldLower = SignalStore.account().username?.split(Usernames.DELIMITER)?.firstOrNull()?.lowercase() + + return newLower == oldLower + } + /** Triggered when the debounced nickname event stream fires. */ - private fun onNicknameUpdatedDebounced(nickname: String) { + private fun onUsernameStateUpdateDebounced(state: UsernameEditStateMachine.State) { + val nickname = state.nickname if (nickname.isBlank()) { return } + if (state is UsernameEditStateMachine.NoUserEntry || state.stateModifier == UsernameEditStateMachine.StateModifier.SYSTEM) { + return + } + val invalidReason: InvalidReason? = checkUsername(nickname) if (invalidReason != null) { - uiState.update { state -> + uiState.update { uiState -> + uiState.copy( + buttonState = ButtonState.SUBMIT_DISABLED, + usernameStatus = mapNicknameError(invalidReason) + ) + } + return + } + + if (isCaseChange(state)) { + val discriminator = SignalStore.account().username?.split(Usernames.DELIMITER)?.lastOrNull() ?: error("Unexpected case change, no discriminator!") + uiState.update { State( + buttonState = ButtonState.SUBMIT, + usernameStatus = UsernameStatus.NONE, + usernameState = UsernameState.CaseChange(Username("${state.nickname}${Usernames.DELIMITER}$discriminator")) + ) + } + + stateMachineStore.update { s -> s.onSystemChangedDiscriminator(discriminator) } + return + } + + val isDiscriminatorSetByUser = state is UsernameEditStateMachine.UserEnteredDiscriminator || state is UsernameEditStateMachine.UserEnteredNicknameAndDiscriminator + val discriminator = if (isDiscriminatorSetByUser) { + state.discriminator + } else { + null + } + + val discriminatorInvalidReason = checkDiscriminator(discriminator) + if (isDiscriminatorSetByUser && discriminatorInvalidReason != null) { + uiState.update { uiState -> + uiState.copy( buttonState = ButtonState.SUBMIT_DISABLED, - usernameStatus = mapUsernameError(invalidReason), - username = state.username + usernameStatus = mapDiscriminatorError(discriminatorInvalidReason) ) } return @@ -189,26 +278,41 @@ internal class UsernameEditViewModel private constructor(private val isInRegistr uiState.update { State(ButtonState.SUBMIT_DISABLED, UsernameStatus.NONE, UsernameState.Loading) } - disposables += UsernameRepository.reserveUsername(nickname).subscribe { result: Result -> + disposables += UsernameRepository.reserveUsername(nickname, discriminator).subscribe { result: Result -> result.either( onSuccess = { reserved: UsernameState.Reserved -> uiState.update { State(ButtonState.SUBMIT, UsernameStatus.NONE, reserved) } + + val d = reserved.getDiscriminator() + if (!isDiscriminatorSetByUser && d != null) { + stateMachineStore.update { s -> s.onSystemChangedDiscriminator(d) } + } }, onFailure = { failure: UsernameSetResult -> when (failure) { UsernameSetResult.SUCCESS -> { throw AssertionError() } + UsernameSetResult.USERNAME_INVALID -> { uiState.update { State(ButtonState.SUBMIT_DISABLED, UsernameStatus.INVALID_GENERIC, UsernameState.NoUsername) } } + UsernameSetResult.USERNAME_UNAVAILABLE -> { - uiState.update { State(ButtonState.SUBMIT_DISABLED, UsernameStatus.TAKEN, UsernameState.NoUsername) } + val status = if (isDiscriminatorSetByUser) { + UsernameStatus.DISCRIMINATOR_NOT_AVAILABLE + } else { + UsernameStatus.TAKEN + } + + uiState.update { State(ButtonState.SUBMIT_DISABLED, status, UsernameState.NoUsername) } } + UsernameSetResult.NETWORK_ERROR -> { uiState.update { State(ButtonState.SUBMIT, UsernameStatus.NONE, UsernameState.NoUsername) } events.onNext(Event.NETWORK_FAILURE) } + UsernameSetResult.CANDIDATE_GENERATION_ERROR -> { // TODO -- Retry uiState.update { State(ButtonState.SUBMIT_DISABLED, UsernameStatus.TAKEN, UsernameState.NoUsername) } @@ -219,14 +323,24 @@ internal class UsernameEditViewModel private constructor(private val isInRegistr } } - class State( + data class State( @JvmField val buttonState: ButtonState, @JvmField val usernameStatus: UsernameStatus, - @JvmField val username: UsernameState + @JvmField val usernameState: UsernameState ) enum class UsernameStatus { - NONE, TAKEN, TOO_SHORT, TOO_LONG, CANNOT_START_WITH_NUMBER, INVALID_CHARACTERS, INVALID_GENERIC + NONE, + TAKEN, + TOO_SHORT, + TOO_LONG, + CANNOT_START_WITH_NUMBER, + INVALID_CHARACTERS, + INVALID_GENERIC, + DISCRIMINATOR_NOT_AVAILABLE, + DISCRIMINATOR_TOO_SHORT, + DISCRIMINATOR_TOO_LONG, + DISCRIMINATOR_HAS_INVALID_CHARACTERS } enum class ButtonState { @@ -244,14 +358,24 @@ internal class UsernameEditViewModel private constructor(private val isInRegistr } companion object { + private val TAG = Log.tag(UsernameEditViewModel::class.java) + private const val NICKNAME_PUBLISHER_DEBOUNCE_TIMEOUT_MILLIS: Long = 1000 - private fun mapUsernameError(invalidReason: InvalidReason): UsernameStatus { + private fun mapNicknameError(invalidReason: InvalidReason): UsernameStatus { return when (invalidReason) { InvalidReason.TOO_SHORT -> UsernameStatus.TOO_SHORT InvalidReason.TOO_LONG -> UsernameStatus.TOO_LONG InvalidReason.STARTS_WITH_NUMBER -> UsernameStatus.CANNOT_START_WITH_NUMBER InvalidReason.INVALID_CHARACTERS -> UsernameStatus.INVALID_CHARACTERS + } + } + + private fun mapDiscriminatorError(invalidReason: InvalidReason): UsernameStatus { + return when (invalidReason) { + InvalidReason.TOO_SHORT -> UsernameStatus.DISCRIMINATOR_TOO_SHORT + InvalidReason.TOO_LONG -> UsernameStatus.DISCRIMINATOR_TOO_LONG + InvalidReason.INVALID_CHARACTERS -> UsernameStatus.DISCRIMINATOR_HAS_INVALID_CHARACTERS else -> UsernameStatus.INVALID_GENERIC } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameRepository.kt index 3ca42d667a..defe771cec 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameRepository.kt @@ -27,6 +27,7 @@ import org.whispersystems.signalservice.api.push.exceptions.UsernameIsNotAssocia import org.whispersystems.signalservice.api.push.exceptions.UsernameIsNotReservedException import org.whispersystems.signalservice.api.push.exceptions.UsernameMalformedException import org.whispersystems.signalservice.api.push.exceptions.UsernameTakenException +import org.whispersystems.signalservice.api.util.Usernames import org.whispersystems.signalservice.api.util.UuidUtil import org.whispersystems.signalservice.api.util.toByteArray import java.io.IOException @@ -83,45 +84,99 @@ object UsernameRepository { private val URL_REGEX = """(https://)?signal.me/?#eu/([a-zA-Z0-9+\-_/]+)""".toRegex() - val BASE_URL = "https://signal.me/#eu/" + private const val BASE_URL = "https://signal.me/#eu/" + private const val USERNAME_SYNC_ERROR_THRESHOLD = 3 private val accountManager: SignalServiceAccountManager get() = ApplicationDependencies.getSignalServiceAccountManager() /** - * Given a nickname, this will temporarily reserve a matching discriminator that can later be confirmed via [confirmUsername]. + * Given a nickname, this will temporarily reserve a matching discriminator that can later be confirmed via [confirmUsernameAndCreateNewLink]. */ - fun reserveUsername(nickname: String): Single> { + fun reserveUsername(nickname: String, discriminator: String?): Single> { return Single - .fromCallable { reserveUsernameInternal(nickname) } + .fromCallable { reserveUsernameInternal(nickname, discriminator) } + .subscribeOn(Schedulers.io()) + } + + /** + * This changes the encrypted username associated with your current username link. + * The intent of this is to allow users to change the casing of their username without changing the link, + * since usernames are case-insensitive. + */ + fun updateUsernameDisplayForCurrentLink(updatedUsername: Username): Single { + return Single + .fromCallable { updateUsernameDisplayForCurrentLinkInternal(updatedUsername) } .subscribeOn(Schedulers.io()) } /** * Given a reserved username (obtained via [reserveUsername]), this will confirm that reservation, assigning the user that username. + * It will also create a new username link. Therefore, be sure to call [updateUsernameDisplayForCurrentLink] instead if all that has changed is the + * casing, and you want to keep the link the same. */ - fun confirmUsername(reserved: UsernameState.Reserved): Single { + fun confirmUsernameAndCreateNewLink(username: Username): Single { return Single - .fromCallable { confirmUsernameInternal(reserved) } + .fromCallable { confirmUsernameAndCreateNewLinkInternal(username) } .subscribeOn(Schedulers.io()) } + /** + * Attempts to reclaim the username that is currently stored on disk if necessary. + * This is intended to be used after registration. + * + * This method call may result in mutating [SignalStore] state. + */ + @WorkerThread + @JvmStatic + fun reclaimUsernameIfNecessary(): UsernameReclaimResult { + if (!SignalStore.misc().needsUsernameRestore()) { + Log.d(TAG, "[reclaimUsernameIfNecessary] No need to restore username. Skipping.") + return UsernameReclaimResult.SUCCESS + } + + val username = SignalStore.account().username + val link = SignalStore.account().usernameLink + + if (username == null || link == null) { + Log.d(TAG, "[reclaimUsernameIfNecessary] No username or link to restore. Skipping.") + SignalStore.misc().setNeedsUsernameRestore(false) + return UsernameReclaimResult.SUCCESS + } + + val result = reclaimUsernameIfNecessaryInternal(Username(username), link) + + when (result) { + UsernameReclaimResult.SUCCESS -> { + Log.i(TAG, "[reclaimUsernameIfNecessary] Successfully reclaimed username and link.") + SignalStore.misc().setNeedsUsernameRestore(false) + } + + UsernameReclaimResult.PERMANENT_ERROR -> { + Log.w(TAG, "[reclaimUsernameIfNecessary] Permanently failed to reclaim username and link. User will see an error.") + SignalStore.account().usernameSyncState = AccountValues.UsernameSyncState.USERNAME_AND_LINK_CORRUPTED + SignalStore.misc().setNeedsUsernameRestore(false) + } + + UsernameReclaimResult.NETWORK_ERROR -> { + Log.w(TAG, "[reclaimUsernameIfNecessary] Hit a transient network error while trying to reclaim username and link.") + } + } + + return result + } + /** * Deletes the username from the local user's account */ @JvmStatic - fun deleteUsername(): Single { + fun deleteUsernameAndLink(): Single { return Single .fromCallable { deleteUsernameInternal() } .subscribeOn(Schedulers.io()) } /** - * Creates or rotates the username link for the local user. If successful, the [UsernameLinkComponents] will be returned. - * If it fails for any reason, the optional will be empty. - * - * The assumption here is that when the user clicks this button, they will either have a new link, or no link at all. - * This is to prevent indeterminate states where the network call fails but may have actually succeeded, that kind of thing. - * As such, it's recommended to block calling this method on a network check. + * Creates or rotates the username link for the local user. */ fun createOrResetUsernameLink(): Single { if (!NetworkUtil.isConnected(ApplicationDependencies.getApplication())) { @@ -153,6 +208,7 @@ object UsernameRepository { if (SignalStore.account().usernameSyncState == AccountValues.UsernameSyncState.LINK_CORRUPTED) { SignalStore.account().usernameSyncState = AccountValues.UsernameSyncState.IN_SYNC + SignalStore.account().usernameSyncErrorCount = 0 } SignalDatabase.recipients.markNeedsSync(Recipient.self().id) @@ -217,6 +273,9 @@ object UsernameRepository { } catch (e: UsernameIsNotAssociatedWithAnAccountException) { Log.w(TAG, "[fetchAciFromUsername] Failed to get ACI for username hash", e) UsernameAciFetchResult.NotFound + } catch (e: BaseUsernameException) { + Log.w(TAG, "[fetchAciFromUsername] Invalid username", e) + UsernameAciFetchResult.NotFound } catch (e: IOException) { Log.w(TAG, "[fetchAciFromUsername] Hit network error while trying to resolve ACI from username", e) UsernameAciFetchResult.NetworkError @@ -251,10 +310,52 @@ object UsernameRepository { return BASE_URL + base64 } + @JvmStatic + fun onUsernameConsistencyValidated() { + SignalStore.account().usernameSyncState = AccountValues.UsernameSyncState.IN_SYNC + + if (SignalStore.account().usernameSyncErrorCount > 0) { + Log.i(TAG, "Username consistency validated. There were previously ${SignalStore.account().usernameSyncErrorCount} error(s).") + SignalStore.account().usernameSyncErrorCount = 0 + } + } + + @JvmStatic + fun onUsernameMismatchDetected() { + SignalStore.account().usernameSyncErrorCount++ + + if (SignalStore.account().usernameSyncErrorCount >= USERNAME_SYNC_ERROR_THRESHOLD) { + Log.w(TAG, "We've now seen ${SignalStore.account().usernameSyncErrorCount} mismatches in a row. Marking username and link as corrupted.") + SignalStore.account().usernameSyncState = AccountValues.UsernameSyncState.USERNAME_AND_LINK_CORRUPTED + SignalStore.account().usernameSyncErrorCount = 0 + } else { + Log.w(TAG, "Username mismatch reported. At ${SignalStore.account().usernameSyncErrorCount} / $USERNAME_SYNC_ERROR_THRESHOLD tries.") + } + } + + @JvmStatic + fun onUsernameLinkMismatchDetected() { + SignalStore.account().usernameSyncErrorCount++ + + if (SignalStore.account().usernameSyncErrorCount >= USERNAME_SYNC_ERROR_THRESHOLD) { + Log.w(TAG, "We've now seen ${SignalStore.account().usernameSyncErrorCount} mismatches in a row. Marking link as corrupted.") + SignalStore.account().usernameSyncState = AccountValues.UsernameSyncState.LINK_CORRUPTED + SignalStore.account().usernameLink = null + SignalStore.account().usernameSyncErrorCount = 0 + StorageSyncHelper.scheduleSyncForDataChange() + } else { + Log.w(TAG, "Link mismatch reported. At ${SignalStore.account().usernameSyncErrorCount} / $USERNAME_SYNC_ERROR_THRESHOLD tries.") + } + } + @WorkerThread - private fun reserveUsernameInternal(nickname: String): Result { + private fun reserveUsernameInternal(nickname: String, discriminator: String?): Result { return try { - val candidates: List = Username.candidatesFrom(nickname, UsernameUtil.MIN_LENGTH, UsernameUtil.MAX_LENGTH) + val candidates: List = if (discriminator == null) { + Username.candidatesFrom(nickname, UsernameUtil.MIN_NICKNAME_LENGTH, UsernameUtil.MAX_NICKNAME_LENGTH) + } else { + listOf(Username("$nickname${Usernames.DELIMITER}$discriminator")) + } val hashes: List = candidates .map { Base64.encodeUrlSafeWithoutPadding(it.hash) } @@ -268,7 +369,7 @@ object UsernameRepository { } Log.i(TAG, "[reserveUsername] Successfully reserved username.") - success(UsernameState.Reserved(candidates[hashIndex].username, response)) + success(UsernameState.Reserved(candidates[hashIndex])) } catch (e: BaseUsernameException) { Log.w(TAG, "[reserveUsername] An error occurred while generating candidates.") failure(UsernameSetResult.CANDIDATE_GENERATION_ERROR) @@ -285,58 +386,64 @@ object UsernameRepository { } @WorkerThread - private fun confirmUsernameInternal(reserved: UsernameState.Reserved): UsernameSetResult { + private fun updateUsernameDisplayForCurrentLinkInternal(updatedUsername: Username): UsernameSetResult { + Log.i(TAG, "[updateUsernameDisplayForCurrentLink] Beginning username update...") + return try { - val username = Username(reserved.username) - accountManager.confirmUsername(reserved.username, reserved.reserveUsernameResponse) - SignalStore.account().username = username.username - SignalStore.account().usernameLink = null - SignalDatabase.recipients.setUsername(Recipient.self().id, reserved.username) + val oldUsernameLink = SignalStore.account().usernameLink ?: return UsernameSetResult.USERNAME_INVALID + val newUsernameLink = updatedUsername.generateLink(oldUsernameLink.entropy) + val usernameLinkComponents = accountManager.updateUsernameLink(newUsernameLink) + + SignalStore.account().username = updatedUsername.username + SignalStore.account().usernameLink = usernameLinkComponents + SignalDatabase.recipients.setUsername(Recipient.self().id, updatedUsername.username) SignalStore.account().usernameSyncState = AccountValues.UsernameSyncState.IN_SYNC + SignalStore.account().usernameSyncErrorCount = 0 SignalDatabase.recipients.markNeedsSync(Recipient.self().id) StorageSyncHelper.scheduleSyncForDataChange() - Log.i(TAG, "[confirmUsername] Successfully confirmed username.") + Log.i(TAG, "[updateUsernameDisplayForCurrentLink] Successfully updated username.") - if (tryToSetUsernameLink(username)) { - Log.i(TAG, "[confirmUsername] Successfully confirmed username link.") - } else { - Log.w(TAG, "[confirmUsername] Failed to confirm a username link. We'll try again when the user goes to view their link.") - } + UsernameSetResult.SUCCESS + } catch (e: IOException) { + Log.w(TAG, "[updateUsernameDisplayForCurrentLink] Generic network exception.", e) + UsernameSetResult.NETWORK_ERROR + } + } + + @WorkerThread + private fun confirmUsernameAndCreateNewLinkInternal(username: Username): UsernameSetResult { + Log.i(TAG, "[confirmUsernameAndCreateNewLink] Beginning username confirmation...") + + return try { + val linkComponents: UsernameLinkComponents = accountManager.confirmUsernameAndCreateNewLink(username) + + SignalStore.account().username = username.username + SignalStore.account().usernameLink = linkComponents + SignalDatabase.recipients.setUsername(Recipient.self().id, username.username) + SignalStore.account().usernameSyncState = AccountValues.UsernameSyncState.IN_SYNC + SignalStore.account().usernameSyncErrorCount = 0 + + SignalDatabase.recipients.markNeedsSync(Recipient.self().id) + StorageSyncHelper.scheduleSyncForDataChange() + Log.i(TAG, "[confirmUsernameAndCreateNewLink] Successfully confirmed username.") UsernameSetResult.SUCCESS } catch (e: UsernameTakenException) { - Log.w(TAG, "[confirmUsername] Username gone.") + Log.w(TAG, "[confirmUsernameAndCreateNewLink] Username gone.") UsernameSetResult.USERNAME_UNAVAILABLE } catch (e: UsernameIsNotReservedException) { - Log.w(TAG, "[confirmUsername] Username was not reserved.") + Log.w(TAG, "[confirmUsernameAndCreateNewLink] Username was not reserved.") UsernameSetResult.USERNAME_INVALID } catch (e: BaseUsernameException) { - Log.w(TAG, "[confirmUsername] Username was not reserved.") + Log.w(TAG, "[confirmUsernameAndCreateNewLink] Username was not reserved.") UsernameSetResult.USERNAME_INVALID } catch (e: IOException) { - Log.w(TAG, "[confirmUsername] Generic network exception.", e) + Log.w(TAG, "[confirmUsernameAndCreateNewLink] Generic network exception.", e) UsernameSetResult.NETWORK_ERROR } } - private fun tryToSetUsernameLink(username: Username): Boolean { - for (i in 0..2) { - try { - val linkComponents = accountManager.createUsernameLink(username) - SignalStore.account().usernameLink = linkComponents - - SignalDatabase.recipients.markNeedsSync(Recipient.self().id) - StorageSyncHelper.scheduleSyncForDataChange() - return true - } catch (e: IOException) { - Log.w(TAG, "[tryToSetUsernameLink] Failed with IOException on attempt " + (i + 1) + "/3", e) - } - } - - return false - } - @WorkerThread private fun deleteUsernameInternal(): UsernameDeleteResult { return try { @@ -345,6 +452,7 @@ object UsernameRepository { SignalStore.account().username = null SignalStore.account().usernameLink = null SignalStore.account().usernameSyncState = AccountValues.UsernameSyncState.IN_SYNC + SignalStore.account().usernameSyncErrorCount = 0 SignalDatabase.recipients.markNeedsSync(Recipient.self().id) StorageSyncHelper.scheduleSyncForDataChange() Log.i(TAG, "[deleteUsername] Successfully deleted the username.") @@ -355,10 +463,36 @@ object UsernameRepository { } } + @WorkerThread + @JvmStatic + private fun reclaimUsernameIfNecessaryInternal(username: Username, usernameLinkComponents: UsernameLinkComponents): UsernameReclaimResult { + try { + accountManager.reclaimUsernameAndLink(username, usernameLinkComponents) + } catch (e: UsernameTakenException) { + Log.w(TAG, "[reclaimUsername] Username gone.") + return UsernameReclaimResult.PERMANENT_ERROR + } catch (e: UsernameIsNotReservedException) { + Log.w(TAG, "[reclaimUsername] Username was not reserved.") + return UsernameReclaimResult.PERMANENT_ERROR + } catch (e: BaseUsernameException) { + Log.w(TAG, "[reclaimUsername] Invalid username.") + return UsernameReclaimResult.PERMANENT_ERROR + } catch (e: IOException) { + Log.w(TAG, "[reclaimUsername] Network error.", e) + return UsernameReclaimResult.NETWORK_ERROR + } + + return UsernameReclaimResult.SUCCESS + } + enum class UsernameSetResult { SUCCESS, USERNAME_UNAVAILABLE, USERNAME_INVALID, NETWORK_ERROR, CANDIDATE_GENERATION_ERROR } + enum class UsernameReclaimResult { + SUCCESS, PERMANENT_ERROR, NETWORK_ERROR + } + enum class UsernameDeleteResult { SUCCESS, NETWORK_ERROR } diff --git a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameShareBottomSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameShareBottomSheet.kt index ab9a9e2bbd..ba8510dae1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameShareBottomSheet.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameShareBottomSheet.kt @@ -38,6 +38,10 @@ class UsernameShareBottomSheet : DSLSettingsBottomSheetFragment() { ShareButton.register(adapter) lifecycleDisposable += Recipient.observable(Recipient.self().id).subscribe { + if (context == null) { + return@subscribe + } + adapter.submitList(getConfiguration(it).toMappingModelList()) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameState.kt b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameState.kt index e6e225e420..da5094b663 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/profiles/manage/UsernameState.kt @@ -1,15 +1,19 @@ package org.thoughtcrime.securesms.profiles.manage -import org.whispersystems.signalservice.internal.push.ReserveUsernameResponse +import org.signal.libsignal.usernames.Username +import org.whispersystems.signalservice.api.util.discriminator +import org.whispersystems.signalservice.api.util.nickname /** * Describes the state of the username suffix, which is a spanned CharSequence. */ sealed class UsernameState { - protected open val username: String? = null + protected open val username: Username? = null open val isInProgress: Boolean = false + fun requireUsername(): Username = username!! + object Loading : UsernameState() { override val isInProgress: Boolean = true } @@ -17,23 +21,22 @@ sealed class UsernameState { object NoUsername : UsernameState() data class Reserved( - public override val username: String, - val reserveUsernameResponse: ReserveUsernameResponse + public override val username: Username + ) : UsernameState() + + data class CaseChange( + public override val username: Username ) : UsernameState() data class Set( - override val username: String + override val username: Username ) : UsernameState() fun getNickname(): String? { - return username?.split(DELIMITER)?.firstOrNull() + return username?.nickname } fun getDiscriminator(): String? { - return username?.split(DELIMITER)?.lastOrNull() - } - - companion object { - const val DELIMITER = "." + return username?.discriminator } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/profiles/spoofing/ReviewBannerView.java b/app/src/main/java/org/thoughtcrime/securesms/profiles/spoofing/ReviewBannerView.java index 596525842b..7d4c3f2cea 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/profiles/spoofing/ReviewBannerView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/profiles/spoofing/ReviewBannerView.java @@ -3,34 +3,26 @@ import android.content.Context; import android.graphics.drawable.Drawable; import android.util.AttributeSet; -import android.view.View; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; +import android.widget.FrameLayout; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import org.thoughtcrime.securesms.R; -import org.thoughtcrime.securesms.components.AvatarImageView; import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto; import org.thoughtcrime.securesms.contacts.avatars.FallbackPhoto20dp; import org.thoughtcrime.securesms.contacts.avatars.GeneratedContactPhoto; import org.thoughtcrime.securesms.conversation.colors.AvatarColor; +import org.thoughtcrime.securesms.databinding.ReviewBannerViewBinding; import org.thoughtcrime.securesms.recipients.Recipient; /** * Banner displayed within a conversation when a review is suggested. */ -public class ReviewBannerView extends LinearLayout { +public class ReviewBannerView extends FrameLayout { - private ImageView bannerIcon; - private TextView bannerMessage; - private View bannerClose; - private AvatarImageView topLeftAvatar; - private AvatarImageView bottomRightAvatar; - private View stroke; - private OnHideListener onHideListener; + private ReviewBannerViewBinding binding; + private OnHideListener onHideListener; public ReviewBannerView(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); @@ -44,19 +36,14 @@ public ReviewBannerView(@NonNull Context context, @Nullable AttributeSet attrs, protected void onFinishInflate() { super.onFinishInflate(); - bannerIcon = findViewById(R.id.banner_icon); - bannerMessage = findViewById(R.id.banner_message); - bannerClose = findViewById(R.id.banner_close); - topLeftAvatar = findViewById(R.id.banner_avatar_1); - bottomRightAvatar = findViewById(R.id.banner_avatar_2); - stroke = findViewById(R.id.banner_avatar_stroke); + binding = ReviewBannerViewBinding.bind(this); FallbackPhotoProvider provider = new FallbackPhotoProvider(); - topLeftAvatar.setFallbackPhotoProvider(provider); - bottomRightAvatar.setFallbackPhotoProvider(provider); + binding.bannerBottomRightAvatar.setFallbackPhotoProvider(provider); + binding.bannerTopLeftAvatar.setFallbackPhotoProvider(provider); - bannerClose.setOnClickListener(v -> { + binding.bannerClose.setOnClickListener(v -> { if (onHideListener != null && onHideListener.onHide()) { return; } @@ -70,26 +57,32 @@ public void setOnHideListener(@Nullable OnHideListener onHideListener) { } public void setBannerMessage(@Nullable CharSequence charSequence) { - bannerMessage.setText(charSequence); + binding.bannerMessage.setText(charSequence); } public void setBannerIcon(@Nullable Drawable icon) { - bannerIcon.setImageDrawable(icon); + binding.bannerIcon.setImageDrawable(icon); - bannerIcon.setVisibility(VISIBLE); - topLeftAvatar.setVisibility(GONE); - bottomRightAvatar.setVisibility(GONE); - stroke.setVisibility(GONE); + binding.bannerIcon.setVisibility(VISIBLE); + binding.bannerTopLeftAvatar.setVisibility(GONE); + binding.bannerBottomRightAvatar.setVisibility(GONE); + binding.bannerAvatarStroke.setVisibility(GONE); } public void setBannerRecipient(@NonNull Recipient recipient) { - topLeftAvatar.setAvatar(recipient); - bottomRightAvatar.setAvatar(recipient); + binding.bannerTopLeftAvatar.setAvatar(recipient); + binding.bannerBottomRightAvatar.setAvatar(recipient); - bannerIcon.setVisibility(GONE); - topLeftAvatar.setVisibility(VISIBLE); - bottomRightAvatar.setVisibility(VISIBLE); - stroke.setVisibility(VISIBLE); + binding.bannerIcon.setVisibility(GONE); + binding.bannerTopLeftAvatar.setVisibility(VISIBLE); + binding.bannerBottomRightAvatar.setVisibility(VISIBLE); + binding.bannerAvatarStroke.setVisibility(VISIBLE); + } + + @Override + public void setOnClickListener(@Nullable OnClickListener l) { + super.setOnClickListener(l); + binding.bannerTapToReview.setOnClickListener(l); } private static final class FallbackPhotoProvider extends Recipient.FallbackPhotoProvider { diff --git a/app/src/main/java/org/thoughtcrime/securesms/profiles/spoofing/ReviewCardAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/profiles/spoofing/ReviewCardAdapter.java index 5178deb4eb..2432e42d8f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/profiles/spoofing/ReviewCardAdapter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/profiles/spoofing/ReviewCardAdapter.java @@ -44,6 +44,7 @@ public void onBindViewHolder(@NonNull ReviewCardViewHolder holder, int position) interface Callbacks { void onCardClicked(@NonNull ReviewCard card); void onActionClicked(@NonNull ReviewCard card, @NonNull ReviewCard.Action action); + void onSignalConnectionClicked(); } private final class CallbacksAdapter implements ReviewCardViewHolder.Callbacks { @@ -70,5 +71,10 @@ public void onSecondaryActionItemClicked(int position) { ReviewCard card = getItem(position); callback.onActionClicked(card, Objects.requireNonNull(card.getSecondaryAction())); } + + @Override + public void onSignalConnectionClicked() { + callback.onSignalConnectionClicked(); + } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/profiles/spoofing/ReviewCardDialogFragment.java b/app/src/main/java/org/thoughtcrime/securesms/profiles/spoofing/ReviewCardDialogFragment.java index d89de6e602..f4e253c897 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/profiles/spoofing/ReviewCardDialogFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/profiles/spoofing/ReviewCardDialogFragment.java @@ -22,6 +22,8 @@ import org.thoughtcrime.securesms.groups.GroupId; import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.recipients.ui.bottomsheet.RecipientBottomSheetDialogFragment; +import org.thoughtcrime.securesms.stories.settings.my.SignalConnectionsBottomSheetDialogFragment; +import org.thoughtcrime.securesms.util.BottomSheetUtil; public class ReviewCardDialogFragment extends FullScreenDialogFragment { @@ -202,5 +204,10 @@ public void onActionClicked(@NonNull ReviewCard card, @NonNull ReviewCard.Action viewModel.act(card, action); } } + + @Override + public void onSignalConnectionClicked() { + new SignalConnectionsBottomSheetDialogFragment().show(getParentFragmentManager(), BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG); + } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/profiles/spoofing/ReviewCardViewHolder.java b/app/src/main/java/org/thoughtcrime/securesms/profiles/spoofing/ReviewCardViewHolder.java index d23d236342..63f6836e12 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/profiles/spoofing/ReviewCardViewHolder.java +++ b/app/src/main/java/org/thoughtcrime/securesms/profiles/spoofing/ReviewCardViewHolder.java @@ -1,31 +1,37 @@ package org.thoughtcrime.securesms.profiles.spoofing; import android.content.Context; +import android.graphics.drawable.Drawable; +import android.text.SpannableStringBuilder; +import android.util.Pair; import android.view.View; import android.widget.Button; +import android.widget.ImageView; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.PluralsRes; import androidx.annotation.StringRes; +import androidx.core.content.ContextCompat; import androidx.recyclerview.widget.RecyclerView; import org.thoughtcrime.securesms.R; -import org.thoughtcrime.securesms.components.AvatarImageView; +import org.thoughtcrime.securesms.databinding.ReviewCardBinding; import org.thoughtcrime.securesms.util.SpanUtil; +import org.whispersystems.signalservice.api.util.Preconditions; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; class ReviewCardViewHolder extends RecyclerView.ViewHolder { - private final int noGroupsInCommonResId; - private final int groupsInCommonResId; - private final TextView title; - private final AvatarImageView avatar; - private final TextView name; - private final TextView subtextLine1; - private final TextView subtextLine2; - private final Button primaryAction; - private final Button secondaryAction; + private final int noGroupsInCommonResId; + private final int groupsInCommonResId; + private final ReviewCardBinding binding; + private final List> subtextGroups; + private final Runnable onSignalConnectionClicked; public ReviewCardViewHolder(@NonNull View itemView, @StringRes int noGroupsInCommonResId, @@ -36,69 +42,188 @@ public ReviewCardViewHolder(@NonNull View itemView, this.noGroupsInCommonResId = noGroupsInCommonResId; this.groupsInCommonResId = groupsInCommonResId; - this.title = itemView.findViewById(R.id.card_title); - this.avatar = itemView.findViewById(R.id.card_avatar); - this.name = itemView.findViewById(R.id.card_name); - this.subtextLine1 = itemView.findViewById(R.id.card_subtext_line1); - this.subtextLine2 = itemView.findViewById(R.id.card_subtext_line2); - this.primaryAction = itemView.findViewById(R.id.card_primary_action_button); - this.secondaryAction = itemView.findViewById(R.id.card_secondary_action_button); + this.binding = ReviewCardBinding.bind(itemView); + + this.subtextGroups = Arrays.asList( + Pair.create(binding.cardSubtextLine1, binding.cardSubtextIcon1), + Pair.create(binding.cardSubtextLine2, binding.cardSubtextIcon2), + Pair.create(binding.cardSubtextLine3, binding.cardSubtextIcon3), + Pair.create(binding.cardSubtextLine4, binding.cardSubtextIcon4) + ); itemView.findViewById(R.id.card_tap_target).setOnClickListener(unused -> { - if (getAdapterPosition() != RecyclerView.NO_POSITION) { - callbacks.onCardClicked(getAdapterPosition()); + if (getBindingAdapterPosition() != RecyclerView.NO_POSITION) { + callbacks.onCardClicked(getBindingAdapterPosition()); } }); - primaryAction.setOnClickListener(unused -> { - if (getAdapterPosition() != RecyclerView.NO_POSITION) { - callbacks.onPrimaryActionItemClicked(getAdapterPosition()); + binding.cardPrimaryActionButton.setOnClickListener(unused -> { + if (getBindingAdapterPosition() != RecyclerView.NO_POSITION) { + callbacks.onPrimaryActionItemClicked(getBindingAdapterPosition()); } }); - secondaryAction.setOnClickListener(unused -> { - if (getAdapterPosition() != RecyclerView.NO_POSITION) { - callbacks.onSecondaryActionItemClicked(getAdapterPosition()); + binding.cardSecondaryActionButton.setOnClickListener(unused -> { + if (getBindingAdapterPosition() != RecyclerView.NO_POSITION) { + callbacks.onSecondaryActionItemClicked(getBindingAdapterPosition()); } }); + + onSignalConnectionClicked = callbacks::onSignalConnectionClicked; } void bind(@NonNull ReviewCard reviewCard) { Context context = itemView.getContext(); - avatar.setAvatar(reviewCard.getReviewRecipient()); - name.setText(reviewCard.getReviewRecipient().getDisplayName(context)); - title.setText(getTitleResId(reviewCard.getCardType())); - - switch (reviewCard.getCardType()) { - case MEMBER: - case REQUEST: - setNonContactSublines(context, reviewCard); - break; - case YOUR_CONTACT: - subtextLine1.setText(reviewCard.getReviewRecipient().getE164().orElse(null)); - subtextLine2.setText(getGroupsInCommon(reviewCard.getInCommonGroupsCount())); - break; - default: - throw new AssertionError(); + binding.cardAvatar.setAvatarUsingProfile(reviewCard.getReviewRecipient()); + + String name = reviewCard.getReviewRecipient().isSelf() + ? context.getString(R.string.AboutSheet__you) + : reviewCard.getReviewRecipient().getDisplayName(context); + + binding.cardName.setText(name); + + int titleTextResId = getTitleResId(reviewCard.getCardType()); + if (titleTextResId > 0) { + binding.cardTitle.setText(getTitleResId(reviewCard.getCardType())); + } else { + binding.cardTitle.setVisibility(View.GONE); } + List rows = switch (reviewCard.getCardType()) { + case MEMBER, REQUEST -> getNonContactSublines(reviewCard); + case YOUR_CONTACT -> getContactSublines(reviewCard); + }; + + presentReviewTextRows(rows, context, reviewCard); + setActions(reviewCard); } - private void setNonContactSublines(@NonNull Context context, @NonNull ReviewCard reviewCard) { - subtextLine1.setText(getGroupsInCommon(reviewCard.getInCommonGroupsCount())); + private List getNonContactSublines(@NonNull ReviewCard reviewCard) { + List reviewTextRows = new ArrayList<>(subtextGroups.size()); + + if (reviewCard.getReviewRecipient().isProfileSharing() && !reviewCard.getReviewRecipient().isSelf()) { + reviewTextRows.add(ReviewTextRow.SIGNAL_CONNECTION); + } + + if (reviewCard.getReviewRecipient().isSystemContact()) { + reviewTextRows.add(ReviewTextRow.SYSTEM_CONTACTS); + } if (reviewCard.getNameChange() != null) { - subtextLine2.setText(SpanUtil.italic(context.getString(R.string.ReviewCard__recently_changed, - reviewCard.getNameChange().previous, - reviewCard.getNameChange().newValue))); + reviewTextRows.add(ReviewTextRow.RECENTLY_CHANGED); + } + + reviewTextRows.add(ReviewTextRow.GROUPS_IN_COMMON); + + return reviewTextRows; + } + + private List getContactSublines(@NonNull ReviewCard reviewCard) { + List reviewTextRows = new ArrayList<>(subtextGroups.size()); + + if (reviewCard.getReviewRecipient().isProfileSharing() && !reviewCard.getReviewRecipient().isSelf()) { + reviewTextRows.add(ReviewTextRow.SIGNAL_CONNECTION); + } + + if (reviewCard.getReviewRecipient().isSystemContact()) { + reviewTextRows.add(ReviewTextRow.SYSTEM_CONTACTS); + } + + if (reviewCard.getReviewRecipient().hasE164() && reviewCard.getReviewRecipient().shouldShowE164()) { + reviewTextRows.add(ReviewTextRow.PHONE_NUMBER); + } + + reviewTextRows.add(ReviewTextRow.GROUPS_IN_COMMON); + + return reviewTextRows; + } + + private void presentReviewTextRows(@NonNull List reviewTextRows, @NonNull Context context, @NonNull ReviewCard reviewCard) { + + for (Pair group : subtextGroups) { + setVisibility(View.GONE, group.first, group.second); + } + + for (int i = 0; i < Math.min(reviewTextRows.size(), subtextGroups.size()); i++) { + ReviewTextRow row = reviewTextRows.get(i); + Pair group = subtextGroups.get(i); + + setVisibility(View.VISIBLE, group.first, group.second); + + switch (row) { + case SIGNAL_CONNECTION -> presentSignalConnection(group.first, group.second, context, reviewCard); + case PHONE_NUMBER -> presentPhoneNumber(group.first, group.second, reviewCard); + case RECENTLY_CHANGED -> presentRecentlyChanged(group.first, group.second, context, reviewCard); + case GROUPS_IN_COMMON -> presentGroupsInCommon(group.first, group.second, reviewCard); + case SYSTEM_CONTACTS -> presentSystemContacts(group.first, group.second, context, reviewCard); + } + } + } + + private void presentSignalConnection(@NonNull TextView line, @NonNull ImageView icon, @NonNull Context context, @NonNull ReviewCard reviewCard) { + Preconditions.checkArgument(reviewCard.getReviewRecipient().isProfileSharing()); + + Drawable chevron = ContextCompat.getDrawable(context, R.drawable.symbol_chevron_right_24); + Preconditions.checkNotNull(chevron); + chevron.setTint(ContextCompat.getColor(context, R.color.core_grey_45)); + + SpannableStringBuilder builder = new SpannableStringBuilder(context.getString(R.string.AboutSheet__signal_connection)); + SpanUtil.appendCenteredImageSpan(builder, chevron, 20, 20); + + icon.setImageResource(R.drawable.symbol_connections_compact_16); + line.setText(builder); + line.setOnClickListener(v -> onSignalConnectionClicked.run()); + } + + private void presentPhoneNumber(@NonNull TextView line, @NonNull ImageView icon, @NonNull ReviewCard reviewCard) { + icon.setImageResource(R.drawable.symbol_phone_compact_16); + line.setText(reviewCard.getReviewRecipient().requireE164()); + line.setOnClickListener(null); + line.setClickable(false); + } + + private void presentRecentlyChanged(@NonNull TextView line, @NonNull ImageView icon, @NonNull Context context, @NonNull ReviewCard reviewCard) { + Preconditions.checkNotNull(reviewCard.getNameChange()); + + icon.setImageResource(R.drawable.symbol_person_compact_16); + line.setText(context.getString(R.string.ReviewCard__s_recently_changed, + reviewCard.getReviewRecipient().getShortDisplayName(context), + reviewCard.getNameChange().previous, + reviewCard.getNameChange().newValue)); + line.setOnClickListener(null); + line.setClickable(false); + } + + private void presentGroupsInCommon(@NonNull TextView line, @NonNull ImageView icon, @NonNull ReviewCard reviewCard) { + icon.setImageResource(R.drawable.symbol_group_compact_16); + line.setText(getGroupsInCommon(reviewCard.getInCommonGroupsCount())); + line.setOnClickListener(null); + line.setClickable(false); + } + + private void presentSystemContacts(@NonNull TextView line, @NonNull ImageView icon, @NonNull Context context, @NonNull ReviewCard reviewCard) { + icon.setImageResource(R.drawable.symbol_person_circle_compat_16); + line.setText(context.getString(R.string.ReviewCard__s_is_in_your_system_contacts, reviewCard.getReviewRecipient().getShortDisplayName(context))); + line.setOnClickListener(null); + line.setClickable(false); + } + + private void setVisibility(int visibility, View... views) { + for (View view : views) { + view.setVisibility(visibility); } } private void setActions(@NonNull ReviewCard reviewCard) { - setAction(reviewCard.getPrimaryAction(), primaryAction); - setAction(reviewCard.getSecondaryAction(), secondaryAction); + if (reviewCard.getReviewRecipient().isSelf()) { + setAction(null, binding.cardPrimaryActionButton); + setAction(null, binding.cardSecondaryActionButton); + } else { + setAction(reviewCard.getPrimaryAction(), binding.cardPrimaryActionButton); + setAction(reviewCard.getSecondaryAction(), binding.cardSecondaryActionButton); + } } private String getGroupsInCommon(int groupsInCommon) { @@ -120,35 +245,36 @@ private static void setAction(@Nullable ReviewCard.Action action, @NonNull Butto interface Callbacks { void onCardClicked(int position); + void onPrimaryActionItemClicked(int position); + void onSecondaryActionItemClicked(int position); + + void onSignalConnectionClicked(); } private static @StringRes int getTitleResId(@NonNull ReviewCard.CardType cardType) { - switch (cardType) { - case MEMBER: - return R.string.ReviewCard__member; - case REQUEST: - return R.string.ReviewCard__request; - case YOUR_CONTACT: - return R.string.ReviewCard__your_contact; - default: - throw new IllegalArgumentException("Unsupported card type " + cardType); - } + return switch (cardType) { + case MEMBER -> -1; + case REQUEST -> R.string.ReviewCard__request; + case YOUR_CONTACT -> R.string.ReviewCard__your_contact; + }; } private static @StringRes int getActionLabelResId(@NonNull ReviewCard.Action action) { - switch (action) { - case UPDATE_CONTACT: - return R.string.ReviewCard__update_contact; - case DELETE: - return R.string.ReviewCard__delete; - case BLOCK: - return R.string.ReviewCard__block; - case REMOVE_FROM_GROUP: - return R.string.ReviewCard__remove_from_group; - default: - throw new IllegalArgumentException("Unsupported action: " + action); - } + return switch (action) { + case UPDATE_CONTACT -> R.string.ReviewCard__update_contact; + case DELETE -> R.string.ReviewCard__delete; + case BLOCK -> R.string.ReviewCard__block; + case REMOVE_FROM_GROUP -> R.string.ReviewCard__remove_from_group; + }; + } + + private enum ReviewTextRow { + SIGNAL_CONNECTION, + PHONE_NUMBER, + RECENTLY_CHANGED, + GROUPS_IN_COMMON, + SYSTEM_CONTACTS } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/providers/PartProvider.java b/app/src/main/java/org/thoughtcrime/securesms/providers/PartProvider.java index 79ad0cc8f4..db8ffc3461 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/providers/PartProvider.java +++ b/app/src/main/java/org/thoughtcrime/securesms/providers/PartProvider.java @@ -78,8 +78,7 @@ public boolean onCreate() { } public static Uri getContentUri(AttachmentId attachmentId) { - Uri uri = Uri.withAppendedPath(CONTENT_URI, String.valueOf(attachmentId.getUniqueId())); - return ContentUris.withAppendedId(uri, attachmentId.getRowId()); + return ContentUris.withAppendedId(CONTENT_URI, attachmentId.id); } @Override @@ -134,8 +133,8 @@ public String getType(@NonNull Uri uri) { DatabaseAttachment attachment = SignalDatabase.attachments().getAttachment(partUriParser.getPartId()); if (attachment != null) { - Log.i(TAG, "getType() called: " + uri + " It's " + attachment.getContentType()); - return attachment.getContentType(); + Log.i(TAG, "getType() called: " + uri + " It's " + attachment.contentType); + return attachment.contentType; } } @@ -163,15 +162,15 @@ public Cursor query(@NonNull Uri url, @Nullable String[] projection, String sele if (attachment == null) return null; - long fileSize = attachment.getSize(); + long fileSize = attachment.size; if (fileSize <= 0) { Log.w(TAG, "Empty file " + fileSize); return null; } - String fileName = attachment.getFileName() != null ? attachment.getFileName() - : createFileNameForMimeType(attachment.getContentType()); + String fileName = attachment.fileName != null ? attachment.fileName + : createFileNameForMimeType(attachment.contentType); return createCursor(projection, fileName, fileSize); } else { @@ -229,9 +228,9 @@ public ProxyCallback(@NonNull AttachmentTable attachments, @NonNull AttachmentId @Override public long onGetSize() throws ErrnoException { DatabaseAttachment attachment = attachments.getAttachment(attachmentId); - if (attachment != null && attachment.getSize() > 0) { + if (attachment != null && attachment.size > 0) { Log.i(TAG, attachmentId + ":getSize"); - return attachment.getSize(); + return attachment.size; } else { Log.w(TAG, attachmentId + ":getSize:attachment is null or size is 0"); throw new ErrnoException("Attachment is invalid", OsConstants.ENOENT); @@ -242,7 +241,7 @@ public long onGetSize() throws ErrnoException { public int onRead(long offset, int size, byte[] data) throws ErrnoException { try { DatabaseAttachment attachment = attachments.getAttachment(attachmentId); - if (attachment == null || attachment.getSize() <= 0) { + if (attachment == null || attachment.size <= 0) { Log.w(TAG, attachmentId + ":onRead:attachment is null or size is 0"); throw new ErrnoException("Attachment is invalid", OsConstants.ENOENT); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/push/SignalServiceNetworkAccess.kt b/app/src/main/java/org/thoughtcrime/securesms/push/SignalServiceNetworkAccess.kt index 25d5f94f0c..afd75abc81 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/push/SignalServiceNetworkAccess.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/push/SignalServiceNetworkAccess.kt @@ -192,6 +192,12 @@ open class SignalServiceNetworkAccess(context: Context) { throw AssertionError(e) } + private val backupServerPublicParams: ByteArray = try { + Base64.decode(BuildConfig.BACKUP_SERVER_PUBLIC_PARAMS) + } catch (e: IOException) { + throw AssertionError(e) + } + private val baseGHostConfigs: List = listOf( HostConfig(HTTPS_WWW_GOOGLE_COM, G_HOST, GMAIL_CONNECTION_SPEC), HostConfig(HTTPS_ANDROID_CLIENTS_GOOGLE_COM, G_HOST, PLAY_CONNECTION_SPEC), @@ -217,7 +223,8 @@ open class SignalServiceNetworkAccess(context: Context) { proxySelector = Network.proxySelectorForSocks, dns = Network.dns, zkGroupServerPublicParams = zkGroupServerPublicParams, - genericServerPublicParams = genericServerPublicParams + genericServerPublicParams = genericServerPublicParams, + backupServerPublicParams = backupServerPublicParams ) private val censorshipConfiguration: Map = mapOf( @@ -270,7 +277,8 @@ open class SignalServiceNetworkAccess(context: Context) { proxySelector = Network.proxySelectorForSocks, dns = Network.dns, zkGroupServerPublicParams = zkGroupServerPublicParams, - genericServerPublicParams = genericServerPublicParams + genericServerPublicParams = genericServerPublicParams, + backupServerPublicParams = backupServerPublicParams ) open fun getConfiguration(): SignalServiceConfiguration { @@ -340,7 +348,8 @@ open class SignalServiceNetworkAccess(context: Context) { proxySelector = Network.proxySelectorForSocks, dns = Network.dns, zkGroupServerPublicParams = zkGroupServerPublicParams, - genericServerPublicParams = genericServerPublicParams + genericServerPublicParams = genericServerPublicParams, + backupServerPublicParams = backupServerPublicParams ) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/reactions/any/ReactWithAnyEmojiBottomSheetDialogFragment.java b/app/src/main/java/org/thoughtcrime/securesms/reactions/any/ReactWithAnyEmojiBottomSheetDialogFragment.java index 71b76d0fc1..16ae57fcc1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/reactions/any/ReactWithAnyEmojiBottomSheetDialogFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/reactions/any/ReactWithAnyEmojiBottomSheetDialogFragment.java @@ -59,7 +59,7 @@ public final class ReactWithAnyEmojiBottomSheetDialogFragment extends FixedRound private static final String ARG_EDIT = "arg_edit"; private ReactWithAnyEmojiViewModel viewModel; - private Callback callback; + private Callback callback = null; private EmojiPageView emojiPageView; private KeyboardPageSearchView search; private View tabBar; @@ -123,6 +123,20 @@ public static DialogFragment createForEditReactions() { return fragment; } + public static ReactWithAnyEmojiBottomSheetDialogFragment createForCallingReactions() { + ReactWithAnyEmojiBottomSheetDialogFragment fragment = new ReactWithAnyEmojiBottomSheetDialogFragment(); + Bundle args = new Bundle(); + + args.putLong(ARG_MESSAGE_ID, -1); + args.putBoolean(ARG_IS_MMS, false); + args.putInt(ARG_START_PAGE, -1); + args.putBoolean(ARG_SHADOWS, false); + args.putString(ARG_RECENT_KEY, REACTION_STORAGE_KEY); + fragment.setArguments(args); + + return fragment; + } + @Override public void onAttach(@NonNull Context context) { super.onAttach(context); @@ -229,8 +243,7 @@ public void onDestroyView() { @Override public void onDismiss(@NonNull DialogInterface dialog) { super.onDismiss(dialog); - - callback.onReactWithAnyEmojiDialogDismissed(); + if (callback != null) callback.onReactWithAnyEmojiDialogDismissed(); } private void initializeViewModel() { @@ -244,7 +257,7 @@ private void initializeViewModel() { @Override public void onEmojiSelected(String emoji) { viewModel.onEmojiSelected(emoji); - callback.onReactWithAnyEmojiSelected(emoji); + if (callback != null) callback.onReactWithAnyEmojiSelected(emoji); dismiss(); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java index be425d5a7f..320ae3f8cb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java @@ -28,6 +28,7 @@ import org.thoughtcrime.securesms.conversation.colors.ChatColorsPalette; import org.thoughtcrime.securesms.database.RecipientTable; import org.thoughtcrime.securesms.database.RecipientTable.MentionSetting; +import org.thoughtcrime.securesms.database.RecipientTable.PhoneNumberSharingState; import org.thoughtcrime.securesms.database.RecipientTable.RegisteredState; import org.thoughtcrime.securesms.database.RecipientTable.UnidentifiedAccessMode; import org.thoughtcrime.securesms.database.RecipientTable.VibrateState; @@ -47,6 +48,7 @@ import org.thoughtcrime.securesms.service.webrtc.links.CallLinkRoomId; import org.thoughtcrime.securesms.util.AvatarUtil; import org.thoughtcrime.securesms.util.FeatureFlags; +import org.thoughtcrime.securesms.util.UsernameUtil; import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.wallpaper.ChatWallpaper; import org.whispersystems.signalservice.api.push.ServiceId; @@ -137,6 +139,7 @@ public class Recipient { private final boolean needsPniSignature; private final CallLinkRoomId callLinkRoomId; private final Optional groupRecord; + private final PhoneNumberSharingState phoneNumberSharing; /** * Returns a {@link LiveRecipient}, which contains a {@link Recipient} that may or may not be @@ -355,6 +358,8 @@ public class Recipient { id = db.getOrInsertFromGroupId(GroupId.parseOrThrow(identifier)); } else if (NumberUtil.isValidEmail(identifier)) { id = db.getOrInsertFromEmail(identifier); + } else if (UsernameUtil.isValidUsernameForSearch(identifier)) { + throw new IllegalArgumentException("Creating a recipient based on username alone is not supported!"); } else { String e164 = PhoneNumberFormatter.get(context).format(identifier); id = db.getOrInsertFromE164(e164); @@ -424,6 +429,7 @@ public static boolean isSelfSet() { this.isActiveGroup = false; this.callLinkRoomId = null; this.groupRecord = Optional.empty(); + this.phoneNumberSharing = PhoneNumberSharingState.UNKNOWN; } public Recipient(@NonNull RecipientId id, @NonNull RecipientDetails details, boolean resolved) { @@ -479,6 +485,7 @@ public Recipient(@NonNull RecipientId id, @NonNull RecipientDetails details, boo this.isActiveGroup = details.isActiveGroup; this.callLinkRoomId = details.callLinkRoomId; this.groupRecord = details.groupRecord; + this.phoneNumberSharing = details.phoneNumberSharing; } public @NonNull RecipientId getId() { @@ -677,6 +684,13 @@ public boolean hasNonUsernameDisplayName(@NonNull Context context) { return Optional.ofNullable(e164); } + /** + * Whether or not we should show this user's e164 in the interface. + */ + public boolean shouldShowE164() { + return hasE164() && (isSystemContact() || getPhoneNumberSharing() != PhoneNumberSharingState.DISABLED); + } + public @NonNull Optional getEmail() { return Optional.ofNullable(email); } @@ -1232,6 +1246,10 @@ public boolean isCallLink() { return Objects.requireNonNull(callLinkRoomId); } + public PhoneNumberSharingState getPhoneNumberSharing() { + return phoneNumberSharing; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -1388,7 +1406,8 @@ public boolean hasSameContent(@NonNull Recipient other) { hasGroupsInCommon == other.hasGroupsInCommon && Objects.equals(badges, other.badges) && isActiveGroup == other.isActiveGroup && - Objects.equals(callLinkRoomId, other.callLinkRoomId); + Objects.equals(callLinkRoomId, other.callLinkRoomId) && + phoneNumberSharing == other.phoneNumberSharing; } private static boolean allContentsAreTheSame(@NonNull List a, @NonNull List b) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientDetails.kt b/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientDetails.kt index 039a07ffd0..4244012a2e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientDetails.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientDetails.kt @@ -8,6 +8,7 @@ import org.thoughtcrime.securesms.badges.models.Badge import org.thoughtcrime.securesms.conversation.colors.AvatarColor import org.thoughtcrime.securesms.conversation.colors.ChatColors import org.thoughtcrime.securesms.database.RecipientTable.MentionSetting +import org.thoughtcrime.securesms.database.RecipientTable.PhoneNumberSharingState import org.thoughtcrime.securesms.database.RecipientTable.RegisteredState import org.thoughtcrime.securesms.database.RecipientTable.UnidentifiedAccessMode import org.thoughtcrime.securesms.database.RecipientTable.VibrateState @@ -79,7 +80,8 @@ class RecipientDetails private constructor( @JvmField val isReleaseChannel: Boolean, @JvmField val needsPniSignature: Boolean, @JvmField val callLinkRoomId: CallLinkRoomId?, - @JvmField val groupRecord: Optional + @JvmField val groupRecord: Optional, + @JvmField val phoneNumberSharing: PhoneNumberSharingState ) { @VisibleForTesting @@ -143,7 +145,8 @@ class RecipientDetails private constructor( isReleaseChannel = isReleaseChannel, needsPniSignature = record.needsPniSignature, callLinkRoomId = record.callLinkRoomId, - groupRecord = groupRecord + groupRecord = groupRecord, + phoneNumberSharing = record.phoneNumberSharing ) companion object { @@ -271,7 +274,8 @@ class RecipientDetails private constructor( needsPniSignature = false, isActiveGroup = false, callLinkRoomId = null, - groupRecord = Optional.empty() + groupRecord = Optional.empty(), + phoneNumberSharing = PhoneNumberSharingState.UNKNOWN ) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientExporter.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientExporter.java index 63935e5915..8a935504bd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientExporter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientExporter.java @@ -34,7 +34,7 @@ private static void addNameToIntent(Intent intent, String profileName) { } private static void addAddressToIntent(Intent intent, Recipient recipient) { - if (recipient.getE164().isPresent()) { + if (recipient.getE164().isPresent() && recipient.shouldShowE164()) { intent.putExtra(ContactsContract.Intents.Insert.PHONE, recipient.requireE164()); } else if (recipient.getEmail().isPresent()) { intent.putExtra(ContactsContract.Intents.Insert.EMAIL, recipient.requireEmail()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientUtil.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientUtil.java index a7534e8f67..44ca0b2965 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientUtil.java @@ -8,7 +8,6 @@ import com.annimon.stream.Stream; -import org.signal.contacts.SystemContactsRepository; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.contacts.sync.ContactDiscovery; import org.thoughtcrime.securesms.database.GroupTable; @@ -22,15 +21,12 @@ import org.thoughtcrime.securesms.groups.GroupChangeFailedException; import org.thoughtcrime.securesms.groups.GroupManager; import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob; -import org.thoughtcrime.securesms.jobs.MultiDeviceMessageRequestResponseJob; import org.thoughtcrime.securesms.jobs.RefreshOwnProfileJob; import org.thoughtcrime.securesms.jobs.RotateProfileKeyJob; import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.mms.OutgoingMessage; -import org.thoughtcrime.securesms.notifications.NotificationChannels; import org.thoughtcrime.securesms.sms.MessageSender; import org.thoughtcrime.securesms.storage.StorageSyncHelper; -import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.whispersystems.signalservice.api.push.ServiceId; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.exceptions.NotFoundException; @@ -214,35 +210,6 @@ public static void unblock(@NonNull Recipient recipient) { StorageSyncHelper.scheduleSyncForDataChange(); } - @WorkerThread - public static void delete(@NonNull Context context, @NonNull Recipient recipient) { - Recipient resolved = recipient.resolve(); - - ThreadTable threadTable = SignalDatabase.threads(); - long existingThread = threadTable.getThreadIdIfExistsFor(resolved.getId()); - if (existingThread > -1) { - threadTable.deleteConversation(existingThread); - ApplicationDependencies.getMessageNotifier().updateNotification(context); - } - - if (recipient.isSystemContact() && recipient.getContactUri() != null) { - SystemContactsRepository.lookupAndDeleteContact(context, recipient.getContactUri()); - } - - SignalDatabase.recipients().clearFieldsForDeletion(resolved.getId()); - NotificationChannels.getInstance().deleteChannelFor(resolved); - - if (!resolved.isBlocked()) { - ApplicationDependencies.getJobManager().startChain(new RefreshOwnProfileJob()) - .then(new RotateProfileKeyJob()) - .enqueue(); - } - - if (TextSecurePreferences.isMultiDevice(context)) { - ApplicationDependencies.getJobManager().add(MultiDeviceMessageRequestResponseJob.forDelete(recipient.getId())); - } - } - @WorkerThread public static Recipient.HiddenState getRecipientHiddenState(long threadId) { if (threadId < 0) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/about/AboutSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/about/AboutSheet.kt new file mode 100644 index 0000000000..660a426018 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/about/AboutSheet.kt @@ -0,0 +1,321 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.recipients.ui.about + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.viewinterop.AndroidView +import androidx.core.os.bundleOf +import androidx.core.widget.TextViewCompat +import org.signal.core.ui.BottomSheets +import org.signal.core.ui.theme.SignalTheme +import org.signal.core.util.getParcelableCompat +import org.thoughtcrime.securesms.AvatarPreviewActivity +import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.avatar.AvatarImage +import org.thoughtcrime.securesms.components.emoji.EmojiTextView +import org.thoughtcrime.securesms.compose.ComposeBottomSheetDialogFragment +import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter +import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.recipients.RecipientId +import org.thoughtcrime.securesms.stories.settings.my.SignalConnectionsBottomSheetDialogFragment +import org.thoughtcrime.securesms.util.viewModel + +/** + * Displays all relevant context you know for a given user on the sheet. + */ +class AboutSheet : ComposeBottomSheetDialogFragment() { + + companion object { + + private const val RECIPIENT_ID = "recipient_id" + + @JvmStatic + fun create(recipient: Recipient): AboutSheet { + return AboutSheet().apply { + arguments = bundleOf( + RECIPIENT_ID to recipient.id + ) + } + } + } + + override val peekHeightPercentage: Float = 1f + + private val recipientId: RecipientId + get() = requireArguments().getParcelableCompat(RECIPIENT_ID, RecipientId::class.java)!! + + private val viewModel by viewModel { + AboutSheetViewModel(recipientId) + } + + @Composable + override fun SheetContent() { + val recipient by viewModel.recipient + val groupsInCommonCount by viewModel.groupsInCommonCount + + if (recipient.isPresent) { + AboutSheetContent( + recipient = recipient.get(), + groupsInCommonCount = groupsInCommonCount, + onClickSignalConnections = this::openSignalConnectionsSheet, + onAvatarClicked = this::openProfilePhotoViewer + ) + } + } + + private fun openSignalConnectionsSheet() { + dismiss() + SignalConnectionsBottomSheetDialogFragment().show(parentFragmentManager, null) + } + + private fun openProfilePhotoViewer() { + startActivity(AvatarPreviewActivity.intentFromRecipientId(requireContext(), recipientId)) + } +} + +@Preview +@Composable +private fun AboutSheetContentPreview() { + SignalTheme { + Surface { + AboutSheetContent( + recipient = Recipient.UNKNOWN, + groupsInCommonCount = 0, + onClickSignalConnections = {}, + onAvatarClicked = {} + ) + } + } +} + +@Composable +private fun AboutSheetContent( + recipient: Recipient, + groupsInCommonCount: Int, + onClickSignalConnections: () -> Unit, + onAvatarClicked: () -> Unit +) { + Box( + contentAlignment = Alignment.Center, + modifier = Modifier.fillMaxWidth() + ) { + BottomSheets.Handle(modifier = Modifier.padding(top = 6.dp)) + } + + val avatarOnClick = remember(recipient.profileAvatarFileDetails.hasFile()) { + if (recipient.profileAvatarFileDetails.hasFile()) { + onAvatarClicked + } else { + { } + } + } + + Column(horizontalAlignment = Alignment.CenterHorizontally) { + AvatarImage( + recipient = recipient, + modifier = Modifier + .padding(top = 56.dp) + .size(240.dp) + .clip(CircleShape) + .clickable(onClick = avatarOnClick) + ) + + Text( + text = stringResource(id = if (recipient.isSelf) R.string.AboutSheet__you else R.string.AboutSheet__about), + style = MaterialTheme.typography.headlineMedium, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 32.dp) + .padding(top = 20.dp, bottom = 14.dp) + ) + + val context = LocalContext.current + val displayName = remember(recipient) { recipient.getDisplayName(context) } + + AboutRow( + startIcon = painterResource(R.drawable.symbol_person_24), + text = displayName, + modifier = Modifier.fillMaxWidth() + ) + + if (!recipient.about.isNullOrBlank()) { + AboutRow( + startIcon = painterResource(R.drawable.symbol_edit_24), + text = { + Row { + AndroidView(factory = ::EmojiTextView) { + it.text = recipient.combinedAboutAndEmoji + + TextViewCompat.setTextAppearance(it, R.style.Signal_Text_BodyLarge) + } + } + }, + modifier = Modifier.fillMaxWidth() + ) + } + + if (recipient.isProfileSharing) { + AboutRow( + startIcon = painterResource(id = R.drawable.symbol_connections_24), + text = stringResource(id = R.string.AboutSheet__signal_connection), + endIcon = painterResource(id = R.drawable.symbol_chevron_right_compact_bold_16), + modifier = Modifier.align(alignment = Alignment.Start), + onClick = onClickSignalConnections + ) + } + + val shortName = remember(recipient) { recipient.getShortDisplayName(context) } + if (recipient.isSystemContact) { + AboutRow( + startIcon = painterResource(id = R.drawable.symbol_person_circle_24), + text = stringResource(id = R.string.AboutSheet__s_is_in_your_system_contacts, shortName), + modifier = Modifier.fillMaxWidth() + ) + } + + if (recipient.e164.isPresent && recipient.shouldShowE164()) { + val e164 = remember(recipient.e164.get()) { + PhoneNumberFormatter.get(context).prettyPrintFormat(recipient.e164.get()) + } + + AboutRow( + startIcon = painterResource(R.drawable.symbol_phone_24), + text = e164, + modifier = Modifier.fillMaxWidth() + ) + } + + val groupsInCommonText = if (recipient.hasGroupsInCommon()) { + stringResource(id = R.string.AboutSheet__d_groups_in_common, groupsInCommonCount) + } else { + stringResource(id = R.string.AboutSheet__you_have_no_groups_in_common) + } + + AboutRow( + startIcon = painterResource(R.drawable.symbol_group_24), + text = groupsInCommonText, + modifier = Modifier.fillMaxWidth() + ) + + if (!recipient.isProfileSharing) { + AboutRow( + startIcon = painterResource(R.drawable.symbol_error_circle_24), + text = stringResource(id = R.string.AboutSheet__review_requests_carefully), + modifier = Modifier.fillMaxWidth() + ) + } + + Spacer(modifier = Modifier.size(26.dp)) + } +} + +@Preview +@Composable +private fun AboutRowPreview() { + SignalTheme { + Surface { + AboutRow( + startIcon = painterResource(R.drawable.symbol_person_24), + text = "Maya Johnson", + endIcon = painterResource(id = R.drawable.symbol_chevron_right_compact_bold_16) + ) + } + } +} + +@Composable +private fun AboutRow( + startIcon: Painter, + text: String, + modifier: Modifier = Modifier, + endIcon: Painter? = null, + onClick: (() -> Unit)? = null +) { + AboutRow( + startIcon = startIcon, + text = { + Text( + text = text, + style = MaterialTheme.typography.bodyLarge + ) + }, + modifier = modifier, + endIcon = endIcon, + onClick = onClick + ) +} + +@Composable +private fun AboutRow( + startIcon: Painter, + text: @Composable () -> Unit, + modifier: Modifier = Modifier, + endIcon: Painter? = null, + onClick: (() -> Unit)? = null +) { + val padHorizontal = if (onClick != null) 19.dp else 32.dp + val padVertical = if (onClick != null) 4.dp else 6.dp + + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = modifier + .padding(horizontal = padHorizontal) + .padding(vertical = padVertical) + .let { + if (onClick != null) { + it + .clip(RoundedCornerShape(16.dp)) + .clickable(onClick = onClick) + .padding(top = 2.dp, bottom = 2.dp, start = 13.dp, end = 8.dp) + } else { + it + } + } + ) { + Icon( + painter = startIcon, + contentDescription = null, + modifier = Modifier + .padding(end = 16.dp) + .size(20.dp) + ) + + text() + + if (endIcon != null) { + Icon( + painter = endIcon, + contentDescription = null, + tint = MaterialTheme.colorScheme.outline + ) + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/about/AboutSheetRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/about/AboutSheetRepository.kt new file mode 100644 index 0000000000..587638d63e --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/about/AboutSheetRepository.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.recipients.ui.about + +import io.reactivex.rxjava3.core.Single +import io.reactivex.rxjava3.schedulers.Schedulers +import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.recipients.RecipientId + +class AboutSheetRepository { + fun getGroupsInCommonCount(recipientId: RecipientId): Single { + return Single.fromCallable { + SignalDatabase.groups.getPushGroupsContainingMember(recipientId).size + }.subscribeOn(Schedulers.io()) + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/about/AboutSheetViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/about/AboutSheetViewModel.kt new file mode 100644 index 0000000000..1d936ffb4c --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/about/AboutSheetViewModel.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.recipients.ui.about + +import androidx.compose.runtime.IntState +import androidx.compose.runtime.MutableIntState +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.State +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.lifecycle.ViewModel +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers +import io.reactivex.rxjava3.disposables.Disposable +import io.reactivex.rxjava3.kotlin.subscribeBy +import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.recipients.RecipientId +import java.util.Optional + +class AboutSheetViewModel( + recipientId: RecipientId, + repository: AboutSheetRepository = AboutSheetRepository() +) : ViewModel() { + + private val _recipient: MutableState> = mutableStateOf(Optional.empty()) + val recipient: State> = _recipient + + private val _groupsInCommonCount: MutableIntState = mutableIntStateOf(0) + val groupsInCommonCount: IntState = _groupsInCommonCount + + private val recipientDisposable: Disposable = Recipient + .observable(recipientId) + .observeOn(AndroidSchedulers.mainThread()) + .subscribeBy { + _recipient.value = Optional.of(it) + } + + private val groupsInCommonDisposable: Disposable = repository + .getGroupsInCommonCount(recipientId) + .observeOn(AndroidSchedulers.mainThread()) + .subscribeBy { + _groupsInCommonCount.intValue = it + } + + override fun onCleared() { + recipientDisposable.dispose() + groupsInCommonDisposable.dispose() + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/bottomsheet/RecipientBottomSheetDialogFragment.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/bottomsheet/RecipientBottomSheetDialogFragment.java index d540350735..a81be2f9c3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/bottomsheet/RecipientBottomSheetDialogFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/bottomsheet/RecipientBottomSheetDialogFragment.java @@ -39,6 +39,7 @@ import org.thoughtcrime.securesms.recipients.RecipientExporter; import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.recipients.RecipientUtil; +import org.thoughtcrime.securesms.recipients.ui.about.AboutSheet; import org.thoughtcrime.securesms.util.BottomSheetUtil; import org.thoughtcrime.securesms.util.ContextUtil; import org.thoughtcrime.securesms.util.DrawableUtil; @@ -191,7 +192,13 @@ public void onViewCreated(@NonNull View fragmentView, @Nullable Bundle savedInst } else if (recipient.showVerified()) { SpanUtil.appendCenteredImageSpan(nameBuilder, ContextUtil.requireDrawable(requireContext(), R.drawable.ic_official_28), 28, 28); } + + SpanUtil.appendCenteredImageSpan(nameBuilder, ContextUtil.requireDrawable(requireContext(), R.drawable.symbol_chevron_right_24_color_on_secondary_container), 24, 24); fullName.setText(nameBuilder); + fullName.setOnClickListener(v -> { + dismiss(); + AboutSheet.create(recipient).show(getParentFragmentManager(), null); + }); String aboutText = recipient.getCombinedAboutAndEmoji(); if (recipient.isReleaseNotes()) { @@ -205,7 +212,7 @@ public void onViewCreated(@NonNull View fragmentView, @Nullable Bundle savedInst about.setVisibility(View.GONE); } - String usernameNumberString = recipient.hasAUserSetDisplayName(requireContext()) && !recipient.isSelf() + String usernameNumberString = recipient.hasAUserSetDisplayName(requireContext()) && !recipient.isSelf() && recipient.shouldShowE164() ? recipient.getSmsAddress().map(PhoneNumberFormatter::prettyPrint).orElse("").trim() : ""; usernameNumber.setText(usernameNumberString); diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/RegistrationLockFragment.java b/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/RegistrationLockFragment.java index 4869c41bae..1298784b9f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/RegistrationLockFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/RegistrationLockFragment.java @@ -8,7 +8,7 @@ import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; -import org.thoughtcrime.securesms.jobs.NewRegistrationUsernameSyncJob; +import org.thoughtcrime.securesms.jobs.ReclaimUsernameAndLinkJob; import org.thoughtcrime.securesms.jobs.StorageAccountRestoreJob; import org.thoughtcrime.securesms.jobs.StorageSyncJob; import org.thoughtcrime.securesms.keyvalue.SignalStore; @@ -56,7 +56,7 @@ protected void handleSuccessfulPinEntry(@NonNull String pin) { ApplicationDependencies .getJobManager() .startChain(new StorageSyncJob()) - .then(new NewRegistrationUsernameSyncJob()) + .then(new ReclaimUsernameAndLinkJob()) .enqueueAndBlockUntilCompletion(TimeUnit.SECONDS.toMillis(10)); stopwatch.split("ContactRestore"); diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/viewmodel/RegistrationViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/registration/viewmodel/RegistrationViewModel.java index ed7faa6bb5..9be0e0badc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/viewmodel/RegistrationViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/viewmodel/RegistrationViewModel.java @@ -12,7 +12,7 @@ import org.signal.core.util.Stopwatch; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; -import org.thoughtcrime.securesms.jobs.NewRegistrationUsernameSyncJob; +import org.thoughtcrime.securesms.jobs.ReclaimUsernameAndLinkJob; import org.thoughtcrime.securesms.jobs.StorageAccountRestoreJob; import org.thoughtcrime.securesms.jobs.StorageSyncJob; import org.thoughtcrime.securesms.keyvalue.SignalStore; @@ -435,7 +435,7 @@ private void restoreFromStorageService() { ApplicationDependencies .getJobManager() .startChain(new StorageSyncJob()) - .then(new NewRegistrationUsernameSyncJob()) + .then(new ReclaimUsernameAndLinkJob()) .enqueueAndBlockUntilCompletion(TimeUnit.SECONDS.toMillis(10)); stopwatch.split("ContactRestore"); diff --git a/app/src/main/java/org/thoughtcrime/securesms/revealable/ViewOnceMessageView.java b/app/src/main/java/org/thoughtcrime/securesms/revealable/ViewOnceMessageView.java index 6b46bdc9af..b7dd4fece1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/revealable/ViewOnceMessageView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/revealable/ViewOnceMessageView.java @@ -95,8 +95,8 @@ public boolean requiresTapToDownload(@NonNull MmsMessageRecord messageRecord) { } Attachment attachment = messageRecord.getSlideDeck().getThumbnailSlide().asAttachment(); - return attachment.getTransferState() == AttachmentTable.TRANSFER_PROGRESS_FAILED || - attachment.getTransferState() == AttachmentTable.TRANSFER_PROGRESS_PENDING; + return attachment.transferState == AttachmentTable.TRANSFER_PROGRESS_FAILED || + attachment.transferState == AttachmentTable.TRANSFER_PROGRESS_PENDING; } public void setMessage(@NonNull MmsMessageRecord message, boolean hasWallpaper) { @@ -169,7 +169,7 @@ private boolean networkInProgress(@NonNull MmsMessageRecord messageRecord) { if (messageRecord.getSlideDeck().getThumbnailSlide() == null) return false; Attachment attachment = messageRecord.getSlideDeck().getThumbnailSlide().asAttachment(); - return attachment.getTransferState() == AttachmentTable.TRANSFER_PROGRESS_STARTED; + return attachment.transferState == AttachmentTable.TRANSFER_PROGRESS_STARTED; } private @NonNull String formatFileSize(@NonNull MmsMessageRecord messageRecord) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/safety/SafetyNumberBottomSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/safety/SafetyNumberBottomSheet.kt index ff388953cf..1ce41bc749 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/safety/SafetyNumberBottomSheet.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/safety/SafetyNumberBottomSheet.kt @@ -82,7 +82,7 @@ object SafetyNumberBottomSheet { @JvmStatic fun forMessageRecord(context: Context, messageRecord: MessageRecord): Factory { val args = SafetyNumberBottomSheetArgs( - untrustedRecipients = messageRecord.identityKeyMismatches.map { it.getRecipientId(context) }, + untrustedRecipients = messageRecord.identityKeyMismatches.map { it.recipientId }, destinations = getDestinationFromRecord(messageRecord), messageId = MessageId(messageRecord.id) ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/scribbles/VideoEditorHud.java b/app/src/main/java/org/thoughtcrime/securesms/scribbles/VideoEditorHud.java index 0679c3d8b7..66e5b544e6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/scribbles/VideoEditorHud.java +++ b/app/src/main/java/org/thoughtcrime/securesms/scribbles/VideoEditorHud.java @@ -3,6 +3,7 @@ import android.animation.Animator; import android.content.Context; import android.database.Cursor; +import android.graphics.Rect; import android.net.Uri; import android.provider.OpenableColumns; import android.util.AttributeSet; @@ -13,6 +14,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; +import androidx.core.view.ViewCompat; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.R; @@ -24,6 +26,7 @@ import org.thoughtcrime.securesms.video.videoconverter.VideoThumbnailsRangeSelectorView; import java.io.IOException; +import java.util.List; import java.util.concurrent.TimeUnit; /** @@ -34,6 +37,8 @@ public final class VideoEditorHud extends LinearLayout { @SuppressWarnings("unused") private static final String TAG = Log.tag(VideoEditorHud.class); + private final List exclusionZone = List.of(new Rect()); + private VideoThumbnailsRangeSelectorView videoTimeLine; private EventListener eventListener; private View playOverlay; @@ -67,6 +72,16 @@ public void setEventListener(EventListener eventListener) { this.eventListener = eventListener; } + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + final Rect outRect = exclusionZone.get(0); + videoTimeLine.getHitRect(outRect); + outRect.left = l; + outRect.right = r; + ViewCompat.setSystemGestureExclusionRects(this, exclusionZone); + super.onLayout(changed, l, t, r, b); + } + @RequiresApi(api = 23) public void setVideoSource(@NonNull VideoSlide slide, @NonNull VideoBitRateCalculator videoBitRateCalculator, long maxSendSize) throws IOException diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/BeginCallActionProcessorDelegate.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/BeginCallActionProcessorDelegate.java index 3c7903e31e..5bf1b23d00 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/BeginCallActionProcessorDelegate.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/BeginCallActionProcessorDelegate.java @@ -50,6 +50,7 @@ public BeginCallActionProcessorDelegate(@NonNull WebRtcInteractor webRtcInteract true, true, false, + CallParticipant.HAND_LOWERED, 0, true, 0, @@ -108,6 +109,7 @@ public BeginCallActionProcessorDelegate(@NonNull WebRtcInteractor webRtcInteract true, true, false, + CallParticipant.HAND_LOWERED, 0, true, 0, diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/ConnectedCallActionProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/ConnectedCallActionProcessor.java index 446c44c509..e618c24f99 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/ConnectedCallActionProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/ConnectedCallActionProcessor.java @@ -95,7 +95,8 @@ public ConnectedCallActionProcessor(@NonNull WebRtcInteractor webRtcInteractor) return ephemeralState.copy( CallParticipant.AudioLevel.fromRawAudioLevel(localLevel), callParticipantId.map(participantId -> Collections.singletonMap(participantId, CallParticipant.AudioLevel.fromRawAudioLevel(remoteLevel))) - .orElse(Collections.emptyMap()) + .orElse(Collections.emptyMap()), + ephemeralState.getUnexpiredReactions() ); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/DeviceAwareActionProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/DeviceAwareActionProcessor.java index e419d2f0fb..c02845fb75 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/DeviceAwareActionProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/DeviceAwareActionProcessor.java @@ -4,6 +4,7 @@ import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.components.webrtc.BroadcastVideoSink; +import org.thoughtcrime.securesms.events.WebRtcViewModel; import org.thoughtcrime.securesms.ringrtc.CameraState; import org.thoughtcrime.securesms.ringrtc.RemotePeer; import org.thoughtcrime.securesms.service.webrtc.state.WebRtcServiceState; @@ -27,7 +28,11 @@ public DeviceAwareActionProcessor(@NonNull WebRtcInteractor webRtcInteractor, @N Log.i(tag, "handleAudioDeviceChanged(): active: " + activeDevice + " available: " + availableDevices); if (!currentState.getLocalDeviceState().getCameraState().isEnabled()) { - webRtcInteractor.updatePhoneState(WebRtcUtil.getInCallPhoneState(context)); + if (currentState.getCallInfoState().getCallState() == WebRtcViewModel.State.CALL_CONNECTED) { + webRtcInteractor.updatePhoneState(WebRtcUtil.getInCallPhoneState(context)); + } else { + Log.i(tag, "handleAudioDeviceChanged(): call not connected, not updating phone state"); + } } return currentState.builder() diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupActionProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupActionProcessor.java index 08f92877b5..57457d94d5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupActionProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupActionProcessor.java @@ -108,6 +108,8 @@ public GroupActionProcessor(@NonNull MultiPeerActionProcessorFactory actionProce videoSink = new BroadcastVideoSink(); } + long handRaisedTimestamp = callParticipant != null ? callParticipant.getHandRaisedTimestamp() : CallParticipant.HAND_LOWERED; + builder.putParticipant(callParticipantId, CallParticipant.createRemote(callParticipantId, recipient, @@ -116,6 +118,7 @@ public GroupActionProcessor(@NonNull MultiPeerActionProcessorFactory actionProce device.getForwardingVideo() == null || device.getForwardingVideo(), Boolean.FALSE.equals(device.getAudioMuted()), Boolean.FALSE.equals(device.getVideoMuted()), + handRaisedTimestamp, device.getSpeakerTime(), device.getMediaKeysReceived(), device.getAddedTime(), diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupConnectedActionProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupConnectedActionProcessor.java index 70ff27af89..f91451d7cb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupConnectedActionProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupConnectedActionProcessor.java @@ -14,15 +14,19 @@ import org.signal.ringrtc.PeekInfo; import org.thoughtcrime.securesms.events.CallParticipant; import org.thoughtcrime.securesms.events.CallParticipantId; +import org.thoughtcrime.securesms.events.GroupCallReactionEvent; import org.thoughtcrime.securesms.events.WebRtcViewModel; import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.ringrtc.Camera; import org.thoughtcrime.securesms.ringrtc.RemotePeer; +import org.thoughtcrime.securesms.service.webrtc.state.CallInfoState; import org.thoughtcrime.securesms.service.webrtc.state.WebRtcEphemeralState; import org.thoughtcrime.securesms.service.webrtc.state.WebRtcServiceState; +import org.thoughtcrime.securesms.service.webrtc.state.WebRtcServiceStateBuilder; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.UUID; @@ -135,7 +139,7 @@ protected GroupConnectedActionProcessor(@NonNull MultiPeerActionProcessorFactory } } - return ephemeralState.copy(localAudioLevel, remoteAudioLevels); + return ephemeralState.copy(localAudioLevel, remoteAudioLevels, ephemeralState.getUnexpiredReactions()); } @Override @@ -155,7 +159,7 @@ protected GroupConnectedActionProcessor(@NonNull MultiPeerActionProcessorFactory boolean remoteUserRangTheCall = currentState.getCallSetupState(RemotePeer.GROUP_CALL_ID).getRingerRecipient() != Recipient.self(); String eraId = WebRtcUtil.getGroupCallEraId(groupCall); - webRtcInteractor.sendGroupCallMessage(currentState.getCallInfoState().getCallRecipient(), eraId, remoteUserRangTheCall, true); + webRtcInteractor.sendGroupCallMessage(currentState.getCallInfoState().getCallRecipient(), eraId, null, remoteUserRangTheCall, true); List members = new ArrayList<>(peekInfo.getJoinedMembers()); if (!members.contains(SignalStore.account().requireAci().getRawUuid())) { @@ -182,7 +186,7 @@ protected GroupConnectedActionProcessor(@NonNull MultiPeerActionProcessorFactory } String eraId = WebRtcUtil.getGroupCallEraId(groupCall); - webRtcInteractor.sendGroupCallMessage(currentState.getCallInfoState().getCallRecipient(), eraId, false, false); + webRtcInteractor.sendGroupCallMessage(currentState.getCallInfoState().getCallRecipient(), eraId, null, false, false); List members = Stream.of(currentState.getCallInfoState().getRemoteCallParticipants()).map(p -> p.getRecipient().requireServiceId().getRawUuid()).toList(); webRtcInteractor.updateGroupCallUpdateMessage(currentState.getCallInfoState().getCallRecipient().getId(), eraId, members, false); @@ -197,4 +201,106 @@ protected GroupConnectedActionProcessor(@NonNull MultiPeerActionProcessorFactory return terminateGroupCall(currentState); } + + @Override + protected @NonNull WebRtcServiceState handleSelfRaiseHand(@NonNull WebRtcServiceState currentState, boolean raised) { + Log.i(tag, "handleSelfRaiseHand():"); + try { + currentState.getCallInfoState().requireGroupCall().raiseHand(raised); + + return currentState; + } catch (CallException e) { + Log.w(TAG, "Unable to " + (raised ? "raise" : "lower") + " hand in group call", e); + } + return currentState; + } + + @Override + protected @NonNull WebRtcEphemeralState handleSendGroupReact(@NonNull WebRtcServiceState currentState, @NonNull WebRtcEphemeralState ephemeralState, @NonNull String reaction) { + try { + currentState.getCallInfoState().requireGroupCall().react(reaction); + + List reactionList = ephemeralState.getUnexpiredReactions(); + reactionList.add(new GroupCallReactionEvent(Recipient.self(), reaction, System.currentTimeMillis())); + + return ephemeralState.copy(ephemeralState.getLocalAudioLevel(), ephemeralState.getRemoteAudioLevels(), reactionList); + } catch (CallException e) { + Log.w(TAG,"Unable to send reaction in group call", e); + } + return ephemeralState; + } + + @Override + protected @NonNull WebRtcEphemeralState handleGroupCallReaction(@NonNull WebRtcServiceState currentState, @NonNull WebRtcEphemeralState ephemeralState, List reactions) { + List reactionList = ephemeralState.getUnexpiredReactions(); + List participants = currentState.getCallInfoState().getRemoteCallParticipants(); + + for (GroupCall.Reaction reaction : reactions) { + final GroupCallReactionEvent event = createGroupCallReaction(participants, reaction); + if (event != null) { + reactionList.add(event); + } + } + + return ephemeralState.copy(ephemeralState.getLocalAudioLevel(), ephemeralState.getRemoteAudioLevels(), reactionList); + } + + @Nullable + private GroupCallReactionEvent createGroupCallReaction(Collection participants, final GroupCall.Reaction reaction) { + CallParticipant participant = participants.stream().filter(it -> it.getCallParticipantId().getDemuxId() == reaction.demuxId).findFirst().orElse(null); + if (participant == null) { + Log.v(TAG, "Could not find CallParticipantId in list of call participants based on demuxId for reaction."); + return null; + } + + return new GroupCallReactionEvent(participant.getRecipient(), reaction.value, System.currentTimeMillis()); + } + + @Override + protected @NonNull WebRtcServiceState handleGroupCallRaisedHand(@NonNull WebRtcServiceState currentState, List raisedHands) { + Log.i(TAG, "handleGroupCallRaisedHand():"); + + boolean playSound = !raisedHands.isEmpty(); + long now = System.currentTimeMillis(); + WebRtcServiceStateBuilder.CallInfoStateBuilder builder = currentState.builder().changeCallInfoState(); + Long localDemuxId = currentState.getCallInfoState().requireGroupCall().getLocalDeviceState().getDemuxId(); + + List participants = currentState.getCallInfoState().getRemoteCallParticipants(); + + for (CallParticipant updatedParticipant : participants) { + int raisedHandIndex = raisedHands.indexOf(updatedParticipant.getCallParticipantId().getDemuxId()); + boolean wasHandAlreadyRaised = updatedParticipant.isHandRaised(); + + if (wasHandAlreadyRaised) { + playSound = false; + } + + if (raisedHandIndex >= 0 && !wasHandAlreadyRaised) { + builder.putParticipant(updatedParticipant.getCallParticipantId(), updatedParticipant.withHandRaisedTimestamp(now + raisedHandIndex)); + } else if (raisedHandIndex < 0 && wasHandAlreadyRaised) { + builder.putParticipant(updatedParticipant.getCallParticipantId(), updatedParticipant.withHandRaisedTimestamp(CallParticipant.HAND_LOWERED)); + } + } + + if (localDemuxId != null) { + if (raisedHands.contains(localDemuxId)) { + builder.setLocalParticipant(CallParticipant.createLocal(currentState.getLocalDeviceState().getCameraState(), + currentState.getVideoState().requireLocalSink(), + currentState.getLocalDeviceState().isMicrophoneEnabled(), + now, + new CallParticipantId(localDemuxId, Recipient.self().getId()))); + } else { + builder.setLocalParticipant(CallParticipant.createLocal(currentState.getLocalDeviceState().getCameraState(), + currentState.getVideoState().requireLocalSink(), + currentState.getLocalDeviceState().isMicrophoneEnabled(), + CallParticipant.HAND_LOWERED, + new CallParticipantId(localDemuxId, Recipient.self().getId()))); + } + } + if (playSound) { + webRtcInteractor.playStateChangeUp(); + } + + return builder.build(); + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupPreJoinActionProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupPreJoinActionProcessor.java index 3e60ebebc9..3b7b452852 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupPreJoinActionProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupPreJoinActionProcessor.java @@ -137,6 +137,7 @@ protected GroupPreJoinActionProcessor(@NonNull MultiPeerActionProcessorFactory a true, true, true, + CallParticipant.HAND_LOWERED, 0, false, 0, diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IdleActionProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IdleActionProcessor.java index a46d3da071..692471738f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IdleActionProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IdleActionProcessor.java @@ -124,7 +124,7 @@ public IdleActionProcessor(@NonNull WebRtcInteractor webRtcInteractor) { if (activeProfile != null && !(activeProfile.isRecipientAllowed(remotePeerGroup.getId()) || activeProfile.getAllowAllCalls())) { try { Log.i(TAG, "Incoming ring request for profile restricted recipient"); - SignalDatabase.calls().insertOrUpdateGroupCallFromRingState(ringId, remotePeerGroup.getId(), sender, System.currentTimeMillis(), CallManager.RingUpdate.EXPIRED_REQUEST); + SignalDatabase.calls().insertOrUpdateGroupCallFromRingState(ringId, remotePeerGroup.getId(), sender, System.currentTimeMillis(), CallManager.RingUpdate.EXPIRED_REQUEST, true); webRtcInteractor.getCallManager().cancelGroupRing(groupId.getDecodedId(), ringId, CallManager.RingCancelReason.DeclinedByUser); } catch (CallException e) { Log.w(TAG, "Error while trying to cancel ring: " + ringId, e); diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IncomingCallActionProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IncomingCallActionProcessor.java index b815b04179..2d8b151323 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IncomingCallActionProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IncomingCallActionProcessor.java @@ -184,10 +184,10 @@ public IncomingCallActionProcessor(@NonNull WebRtcInteractor webRtcInteractor) { CallTable.Direction.INCOMING, CallTable.Event.ONGOING); - webRtcInteractor.updatePhoneState(LockManager.PhoneState.INTERACTIVE); boolean shouldDisturbUserWithCall = DoNotDisturbUtil.shouldDisturbUserWithCall(context.getApplicationContext(), recipient); if (shouldDisturbUserWithCall) { + webRtcInteractor.updatePhoneState(LockManager.PhoneState.INTERACTIVE); boolean started = webRtcInteractor.startWebRtcCallActivityIfPossible(); if (!started) { Log.i(TAG, "Unable to start call activity due to OS version or not being in the foreground"); diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IncomingGroupCallActionProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IncomingGroupCallActionProcessor.java index 4befdbdc40..e03fb1c10e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IncomingGroupCallActionProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IncomingGroupCallActionProcessor.java @@ -6,6 +6,7 @@ import org.signal.core.util.logging.Log; import org.signal.ringrtc.CallException; +import org.signal.ringrtc.CallId; import org.signal.ringrtc.CallManager; import org.signal.ringrtc.GroupCall; import org.thoughtcrime.securesms.components.webrtc.BroadcastVideoSink; @@ -51,18 +52,18 @@ public IncomingGroupCallActionProcessor(WebRtcInteractor webRtcInteractor) { @NonNull ACI sender, @NonNull CallManager.RingUpdate ringUpdate) { - Log.i(TAG, "handleGroupCallRingUpdate(): recipient: " + remotePeerGroup.getId() + " ring: " + Long.toHexString(ringId) + " update: " + ringUpdate); + Log.i(TAG, "handleGroupCallRingUpdate(): recipient: " + remotePeerGroup.getId() + " ring: " + ringId + " update: " + ringUpdate); Recipient recipient = remotePeerGroup.getRecipient(); boolean updateForCurrentRingId = ringId == currentState.getCallSetupState(RemotePeer.GROUP_CALL_ID).getRingId(); boolean isCurrentlyRinging = currentState.getCallInfoState().getGroupCallState().isRinging(); - if (SignalDatabase.calls().isRingCancelled(ringId, remotePeerGroup.getId())) { + if (SignalDatabase.calls().isRingCancelled(ringId, remotePeerGroup.getId()) && !updateForCurrentRingId) { try { - Log.i(TAG, "Ignoring incoming ring request for already cancelled ring: " + Long.toHexString(ringId)); + Log.i(TAG, "Ignoring incoming ring request for already cancelled ring: " + ringId); webRtcInteractor.getCallManager().cancelGroupRing(groupId.getDecodedId(), ringId, null); } catch (CallException e) { - Log.w(TAG, "Error while trying to cancel ring: " + Long.toHexString(ringId), e); + Log.w(TAG, "Error while trying to cancel ring: " + ringId, e); } return currentState; } @@ -75,7 +76,7 @@ public IncomingGroupCallActionProcessor(WebRtcInteractor webRtcInteractor) { ringUpdate); if (updateForCurrentRingId && isCurrentlyRinging) { - Log.i(TAG, "Cancelling current ring: " + Long.toHexString(ringId)); + Log.i(TAG, "Cancelling current ring: " + ringId); currentState = currentState.builder() .changeCallInfoState() @@ -92,20 +93,20 @@ public IncomingGroupCallActionProcessor(WebRtcInteractor webRtcInteractor) { if (!updateForCurrentRingId && isCurrentlyRinging) { try { - Log.i(TAG, "Already ringing so reply busy for new ring: " + Long.toHexString(ringId)); + Log.i(TAG, "Already ringing so reply busy for new ring: " + ringId); webRtcInteractor.getCallManager().cancelGroupRing(groupId.getDecodedId(), ringId, CallManager.RingCancelReason.Busy); } catch (CallException e) { - Log.w(TAG, "Error while trying to cancel ring: " + Long.toHexString(ringId), e); + Log.w(TAG, "Error while trying to cancel ring: " + ringId, e); } return currentState; } if (updateForCurrentRingId) { - Log.i(TAG, "Already ringing for ring: " + Long.toHexString(ringId)); + Log.i(TAG, "Already ringing for ring: " + ringId); return currentState; } - Log.i(TAG, "Requesting new ring: " + Long.toHexString(ringId)); + Log.i(TAG, "Requesting new ring: " + ringId); Recipient ringerRecipient = Recipient.externalPush(sender); SignalDatabase.calls().insertOrUpdateGroupCallFromRingState( @@ -119,11 +120,11 @@ public IncomingGroupCallActionProcessor(WebRtcInteractor webRtcInteractor) { currentState = WebRtcVideoUtil.initializeVideo(context, webRtcInteractor.getCameraEventListener(), currentState, RemotePeer.GROUP_CALL_ID.longValue()); webRtcInteractor.setCallInProgressNotification(TYPE_INCOMING_RINGING, remotePeerGroup, true); - webRtcInteractor.updatePhoneState(LockManager.PhoneState.INTERACTIVE); webRtcInteractor.initializeAudioForCall(); boolean shouldDisturbUserWithCall = DoNotDisturbUtil.shouldDisturbUserWithCall(context.getApplicationContext()); if (shouldDisturbUserWithCall) { + webRtcInteractor.updatePhoneState(LockManager.PhoneState.INTERACTIVE); boolean started = webRtcInteractor.startWebRtcCallActivityIfPossible(); if (!started) { Log.i(TAG, "Unable to start call activity due to OS version or not being in the foreground"); @@ -166,6 +167,7 @@ public IncomingGroupCallActionProcessor(WebRtcInteractor webRtcInteractor) { true, true, false, + CallParticipant.HAND_LOWERED, 0, true, 0, @@ -233,6 +235,8 @@ public IncomingGroupCallActionProcessor(WebRtcInteractor webRtcInteractor) { @Override protected @NonNull WebRtcServiceState handleDenyCall(@NonNull WebRtcServiceState currentState) { + Log.i(TAG, "handleDenyCall():"); + Recipient recipient = currentState.getCallInfoState().getCallRecipient(); Optional groupId = recipient.getGroupId(); long ringId = currentState.getCallSetupState(RemotePeer.GROUP_CALL_ID).getRingId(); @@ -252,6 +256,11 @@ public IncomingGroupCallActionProcessor(WebRtcInteractor webRtcInteractor) { Log.w(TAG, "Error while trying to cancel ring " + ringId, e); } + CallId callId = new CallId(ringId); + RemotePeer remotePeer = new RemotePeer(recipient.getId(), callId); + + webRtcInteractor.sendGroupCallNotAcceptedCallEventSyncMessage(remotePeer, false); + webRtcInteractor.sendGroupCallMessage(currentState.getCallInfoState().getCallRecipient(), null, callId, true, false); webRtcInteractor.updatePhoneState(LockManager.PhoneState.PROCESSING); webRtcInteractor.stopAudio(false); webRtcInteractor.updatePhoneState(LockManager.PhoneState.IDLE); diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/SignalCallManager.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/SignalCallManager.java index aa22fb83ba..e22f54de2f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/SignalCallManager.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/SignalCallManager.java @@ -64,7 +64,6 @@ import org.thoughtcrime.securesms.service.webrtc.state.WebRtcEphemeralState; import org.thoughtcrime.securesms.service.webrtc.state.WebRtcServiceState; import org.thoughtcrime.securesms.util.AppForegroundObserver; -import org.thoughtcrime.securesms.util.BubbleUtil; import org.thoughtcrime.securesms.util.FeatureFlags; import org.thoughtcrime.securesms.util.RecipientAccessList; import org.thoughtcrime.securesms.util.TextSecurePreferences; @@ -295,6 +294,14 @@ public void screenOff() { process((s, p) -> p.handleScreenOffChange(s)); } + public void raiseHand(boolean raised) { + process((s, p) -> p.handleSelfRaiseHand(s, raised)); + } + + public void react(@NonNull String reaction) { + processStateless(s -> serviceState.getActionProcessor().handleSendGroupReact(serviceState, s, reaction)); + } + public void postStateUpdate(@NonNull WebRtcServiceState state) { EventBus.getDefault().postSticky(new WebRtcViewModel(state)); } @@ -452,7 +459,7 @@ public void peekGroupCall(@NonNull RecipientId id) { WebRtcUtil.isCallFull(peekInfo), group.getExpiresInMillis()); - ApplicationDependencies.getMessageNotifier().updateNotification(context, ConversationId.forConversation(threadId), true, 0, BubbleUtil.BubbleState.HIDDEN); + ApplicationDependencies.getMessageNotifier().updateNotification(context, ConversationId.forConversation(threadId)); EventBus.getDefault().postSticky(new GroupCallPeekEvent(id, peekInfo.getEraId(), peekInfo.getDeviceCount(), peekInfo.getMaxDevices())); } @@ -671,7 +678,7 @@ public void onSendOffer(@NonNull CallId callId, OfferMessage.Type offerType = WebRtcUtil.getOfferTypeFromCallMediaType(callMediaType); WebRtcData.CallMetadata callMetadata = new WebRtcData.CallMetadata(remotePeer, remoteDevice); - WebRtcData.OfferMetadata offerMetadata = new WebRtcData.OfferMetadata(opaque, null, offerType); + WebRtcData.OfferMetadata offerMetadata = new WebRtcData.OfferMetadata(opaque, offerType); process((s, p) -> p.handleSendOffer(s, callMetadata, offerMetadata, broadcast)); } @@ -692,7 +699,7 @@ public void onSendAnswer(@NonNull CallId callId, Log.i(TAG, "onSendAnswer: id: " + remotePeer.getCallId().format(remoteDevice)); WebRtcData.CallMetadata callMetadata = new WebRtcData.CallMetadata(remotePeer, remoteDevice); - WebRtcData.AnswerMetadata answerMetadata = new WebRtcData.AnswerMetadata(opaque, null); + WebRtcData.AnswerMetadata answerMetadata = new WebRtcData.AnswerMetadata(opaque); process((s, p) -> p.handleSendAnswer(s, callMetadata, answerMetadata, broadcast)); } @@ -753,7 +760,7 @@ public void onSendCallMessage(@NonNull UUID aciUuid, @NonNull byte[] bytes, @Non Log.i(TAG, "onSendCallMessage():"); OpaqueMessage opaqueMessage = new OpaqueMessage(bytes, getUrgencyFromCallUrgency(urgency)); - SignalServiceCallMessage callMessage = SignalServiceCallMessage.forOpaque(opaqueMessage, true, null); + SignalServiceCallMessage callMessage = SignalServiceCallMessage.forOpaque(opaqueMessage, null); networkExecutor.execute(() -> { Recipient recipient = Recipient.resolved(RecipientId.from(ACI.from(aciUuid))); @@ -790,7 +797,7 @@ public void onSendCallMessageToGroup(@NonNull byte[] groupIdBytes, @NonNull byte .collect(Collectors.toList()))); OpaqueMessage opaqueMessage = new OpaqueMessage(message, getUrgencyFromCallUrgency(urgency)); - SignalServiceCallMessage callMessage = SignalServiceCallMessage.forOutgoingGroupOpaque(groupId.getDecodedId(), System.currentTimeMillis(), opaqueMessage, true, null); + SignalServiceCallMessage callMessage = SignalServiceCallMessage.forOutgoingGroupOpaque(groupId.getDecodedId(), System.currentTimeMillis(), opaqueMessage, null); RecipientAccessList accessList = new RecipientAccessList(recipients); List results = GroupSendUtil.sendCallMessage(context, @@ -899,12 +906,16 @@ public void onLowBandwidthForVideo(@NonNull GroupCall groupCall, boolean recover @Override public void onReactions(@NonNull GroupCall groupCall, List reactions) { - // TODO: Implement handling of reactions. + if (FeatureFlags.groupCallReactions()) { + processStateless(s -> serviceState.getActionProcessor().handleGroupCallReaction(serviceState, s, reactions)); + } } @Override public void onRaisedHands(@NonNull GroupCall groupCall, List raisedHands) { - // TODO: Implement handling of raise hand. + if (FeatureFlags.groupCallRaiseHand()) { + process((s, p) -> p.handleGroupCallRaisedHand(s, raisedHands)); + } } @Override @@ -953,15 +964,15 @@ public void onForeground() { }); } - public void insertMissedCall(@NonNull RemotePeer remotePeer, long timestamp, boolean isVideoOffer) { + public void insertMissedCall(@NonNull RemotePeer remotePeer, long timestamp, boolean isVideoOffer, @NonNull CallTable.Event missedEvent) { CallTable.Call call = SignalDatabase.calls() - .updateOneToOneCall(remotePeer.getCallId().longValue(), CallTable.Event.MISSED, timestamp); + .updateOneToOneCall(remotePeer.getCallId().longValue(), missedEvent, timestamp); if (call == null) { CallTable.Type type = isVideoOffer ? CallTable.Type.VIDEO_CALL : CallTable.Type.AUDIO_CALL; SignalDatabase.calls() - .insertOneToOneCall(remotePeer.getCallId().longValue(), timestamp, remotePeer.getId(), type, CallTable.Direction.INCOMING, CallTable.Event.MISSED); + .insertOneToOneCall(remotePeer.getCallId().longValue(), timestamp, remotePeer.getId(), type, CallTable.Direction.INCOMING, missedEvent); } } @@ -1010,7 +1021,9 @@ public void retrieveTurnServers(@NonNull RemotePeer remotePeer) { }); } - public void sendGroupCallUpdateMessage(@NonNull Recipient recipient, @Nullable String groupCallEraId, boolean isIncoming, boolean isJoinEvent) { + public void sendGroupCallUpdateMessage(@NonNull Recipient recipient, @Nullable String groupCallEraId, final @Nullable CallId callId, boolean isIncoming, boolean isJoinEvent) { + Log.i(TAG, "sendGroupCallUpdateMessage id: " + recipient.getId() + " era: " + groupCallEraId + " isIncoming: " + isIncoming + " isJoinEvent: " + isJoinEvent); + if (recipient.isCallLink()) { Log.i(TAG, "sendGroupCallUpdateMessage -- ignoring for call link"); return; @@ -1019,15 +1032,28 @@ public void sendGroupCallUpdateMessage(@NonNull Recipient recipient, @Nullable S SignalExecutors.BOUNDED.execute(() -> { GroupCallUpdateSendJob updateSendJob = GroupCallUpdateSendJob.create(recipient.getId(), groupCallEraId); JobManager.Chain chain = ApplicationDependencies.getJobManager().startChain(updateSendJob); + CallId callIdLocal = callId; + + if (callIdLocal == null && groupCallEraId != null) { + callIdLocal = CallId.fromEra(groupCallEraId); + } - if (isJoinEvent && groupCallEraId != null) { - chain.then(CallSyncEventJob.createForJoin( - recipient.getId(), - CallId.fromEra(groupCallEraId).longValue(), - isIncoming - )); - } else if (isJoinEvent) { - Log.w(TAG, "Can't send join event sync message without an era id."); + if (callIdLocal != null) { + if (isJoinEvent) { + chain.then(CallSyncEventJob.createForJoin( + recipient.getId(), + callIdLocal.longValue(), + isIncoming + )); + } else if (isIncoming) { + chain.then(CallSyncEventJob.createForNotAccepted( + recipient.getId(), + callIdLocal.longValue(), + isIncoming + )); + } + } else { + Log.w(TAG, "Can't send sync message without a call id. isIncoming: " + isIncoming + " isJoinEvent: " + isJoinEvent); } chain.enqueue(); @@ -1109,6 +1135,19 @@ public void sendNotAcceptedCallEventSyncMessage(@NonNull RemotePeer remotePeer, } } + public void sendGroupCallNotAcceptedCallEventSyncMessage(@NonNull RemotePeer remotePeer, boolean isOutgoing) { + if (TextSecurePreferences.isMultiDevice(context)) { + networkExecutor.execute(() -> { + try { + SyncMessage.CallEvent callEvent = CallEventSyncMessageUtil.createNotAcceptedSyncMessage(remotePeer, System.currentTimeMillis(), isOutgoing, true); + ApplicationDependencies.getSignalServiceMessageSender().sendSyncMessage(SignalServiceSyncMessage.forCallEvent(callEvent), Optional.empty()); + } catch (IOException | UntrustedIdentityException e) { + Log.w(TAG, "Unable to send call event sync message for " + remotePeer.getCallId().longValue(), e); + } + }); + } + } + public @NonNull SignalCallLinkManager getCallLinkManager() { return new SignalCallLinkManager(Objects.requireNonNull(callManager)); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcActionProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcActionProcessor.java index ad5a9c585d..e0091015a9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcActionProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcActionProcessor.java @@ -22,6 +22,7 @@ import org.thoughtcrime.securesms.components.webrtc.BroadcastVideoSink; import org.thoughtcrime.securesms.components.webrtc.EglBaseWrapper; import org.thoughtcrime.securesms.components.webrtc.GroupCallSafetyNumberChangeNotificationUtil; +import org.thoughtcrime.securesms.database.CallTable; import org.thoughtcrime.securesms.database.SignalDatabase; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.events.CallParticipant; @@ -132,9 +133,9 @@ public WebRtcActionProcessor(@NonNull WebRtcInteractor webRtcInteractor, @NonNul protected final @NonNull WebRtcServiceState handleSendOffer(@NonNull WebRtcServiceState currentState, @NonNull CallMetadata callMetadata, @NonNull OfferMetadata offerMetadata, boolean broadcast) { Log.i(tag, "handleSendOffer(): id: " + callMetadata.getCallId().format(callMetadata.getRemoteDevice())); - OfferMessage offerMessage = new OfferMessage(callMetadata.getCallId().longValue(), offerMetadata.getSdp(), offerMetadata.getOfferType(), offerMetadata.getOpaque()); + OfferMessage offerMessage = new OfferMessage(callMetadata.getCallId().longValue(), offerMetadata.getOfferType(), offerMetadata.getOpaque()); Integer destinationDeviceId = broadcast ? null : callMetadata.getRemoteDevice(); - SignalServiceCallMessage callMessage = SignalServiceCallMessage.forOffer(offerMessage, true, destinationDeviceId); + SignalServiceCallMessage callMessage = SignalServiceCallMessage.forOffer(offerMessage, destinationDeviceId); Recipient callRecipient = currentState.getCallInfoState().getCallRecipient(); RecipientUtil.shareProfileIfFirstSecureMessage(callRecipient); @@ -197,7 +198,8 @@ public WebRtcActionProcessor(@NonNull WebRtcInteractor webRtcInteractor, @NonNul NotificationProfile activeProfile = NotificationProfiles.getActiveProfile(SignalDatabase.notificationProfiles().getProfiles()); if (activeProfile != null && !(activeProfile.isRecipientAllowed(callMetadata.getRemotePeer().getId()) || activeProfile.getAllowAllCalls())) { Log.w(tag, "Caller is excluded by notification profile."); - webRtcInteractor.insertMissedCall(callMetadata.getRemotePeer(), receivedOfferMetadata.getServerReceivedTimestamp(), offerMetadata.getOfferType() == OfferMessage.Type.VIDEO_CALL); + currentState = currentState.getActionProcessor().handleSendHangup(currentState, callMetadata, WebRtcData.HangupMetadata.fromType(HangupMessage.Type.BUSY), true); + webRtcInteractor.insertMissedCall(callMetadata.getRemotePeer(), receivedOfferMetadata.getServerReceivedTimestamp(), offerMetadata.getOfferType() == OfferMessage.Type.VIDEO_CALL, CallTable.Event.MISSED_NOTIFICATION_PROFILE); return currentState; } @@ -320,9 +322,9 @@ public WebRtcActionProcessor(@NonNull WebRtcInteractor webRtcInteractor, @NonNul { Log.i(tag, "handleSendAnswer(): id: " + callMetadata.getCallId().format(callMetadata.getRemoteDevice())); - AnswerMessage answerMessage = new AnswerMessage(callMetadata.getCallId().longValue(), answerMetadata.getSdp(), answerMetadata.getOpaque()); + AnswerMessage answerMessage = new AnswerMessage(callMetadata.getCallId().longValue(), answerMetadata.getOpaque()); Integer destinationDeviceId = broadcast ? null : callMetadata.getRemoteDevice(); - SignalServiceCallMessage callMessage = SignalServiceCallMessage.forAnswer(answerMessage, true, destinationDeviceId); + SignalServiceCallMessage callMessage = SignalServiceCallMessage.forAnswer(answerMessage, destinationDeviceId); webRtcInteractor.sendCallMessage(callMetadata.getRemotePeer(), callMessage); @@ -348,7 +350,7 @@ public WebRtcActionProcessor(@NonNull WebRtcInteractor webRtcInteractor, @NonNul BusyMessage busyMessage = new BusyMessage(callMetadata.getCallId().longValue()); Integer destinationDeviceId = broadcast ? null : callMetadata.getRemoteDevice(); - SignalServiceCallMessage callMessage = SignalServiceCallMessage.forBusy(busyMessage, true, destinationDeviceId); + SignalServiceCallMessage callMessage = SignalServiceCallMessage.forBusy(busyMessage, destinationDeviceId); webRtcInteractor.sendCallMessage(callMetadata.getRemotePeer(), callMessage); @@ -411,7 +413,7 @@ public WebRtcActionProcessor(@NonNull WebRtcInteractor webRtcInteractor, @NonNul HangupMessage hangupMessage = new HangupMessage(callMetadata.getCallId().longValue(), hangupMetadata.getType(), hangupMetadata.getDeviceId()); Integer destinationDeviceId = broadcast ? null : callMetadata.getRemoteDevice(); - SignalServiceCallMessage callMessage = SignalServiceCallMessage.forHangup(hangupMessage, true, destinationDeviceId); + SignalServiceCallMessage callMessage = SignalServiceCallMessage.forHangup(hangupMessage, destinationDeviceId); webRtcInteractor.sendCallMessage(callMetadata.getRemotePeer(), callMessage); @@ -494,11 +496,11 @@ public WebRtcActionProcessor(@NonNull WebRtcInteractor webRtcInteractor, @NonNul Log.i(tag, "handleSendIceCandidates(): id: " + callMetadata.getCallId().format(callMetadata.getRemoteDevice())); List iceUpdateMessages = Stream.of(iceCandidates) - .map(c -> new IceUpdateMessage(callMetadata.getCallId().longValue(), c, null)) + .map(c -> new IceUpdateMessage(callMetadata.getCallId().longValue(), c)) .toList(); Integer destinationDeviceId = broadcast ? null : callMetadata.getRemoteDevice(); - SignalServiceCallMessage callMessage = SignalServiceCallMessage.forIceUpdates(iceUpdateMessages, true, destinationDeviceId); + SignalServiceCallMessage callMessage = SignalServiceCallMessage.forIceUpdates(iceUpdateMessages, destinationDeviceId); webRtcInteractor.sendCallMessage(callMetadata.getRemotePeer(), callMessage); @@ -546,6 +548,17 @@ public WebRtcActionProcessor(@NonNull WebRtcInteractor webRtcInteractor, @NonNul return currentState; } + + protected @NonNull WebRtcServiceState handleSelfRaiseHand(@NonNull WebRtcServiceState currentState, boolean raised) { + Log.i(tag, "raiseHand not processed"); + return currentState; + } + + protected @NonNull WebRtcEphemeralState handleSendGroupReact(@NonNull WebRtcServiceState currentState, @NonNull WebRtcEphemeralState ephemeralState, @NonNull String reaction) { + Log.i(tag, "react not processed"); + return ephemeralState; + } + public @NonNull WebRtcServiceState handleCameraSwitchCompleted(@NonNull WebRtcServiceState currentState, @NonNull CameraState newCameraState) { Log.i(tag, "handleCameraSwitchCompleted not processed"); return currentState; @@ -729,6 +742,16 @@ public WebRtcActionProcessor(@NonNull WebRtcInteractor webRtcInteractor, @NonNul return currentState; } + protected @NonNull WebRtcEphemeralState handleGroupCallReaction(@NonNull WebRtcServiceState currentState, @NonNull WebRtcEphemeralState ephemeralState, List reactions) { + Log.i(tag, "handleGroupCallReaction not processed"); + return ephemeralState; + } + + protected @NonNull WebRtcServiceState handleGroupCallRaisedHand(@NonNull WebRtcServiceState currentState, List raisedHands) { + Log.i(tag, "handleGroupCallRaisedHand not processed"); + return currentState; + } + protected @NonNull WebRtcServiceState handleGroupRequestMembershipProof(@NonNull WebRtcServiceState currentState, int groupCallHashCode) { Log.i(tag, "handleGroupRequestMembershipProof not processed"); return currentState; @@ -828,7 +851,7 @@ public WebRtcActionProcessor(@NonNull WebRtcInteractor webRtcInteractor, @NonNul Recipient recipient = currentState.getCallInfoState().getCallRecipient(); if (recipient != null && currentState.getCallInfoState().getGroupCallState().isConnected()) { - webRtcInteractor.sendGroupCallMessage(recipient, WebRtcUtil.getGroupCallEraId(groupCall), false, false); + webRtcInteractor.sendGroupCallMessage(recipient, WebRtcUtil.getGroupCallEraId(groupCall), null, false, false); } currentState = currentState.builder() diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcData.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcData.java index 55b9276ed9..e111cc3ace 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcData.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcData.java @@ -46,12 +46,10 @@ int getRemoteDevice() { */ public static class OfferMetadata { private final @Nullable byte[] opaque; - private final @Nullable String sdp; private final @NonNull OfferMessage.Type offerType; - public OfferMetadata(@Nullable byte[] opaque, @Nullable String sdp, @NonNull OfferMessage.Type offerType) { + public OfferMetadata(@Nullable byte[] opaque, @NonNull OfferMessage.Type offerType) { this.opaque = opaque; - this.sdp = sdp; this.offerType = offerType; } @@ -59,10 +57,6 @@ public OfferMetadata(@Nullable byte[] opaque, @Nullable String sdp, @NonNull Off return opaque; } - @Nullable String getSdp() { - return sdp; - } - @NonNull OfferMessage.Type getOfferType() { return offerType; } @@ -100,20 +94,14 @@ long getServerDeliveredTimestamp() { */ public static class AnswerMetadata { private final @Nullable byte[] opaque; - private final @Nullable String sdp; - public AnswerMetadata(@Nullable byte[] opaque, @Nullable String sdp) { + public AnswerMetadata(@Nullable byte[] opaque) { this.opaque = opaque; - this.sdp = sdp; } @Nullable byte[] getOpaque() { return opaque; } - - @Nullable String getSdp() { - return sdp; - } } /** diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcInteractor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcInteractor.java index ffee8f9265..37c5e7e31d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcInteractor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcInteractor.java @@ -5,10 +5,11 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; +import org.signal.ringrtc.CallId; import org.signal.ringrtc.CallManager; import org.signal.ringrtc.GroupCall; +import org.thoughtcrime.securesms.database.CallTable; import org.thoughtcrime.securesms.groups.GroupId; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientId; @@ -20,6 +21,7 @@ import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager; import org.thoughtcrime.securesms.webrtc.locks.LockManager; import org.whispersystems.signalservice.api.messages.calls.SignalServiceCallMessage; +import org.whispersystems.signalservice.internal.push.SyncMessage; import java.util.Collection; import java.util.UUID; @@ -85,8 +87,8 @@ void sendCallMessage(@NonNull RemotePeer remotePeer, @NonNull SignalServiceCallM signalCallManager.sendCallMessage(remotePeer, callMessage); } - void sendGroupCallMessage(@NonNull Recipient recipient, @Nullable String groupCallEraId, boolean isIncoming, boolean isJoinEvent) { - signalCallManager.sendGroupCallUpdateMessage(recipient, groupCallEraId, isIncoming, isJoinEvent); + void sendGroupCallMessage(@NonNull Recipient recipient, @Nullable String groupCallEraId, @Nullable CallId callId, boolean isIncoming, boolean isJoinEvent) { + signalCallManager.sendGroupCallUpdateMessage(recipient, groupCallEraId, callId, isIncoming, isJoinEvent); } void updateGroupCallUpdateMessage(@NonNull RecipientId groupId, @Nullable String groupCallEraId, @NonNull Collection joinedMembers, boolean isCallFull) { @@ -110,7 +112,11 @@ void stopForegroundService() { } void insertMissedCall(@NonNull RemotePeer remotePeer, long timestamp, boolean isVideoOffer) { - signalCallManager.insertMissedCall(remotePeer, timestamp, isVideoOffer); + insertMissedCall(remotePeer, timestamp, isVideoOffer, CallTable.Event.MISSED); + } + + void insertMissedCall(@NonNull RemotePeer remotePeer, long timestamp, boolean isVideoOffer, @NonNull CallTable.Event missedEvent) { + signalCallManager.insertMissedCall(remotePeer, timestamp, isVideoOffer, missedEvent); } void insertReceivedCall(@NonNull RemotePeer remotePeer, boolean isVideoOffer) { @@ -165,6 +171,10 @@ public void setDefaultAudioDevice(@NonNull RecipientId recipientId, @NonNull Sig WebRtcCallService.sendAudioManagerCommand(context, new AudioManagerCommand.SetDefaultDevice(recipientId, userDevice, clearUserEarpieceSelection)); } + public void playStateChangeUp() { + WebRtcCallService.sendAudioManagerCommand(context, new AudioManagerCommand.PlayStateChangeUp()); + } + void peekGroupCallForRingingCheck(@NonNull GroupCallRingCheckInfo groupCallRingCheckInfo) { signalCallManager.peekGroupCallForRingingCheck(groupCallRingCheckInfo); } @@ -200,4 +210,8 @@ public void sendAcceptedCallEventSyncMessage(@NonNull RemotePeer remotePeer, boo public void sendNotAcceptedCallEventSyncMessage(@NonNull RemotePeer remotePeer, boolean isOutgoing, boolean isVideoCall) { signalCallManager.sendNotAcceptedCallEventSyncMessage(remotePeer, isOutgoing, isVideoCall); } + + public void sendGroupCallNotAcceptedCallEventSyncMessage(@NonNull RemotePeer remotePeer, boolean isOutgoing) { + signalCallManager.sendGroupCallNotAcceptedCallEventSyncMessage(remotePeer, isOutgoing); + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/collections/ParticipantCollection.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/collections/ParticipantCollection.java index 578eb2c09c..4c11f364d6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/collections/ParticipantCollection.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/collections/ParticipantCollection.java @@ -21,9 +21,22 @@ */ public class ParticipantCollection { - private static final Comparator LEAST_RECENTLY_ADDED = (a, b) -> Long.compare(a.getAddedToCallTime(), b.getAddedToCallTime()); - private static final Comparator MOST_RECENTLY_SPOKEN = (a, b) -> Long.compare(b.getLastSpoke(), a.getLastSpoke()); - private static final Comparator MOST_RECENTLY_SPOKEN_THEN_LEAST_RECENTLY_ADDED = ComparatorCompat.chain(MOST_RECENTLY_SPOKEN).thenComparing(LEAST_RECENTLY_ADDED); + private static final Comparator LEAST_RECENTLY_ADDED = (a, b) -> Long.compare(a.getAddedToCallTime(), b.getAddedToCallTime()); + private static final Comparator MOST_RECENTLY_SPOKEN = (a, b) -> Long.compare(b.getLastSpoke(), a.getLastSpoke()); + private static final Comparator HAND_RAISED = (a, b) -> { + if (a.isHandRaised() && b.isHandRaised()) { + return Long.compare(a.getHandRaisedTimestamp(), b.getHandRaisedTimestamp()); + } else if (a.isHandRaised()) { + return -1; + } else if (b.isHandRaised()) { + return 1; + } else { + return 0; + } + }; + private static final Comparator COMPLEX_COMPARATOR_CHAIN = ComparatorCompat.chain(HAND_RAISED) + .thenComparing(MOST_RECENTLY_SPOKEN) + .thenComparing(LEAST_RECENTLY_ADDED); private final int maxGridCellCount; private final List participants; @@ -43,12 +56,12 @@ private ParticipantCollection(int maxGridCellCount, @NonNull List newParticipants = new ArrayList<>(participants); - Collections.sort(newParticipants, participants.size() <= maxGridCellCount ? LEAST_RECENTLY_ADDED : MOST_RECENTLY_SPOKEN_THEN_LEAST_RECENTLY_ADDED); + Collections.sort(newParticipants, participants.size() <= maxGridCellCount ? LEAST_RECENTLY_ADDED : COMPLEX_COMPARATOR_CHAIN); return new ParticipantCollection(maxGridCellCount, newParticipants); } else { List newParticipants = new ArrayList<>(participants); - Collections.sort(newParticipants, MOST_RECENTLY_SPOKEN_THEN_LEAST_RECENTLY_ADDED); + Collections.sort(newParticipants, COMPLEX_COMPARATOR_CHAIN); List oldGridParticipantIds = Stream.of(getGridParticipants()) .map(CallParticipant::getCallParticipantId) diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/links/UpdateCallLinkResult.kt b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/links/UpdateCallLinkResult.kt index 128a22f102..c90033f282 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/links/UpdateCallLinkResult.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/links/UpdateCallLinkResult.kt @@ -13,7 +13,7 @@ sealed interface UpdateCallLinkResult { val state: SignalCallLinkState ) : UpdateCallLinkResult - class Failure( + data class Failure( val status: Short ) : UpdateCallLinkResult diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/state/CallInfoState.kt b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/state/CallInfoState.kt index 511aa45993..81a8be220e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/state/CallInfoState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/state/CallInfoState.kt @@ -22,6 +22,7 @@ data class CallInfoState( var callRecipient: Recipient = Recipient.UNKNOWN, var callConnectedTime: Long = -1, @get:JvmName("getRemoteCallParticipantsMap") var remoteParticipants: MutableMap = mutableMapOf(), + var localParticipant: CallParticipant? = null, var peerMap: MutableMap = mutableMapOf(), var activePeer: RemotePeer? = null, var groupCall: GroupCall? = null, diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/state/WebRtcEphemeralState.kt b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/state/WebRtcEphemeralState.kt index a0a9da3f13..f1ce6f9ed8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/state/WebRtcEphemeralState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/state/WebRtcEphemeralState.kt @@ -2,11 +2,18 @@ package org.thoughtcrime.securesms.service.webrtc.state import org.thoughtcrime.securesms.events.CallParticipant import org.thoughtcrime.securesms.events.CallParticipantId +import org.thoughtcrime.securesms.events.GroupCallReactionEvent /** * The state of the call system which contains data which changes frequently. */ data class WebRtcEphemeralState( val localAudioLevel: CallParticipant.AudioLevel = CallParticipant.AudioLevel.LOWEST, - val remoteAudioLevels: Map = emptyMap() -) + val remoteAudioLevels: Map = emptyMap(), + private val reactions: List = emptyList() +) { + + fun getUnexpiredReactions(): List { + return reactions.filter { System.currentTimeMillis() < it.getExpirationTimestamp() } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/state/WebRtcServiceStateBuilder.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/state/WebRtcServiceStateBuilder.java index 8a32ec5378..714e6c05b6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/state/WebRtcServiceStateBuilder.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/state/WebRtcServiceStateBuilder.java @@ -364,5 +364,10 @@ public CallInfoStateBuilder() { toBuild.setCallLinkDisconnectReason(callLinkDisconnectReason); return this; } + + public @NonNull CallInfoStateBuilder setLocalParticipant(@NonNull CallParticipant callParticipant) { + toBuild.setLocalParticipant(callParticipant); + return this; + } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/sharing/MultiShareSender.java b/app/src/main/java/org/thoughtcrime/securesms/sharing/MultiShareSender.java index c36d7899a9..2b68b3e737 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/sharing/MultiShareSender.java +++ b/app/src/main/java/org/thoughtcrime/securesms/sharing/MultiShareSender.java @@ -328,15 +328,15 @@ private static List buildLinkPreviews(@NonNull Context context, @Nu : thumbnail.getUri() == null ? null : new ImageSlide(context, - thumbnail.getUri(), - thumbnail.getContentType(), - thumbnail.getSize(), - thumbnail.getWidth(), - thumbnail.getHeight(), - thumbnail.isBorderless(), - thumbnail.getCaption(), - thumbnail.getBlurHash(), - thumbnail.getTransformProperties()).asAttachment() + thumbnail.getUri(), + thumbnail.contentType, + thumbnail.size, + thumbnail.width, + thumbnail.height, + thumbnail.borderless, + thumbnail.caption, + thumbnail.blurHash, + thumbnail.transformProperties).asAttachment() ) )); } @@ -344,17 +344,17 @@ private static List buildLinkPreviews(@NonNull Context context, @Nu private static Slide ensureDefaultQuality(@NonNull Context context, @NonNull ImageSlide imageSlide) { Attachment attachment = imageSlide.asAttachment(); - if (attachment.getTransformProperties().getSentMediaQuality() == SentMediaQuality.HIGH.getCode()) { + if (attachment.transformProperties.sentMediaQuality == SentMediaQuality.HIGH.getCode()) { return new ImageSlide( context, attachment.getUri(), - attachment.getContentType(), - attachment.getSize(), - attachment.getWidth(), - attachment.getHeight(), - attachment.isBorderless(), - attachment.getCaption(), - attachment.getBlurHash(), + attachment.contentType, + attachment.size, + attachment.width, + attachment.height, + attachment.borderless, + attachment.caption, + attachment.blurHash, AttachmentTable.TransformProperties.empty() ); } else { diff --git a/app/src/main/java/org/thoughtcrime/securesms/sms/MessageSender.java b/app/src/main/java/org/thoughtcrime/securesms/sms/MessageSender.java index 1b0931ae0a..b2c1abf41c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/sms/MessageSender.java +++ b/app/src/main/java/org/thoughtcrime/securesms/sms/MessageSender.java @@ -342,7 +342,7 @@ public static void sendMediaBroadcast(@NonNull Context context, List attachmentIds = new ArrayList<>(preUploadAttachmentIds.size()); for (int i = 0; i < preUploadAttachments.size(); i++) { - AttachmentId attachmentId = attachmentDatabase.insertAttachmentForPreUpload(preUploadAttachments.get(i)).getAttachmentId(); + AttachmentId attachmentId = attachmentDatabase.insertAttachmentForPreUpload(preUploadAttachments.get(i)).attachmentId; attachmentCopies.get(i).add(attachmentId); attachmentIds.add(attachmentId); } @@ -416,14 +416,14 @@ public static void sendMediaBroadcast(@NonNull Context context, DatabaseAttachment databaseAttachment = attachmentDatabase.insertAttachmentForPreUpload(attachment); Job compressionJob = AttachmentCompressionJob.fromAttachment(databaseAttachment, false, -1); - Job uploadJob = new AttachmentUploadJob(databaseAttachment.getAttachmentId()); + Job uploadJob = new AttachmentUploadJob(databaseAttachment.attachmentId); ApplicationDependencies.getJobManager() .startChain(compressionJob) .then(uploadJob) .enqueue(); - return new PreUploadResult(media, databaseAttachment.getAttachmentId(), Arrays.asList(compressionJob.getId(), uploadJob.getId())); + return new PreUploadResult(media, databaseAttachment.attachmentId, Arrays.asList(compressionJob.getId(), uploadJob.getId())); } catch (MmsException e) { Log.w(TAG, "preUploadPushAttachment() - Failed to upload!", e); return null; @@ -641,7 +641,7 @@ private static void sendLocalMediaSelf(Context context, long messageId) { .toList(); List fakeUploadJobs = Stream.of(attachments) - .map(a -> new AttachmentMarkUploadedJob(messageId, ((DatabaseAttachment) a).getAttachmentId())) + .map(a -> new AttachmentMarkUploadedJob(messageId, ((DatabaseAttachment) a).attachmentId)) .toList(); ApplicationDependencies.getJobManager().startChain(compressionJobs) @@ -719,7 +719,7 @@ public void writeToParcel(Parcel dest, int flags) { @Override public @NonNull String toString() { - return "{ID: " + attachmentId.getRowId() + ", URI: " + media.getUri() + ", Jobs: " + jobIds.stream().map(j -> "JOB::" + j).collect(Collectors.toList()) + "}"; + return "{ID: " + attachmentId.id + ", URI: " + media.getUri() + ", Jobs: " + jobIds.stream().map(j -> "JOB::" + j).collect(Collectors.toList()) + "}"; } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/sms/UploadDependencyGraph.kt b/app/src/main/java/org/thoughtcrime/securesms/sms/UploadDependencyGraph.kt index b2d475e0e7..1786a86f5e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/sms/UploadDependencyGraph.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/sms/UploadDependencyGraph.kt @@ -6,6 +6,7 @@ import org.thoughtcrime.securesms.attachments.AttachmentId import org.thoughtcrime.securesms.attachments.DatabaseAttachment import org.thoughtcrime.securesms.attachments.UriAttachment import org.thoughtcrime.securesms.database.AttachmentTable +import org.thoughtcrime.securesms.database.AttachmentTable.TransformProperties import org.thoughtcrime.securesms.jobmanager.Job import org.thoughtcrime.securesms.jobmanager.JobManager import org.thoughtcrime.securesms.jobs.AttachmentCompressionJob @@ -43,7 +44,7 @@ class UploadDependencyGraph private constructor( */ private data class AttachmentKey( val attachment: A, - private val transformProperties: AttachmentTable.TransformProperties = attachment.transformProperties + private val transformProperties: AttachmentTable.TransformProperties = attachment.transformProperties ?: AttachmentTable.TransformProperties.empty() ) private var hasConsumedJobQueue = false @@ -75,14 +76,14 @@ class UploadDependencyGraph private constructor( * Allows representation of a unique database attachment by its internal id and its transform properties. */ private fun DatabaseAttachment.asDatabaseAttachmentKey(): AttachmentKey { - return AttachmentKey(this, this.transformProperties) + return AttachmentKey(this, this.transformProperties ?: TransformProperties.empty()) } /** * Allows representation of a unique URI attachment by its internal Uri and its transform properties. */ private fun UriAttachment.asUriAttachmentKey(): AttachmentKey { - return AttachmentKey(this, transformProperties) + return AttachmentKey(this, transformProperties ?: TransformProperties.empty()) } /** @@ -119,7 +120,7 @@ class UploadDependencyGraph private constructor( message.linkPreviews.mapNotNull { it.thumbnail.orElse(null) } + message.sharedContacts.mapNotNull { it.avatar?.attachment } - val uniqueAttachments: Set> = attachmentList.map { AttachmentKey(it, it.transformProperties) }.toSet() + val uniqueAttachments: Set> = attachmentList.map { AttachmentKey(it, it.transformProperties ?: TransformProperties.empty()) }.toSet() for (attachmentKey in uniqueAttachments) { when (val attachment = attachmentKey.attachment) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerLocator.java b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerLocator.java deleted file mode 100644 index ccf474571c..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerLocator.java +++ /dev/null @@ -1,70 +0,0 @@ -package org.thoughtcrime.securesms.stickers; - -import android.os.Parcel; -import android.os.Parcelable; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -public class StickerLocator implements Parcelable { - - private final String packId; - private final String packKey; - private final int stickerId; - private final String emoji; - - public StickerLocator(@NonNull String packId, @NonNull String packKey, int stickerId, @Nullable String emoji) { - this.packId = packId; - this.packKey = packKey; - this.stickerId = stickerId; - this.emoji = emoji; - } - - private StickerLocator(Parcel in) { - packId = in.readString(); - packKey = in.readString(); - stickerId = in.readInt(); - emoji = in.readString(); - } - - public @NonNull String getPackId() { - return packId; - } - - public @NonNull String getPackKey() { - return packKey; - } - - public int getStickerId() { - return stickerId; - } - - public @Nullable String getEmoji() { - return emoji; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeString(packId); - dest.writeString(packKey); - dest.writeInt(stickerId); - dest.writeString(emoji); - } - - public static final Creator CREATOR = new Creator() { - @Override - public StickerLocator createFromParcel(Parcel in) { - return new StickerLocator(in); - } - - @Override - public StickerLocator[] newArray(int size) { - return new StickerLocator[size]; - } - }; -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerLocator.kt b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerLocator.kt new file mode 100644 index 0000000000..47749a43c5 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerLocator.kt @@ -0,0 +1,16 @@ +package org.thoughtcrime.securesms.stickers + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +@Parcelize +class StickerLocator( + @JvmField + val packId: String, + @JvmField + val packKey: String, + @JvmField + val stickerId: Int, + @JvmField + val emoji: String? +) : Parcelable diff --git a/app/src/main/java/org/thoughtcrime/securesms/storage/ContactRecordProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/storage/ContactRecordProcessor.java index 26959339b4..b090198589 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/storage/ContactRecordProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/storage/ContactRecordProcessor.java @@ -184,7 +184,8 @@ boolean isInvalid(@NonNull SignalContactRecord remote) { byte[] identityKey; if ((remote.getIdentityState() != local.getIdentityState() && remote.getIdentityKey().isPresent()) || - (remote.getIdentityKey().isPresent() && local.getIdentityKey().isEmpty())) + (remote.getIdentityKey().isPresent() && local.getIdentityKey().isEmpty()) || + (remote.getIdentityKey().isPresent() && local.getUnregisteredTimestamp() > 0)) { identityState = remote.getIdentityState(); identityKey = remote.getIdentityKey().get(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/storage/StorageSyncHelper.java b/app/src/main/java/org/thoughtcrime/securesms/storage/StorageSyncHelper.java index df294f09a4..9719942603 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/storage/StorageSyncHelper.java +++ b/app/src/main/java/org/thoughtcrime/securesms/storage/StorageSyncHelper.java @@ -17,6 +17,7 @@ import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob; import org.thoughtcrime.securesms.jobs.StorageSyncJob; +import org.thoughtcrime.securesms.keyvalue.AccountValues; import org.thoughtcrime.securesms.keyvalue.PhoneNumberPrivacyValues; import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.payments.Entropy; @@ -207,7 +208,6 @@ public static void applyAccountStorageSyncUpdates(@NonNull Context context, @Non SignalStore.storyValues().setUserHasViewedOnboardingStory(update.getNew().hasViewedOnboardingStory()); SignalStore.storyValues().setFeatureDisabled(update.getNew().isStoriesDisabled()); SignalStore.storyValues().setUserHasSeenGroupStoryEducationSheet(update.getNew().hasSeenGroupStoryEducationSheet()); - SignalStore.account().setUsername(update.getNew().getUsername()); if (update.getNew().getStoryViewReceiptsState() == OptionalBool.UNSET) { SignalStore.storyValues().setViewedReceiptsEnabled(update.getNew().isReadReceiptsEnabled()); @@ -236,6 +236,12 @@ public static void applyAccountStorageSyncUpdates(@NonNull Context context, @Non ApplicationDependencies.getJobManager().add(new RetrieveProfileAvatarJob(self, update.getNew().getAvatarUrlPath().get())); } + if (!update.getNew().getUsername().equals(update.getOld().getUsername())) { + SignalStore.account().setUsername(update.getNew().getUsername()); + SignalStore.account().setUsernameSyncState(AccountValues.UsernameSyncState.IN_SYNC); + SignalStore.account().setUsernameSyncErrorCount(0); + } + if (update.getNew().getUsernameLink() != null) { SignalStore.account().setUsernameLink( new UsernameLinkComponents( diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/Stories.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/Stories.kt index cd6559cd68..3e48cae81a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/Stories.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/Stories.kt @@ -246,7 +246,7 @@ object Stories { return if (MediaUtil.isVideo(media.mimeType)) { val mediaDuration = if (media.duration == 0L && media.transformProperties.map(TransformProperties::shouldSkipTransform).orElse(true)) { getVideoDuration(media.uri) - } else if (media.transformProperties.map { it.isVideoTrim }.orElse(false)) { + } else if (media.transformProperties.map { it.videoTrim }.orElse(false)) { TimeUnit.MICROSECONDS.toMillis(media.transformProperties.get().videoTrimEndTimeUs - media.transformProperties.get().videoTrimStartTimeUs) } else { media.duration @@ -273,6 +273,8 @@ object Stories { @JvmStatic @WorkerThread fun getVideoDuration(uri: Uri): Long { + ThreadUtil.assertNotMainThread() + var duration = 0L var player: ExoPlayer? = null val countDownLatch = CountDownLatch(1) @@ -339,7 +341,7 @@ object Stories { error("Illegal clip: $startTimeUs > $endTimeUs for clip $clipIndex") } - AttachmentTable.TransformProperties(false, true, startTimeUs, endTimeUs, SentMediaQuality.STANDARD.code) + AttachmentTable.TransformProperties(false, true, startTimeUs, endTimeUs, SentMediaQuality.STANDARD.code, false) }.map { transformMedia(media, it) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/post/StoryPostViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/post/StoryPostViewModel.kt index da2887ba5a..a1389f3ace 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/post/StoryPostViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/post/StoryPostViewModel.kt @@ -41,18 +41,18 @@ class StoryPostViewModel(private val repository: StoryTextPostRepository) : View store.update { StoryPostState.None() } } else if (storyPostContent.isVideo()) { store.update { - val shouldSkipTransform = storyPostContent.attachment.transformProperties.shouldSkipTransform() + val shouldSkipTransform = storyPostContent.attachment.transformProperties?.shouldSkipTransform() == true val clipStart: Duration = if (shouldSkipTransform) { 0L.microseconds } else { - storyPostContent.attachment.transformProperties.videoTrimStartTimeUs.microseconds + storyPostContent.attachment.transformProperties?.videoTrimStartTimeUs?.microseconds ?: 0L.microseconds } val clipEnd: Duration = if (shouldSkipTransform) { 0L.microseconds } else { - storyPostContent.attachment.transformProperties.videoTrimEndTimeUs.microseconds + storyPostContent.attachment.transformProperties?.videoTrimEndTimeUs?.microseconds ?: 0L.microseconds } StoryPostState.VideoPost( diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/reply/reaction/MultiReactionBurstLayout.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/reply/reaction/MultiReactionBurstLayout.kt new file mode 100644 index 0000000000..2b366eafc9 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/reply/reaction/MultiReactionBurstLayout.kt @@ -0,0 +1,73 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.stories.viewer.reply.reaction + +import android.content.Context +import android.util.AttributeSet +import android.widget.FrameLayout +import androidx.core.view.children +import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.components.emoji.EmojiUtil +import org.thoughtcrime.securesms.events.GroupCallReactionEvent +import kotlin.time.Duration.Companion.seconds + +class MultiReactionBurstLayout @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null +) : FrameLayout(context, attrs) { + private val cooldownTimes = mutableMapOf() + + private var nextViewIndex = 0 + + init { + repeat(MAX_SIMULTANEOUS_REACTIONS) { + val view = OnReactionSentView(context, layoutRes = R.layout.reaction_burst_view) + addView(view) + } + } + + fun displayReactions(reactions: List) { + if (children.count() == 0) { + throw IllegalStateException("You must add views before displaying reactions!") + } + + reactions.filter { + if (it.getExpirationTimestamp() < System.currentTimeMillis()) { + return@filter false + } + + val cutoffTimestamp = cooldownTimes[EmojiUtil.getCanonicalRepresentation(it.reaction)] ?: return@filter true + + return@filter cutoffTimestamp < it.timestamp + } + .groupBy { EmojiUtil.getCanonicalRepresentation(it.reaction) } + .filter { it.value.groupBy { event -> event.sender }.size >= REACTION_COUNT_THRESHOLD } + .values + .map { it.sortedBy { event -> event.timestamp } } + .map { it[REACTION_COUNT_THRESHOLD - 1] } + .sortedBy { it.timestamp } + .take(MAX_SIMULTANEOUS_REACTIONS - cooldownTimes.filter { it.value > System.currentTimeMillis() }.size) + .forEach { + val reactionView = getNextReactionView() + reactionView.playForEmoji(it.reaction) + cooldownTimes[EmojiUtil.getCanonicalRepresentation(it.reaction)] = it.timestamp + cooldownDuration.inWholeMilliseconds + } + } + + private fun getNextReactionView(): OnReactionSentView { + val v = getChildAt(nextViewIndex) as OnReactionSentView + + nextViewIndex = (nextViewIndex + 1) % MAX_SIMULTANEOUS_REACTIONS + + return v + } + + companion object { + private const val REACTION_COUNT_THRESHOLD = 3 + private const val MAX_SIMULTANEOUS_REACTIONS = 3 + private val cooldownDuration = 2.seconds + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/reply/reaction/OnReactionSentView.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/reply/reaction/OnReactionSentView.kt index bc31658fd2..074b8362c4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/reply/reaction/OnReactionSentView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/reply/reaction/OnReactionSentView.kt @@ -11,13 +11,14 @@ import org.thoughtcrime.securesms.components.emoji.EmojiImageView class OnReactionSentView @JvmOverloads constructor( context: Context, - attrs: AttributeSet? = null + attrs: AttributeSet? = null, + layoutRes: Int = R.layout.on_reaction_sent_view ) : FrameLayout(context, attrs) { var callback: Callback? = null init { - inflate(context, R.layout.on_reaction_sent_view, this) + inflate(context, layoutRes, this) } private val motionLayout: MotionLayout = findViewById(R.id.motion_layout) diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/AttachmentUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/AttachmentUtil.java index 5a5999ecb9..564594dee6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/AttachmentUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/AttachmentUtil.java @@ -37,15 +37,15 @@ public static boolean isAutoDownloadPermitted(@NonNull Context context, @Nullabl } Set allowedTypes = getAllowedAutoDownloadTypes(context); - String contentType = attachment.getContentType(); + String contentType = attachment.contentType; - if (attachment.isVoiceNote() || - (MediaUtil.isAudio(attachment) && TextUtils.isEmpty(attachment.getFileName())) || - MediaUtil.isLongTextType(attachment.getContentType()) || + if (attachment.voiceNote || + (MediaUtil.isAudio(attachment) && TextUtils.isEmpty(attachment.fileName)) || + MediaUtil.isLongTextType(attachment.contentType) || attachment.isSticker()) { return true; - } else if (attachment.isVideoGif()) { + } else if (attachment.videoGif) { boolean allowed = NotInCallConstraint.isNotInConnectedCall() && allowedTypes.contains("image"); if (!allowed) { Log.w(TAG, "Not auto downloading. inCall: " + NotInCallConstraint.isNotInConnectedCall() + " allowedType: " + allowedTypes.contains("image")); @@ -74,8 +74,8 @@ public static boolean isAutoDownloadPermitted(@NonNull Context context, @Nullabl public static void deleteAttachment(@NonNull Context context, @NonNull DatabaseAttachment attachment) { - AttachmentId attachmentId = attachment.getAttachmentId(); - long mmsId = attachment.getMmsId(); + AttachmentId attachmentId = attachment.attachmentId; + long mmsId = attachment.mmsId; int attachmentCount = SignalDatabase.attachments() .getAttachmentsForMessage(mmsId) .size(); @@ -104,7 +104,7 @@ private static boolean isNonDocumentType(String contentType) { @WorkerThread private static boolean isFromTrustedConversation(@NonNull Context context, @NonNull DatabaseAttachment attachment) { try { - MessageRecord message = SignalDatabase.messages().getMessageRecord(attachment.getMmsId()); + MessageRecord message = SignalDatabase.messages().getMessageRecord(attachment.mmsId); Recipient fromRecipient = message.getFromRecipient(); Recipient toRecipient = SignalDatabase.threads().getRecipientForThreadId(message.getThreadId()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/BubbleUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/BubbleUtil.java index 93e185b000..979759b89c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/BubbleUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/BubbleUtil.java @@ -120,7 +120,7 @@ public static void displayAsBubble(@NonNull Context context, @NonNull RecipientI .orElse(null); if (activeThreadNotification != null && activeThreadNotification.deleteIntent != null) { - ApplicationDependencies.getMessageNotifier().updateNotification(context, conversationId, BubbleState.SHOWN); + ApplicationDependencies.getMessageNotifier().forceBubbleNotification(context, conversationId); } else { Recipient recipient = Recipient.resolved(recipientId); NotificationFactory.notifyToBubbleConversation(context, recipient, threadId); diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/DateUtils.kt b/app/src/main/java/org/thoughtcrime/securesms/util/DateUtils.kt index 6db505a07e..9a560e367e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/DateUtils.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/DateUtils.kt @@ -60,7 +60,7 @@ object DateUtils : android.text.format.DateUtils() { @JvmStatic fun getBriefRelativeTimeSpanString(c: Context, locale: Locale, timestamp: Long): String { return when { - timestamp.isWithin(1.minutes) -> { + isNow(timestamp) -> { c.getString(R.string.DateUtils_just_now) } timestamp.isWithin(1.hours) -> { @@ -89,7 +89,7 @@ object DateUtils : android.text.format.DateUtils() { @JvmStatic fun getExtendedRelativeTimeSpanString(context: Context, locale: Locale, timestamp: Long): String { return when { - timestamp.isWithin(1.minutes) -> { + isNow(timestamp) -> { context.getString(R.string.DateUtils_just_now) } timestamp.isWithin(1.hours) -> { @@ -130,15 +130,15 @@ object DateUtils : android.text.format.DateUtils() { @JvmStatic fun getDatelessRelativeTimeSpanFormattedDate(context: Context, locale: Locale, timestamp: Long): FormattedDate { return when { - timestamp.isWithin(1.minutes) -> { - FormattedDate(true, context.getString(R.string.DateUtils_just_now)) + isNow(timestamp) -> { + FormattedDate(isRelative = true, isNow = true, value = context.getString(R.string.DateUtils_just_now)) } timestamp.isWithin(1.hours) -> { val minutes = timestamp.convertDeltaTo(DurationUnit.MINUTES) - FormattedDate(true, context.resources.getString(R.string.DateUtils_minutes_ago, minutes)) + FormattedDate(isRelative = true, isNow = false, value = context.resources.getString(R.string.DateUtils_minutes_ago, minutes)) } else -> { - FormattedDate(false, getOnlyTimeString(context, timestamp)) + FormattedDate(isRelative = false, isNow = false, value = getOnlyTimeString(context, timestamp)) } } } @@ -336,6 +336,16 @@ object DateUtils : android.text.format.DateUtils() { } } + /** + * This exposes "now" (defined here as a one minute window) to other classes. + * This is because certain locales use different linguistic constructions for "modified n minutes ago" and "modified just now", + * and therefore the caller will need to load different string resources in these situations. + * + * @param timestamp a Unix timestamp + */ + @JvmStatic + fun isNow(timestamp: Long) = timestamp.isWithin(1.minutes) + private fun Long.isWithin(duration: Duration): Boolean { return System.currentTimeMillis() - this <= duration.inWholeMilliseconds } diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java b/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java index 35ef6a7576..1af72abd06 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java @@ -116,6 +116,9 @@ public final class FeatureFlags { private static final String IDEAL_DONATIONS = "android.ideal.donations.5"; public static final String IDEAL_ENABLED_REGIONS = "global.donations.idealEnabledRegions"; public static final String SEPA_ENABLED_REGIONS = "global.donations.sepaEnabledRegions"; + private static final String CALLING_REACTIONS = "android.calling.reactions"; + private static final String NOTIFICATION_THUMBNAIL_BLOCKLIST = "android.notificationThumbnailProductBlocklist"; + private static final String CALLING_RAISE_HAND = "android.calling.raiseHand"; /** * We will only store remote values for flags in this set. If you want a flag to be controllable @@ -183,7 +186,10 @@ public final class FeatureFlags { SEPA_DEBIT_DONATIONS, IDEAL_DONATIONS, IDEAL_ENABLED_REGIONS, - SEPA_ENABLED_REGIONS + SEPA_ENABLED_REGIONS, + CALLING_REACTIONS, + NOTIFICATION_THUMBNAIL_BLOCKLIST, + CALLING_RAISE_HAND ); @VisibleForTesting @@ -253,7 +259,10 @@ public final class FeatureFlags { PROMPT_BATTERY_SAVER, USERNAMES, CRASH_PROMPT_CONFIG, - BLOCK_SSE + BLOCK_SSE, + CALLING_REACTIONS, + NOTIFICATION_THUMBNAIL_BLOCKLIST, + CALLING_RAISE_HAND ); /** @@ -584,6 +593,25 @@ public static boolean instantVideoPlayback() { return getBoolean(INSTANT_VIDEO_PLAYBACK, false); } + /** + * Whether or not group call reactions are enabled. + */ + public static boolean groupCallReactions() { + return getBoolean(CALLING_REACTIONS, false); + } + + /** + * Whether or not group call raise hand is enabled. + */ + public static boolean groupCallRaiseHand() { + return getBoolean(CALLING_RAISE_HAND, false); + } + + /** List of device products that are blocked from showing notification thumbnails. */ + public static String notificationThumbnailProductBlocklist() { + return getString(NOTIFICATION_THUMBNAIL_BLOCKLIST, ""); + } + /** Only for rendering debug info. */ public static synchronized @NonNull Map getMemoryValues() { return new TreeMap<>(REMOTE_VALUES); diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/MediaUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/MediaUtil.java index 2f85500b76..1b664ff0e1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/MediaUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/MediaUtil.java @@ -99,7 +99,7 @@ public static SlideType getSlideTypeFromContentType(@NonNull String contentType) return new StickerSlide(attachment); } - switch (getSlideTypeFromContentType(attachment.getContentType())) { + switch (getSlideTypeFromContentType(attachment.contentType)) { case GIF : return new GifSlide(attachment); case IMAGE : return new ImageSlide(attachment); case VIDEO : return new VideoSlide(attachment); @@ -261,31 +261,31 @@ public static boolean isMms(String contentType) { } public static boolean isGif(Attachment attachment) { - return isGif(attachment.getContentType()); + return isGif(attachment.contentType); } public static boolean isJpeg(Attachment attachment) { - return isJpegType(attachment.getContentType()); + return isJpegType(attachment.contentType); } public static boolean isHeic(Attachment attachment) { - return isHeicType(attachment.getContentType()); + return isHeicType(attachment.contentType); } public static boolean isHeif(Attachment attachment) { - return isHeifType(attachment.getContentType()); + return isHeifType(attachment.contentType); } public static boolean isImage(Attachment attachment) { - return isImageType(attachment.getContentType()); + return isImageType(attachment.contentType); } public static boolean isAudio(Attachment attachment) { - return isAudioType(attachment.getContentType()); + return isAudioType(attachment.contentType); } public static boolean isVideo(Attachment attachment) { - return isVideoType(attachment.getContentType()); + return isVideoType(attachment.contentType); } public static boolean isVideo(String contentType) { @@ -470,7 +470,7 @@ public static boolean isInstantVideoSupported(Slide slide) { } final Attachment attachment = slide.asAttachment(); final boolean isIncremental = attachment.getIncrementalDigest() != null; - final boolean hasIncrementalMacChunkSizeDefined = attachment.getIncrementalMacChunkSize() > 0; + final boolean hasIncrementalMacChunkSizeDefined = attachment.incrementalMacChunkSize > 0; final boolean contentTypeSupported = isVideoType(slide.getContentType()); return isIncremental && contentTypeSupported && hasIncrementalMacChunkSizeDefined; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/ProfileUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/ProfileUtil.java index 2c83ac273f..6008503691 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/ProfileUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/ProfileUtil.java @@ -150,6 +150,17 @@ private static Single>> re return decryptString(profileKey, Base64.decode(encryptedStringBase64)); } + public static Optional decryptBoolean(@NonNull ProfileKey profileKey, @Nullable String encryptedBooleanBase64) + throws InvalidCiphertextException, IOException + { + if (encryptedBooleanBase64 == null) { + return Optional.empty(); + } + + ProfileCipher profileCipher = new ProfileCipher(profileKey); + return profileCipher.decryptBoolean(Base64.decode(encryptedBooleanBase64)); + } + @WorkerThread public static @NonNull MobileCoinPublicAddress getAddressForRecipient(@NonNull Recipient recipient) throws IOException, PaymentsAddressException @@ -337,7 +348,8 @@ private static void uploadProfile(@NonNull ProfileName profileName, aboutEmoji, Optional.ofNullable(paymentsAddress), avatar, - badgeIds).orElse(null); + badgeIds, + SignalStore.phoneNumberPrivacy().isPhoneNumberSharingEnabled()).orElse(null); SignalStore.registrationValues().markHasUploadedProfile(); if (!avatar.keepTheSame) { SignalDatabase.recipients().setProfileAvatar(Recipient.self().getId(), avatarPath); diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/SignalUncaughtExceptionHandler.java b/app/src/main/java/org/thoughtcrime/securesms/util/SignalUncaughtExceptionHandler.java index 102bc770f7..4f44dc1358 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/SignalUncaughtExceptionHandler.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/SignalUncaughtExceptionHandler.java @@ -1,10 +1,14 @@ package org.thoughtcrime.securesms.util; +import android.database.sqlite.SQLiteDatabaseCorruptException; + import androidx.annotation.NonNull; import org.signal.core.util.ExceptionUtil; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.database.LogDatabase; +import org.thoughtcrime.securesms.database.SearchTable; +import org.thoughtcrime.securesms.database.SignalDatabase; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.keyvalue.SignalStore; @@ -36,6 +40,15 @@ public void uncaughtException(@NonNull Thread t, @NonNull Throwable e) { return; } + if (e instanceof SQLiteDatabaseCorruptException) { + if (e.getMessage().indexOf("message_fts") >= 0) { + Log.w(TAG, "FTS corrupted! Resetting FTS index."); + SignalDatabase.messageSearch().fullyResetTables(); + } else { + Log.w(TAG, "Some non-FTS related corruption?"); + } + } + if (e instanceof OnErrorNotImplementedException && e.getCause() != null) { e = e.getCause(); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/SpanUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/SpanUtil.java index 37873dc22d..44dd314459 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/SpanUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/SpanUtil.java @@ -2,7 +2,9 @@ import android.content.Context; import android.content.res.Resources; +import android.graphics.Color; import android.graphics.Typeface; +import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Build; import android.text.Layout; @@ -33,6 +35,7 @@ import androidx.annotation.StyleRes; import androidx.core.content.ContextCompat; +import org.signal.core.util.DimensionUnit; import org.thoughtcrime.securesms.R; public final class SpanUtil { @@ -128,6 +131,13 @@ public static CharSequence buildCenteredImageSpan(@NonNull Drawable drawable) { return imageSpan; } + public static CharSequence space(int width, @NonNull DimensionUnit widthUnit) { + Drawable drawable = new ColorDrawable(Color.TRANSPARENT); + drawable.setBounds(0, 0, (int) widthUnit.toPixels(width), 1); + + return buildCenteredImageSpan(drawable); + } + public static void appendCenteredImageSpan(@NonNull SpannableStringBuilder builder, @NonNull Drawable drawable, int width, int height) { drawable.setBounds(0, 0, ViewUtil.dpToPx(width), ViewUtil.dpToPx(height)); builder.append(" ").append(SpanUtil.buildCenteredImageSpan(drawable)); diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/UsernameUtil.kt b/app/src/main/java/org/thoughtcrime/securesms/util/UsernameUtil.kt index 6defc731c5..0777040305 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/UsernameUtil.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/UsernameUtil.kt @@ -6,9 +6,11 @@ import java.util.regex.Pattern object UsernameUtil { private val TAG = Log.tag(UsernameUtil::class.java) - const val MIN_LENGTH = 3 - const val MAX_LENGTH = 32 - private val FULL_PATTERN = Pattern.compile(String.format(Locale.US, "^[a-zA-Z_][a-zA-Z0-9_]{%d,%d}$", MIN_LENGTH - 1, MAX_LENGTH - 1), Pattern.CASE_INSENSITIVE) + const val MIN_NICKNAME_LENGTH = 3 + const val MAX_NICKNAME_LENGTH = 32 + const val MIN_DISCRIMINATOR_LENGTH = 2 + const val MAX_DISCRIMINATOR_LENGTH = 10 + private val FULL_PATTERN = Pattern.compile(String.format(Locale.US, "^[a-zA-Z_][a-zA-Z0-9_]{%d,%d}$", MIN_NICKNAME_LENGTH - 1, MAX_NICKNAME_LENGTH - 1), Pattern.CASE_INSENSITIVE) private val DIGIT_START_PATTERN = Pattern.compile("^[0-9].*$") private const val BASE_URL_SCHEMELESS = "signal.me/#eu/" private const val BASE_URL = "https://$BASE_URL_SCHEMELESS" @@ -17,8 +19,8 @@ object UsernameUtil { String.format( Locale.US, "^@?[a-zA-Z_][a-zA-Z0-9_]{%d,%d}(.[0-9]+)?$", - MIN_LENGTH - 1, - MAX_LENGTH - 1, + MIN_NICKNAME_LENGTH - 1, + MAX_NICKNAME_LENGTH - 1, Pattern.CASE_INSENSITIVE ) ) @@ -39,10 +41,10 @@ object UsernameUtil { value == null -> { InvalidReason.TOO_SHORT } - value.length < MIN_LENGTH -> { + value.length < MIN_NICKNAME_LENGTH -> { InvalidReason.TOO_SHORT } - value.length > MAX_LENGTH -> { + value.length > MAX_NICKNAME_LENGTH -> { InvalidReason.TOO_LONG } DIGIT_START_PATTERN.matcher(value).matches() -> { @@ -57,6 +59,26 @@ object UsernameUtil { } } + fun checkDiscriminator(value: String?): InvalidReason? { + return when { + value == null -> { + null + } + value.length < MIN_DISCRIMINATOR_LENGTH -> { + InvalidReason.TOO_SHORT + } + value.length > MAX_DISCRIMINATOR_LENGTH -> { + InvalidReason.TOO_LONG + } + value.toIntOrNull() == null -> { + InvalidReason.INVALID_CHARACTERS + } + else -> { + null + } + } + } + enum class InvalidReason { TOO_SHORT, TOO_LONG, diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/views/Stub.java b/app/src/main/java/org/thoughtcrime/securesms/util/views/Stub.java index e8c4838582..d3f3271c04 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/views/Stub.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/views/Stub.java @@ -15,6 +15,10 @@ public Stub(@NonNull ViewStub viewStub) { this.viewStub = viewStub; } + public int getId() { + return (viewStub != null) ? viewStub.getId() : view.getId(); + } + public T get() { if (view == null) { //noinspection unchecked diff --git a/app/src/main/java/org/thoughtcrime/securesms/video/InMemoryTranscoder.java b/app/src/main/java/org/thoughtcrime/securesms/video/InMemoryTranscoder.java index 27cac1570b..6ebc7c2add 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/video/InMemoryTranscoder.java +++ b/app/src/main/java/org/thoughtcrime/securesms/video/InMemoryTranscoder.java @@ -180,7 +180,7 @@ public InMemoryTranscoder(@NonNull Context context, @NonNull MediaDataSource dat if (metadata != null && metadata.getSanitizedMetadata() != null) { memoryFile.seek(metadata.getDataOffset()); - return new MediaStream(new SequenceInputStream(new ByteArrayInputStream(metadata.getSanitizedMetadata()), ByteStreams.limit(new FileInputStream(memoryFileFileDescriptor), metadata.getDataLength())), MimeTypes.VIDEO_MP4, 0, 0); + return new MediaStream(new SequenceInputStream(new ByteArrayInputStream(metadata.getSanitizedMetadata()), ByteStreams.limit(new FileInputStream(memoryFileFileDescriptor), metadata.getDataLength())), MimeTypes.VIDEO_MP4, 0, 0, true); } else { memoryFile.seek(0); return new MediaStream(new FileInputStream(memoryFileFileDescriptor), MimeTypes.VIDEO_MP4, 0, 0); diff --git a/app/src/main/java/org/thoughtcrime/securesms/video/StreamingTranscoder.java b/app/src/main/java/org/thoughtcrime/securesms/video/StreamingTranscoder.java index d56a41b91d..8f1690648f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/video/StreamingTranscoder.java +++ b/app/src/main/java/org/thoughtcrime/securesms/video/StreamingTranscoder.java @@ -29,7 +29,6 @@ public final class StreamingTranscoder { private final long duration; private final int inputBitRate; private final VideoBitRateCalculator.Quality targetQuality; - private final long memoryFileEstimate; private final boolean transcodeRequired; private final long fileSizeEstimate; private final @Nullable TranscoderOptions options; @@ -65,7 +64,6 @@ public StreamingTranscoder(@NonNull MediaDataSource dataSource, } this.fileSizeEstimate = targetQuality.getFileSizeEstimate(); - this.memoryFileEstimate = (long) (fileSizeEstimate * 1.1); } public void transcode(@NonNull Progress progress, @@ -136,14 +134,12 @@ public void transcode(@NonNull Progress progress, "Output size : %s kB\n" + " of Original : %.1f%%\n" + " of Estimate : %.1f%%\n" + - " of Memory : %.1f%%\n" + "Output bitrate : %s bps", encodeDurationSec, durationSec / encodeDurationSec, numberFormat.format(outSize / 1024), (outSize * 100d) / inSize, (outSize * 100d) / fileSizeEstimate, - (outSize * 100d) / memoryFileEstimate, numberFormat.format(VideoBitRateCalculator.bitRate(outSize, duration)))); if (outSize > upperSizeLimit) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/video/exo/PartDataSource.java b/app/src/main/java/org/thoughtcrime/securesms/video/exo/PartDataSource.java index 2fe498c6db..5ba14edc59 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/video/exo/PartDataSource.java +++ b/app/src/main/java/org/thoughtcrime/securesms/video/exo/PartDataSource.java @@ -58,13 +58,14 @@ public long open(DataSpec dataSpec) throws IOException { final boolean hasIncrementalDigest = attachment.getIncrementalDigest() != null; final boolean inProgress = attachment.isInProgress(); - final String attachmentKey = attachment.getKey(); - final boolean hasData = attachment.hasData(); + final String attachmentKey = attachment.remoteKey; + final boolean hasData = attachment.hasData; + if (inProgress && !hasData && hasIncrementalDigest && attachmentKey != null && FeatureFlags.instantVideoPlayback()) { final byte[] decode = Base64.decode(attachmentKey); - final File transferFile = attachmentDatabase.getOrCreateTransferFile(attachment.getAttachmentId()); + final File transferFile = attachmentDatabase.getOrCreateTransferFile(attachment.attachmentId); try { - this.inputStream = AttachmentCipherInputStream.createForAttachment(transferFile, attachment.getSize(), decode, attachment.getDigest(), attachment.getIncrementalDigest(), attachment.getIncrementalMacChunkSize()); + this.inputStream = AttachmentCipherInputStream.createForAttachment(transferFile, attachment.size, decode, attachment.remoteDigest, attachment.getIncrementalDigest(), attachment.incrementalMacChunkSize); long skipped = 0; while (skipped < dataSpec.position) { @@ -81,8 +82,8 @@ public long open(DataSpec dataSpec) throws IOException { Log.d(TAG, "Successfully loaded completed attachment file."); } else { - throw new IOException("Ineligible " + attachment.getAttachmentId().toString() - + "\nTransfer state: " + attachment.getTransferState() + throw new IOException("Ineligible " + attachment.attachmentId.toString() + + "\nTransfer state: " + attachment.transferState + "\nIncremental Digest Present: " + hasIncrementalDigest + "\nAttachment Key Non-Empty: " + (attachmentKey != null && !attachmentKey.isEmpty())); } @@ -91,9 +92,9 @@ public long open(DataSpec dataSpec) throws IOException { listener.onTransferStart(this, dataSpec, false); } - if (attachment.getSize() - dataSpec.position <= 0) throw new EOFException("No more data"); + if (attachment.size - dataSpec.position <= 0) throw new EOFException("No more data"); - return attachment.getSize() - dataSpec.position; + return attachment.size - dataSpec.position; } @Override diff --git a/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/AudioManagerCommand.kt b/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/AudioManagerCommand.kt index d97dda3371..c99acb7e9a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/AudioManagerCommand.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/AudioManagerCommand.kt @@ -115,6 +115,13 @@ sealed class AudioManagerCommand : Parcelable { } } + class PlayStateChangeUp : AudioManagerCommand() { + companion object { + @JvmField + val CREATOR: Parcelable.Creator = ParcelCheat { PlayStateChangeUp() } + } + } + class ParcelCheat(private val createFrom: (Parcel) -> T) : Parcelable.Creator { override fun createFromParcel(parcel: Parcel): T = createFrom(parcel) override fun newArray(size: Int): Array = throw UnsupportedOperationException() diff --git a/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/AudioManagerCompat.java b/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/AudioManagerCompat.java index 291cea37fa..bd5e26f97e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/AudioManagerCompat.java +++ b/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/AudioManagerCompat.java @@ -186,6 +186,10 @@ private static float logVolume(int volume, int maxVolume) { return (float) (1 - (Math.log(maxVolume + 1 - volume) / Math.log(maxVolume + 1))); } + public float getVoiceCallVolume() { + return audioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL); + } + abstract public SoundPool createSoundPool(); abstract public boolean requestCallAudioFocus(); @@ -238,13 +242,17 @@ public boolean requestCallAudioFocus() { Log.w(TAG, "Trying again to request audio focus"); } - int result = audioManager.requestAudioFocus(audioFocusRequest); + try { + int result = audioManager.requestAudioFocus(audioFocusRequest); - if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { - Log.w(TAG, "Audio focus not granted. Result code: " + result); + if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { + Log.w(TAG, "Audio focus not granted. Result code: " + result); + return false; + } + } catch (SecurityException ex) { + Log.w(TAG, "Encountered security exception when requesting audio focus."); return false; } - return true; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/FullSignalAudioManagerApi31.kt b/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/FullSignalAudioManagerApi31.kt index 15a3e30c0a..e662dc789b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/FullSignalAudioManagerApi31.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/FullSignalAudioManagerApi31.kt @@ -109,14 +109,23 @@ class FullSignalAudioManagerApi31(context: Context, eventListener: EventListener val volume: Float = androidAudioManager.ringVolumeWithMinimum() soundPool.play(disconnectedSoundId, volume, volume, 0, 0, 1.0f) } - state = State.UNINITIALIZED androidAudioManager.unregisterAudioDeviceCallback(deviceCallback) - androidAudioManager.clearCommunicationDevice() - setSpeakerphoneOn(savedIsSpeakerPhoneOn) - setMicrophoneMute(savedIsMicrophoneMute) - androidAudioManager.mode = savedAudioMode + if (state == State.UNINITIALIZED && userSelectedAudioDevice != null) { + Log.d( + TAG, + "Stopping audio manager after selecting audio device but never initializing. " + + "This indicates a service spun up solely to set audio device. " + + "Therefore skipping audio device reset." + ) + } else { + androidAudioManager.clearCommunicationDevice() + setSpeakerphoneOn(savedIsSpeakerPhoneOn) + setMicrophoneMute(savedIsMicrophoneMute) + androidAudioManager.mode = savedAudioMode + } androidAudioManager.abandonCallAudioFocus() Log.d(TAG, "Abandoned audio focus for VOICE_CALL streams") + state = State.UNINITIALIZED Log.d(TAG, "Stopped") } diff --git a/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/SignalAudioManager.kt b/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/SignalAudioManager.kt index deac5b868e..0cab43e7f9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/SignalAudioManager.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/SignalAudioManager.kt @@ -44,6 +44,8 @@ sealed class SignalAudioManager(protected val context: Context, protected val ev protected val incomingRinger = IncomingRinger(context) protected val outgoingRinger = OutgoingRinger(context) + private val stateChangeUpSoundId = soundPool.load(context, R.raw.notification_simple_01, 1) + companion object { @JvmStatic fun create(context: Context, eventListener: EventListener?): SignalAudioManager { @@ -66,6 +68,7 @@ sealed class SignalAudioManager(protected val context: Context, protected val ev is AudioManagerCommand.StartIncomingRinger -> startIncomingRinger(command.ringtoneUri, command.vibrate) is AudioManagerCommand.SilenceIncomingRinger -> silenceIncomingRinger() is AudioManagerCommand.StartOutgoingRinger -> startOutgoingRinger() + is AudioManagerCommand.PlayStateChangeUp -> playStateChangeUp() } } } @@ -81,6 +84,11 @@ sealed class SignalAudioManager(protected val context: Context, protected val ev } } + private fun playStateChangeUp() { + val volume: Float = androidAudioManager.voiceCallVolume + soundPool.play(stateChangeUpSoundId, volume, volume, 0, 0, 1f) + } + protected abstract fun initialize() protected abstract fun start() protected abstract fun stop(playDisconnect: Boolean) diff --git a/app/src/main/protowire/Backup.proto b/app/src/main/protowire/Backup.proto new file mode 100644 index 0000000000..6bcb277bbf --- /dev/null +++ b/app/src/main/protowire/Backup.proto @@ -0,0 +1,568 @@ +syntax = "proto3"; + +package signal.backup; + +option java_package = "org.thoughtcrime.securesms.backup.v2.proto"; + +message BackupInfo { + uint64 version = 1; + uint64 backupTimeMs = 2; + bytes iv = 3; +} + +message Frame { + oneof item { + AccountData account = 1; + Recipient recipient = 2; + Chat chat = 3; + ChatItem chatItem = 4; + Call call = 5; + StickerPack stickerPack = 6; + } +} + +message AccountData { + enum PhoneNumberSharingMode { + UNKNOWN = 0; + EVERYBODY = 1; + NOBODY = 2; + } + message UsernameLink { + enum Color { + UNKNOWN = 0; + BLUE = 1; + WHITE = 2; + GREY = 3; + OLIVE = 4; + GREEN = 5; + ORANGE = 6; + PINK = 7; + PURPLE = 8; + } + + bytes entropy = 1; // 32 bytes of entropy used for encryption + bytes serverId = 2; // 16 bytes of encoded UUID provided by the server + Color color = 3; + } + + message AccountSettings { + bool readReceipts = 1; + bool sealedSenderIndicators = 2; + bool typingIndicators = 3; + bool noteToSelfMarkedUnread = 4; + bool linkPreviews = 5; + bool notDiscoverableByPhoneNumber = 6; + bool preferContactAvatars = 7; + uint32 universalExpireTimer = 8; + repeated string preferredReactionEmoji = 9; + bool displayBadgesOnProfile = 10; + bool keepMutedChatsArchived = 11; + bool hasSetMyStoriesPrivacy = 12; + bool hasViewedOnboardingStory = 13; + bool storiesDisabled = 14; + optional bool storyViewReceiptsEnabled = 15; + bool hasSeenGroupStoryEducationSheet = 16; + bool hasCompletedUsernameOnboarding = 17; + PhoneNumberSharingMode phoneNumberSharingMode = 18; + } + + bytes profileKey = 1; + optional string username = 2; + UsernameLink usernameLink = 3; + string givenName = 4; + string familyName = 5; + string avatarUrlPath = 6; + bytes subscriberId = 7; + string subscriberCurrencyCode = 8; + bool subscriptionManuallyCancelled = 9; + AccountSettings accountSettings = 10; +} + +message Recipient { + uint64 id = 1; // generated id for reference only within this file + oneof destination { + Contact contact = 2; + Group group = 3; + DistributionList distributionList = 4; + Self self = 5; + ReleaseNotes releaseNotes = 6; + } +} + +message Contact { + enum Registered { + UNKNOWN = 0; + REGISTERED = 1; + NOT_REGISTERED = 2; + } + + optional bytes aci = 1; // should be 16 bytes + optional bytes pni = 2; // should be 16 bytes + optional string username = 3; + optional uint64 e164 = 4; + bool blocked = 5; + bool hidden = 6; + Registered registered = 7; + uint64 unregisteredTimestamp = 8; + optional bytes profileKey = 9; + bool profileSharing = 10; + optional string profileGivenName = 11; + optional string profileFamilyName = 12; + bool hideStory = 13; +} + +message Group { + enum StorySendMode { + DEFAULT = 0; + DISABLED = 1; + ENABLED = 2; + } + + bytes masterKey = 1; + bool whitelisted = 2; + bool hideStory = 3; + StorySendMode storySendMode = 4; +} + +message Self {} + +message ReleaseNotes {} + +message Chat { + uint64 id = 1; // generated id for reference only within this file + uint64 recipientId = 2; + bool archived = 3; + uint32 pinnedOrder = 4; // 0 = unpinned, otherwise chat is considered pinned and will be displayed in ascending order + uint64 expirationTimerMs = 5; + uint64 muteUntilMs = 6; + bool markedUnread = 7; + bool dontNotifyForMentionsIfMuted = 8; + FilePointer wallpaper = 9; +} + +message DistributionList { + enum PrivacyMode { + UNKNOWN = 0; + ONLY_WITH = 1; + ALL_EXCEPT = 2; + ALL = 3; + } + + string name = 1; + bytes distributionId = 2; // distribution list ids are uuids + bool allowReplies = 3; + uint64 deletionTimestamp = 4; + PrivacyMode privacyMode = 5; + repeated uint64 memberRecipientIds = 6; // generated recipient id +} + +message Identity { + bytes serviceId = 1; + bytes identityKey = 2; + uint64 timestamp = 3; + bool firstUse = 4; + bool verified = 5; + bool nonblockingApproval = 6; +} + +message Call { + enum Type { + UNKNOWN_TYPE = 0; + AUDIO_CALL = 1; + VIDEO_CALL = 2; + GROUP_CALL = 3; + AD_HOC_CALL = 4; + } + + enum Event { + UNKNOWN_EVENT = 0; + OUTGOING = 1; // 1:1 calls only + ACCEPTED = 2; // 1:1 and group calls. Group calls: You accepted a ring. + NOT_ACCEPTED = 3; // 1:1 calls only, + MISSED = 4; // 1:1 and group. Group calls: The remote ring has expired or was cancelled by the ringer. + DELETE = 5; // 1:1 and Group/Ad-Hoc Calls. + GENERIC_GROUP_CALL = 6; // Group/Ad-Hoc Calls only. Initial state + JOINED = 7; // Group Calls: User has joined the group call. + DECLINED = 8; // Group Calls: If you declined a ring. + OUTGOING_RING = 9; // Group Calls: If you are ringing a group. + } + + uint64 callId = 1; + uint64 conversationRecipientId = 2; + Type type = 3; + bool outgoing = 4; + uint64 timestamp = 5; + optional uint64 ringerRecipientId = 6; + Event event = 7; +} + +message ChatItem { + message IncomingMessageDetails { + uint64 dateReceived = 1; + uint64 dateServerSent = 2; + bool read = 3; + } + + message OutgoingMessageDetails { + repeated SendStatus sendStatus = 1; + } + + uint64 chatId = 1; // conversation id + uint64 authorId = 2; // recipient id + uint64 dateSent = 3; + bool sealedSender = 4; + optional uint64 expireStartDate = 5; // timestamp of when expiration timer started ticking down + optional uint64 expiresInMs = 6; // how long timer of message is (ms) + repeated ChatItem revisions = 7; // ordered from oldest to newest + bool sms = 8; + + oneof directionalDetails { + IncomingMessageDetails incoming = 10; + OutgoingMessageDetails outgoing = 12; + } + + oneof item { + StandardMessage standardMessage = 13; + ContactMessage contactMessage = 14; + VoiceMessage voiceMessage = 15; + StickerMessage stickerMessage = 16; + RemoteDeletedMessage remoteDeletedMessage = 17; + ChatUpdateMessage updateMessage = 18; + } +} + +message SendStatus { + enum Status { + UNKNOWN = 0; + FAILED = 1; + PENDING = 2; + SENT = 3; + DELIVERED = 4; + READ = 5; + VIEWED = 6; + SKIPPED = 7; // e.g. user in group was blocked, so we skipped sending to them + } + + uint64 recipientId = 1; + Status deliveryStatus = 2; + bool networkFailure = 3; + bool identityKeyMismatch = 4; + bool sealedSender = 5; + uint64 lastStatusUpdateTimestamp = 6; // the time the status was last updated -- if from a receipt, it should be the sentTime of the receipt +} + +message Text { + string body = 1; + repeated BodyRange bodyRanges = 2; +} + +message StandardMessage { + optional Quote quote = 1; + optional Text text = 2; + repeated FilePointer attachments = 3; + repeated LinkPreview linkPreview = 4; + optional FilePointer longText = 5; + repeated Reaction reactions = 6; +} + +message ContactMessage { + repeated ContactAttachment contact = 1; + repeated Reaction reactions = 2; +} + +message ContactAttachment { + message Name { + optional string givenName = 1; + optional string familyName = 2; + optional string prefix = 3; + optional string suffix = 4; + optional string middleName = 5; + optional string displayName = 6; + } + + message Phone { + enum Type { + UNKNOWN = 0; + HOME = 1; + MOBILE = 2; + WORK = 3; + CUSTOM = 4; + } + + optional string value = 1; + optional Type type = 2; + optional string label = 3; + } + + message Email { + enum Type { + UNKNOWN = 0; + HOME = 1; + MOBILE = 2; + WORK = 3; + CUSTOM = 4; + } + + optional string value = 1; + optional Type type = 2; + optional string label = 3; + } + + message PostalAddress { + enum Type { + UNKNOWN = 0; + HOME = 1; + WORK = 2; + CUSTOM = 3; + } + + optional Type type = 1; + optional string label = 2; + optional string street = 3; + optional string pobox = 4; + optional string neighborhood = 5; + optional string city = 6; + optional string region = 7; + optional string postcode = 8; + optional string country = 9; + } + + message Avatar { + FilePointer avatar = 1; + } + + optional Name name = 1; + repeated Phone number = 3; + repeated Email email = 4; + repeated PostalAddress address = 5; + optional Avatar avatar = 6; + optional string organization = 7; +} + +message DocumentMessage { + Text text = 1; + FilePointer document = 2; + repeated Reaction reactions = 3; +} + +message VoiceMessage { + optional Quote quote = 1; + FilePointer audio = 2; + repeated Reaction reactions = 3; +} + +message StickerMessage { + Sticker sticker = 1; + repeated Reaction reactions = 2; +} + +// Tombstone for remote delete +message RemoteDeletedMessage {} + +message Sticker { + bytes packId = 1; + bytes packKey = 2; + uint32 stickerId = 3; + optional string emoji = 4; +} + +message LinkPreview { + string url = 1; + optional string title = 2; + optional FilePointer image = 3; + optional string description = 4; + optional uint64 date = 5; +} + +message FilePointer { + message BackupLocator { + string mediaName = 1; + uint32 cdnNumber = 2; + } + + message AttachmentLocator { + string cdnKey = 1; + uint32 cdnNumber = 2; + uint64 uploadTimestamp = 3; + } + + message LegacyAttachmentLocator { + fixed64 cdnId = 1; + } + + // An attachment that was backed up without being downloaded. + // Its MediaName should be generated as “{sender_aci}_{cdn_attachment_key}”, + // but should eventually transition to a BackupLocator with mediaName + // being the content hash once it is downloaded. + message UndownloadedBackupLocator { + bytes senderAci = 1; + string cdnKey = 2; + uint32 cdnNumber = 3; + } + + enum Flags { + VOICE_MESSAGE = 0; + BORDERLESS = 1; + GIF = 2; + } + + oneof locator { + BackupLocator backupLocator = 1; + AttachmentLocator attachmentLocator= 2; + LegacyAttachmentLocator legacyAttachmentLocator = 3; + UndownloadedBackupLocator undownloadedBackupLocator = 4; + } + + optional bytes key = 5; + optional string contentType = 6; + optional uint32 size = 7; + optional bytes digest = 8; + optional bytes incrementalMac = 9; + optional bytes incrementalMacChunkSize = 10; + optional string fileName = 11; + optional uint32 flags = 12; + optional uint32 width = 13; + optional uint32 height = 14; + optional string caption = 15; + optional string blurHash = 16; +} + +message Quote { + enum Type { + UNKNOWN = 0; + NORMAL = 1; + GIFTBADGE = 2; + } + + message QuotedAttachment { + optional string contentType = 1; + optional string fileName = 2; + optional FilePointer thumbnail = 3; + } + + optional uint64 targetSentTimestamp = 1; // null if the target message could not be found at time of quote insert + uint64 authorId = 2; + optional string text = 3; + repeated QuotedAttachment attachments = 4; + repeated BodyRange bodyRanges = 5; + Type type = 6; +} + +message BodyRange { + enum Style { + NONE = 0; + BOLD = 1; + ITALIC = 2; + SPOILER = 3; + STRIKETHROUGH = 4; + MONOSPACE = 5; + } + + optional uint32 start = 1; + optional uint32 length = 2; + + oneof associatedValue { + bytes mentionAci = 3; + Style style = 4; + } +} + +message Reaction { + string emoji = 1; + uint64 authorId = 2; + uint64 sentTimestamp = 3; + optional uint64 receivedTimestamp = 4; + uint64 sortOrder = 5; // A higher sort order means that a reaction is more recent +} + +message ChatUpdateMessage { + oneof update { + SimpleChatUpdate simpleUpdate = 1; + GroupDescriptionChatUpdate groupDescription = 2; + ExpirationTimerChatUpdate expirationTimerChange = 3; + ProfileChangeChatUpdate profileChange = 4; + ThreadMergeChatUpdate threadMerge = 5; + SessionSwitchoverChatUpdate sessionSwitchover = 6; + CallChatUpdate callingMessage = 7; + } +} + +message CallChatUpdate{ + oneof call { + uint64 callId = 1; // maps to id of Call from call log + IndividualCallChatUpdate callMessage = 2; + GroupCallChatUpdate groupCall = 3; + } +} + +message IndividualCallChatUpdate { + enum Type { + UNKNOWN = 0; + INCOMING_AUDIO_CALL = 1; + INCOMING_VIDEO_CALL = 2; + OUTGOING_AUDIO_CALL = 3; + OUTGOING_VIDEO_CALL = 4; + MISSED_AUDIO_CALL = 5; + MISSED_VIDEO_CALL = 6; + } + Type type = 1; +} + +message GroupCallChatUpdate { + bytes startedCallAci = 1; + uint64 startedCallTimestamp = 2; + repeated bytes inCallAcis = 3; +} + +message SimpleChatUpdate { + enum Type { + UNKNOWN = 0; + JOINED_SIGNAL = 1; + IDENTITY_UPDATE = 2; + IDENTITY_VERIFIED = 3; + IDENTITY_DEFAULT = 4; // marking as unverified + CHANGE_NUMBER = 5; + BOOST_REQUEST = 6; + END_SESSION = 7; + CHAT_SESSION_REFRESH = 8; + BAD_DECRYPT = 9; + PAYMENTS_ACTIVATED = 10; + PAYMENT_ACTIVATION_REQUEST = 11; + } + + Type type = 1; +} + +message GroupDescriptionChatUpdate { + string newDescription = 1; +} + +message ExpirationTimerChatUpdate { + uint32 expiresInMs = 1; +} + +message ProfileChangeChatUpdate { + string previousName = 1; + string newName = 2; +} + +message ThreadMergeChatUpdate { + uint64 previousE164 = 1; +} + +message SessionSwitchoverChatUpdate { + uint64 e164 = 1; +} + +message StickerPack { + bytes id = 1; + bytes key = 2; + string title = 3; + string author = 4; + repeated StickerPackSticker stickers = 5; // First one should be cover sticker. +} + +message StickerPackSticker { + FilePointer data = 1; + string emoji = 2; +} \ No newline at end of file diff --git a/app/src/main/protowire/JobData.proto b/app/src/main/protowire/JobData.proto index 6503ce3c12..c048c958d6 100644 --- a/app/src/main/protowire/JobData.proto +++ b/app/src/main/protowire/JobData.proto @@ -33,7 +33,7 @@ message CallLinkUpdateSendJobData { } message AttachmentUploadJobData { - uint64 attachmentRowId = 1; - uint64 attachmentUniqueId = 2; + uint64 attachmentId = 1; + reserved /*attachmentUniqueId*/ 2; optional ResumableUpload uploadSpec = 3; } \ No newline at end of file diff --git a/app/src/main/protowire/Payments.proto b/app/src/main/protowire/Payments.proto index 4872263893..2af6146a71 100644 --- a/app/src/main/protowire/Payments.proto +++ b/app/src/main/protowire/Payments.proto @@ -8,11 +8,13 @@ option java_multiple_files = true; message MobileCoinLedger { message OwnedTXO { - uint64 amount = 1; - bytes keyImage = 2; - bytes publicKey = 3; - Block receivedInBlock = 4; - Block spentInBlock = 5; + uint64 deprecatedAmount = 1; + bytes amount = 6; + bytes keyImage = 2; + bytes publicKey = 3; + Block receivedInBlock = 4; + Block spentInBlock = 5; + // Next is 7 } message Block { @@ -20,12 +22,15 @@ message MobileCoinLedger { uint64 timestamp = 2; } - uint64 balance = 1; - uint64 transferableBalance = 2; - Block highestBlock = 3; - uint64 asOfTimeStamp = 4; - repeated OwnedTXO spentTxos = 5; - repeated OwnedTXO unspentTxos = 6; + uint64 deprecatedBalance = 1; + bytes balance = 7; + uint64 deprecatedTransferableBalance = 2; + bytes transferableBalance = 8; + Block highestBlock = 3; + uint64 asOfTimeStamp = 4; + repeated OwnedTXO spentTxos = 5; + repeated OwnedTXO unspentTxos = 6; + // Next is 9 } message PaymentMetaData { diff --git a/app/src/main/res/drawable/message_request_info_outline.xml b/app/src/main/res/drawable/message_request_info_outline.xml new file mode 100644 index 0000000000..8ca7527dc0 --- /dev/null +++ b/app/src/main/res/drawable/message_request_info_outline.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/symbol_chevron_right_24_color_on_secondary_container.xml b/app/src/main/res/drawable/symbol_chevron_right_24_color_on_secondary_container.xml new file mode 100644 index 0000000000..ba3faecca6 --- /dev/null +++ b/app/src/main/res/drawable/symbol_chevron_right_24_color_on_secondary_container.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/symbol_connections_24.xml b/app/src/main/res/drawable/symbol_connections_24.xml new file mode 100644 index 0000000000..fb4e987dd8 --- /dev/null +++ b/app/src/main/res/drawable/symbol_connections_24.xml @@ -0,0 +1,24 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/symbol_connections_compact_16.xml b/app/src/main/res/drawable/symbol_connections_compact_16.xml new file mode 100644 index 0000000000..65ae7de9c9 --- /dev/null +++ b/app/src/main/res/drawable/symbol_connections_compact_16.xml @@ -0,0 +1,26 @@ + + + + + + + + + + diff --git a/app/src/main/res/drawable/symbol_group_light_20.xml b/app/src/main/res/drawable/symbol_group_light_20.xml new file mode 100644 index 0000000000..5a45ae198e --- /dev/null +++ b/app/src/main/res/drawable/symbol_group_light_20.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/app/src/main/res/drawable/symbol_mic_slash_24.xml b/app/src/main/res/drawable/symbol_mic_slash_24.xml new file mode 100644 index 0000000000..af31e172fa --- /dev/null +++ b/app/src/main/res/drawable/symbol_mic_slash_24.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/app/src/main/res/drawable/symbol_mic_slash_fill_compact_16.xml b/app/src/main/res/drawable/symbol_mic_slash_fill_compact_16.xml new file mode 100644 index 0000000000..9418c3e654 --- /dev/null +++ b/app/src/main/res/drawable/symbol_mic_slash_fill_compact_16.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/app/src/main/res/drawable/symbol_more_24.xml b/app/src/main/res/drawable/symbol_more_24.xml new file mode 100644 index 0000000000..b496d9c464 --- /dev/null +++ b/app/src/main/res/drawable/symbol_more_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/src/main/res/drawable/symbol_more_white_24.xml b/app/src/main/res/drawable/symbol_more_white_24.xml new file mode 100644 index 0000000000..a254159c47 --- /dev/null +++ b/app/src/main/res/drawable/symbol_more_white_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/src/main/res/drawable/symbol_person_circle_compat_16.xml b/app/src/main/res/drawable/symbol_person_circle_compat_16.xml new file mode 100644 index 0000000000..1edd0e9bd3 --- /dev/null +++ b/app/src/main/res/drawable/symbol_person_circle_compat_16.xml @@ -0,0 +1,14 @@ + + + + + + diff --git a/app/src/main/res/drawable/symbol_person_compact_16.xml b/app/src/main/res/drawable/symbol_person_compact_16.xml new file mode 100644 index 0000000000..47dbe0d420 --- /dev/null +++ b/app/src/main/res/drawable/symbol_person_compact_16.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/symbol_person_light_24.xml b/app/src/main/res/drawable/symbol_person_light_24.xml new file mode 100644 index 0000000000..99d0ba0506 --- /dev/null +++ b/app/src/main/res/drawable/symbol_person_light_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/symbol_phone_compact_16.xml b/app/src/main/res/drawable/symbol_phone_compact_16.xml new file mode 100644 index 0000000000..80ad4a53a9 --- /dev/null +++ b/app/src/main/res/drawable/symbol_phone_compact_16.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/symbol_phone_light_20.xml b/app/src/main/res/drawable/symbol_phone_light_20.xml new file mode 100644 index 0000000000..05f8214962 --- /dev/null +++ b/app/src/main/res/drawable/symbol_phone_light_20.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/symbol_raise_hand_24.xml b/app/src/main/res/drawable/symbol_raise_hand_24.xml new file mode 100644 index 0000000000..fdef3b6a55 --- /dev/null +++ b/app/src/main/res/drawable/symbol_raise_hand_24.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/symbol_video_slash_24.xml b/app/src/main/res/drawable/symbol_video_slash_24.xml new file mode 100644 index 0000000000..649abb476d --- /dev/null +++ b/app/src/main/res/drawable/symbol_video_slash_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/src/main/res/drawable/username_edit_box_fill.xml b/app/src/main/res/drawable/username_edit_box_fill.xml new file mode 100644 index 0000000000..3f1672c78c --- /dev/null +++ b/app/src/main/res/drawable/username_edit_box_fill.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/webrtc_call_screen_overflow_menu.xml b/app/src/main/res/drawable/webrtc_call_screen_overflow_menu.xml new file mode 100644 index 0000000000..e529dc6d0b --- /dev/null +++ b/app/src/main/res/drawable/webrtc_call_screen_overflow_menu.xml @@ -0,0 +1,10 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/webrtc_call_screen_overflow_menu_small.xml b/app/src/main/res/drawable/webrtc_call_screen_overflow_menu_small.xml new file mode 100644 index 0000000000..f8177e2754 --- /dev/null +++ b/app/src/main/res/drawable/webrtc_call_screen_overflow_menu_small.xml @@ -0,0 +1,15 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/webrtc_call_screen_speaker_toggle_small.xml b/app/src/main/res/drawable/webrtc_call_screen_speaker_toggle_small.xml index 6804fa6b55..1a2365b73f 100644 --- a/app/src/main/res/drawable/webrtc_call_screen_speaker_toggle_small.xml +++ b/app/src/main/res/drawable/webrtc_call_screen_speaker_toggle_small.xml @@ -7,7 +7,7 @@ android:top="4dp" android:left="4dp" android:right="4dp" - android:drawable="@drawable/webrtc_call_screen_circle_unchecked" /> + android:drawable="@drawable/webrtc_call_screen_circle_checked" /> @@ -18,7 +18,7 @@ android:top="4dp" android:left="4dp" android:right="4dp" - android:drawable="@drawable/webrtc_call_screen_circle_checked" /> + android:drawable="@drawable/webrtc_call_screen_circle_unchecked" /> diff --git a/app/src/main/res/layout/activity_shared_contact_details.xml b/app/src/main/res/layout/activity_shared_contact_details.xml index 67f919662e..5cd697c1c3 100644 --- a/app/src/main/res/layout/activity_shared_contact_details.xml +++ b/app/src/main/res/layout/activity_shared_contact_details.xml @@ -104,7 +104,7 @@ @@ -128,7 +128,7 @@ diff --git a/app/src/main/res/layout/audio_indicator_view.xml b/app/src/main/res/layout/audio_indicator_view.xml index b2fe24d236..d44ea85194 100644 --- a/app/src/main/res/layout/audio_indicator_view.xml +++ b/app/src/main/res/layout/audio_indicator_view.xml @@ -5,8 +5,8 @@ xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/mic_muted" - android:layout_width="wrap_content" - android:layout_height="wrap_content" + android:layout_width="16dp" + android:layout_height="16dp" android:layout_gravity="center" - app:srcCompat="@drawable/ic_mic_off_solid_18" + app:srcCompat="@drawable/symbol_mic_slash_fill_compact_16" app:tint="@color/core_white" /> diff --git a/app/src/main/res/layout/call_log_adapter_item.xml b/app/src/main/res/layout/call_log_adapter_item.xml index b793748300..c6de6ffb0d 100644 --- a/app/src/main/res/layout/call_log_adapter_item.xml +++ b/app/src/main/res/layout/call_log_adapter_item.xml @@ -74,7 +74,7 @@ android:drawablePadding="6dp" android:ellipsize="end" android:gravity="start|center_vertical" - android:maxLines="1" + android:maxLines="2" android:textAlignment="viewStart" android:textAppearance="@style/Signal.Text.BodyMedium" android:textColor="@color/signal_colorOnSurfaceVariant" diff --git a/app/src/main/res/layout/call_overflow_holder.xml b/app/src/main/res/layout/call_overflow_holder.xml new file mode 100644 index 0000000000..e916da1e85 --- /dev/null +++ b/app/src/main/res/layout/call_overflow_holder.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/call_overflow_popup.xml b/app/src/main/res/layout/call_overflow_popup.xml new file mode 100644 index 0000000000..8fc37bcd3d --- /dev/null +++ b/app/src/main/res/layout/call_overflow_popup.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/call_participant_item.xml b/app/src/main/res/layout/call_participant_item.xml index d752b70879..3a55f51acc 100644 --- a/app/src/main/res/layout/call_participant_item.xml +++ b/app/src/main/res/layout/call_participant_item.xml @@ -1,12 +1,12 @@ + tools:layout_width="match_parent" + tools:viewBindingIgnore="true"> + + + + + + + + + + diff --git a/app/src/main/res/layout/contact_filter_view.xml b/app/src/main/res/layout/contact_filter_view.xml index 86c6ef3751..743d697f63 100644 --- a/app/src/main/res/layout/contact_filter_view.xml +++ b/app/src/main/res/layout/contact_filter_view.xml @@ -23,7 +23,7 @@ - + - + + + app:layout_constraintTop_toBottomOf="@id/message_request_divider" + app:layout_constraintWidth_max="308dp" + app:layout_constraintWidth_min="256dp"> + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/conversation_item_thread_header.xml b/app/src/main/res/layout/conversation_item_thread_header.xml index b60b16cb05..2630d18ab1 100644 --- a/app/src/main/res/layout/conversation_item_thread_header.xml +++ b/app/src/main/res/layout/conversation_item_thread_header.xml @@ -1,18 +1,14 @@ - - - + android:layout_marginHorizontal="52dp" + android:paddingTop="22dp" + android:paddingBottom="4dp" + app:layout_constraintTop_toTopOf="parent" + tools:viewBindingIgnore="true" /> diff --git a/app/src/main/res/layout/conversation_settings_call_preference_item.xml b/app/src/main/res/layout/conversation_settings_call_preference_item.xml index 39c92bb8e1..0f603d47a2 100644 --- a/app/src/main/res/layout/conversation_settings_call_preference_item.xml +++ b/app/src/main/res/layout/conversation_settings_call_preference_item.xml @@ -21,11 +21,12 @@ + android:orientation="vertical" + tools:viewBindingIgnore="true"> + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/recipient_bottom_sheet.xml b/app/src/main/res/layout/recipient_bottom_sheet.xml index e45b7306b6..b57368211f 100644 --- a/app/src/main/res/layout/recipient_bottom_sheet.xml +++ b/app/src/main/res/layout/recipient_bottom_sheet.xml @@ -1,10 +1,10 @@ + android:layout_height="wrap_content" + tools:viewBindingIgnore="true"> - + android:layout_height="wrap_content"> - + android:layout_height="match_parent" + android:layout_gravity="center" + android:layout_marginHorizontal="12dp" + android:layout_marginVertical="11dp" + android:background="@color/signal_background_primary" + android:minHeight="66dp" + app:cardCornerRadius="12dp" + app:cardElevation="0dp" + app:strokeColor="@color/signal_colorOutline_38" + app:strokeWidth="1dp"> - + - + - + - + - + - + - + - + - + - + - + + \ No newline at end of file diff --git a/app/src/main/res/layout/review_card.xml b/app/src/main/res/layout/review_card.xml index b2995b61ba..cf8ba9c42f 100644 --- a/app/src/main/res/layout/review_card.xml +++ b/app/src/main/res/layout/review_card.xml @@ -1,15 +1,12 @@ + android:background="@drawable/review_card_outline"> + app:layout_constraintTop_toTopOf="@id/card_avatar" /> + tools:text="Maya Johnson" /> + tools:text="Maya recently changed their profile name from Alice Chen to Maya Johnson" /> + + + + + + + + + + + + + app:layout_constraintTop_toTopOf="@id/card_subtext_line4" + app:tint="@color/signal_colorOnSurface" + tools:srcCompat="@drawable/symbol_group_24" /> + + @@ -124,7 +208,7 @@ android:paddingEnd="12dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@id/card_primary_action_button" - app:layout_constraintTop_toBottomOf="@id/card_subtext_line2" + app:layout_constraintTop_toBottomOf="@id/card_subtext_line4" app:layout_constraintVertical_bias="1.0" tools:text="@string/ReviewCard__delete" /> diff --git a/app/src/main/res/layout/time_duration_picker_dialog.xml b/app/src/main/res/layout/time_duration_picker_dialog.xml index a45750673a..f9037c9da1 100644 --- a/app/src/main/res/layout/time_duration_picker_dialog.xml +++ b/app/src/main/res/layout/time_duration_picker_dialog.xml @@ -74,12 +74,16 @@ - + app:layout_constraintTop_toBottomOf="@+id/duration_layout"> + + \ No newline at end of file diff --git a/app/src/main/res/layout/transfer_controls_view.xml b/app/src/main/res/layout/transfer_controls_view.xml index e76f73df5d..ff4373c09f 100644 --- a/app/src/main/res/layout/transfer_controls_view.xml +++ b/app/src/main/res/layout/transfer_controls_view.xml @@ -1,5 +1,4 @@ - - @@ -7,9 +6,9 @@ + app:layout_constraintTop_toTopOf="@+id/horizontal_guideline" + app:stopIconCornerRadius="1dp" + app:stopIconSize="8dp" /> + app:layout_constraintTop_toTopOf="parent" + app:stopIconCornerRadius="2dp" + app:stopIconSize="14dp" /> + app:layout_constraintGuide_begin="4dp" /> + app:layout_constraintGuide_begin="4dp" /> diff --git a/app/src/main/res/layout/username_edit_fragment.xml b/app/src/main/res/layout/username_edit_fragment.xml index dee44d61ec..bc4f5b0a04 100644 --- a/app/src/main/res/layout/username_edit_fragment.xml +++ b/app/src/main/res/layout/username_edit_fragment.xml @@ -35,24 +35,34 @@ app:srcCompat="@drawable/symbol_at_24" app:tint="@color/signal_colorOnSurface" /> + + + app:suffixTextColor="@color/signal_colorOnSurface"> - + + + + app:trackThickness="1.75dp" /> + + + + @@ -115,7 +164,6 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/webrtc_call_participant_recycler_empty_item.xml b/app/src/main/res/layout/webrtc_call_participant_recycler_empty_item.xml index 97416a2d8a..0f1ae19712 100644 --- a/app/src/main/res/layout/webrtc_call_participant_recycler_empty_item.xml +++ b/app/src/main/res/layout/webrtc_call_participant_recycler_empty_item.xml @@ -1,8 +1,8 @@ - \ No newline at end of file diff --git a/app/src/main/res/layout/webrtc_call_reaction_recycler_item.xml b/app/src/main/res/layout/webrtc_call_reaction_recycler_item.xml new file mode 100644 index 0000000000..4911401a02 --- /dev/null +++ b/app/src/main/res/layout/webrtc_call_reaction_recycler_item.xml @@ -0,0 +1,30 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/webrtc_call_view.xml b/app/src/main/res/layout/webrtc_call_view.xml index 0b6905a0b1..a0f3873cf1 100644 --- a/app/src/main/res/layout/webrtc_call_view.xml +++ b/app/src/main/res/layout/webrtc_call_view.xml @@ -5,6 +5,8 @@ tools:parentTag="org.thoughtcrime.securesms.components.webrtc.WebRtcCallView" tools:viewBindingIgnore="true"> + + + + @@ -86,10 +95,24 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> - + - - - - @@ -214,7 +223,7 @@ android:layout_width="match_parent" android:layout_height="@dimen/signal_m3_toolbar_height" android:minHeight="@dimen/signal_m3_toolbar_height" - app:layout_constraintTop_toTopOf="@id/call_screen_status_bar_guideline" + app:layout_constraintTop_toTopOf="@id/status_bar_guideline" app:menu="@menu/webrtc_toolbar_menu" app:navigationIcon="@drawable/ic_arrow_left_24" app:subtitleTextAppearance="@style/Signal.Text.BodyMedium" @@ -228,107 +237,7 @@ android:layout_marginEnd="64dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="@id/call_screen_status_bar_guideline" /> - - - - - - - - - - - - + app:layout_constraintTop_toTopOf="@id/status_bar_guideline" /> - - - - - + @@ -482,7 +373,7 @@ android:layout_marginBottom="32dp" android:inflatedId="@+id/call_screen_pending_recipients_view" android:layout="@layout/call_screen_pending_participants_view" - app:layout_constraintBottom_toTopOf="@id/call_screen_footer_gradient_barrier" + app:layout_constraintBottom_toTopOf="@id/call_screen_above_controls_guideline" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" /> @@ -505,5 +396,12 @@ app:barrierDirection="top" app:constraint_referenced_ids="call_screen_answer_call,call_screen_decline_call,call_screen_audio_mic_toggle,call_screen_camera_direction_toggle,call_screen_video_toggle,call_screen_answer_without_video,call_screen_speaker_toggle,call_screen_end_call" /> - + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/app_settings.xml b/app/src/main/res/navigation/app_settings.xml index 8eecd8be68..d1cc57553d 100644 --- a/app/src/main/res/navigation/app_settings.xml +++ b/app/src/main/res/navigation/app_settings.xml @@ -542,6 +542,9 @@ + + + diff --git a/app/src/main/res/raw/notification_simple_01.ogg b/app/src/main/res/raw/notification_simple_01.ogg new file mode 100755 index 0000000000..93b3a0144e Binary files /dev/null and b/app/src/main/res/raw/notification_simple_01.ogg differ diff --git a/app/src/main/res/values-af/strings.xml b/app/src/main/res/values-af/strings.xml index c316beadea..63ad251f32 100644 --- a/app/src/main/res/values-af/strings.xml +++ b/app/src/main/res/values-af/strings.xml @@ -323,8 +323,12 @@ Kan nie beeld aflaai nie. Jy sal dit weer moet stuur. Kan nie video aflaai nie. Jy sal dit weer moet stuur. - - Gewysig %1$s + + So pas gewysig + + Gewysig %1$s + + Gewysig %1$s Sluit aan by oproep @@ -358,6 +362,8 @@ Onbeveiligde MMS Signal-boodskap + + Stuur boodskap Kom ons skakel oor na Molly %1$s Kies asseblief \'n kontak Die aanhangsel oorskry die groottegrense vir die tipe boodskap wat jy stuur. @@ -1277,6 +1283,10 @@ Gemiste stemoproep Gemiste video-oproep + + Gemiste stemoproep terwyl kennisgewingprofiel aan was + + Gemiste video-oproep terwyl kennisgewingprofiel aan is Jy het ’n stemoproep geweier @@ -1828,6 +1838,8 @@ Kamera aan/af Demp aan/af + + Bykomende aksies Beëindig oproep @@ -1844,10 +1856,68 @@ \'n Ikoon wat \'n toestel se gehoorstuk verteenwoordig. + + Steek hand op + + Steek hand op + + Laat sak jou hand? + + Laat sak hand + + Kanselleer + + Jy het jou hand opgesteek + + Bekyk + + + + %1$s het \'n hand opgesteek + %1$s + %2$d het \'n hand opgesteek + + + + + %1$s + %1$s +%2$d + + + + Meer inligting oor opgesteekte hand + + + + Signal-verbinding + + %1$s is in jou stelselkontakte + + Julle het geen groepe gemeen nie + + Gaan versoeke noukeurig na + + %1$d groepe gemeen + + Meer oor + + Jy + - - In hierdie oproep · %1$d persoon - In hierdie oproep · %1$d mense + + In hierdie oproep (%1$d) + In hierdie oproep (%1$d) + + + Signal sal (%1$d) bel + Signal sal (%1$d) bel + + + Signal sal (%1$d) in kennis stel + Signal sal (%1$d) in kennis stel + + + Opgesteekte hand (%1$d) + Opgesteekte hande (%1$d) @@ -1961,6 +2031,12 @@ Het bekyk Media + + + Naamkonflik gevind + + Bekyk + Geen resultate gevind vir \'%1$s\' nie @@ -2127,6 +2203,8 @@ Stuur + + 00 Voeg \'n gebruikersnaam by @@ -2149,6 +2227,12 @@ Slaan oor Klaar + + Hierdie gebruikernaam is nie beskikbaar nie, probeer \'n ander nommer. + + Ongeldige gebruikernaam, voer \'n minimum van %1$d syfers in. + + Ongeldige gebruikernaam, voer \'n maksimum van %1$d syfers in. %1$d kontak is op Signal! @@ -2346,6 +2430,10 @@ Verwerk tans… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2370,6 +2458,8 @@ Op \'n gekoppelde toestel beantwoord. Op \'n gekoppelde toestel geweier. Besig op \'n gekoppelde toestel. + + Roteer Kamera is hierheen verskuif, tik op jou video om dit te probeer Iemand wie se veiligheidsnommer verander het, het by die oproep aangesluit. @@ -3925,14 +4015,16 @@ Kon nie groeplid verwyder nie. - Lid Versoek Jou kontak Verwyder uit groep Werk kontak by Versper Skrap - Het onlangs hul profielnaam van %1$s na %2$s verander + + %1$s het onlangs hul profielnaam van %2$s na %3$sverander + + %1$s is in jou stelselkontakte %1$s het aangesluit @@ -4988,6 +5080,12 @@ Voeg mense of groepe toe van wie jy kennisgewings wil ontvang wanneer hierdie profiel aan is Voeg mense of groepe toe + + Uitsonderings + + Laat alle oproepe toe + + Laat weet vir alle vermeldings Voeg toe @@ -5602,8 +5700,8 @@ Alle verbindings is nagegaan, tik stuur om voort te gaan. - Jy het %1$d konneksie wat moontlik Signal herinstalleer of van toestelle verander het. Voordat jy stuur, het jy die opsie om hulle veiligheidsnommers na te gaan. - Jy het %1$d konneksies wat moontlik Signal herinstalleer of van toestelle verander het. Voordat jy stuur, het jy die opsie om hulle veiligheidsnommers na te gaan. + %1$d-konneksie het dalk Signal herinstalleer of toestelle verander. Jy kan hul veiligheidsnommer nagaan of voortgaan om te stuur. + %1$d-konneksies het dalk Signal herinstalleer of toestelle verander. Jy kan hul veiligheidsnommers nagaan of voortgaan om te stuur. Verifieer veiligheidsnommer @@ -5961,12 +6059,18 @@ IBAN-landkode word nie ondersteun nie Ongeldige IBAN + + Minimum 2 karakters + + Ongeldige e-posadres iDEAL Voer jou bank, naam en e-posadres in. Stripe gebruik hierdie e-posadres om vir jou opdaterings oor jou skenking te stuur. %1$s + + Voer jou bankbesonderhede in. Signal versamel of stoor nie jou persoonlike inligting nie. %1$s Vind meer uit @@ -6152,6 +6256,8 @@ Uitgaande Gemis + + Gemis terwyl kennisgewingprofiel aan was Sluit aan @@ -6259,6 +6365,8 @@ Groepoproep Gemiste groepoproep + + Gemiste groepoproep terwyl kennisgewingprofiel aan is Inkomende groepoproep diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 2afdb1c1f3..d5d0ecd245 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -327,8 +327,12 @@ تعذّر تحميل الصورة. سَتحتاج إلى إرسالها مرة أخرى. تعذّر تحميل الفيديو. سَتحتاج إلى إرساله مرة أخرى. - - مُعدّلة %1$s + + تم التعديل الآن + + مُعدّلة %1$s + + مُعدّلة %1$s الانضمام للمكالمة @@ -370,6 +374,8 @@ رسالة وسائط متعددة غير آمنة ‫رسالة سيجنال + + إرسال الرسالة هيا ننتقل إلى سيجنال %1$s رجاء اختيار جهة اتصال لقد تجاوز حجم المرفق الحد المسموح به لهذا الصنف من الرسائل. @@ -1453,6 +1459,10 @@ مكالمة صوتية فائتة مكالمة فيديو فائتة + + مكالمة صوتية فائتة أثناء تشغيل الحساب الشخصي للإشعارات + + اتصال فيديو فائت أثناء تشغيل الحساب الشخصي للإشعار لقد رفضت مكالمة صوتية @@ -2112,6 +2122,8 @@ تبديل الكاميرا تبديل الكتم + + إجراءات إضافية إنهاء المُكالمة @@ -2128,14 +2140,92 @@ أيقونة تمثل سماعة الجهاز. + + رفع اليد + + رفع اليد + + تريد خفض يدك؟ + + اخفض اليد + + إلغاء + + لقد رفعت يدك + + إظهار + + + + %1$s + %2$d رفعوا يدًا + %1$s رفع يده + %1$s + %2$d رفعا يديهما + %1$s + %2$d رفعوا أيديهم + %1$s + %2$d رفعوا أيديهم + %1$s + %2$d رفعوا أيديهم + + + + + %1$s +%2$d + %1$s + %1$s +%2$d + %1$s +%2$d + %1$s +%2$d + %1$s +%2$d + + + + توسيع عرض رفع اليد + + + + جهات اتصال Signal + + %1$s موجود في جهات اتصالك + + لا توجد مجموعات مُشتركة بينكم + + مراجعة الطلبات بعناية + + %1$d مجموعات مشتركة + + حوْل + + أنت + - - في هذه المكالمة · %1$d شخص - في هذه المكالمة · %1$d شخص - في هذه المكالمة · %1$d أشخاص - في هذه المكالمة · %1$d أشخاص - في هذه المكالمة · %1$d شخصا - في هذه المكالمة · %1$d شخص + + في هذه المكالمة (%1$d) + في هذه المكالمة (%1$d) + في هذه المكالمة (%1$d) + في هذه المكالمة (%1$d) + في هذه المكالمة (%1$d) + في هذه المكالمة (%1$d) + + + سيرنّ سيجنال على (%1$d) + سيرنّ سيجنال على (%1$d) + سيرنّ سيجنال على (%1$d) + سيرنّ سيجنال على (%1$d) + سيرنّ سيجنال على (%1$d) + سيرنّ سيجنال على (%1$d) + + + سينبّه سيجنال (%1$d) + سينبّه سيجنال (%1$d) + سينبّه سيجنال (%1$d) + سينبّه سيجنال (%1$d) + سينبّه سيجنال (%1$d) + سينبّه سيجنال (%1$d) + + + أيادي مرفوعة (%1$d) + أيادي مرفوعة (%1$d) + أيادي مرفوعة (%1$d) + أيادي مرفوعة (%1$d) + أيادي مرفوعة (%1$d) + أيادي مرفوعة (%1$d) @@ -2253,6 +2343,12 @@ تمت المشاهدة الوسائط + + + تم العثور على تضارب أسماء + + إظهار + لم يتم العثور على أي نتيجة لـ \'%1$s\' @@ -2419,6 +2515,8 @@ أرسل + + 00 إضافة اسم مُستخدم @@ -2441,6 +2539,12 @@ تخطّي تمّ + + اسم المستخدم هذا ليس موجودًا، جرّب رقمًا آخر. + + اسم مستخدم غير صحيح، أدخل رمزيْن %1$d على الأقل. + + اسم مستخدم غير صحيح، أدخل %1$d رمزًا كحد أقصى. %1$d جهة اتصال تستخدم سيجنال!‏ @@ -2654,6 +2758,10 @@ جارٍ المعالجة… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2678,6 +2786,8 @@ أجاب عبر جهاز مرتبط. رُفض عبر جهاز مرتبط. مشغول في جهاز مرتبط. + + لقد نُقل زر تغيير الكاميرا إلى هنا، انقر على الفيديو الخاص بك لتُجرّبه. انضم شخص لهذه المكالمة برقم أمان مختلف. @@ -4329,14 +4439,16 @@ فشلت إزالة عضو من المجموعة. - عضو الطلب جهة اتصالك أزل من المجموعة تحديث جهة الاتصال حظر حذف - ‫تم تغيير اسم الملف الشخصي من %1$s إلى %2$s + + غيّر مؤخرًا %1$s اسم حسابه الشخصي من %2$s إلى %3$s + + %1$s موجود في جهات اتصالك انضم %1$s @@ -5428,6 +5540,12 @@ يُرجى إضافة الأفراد والمجموعات المراد تلقي اﻹشعارات والمكالمات من عندها عندما تكون هيئة إشعارك مُشغَّلة إضافة اﻷفراد أو المجموعات + + الاستثناءات + + السماح لِجميع المكالمات + + الإشعار عن كل الإشارات للأفراد إضافة @@ -6110,12 +6228,12 @@ تمت مراجعة كل جهات الاتصال، انقر إرسال للمواصلة. - %1$d من جهات اتصالك قد يكون أعاد تثبيت سيجنال أو غيّر جهازه. قبل مشاركة قِصتك معه، يُرجى مراجعة أرقام الأمان الخاصة به أو فكّر في حذفه من قصتك. - %1$d من جهات اتصالك قد يكون أعاد تثبيت سيجنال أو غيّر جهازه. قبل مشاركة قِصتك معه، يُرجى مراجعة أرقام الأمان الخاصة به أو فكّر في حذفه من قصتك. - %1$d من جهات اتصالك قد يكونوا أعادو تثبيت سيجنال أو غيّروا أجهزتهم. قبل مشاركة قِصتك معهم، يُرجى مراجعة أرقام الأمان الخاصة بهم أو فكّر في حذفهم من قصتك. - %1$d من جهات اتصالك قد يكونوا أعادو تثبيت سيجنال أو غيّروا أجهزتهم. قبل مشاركة قِصتك معهم، يُرجى مراجعة أرقام الأمان الخاصة بهم أو فكّر في حذفهم من قصتك. - %1$d من جهات اتصالك قد يكونوا أعادو تثبيت سيجنال أو غيّروا أجهزتهم. قبل مشاركة قِصتك معهم، يُرجى مراجعة أرقام الأمان الخاصة بهم أو فكّر في حذفهم من قصتك. - %1$d من جهات اتصالك قد يكونوا أعادو تثبيت سيجنال أو غيّروا أجهزتهم. قبل مشاركة قِصتك معهم، يُرجى مراجعة أرقام الأمان الخاصة بهم أو فكّر في حذفهم من قصتك. + %1$d جهة اتصال قد يكونوا أعادوا تثبيت سيجنال أو غيّروا أجهزتهم. يُمكنك مراجعة أرقام الأمان الخاصة بهم أو المواصلة للإرسال. + %1$d جهة اتصال قد يكون أعاد تثبيت سيجنال أو غيّر جهازه. يمكنك مراجعة رقم الأمان الخاص بهم أو المواصلة للإرسال. + %1$d جهتا اتصال قد يكونا أعادا تثبيت سيجنال أو غيّرا جهازيهما. يُمكنك مراجعة أرقام الأمان الخاصة بهما أو المواصلة للإرسال. + %1$d جهات اتصال قد يكونوا أعادوا تثبيت سيجنال أو غيّروا أجهزتهم. يُمكنك مراجعة أرقام الأمان الخاصة بهم أو المواصلة للإرسال. + %1$d جهة اتصال قد يكونوا أعادوا تثبيت سيجنال أو غيّروا أجهزتهم. يمكنك مراجعة أرقام الأمان الخاصة بهم أو المواصلة للإرسال. + %1$d جهة اتصال قد يكونوا أعادوا تثبيت سيجنال أو غيّروا أجهزتهم. يمكنك مراجعة أرقام الأمان الخاصة بهم أو المواصلة للإرسال. تحقق من رقم اﻷمان @@ -6489,12 +6607,18 @@ رقم IBAN الخاص ببلدك غير مدعوم رقم IBAN غير صحيح + + حرفان كحد أدنى + + عنوان بريد إلكتروني غير صحيح iDEAL يُرجى إدخال معلومات مصرفك واسمك وبريدك الإلكتروني. يستخدم Stripe هذا البريد الإلكتروني لإرسال تحديثات متعلقة بتبرعاتك. %1$s + + أدخل معلومات مصرفك. لا يقوم سيجنال بجمع أو تخزين معلوماتك الشخصية. %1$s معرفة المزيد @@ -6684,6 +6808,8 @@ صادرة فائتة + + فائتة عند تشغيل الحساب الشخصي للإشعارات انضم @@ -6803,6 +6929,8 @@ المكالمة الجماعية مكالمة جماعية فائتة + + اتصال جماعي فائت أثناء تشغيل الحساب الشخصي للإشعار مكالمة جماعية واردة diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml index 7e2a21bf3e..a31ccfbe0a 100644 --- a/app/src/main/res/values-az/strings.xml +++ b/app/src/main/res/values-az/strings.xml @@ -323,8 +323,12 @@ Şəkli endirmək mümkün deyil. Bunu yenidən göndərməli olacaqsınız. Videonu endirmək mümkün deyil. Bunu yenidən göndərməli olacaqsınız. - - %1$s redaktə edildi + + İndi redaktə edildi + + %1$s redaktə edildi + + %1$s redaktə edildi Zəngə qoşul @@ -358,6 +362,8 @@ Güvənli olmayan MMS Signal mesajı + + Mesaj göndər Gəlin Molly-a keçək: %1$s Zəhmət olmasa bir əlaqə seçin Qoşma, göndərdiyiniz mesaj növü üçün həcm limitləri aşır. @@ -1277,6 +1283,10 @@ Buraxılmış audio zəng Buraxılmış video zəng + + Profil statusu aktiv ikən buraxılmış səsli zəng + + Profil statusu aktiv ikən buraxılmış video zəng Bir audio zəngi rədd etdiniz @@ -1828,6 +1838,8 @@ Kameranı dəyiş Səsi aç + + Əlavə funksiyalar Zəngi bitir @@ -1844,10 +1856,68 @@ Cihazın qulaqcığını göstərən piktoqram. + + Əlini qaldır + + Əlini qaldır + + Əl endirilsin? + + Əlini endir + + Ləğv et + + Əl qaldırdınız + + Bax + + + + %1$s əl qaldırdı + %1$s + %2$d əl qaldırdı + + + + + %1$s + %1$s +%2$d + + + + Qaldırılmış əl görünüşünü genişləndir + + + + Signal bağlantısı + + %1$s sistem kontaktlarınız arasındadır + + Ortaq bir qrupunuz yoxdur + + Tələbləri diqqətlə nəzərdən keçirin + + %1$d ilə ortaq qrupunuz var + + Haqqında + + Siz + - - Bu zəngdə · %1$d nəfər - Bu zəngdə · %1$d nəfər + + Bu zəngdə (%1$d) nəfər var + Bu zəngdə (%1$d) nəfər var + + + Signal (%1$d) nəfərə zəng edəcək + Signal (%1$d) nəfərə zəng edəcək + + + Signal (%1$d) nəfərə bildiriş göndərəcək + Signal (%1$d) nəfərə bildiriş göndərəcək + + + (%1$d) qaldırılmış əl + (%1$d) qaldırılmış əl @@ -1961,6 +2031,12 @@ Baxıldı Media + + + Eyni adla bir çox istifadəçi aşkar olundu + + Bax + \'%1$s\' üçün heç nə tapılmadı @@ -2127,6 +2203,8 @@ Göndər + + 00 Bir istifadəçi adı əlavə edin @@ -2149,6 +2227,12 @@ Ötür Bitdi + + Bu istifadəçi adı mövcud deyil, başqa bir nömrə ilə cəhd edin. + + Səhv istifadəçi adı, minimum %1$d rəqəm daxil edin. + + Səhv istifadəçi adı, maksimum %1$d rəqəm daxil edin. %1$d əlaqə Signal-dadır! @@ -2346,6 +2430,10 @@ Emal olunur… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2370,6 +2458,8 @@ Əlaqə yaradılmış cihazda cavablandı. Əlaqə yaradılmış cihazda rədd edildi. Əlaqə yaradılmış cihazda məşğuldur. + + Flip Camera buraya köçürüldü, sınaqdan keçirmək üçün videonuza toxunun Güvənlik nömrəsi dəyişən kimsə bu zəngə qoşuldu. @@ -3925,14 +4015,16 @@ Qrup üzvlüyündən çıxarılmadı. - Üzv Tələb Əlaqəniz Qrupdan çıxart Əlaqəni yenilə Blokla Sil - %1$s, təzəlikcə profil adını %2$s olaraq dəyişdirdi + + %1$s istifadəçi az əvvəl %2$s profil adını %3$s şəklində dəyişdi + + %1$s sistem kontaktlarınız arasındadır %1$s qoşuldu @@ -4988,6 +5080,12 @@ Bu profil açıq olanda bildiriş və zəng almaq istədiyiniz şəxsləri və qrupları əlavə edin Şəxsləri və ya qrupları əlavə edin + + İstisnalar + + Bütün zənglərə icazə ver + + Bütün adçəkmələr üçün bildir Əlavə et @@ -5602,8 +5700,8 @@ Bütün əlaqələr nəzərdən keçirilib, davam etmək üçün \"Göndər\" düyməsinə toxunun. - %1$d kontaktınız Signal-ı yenidən quraşdırmış və ya cihazlarını dəyişmiş ola bilər. Onunla hekayəni paylaşmazdan əvvəl təhlükəsizlik nömrəsini nəzərdən keçirə və ya onu hekayəndən silməyi seçə bilərsiniz. - %1$d kontaktınız Signal-ı yenidən quraşdırmış və ya cihazlarını dəyişmiş ola bilər. Onunla hekayəni paylaşmazdan əvvəl təhlükəsizlik nömrəsini nəzərdən keçirə və ya onu hekayəndən silməyi seçə bilərsiniz. + %1$d istifadəçi Signal-ı yenidən quraşdırmış və ya cihazlarını dəyişdirmiş ola bilər. Onun təhlükəsizlik nömrəsinə baxın və ya mesaj göndərərək davam edin. + %1$d istifadəçi Signal-ı yenidən quraşdırmış və ya cihazlarını dəyişdirmiş ola bilər. Onların təhlükəsizlik nömrəsinə baxın və ya mesaj göndərərək davam edin. Güvənlik nömrəsini təsdiqlə @@ -5961,12 +6059,18 @@ IBAN ölkə kodu dəstəklənmir Səhv IBAN nömrəsi + + Minimum 2 simvol + + Səhv e-poçt ünvanı iDEAL Bankınızı, adınızı və e-poçtunuzu daxil edin. Sizə ianənizlə əlaqəli yenilikləri göndərmək üçün Stripe bu e-poçt ünvanından istifadə edir. %1$s + + Bank məlumatlarınızı daxil edin. Signal şəxsi məlumatlarınızı toplamır və ya saxlamır. %1$s Daha ətraflı @@ -6152,6 +6256,8 @@ Gedən zəng Cavabsız zəng + + Profil statusu aktiv ikən buraxılan zəng Qoşul @@ -6259,6 +6365,8 @@ Qrup zəngi Cavabsız qrup zəngi + + Profil statusu aktiv ikən buraxılmış qrup zəngi Gələn qrup zəngi diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index 15f2399789..bdc0b35d25 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -323,8 +323,12 @@ Неуспешно изтегляне на изображението. Ще трябва да го изпратите отново. Неуспешно изтегляне на видеото. Ще трябва да го изпратите отново. - - Редактирано %1$s + + Редактирано сега + + Редактирано %1$s + + Редактирано %1$s Присъединете се към разговор @@ -358,6 +362,8 @@ Некриптиран MMS Съобщение в Signal + + Изпрати съобщение Да преминем към Molly %1$s Моля, избери контакт Размерът на прикачения файл надминава допустимия лимит за типа съобщение, който изпращате. @@ -1277,6 +1283,10 @@ Пропуснато гласово повикване Пропуснато видео повикване + + Пропуснато гласово обаждане при включен профил за известия + + Пропуснато видео обаждане при включен профил за известия Отказахте гласово повикване @@ -1828,6 +1838,8 @@ Превключване на камера Превключване на тих режим + + Допълнителни действия Прекрати обаждането @@ -1844,10 +1856,68 @@ Икона със слушалка на устройство. + + Вдигнете ръка + + Вдигнете ръка + + Сваляте ли ръка? + + Свалете ръка + + Отказ + + Вие вдигнахте ръка + + Преглед + + + + %1$s вдигна ръка + %1$s + %2$d вдигнаха ръка + + + + + %1$s + %1$s + %2$d + + + + Разширяване на изгледа с вдигнати ръце + + + + Signal връзка + + %1$s е в контактите на системата ви + + Нямате общи групи + + Прегледайте внимателно заявката + + %1$d общи групи + + Биография + + Ти + - - В този разговор · %1$d човек - В този разговор · %1$d хора + + В този разговор (%1$d) + В този разговор (%1$d) + + + Signal ще позвъни (%1$d) + Signal ще позвъни (%1$d) + + + Signal ще извести (%1$d) + Signal ще извести (%1$d) + + + Вдигната ръка (%1$d) + Вдигнати ръце (%1$d) @@ -1961,6 +2031,12 @@ Видяно Медия + + + Открит е конфликт на имена + + Преглед + Не са открити резултати за \'%1$s\' @@ -2127,6 +2203,8 @@ Изпращане + + 00 Добавяне на потребителско име @@ -2149,6 +2227,12 @@ Пропусни Готово + + Това потребителско име не е налично, пробвайте с друг номер. + + Невалидно потребителско име, въведете най-малко %1$d цифри. + + Невалидно потребителско име, въведете най-много %1$d цифри. %1$d контакт е в Signal! @@ -2346,6 +2430,10 @@ Обработва се… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2370,6 +2458,8 @@ Отговорено на свързано устройство. Отказано на свързано устройство. Сигнал заетo на свързано устройство. + + Бутонът „Смени камерата“ беше преместен тук ‑ докоснете видеото си, за да го изпробвате Някой се е присъединил към това обаждане с променен номер за безопасност. @@ -3925,14 +4015,16 @@ Неуспешно премахване на член от група. - Член Искай Твоя контакт Премахване от групата Поднови контакт Блокиране Изтриване - Скоро смениха името от %1$s до %2$s + + %1$s наскоро промени името на профила си от %2$s на %3$s + + %1$s е в контактите на системата ви %1$s се присъедини @@ -4988,6 +5080,12 @@ Добавете хора и групи, от които искате да получавате известия и повиквания, когато този профил е включен Добавяне на хора или групи + + Изключения + + Разрешаване на всички обаждания + + Известяване за всички споменавания Добави @@ -5602,8 +5700,8 @@ Всички връзки бяха прегледани, докоснете \"изпращане\", за да продължите. - Имате %1$d връзка, която е възможно да е преинсталирала Signal или да е сменила устройството си. Преди изпращането можете да прегледате нейния номер за безопасност, ако желаете. - Имате %1$d връзки, които е възможно да са преинсталирали Signal или да са сменили устройството си. Преди изпращането можете да прегледате техните номера за безопасност, ако желаете. + %1$d връзка може да е преинсталирала Signal или да е сменила устройството си. Ако желаете, преди изпращането можете да прегледате техния номер за сигурност. + %1$d връзки може да са преинсталирали Signal или да са сменили устройството си. Ако желаете, преди изпращането можете да прегледате техните номера за сигурност. Проверка на числата за сигурност @@ -5961,12 +6059,18 @@ Кодът на държавата в този IBAN не се поддържа Невалиден IBAN + + Минимум 2 знака + + Невалиден имейл адрес iDEAL Въведете вашите банка, име и имейл. Stripe използва този имейл, за да ви изпраща актуализации за вашето дарение. %1$s + + Въведете своите банкови данни. Signal не събира и не съхранява личната ви информация. %1$s Научете повече @@ -6152,6 +6256,8 @@ Изходящо Пропуснато + + Пропуснато при включен профил за известия Присъедини се @@ -6259,6 +6365,8 @@ Групов разговор Пропуснато групово повикване + + Пропуснато групово обаждане при включен профил за известия Входящо групово повикване diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml index 213f3672f5..6c653c642b 100644 --- a/app/src/main/res/values-bn/strings.xml +++ b/app/src/main/res/values-bn/strings.xml @@ -323,8 +323,12 @@ ছবি ডাউনলোড করা যাচ্ছে না। আপনাকে এটি আবার পাঠাতে হবে। ভিডিও ডাউনলোড করা যাচ্ছে না। আপনাকে এটি আবার পাঠাতে হবে। - - %1$s এডিট করা হয়েছে + + এখন এডিট করা হয়েছে + + %1$s এডিট করা হয়েছে + + %1$s এডিট করা হয়েছে কলে যোগ দিন @@ -358,6 +362,8 @@ অনিরাপদ এমএমএস Signal বার্তা + + মেসেজ পাঠান চলুন Molly এ যোগ দেই %1$s দয়া করে একটি পরিচিতি চয়ন করুন আপনি যে বার্তা প্রেরণ করছেন সংযুক্তি তার আকারের সীমা অতিক্রম করেছে। @@ -1277,6 +1283,10 @@ মিসড ভয়েস কল মিসড ভিডিও কল + + নোটিফিকেশন প্রোফাইল চালু থাকা অবস্থায় মিসড ভয়েস কল + + নোটিফিকেশন প্রোফাইল চালু থাকা অবস্থায় মিসড ভিডিও কল আপনি একটি ভয়েস কল প্রত্যাখ্যান করেছেন @@ -1828,6 +1838,8 @@ ক্যামেরা টগল করুন মিউট টগল করুন + + আরো কাজ কল শেষ করুন @@ -1844,10 +1856,68 @@ একটি ডিভাইসের ইয়ারপিসকে নির্দেশকারী একটি আইকন। + + হাত তুলুন + + হাত তুলুন + + হাত নামবেন? + + হাত নামান + + বাতিল করুন + + আপনি হাত তুলেছেন + + দেখুন + + + + %1$s হাত তুলেছেন + %1$s + %2$d হাত তুলেছেন + + + + + %1$s + %1$s +%2$d + + + + হাত উঠানোর ভিউ সম্প্রসারিত করুন + + + + Signal কানেকশন + + %1$s আপনার সিস্টেমের কন্টাক্টে আছেন + + আপনারা দুজনই আছেন এমন কোনো গ্রুপ নেই + + তোমার অনুরোধ ভালোভাবে দেখো + + %1$d-টি একই গ্রুপ আছে + + পরিচিতি + + আপনি + - - এই কল-এ · %1$d জন - এই কলে · %1$d জন + + এই কলে (%1$d) + এই কলে (%1$d) + + + Signal (%1$d)-কে রিং করবে + Signal (%1$d)-কে রিং করবে + + + Signal (%1$d)-কে জানাবে + Signal (%1$d)-কে জানাবে + + + হাত তুলেছেন (%1$d) + হাত তুলেছেন (%1$d) @@ -1961,6 +2031,12 @@ দেখা হয়ে গেছে মিডিয়া + + + একই নাম পাওয়া গেছে + + দেখুন + \'%1$s\' এর জন্য কোনও ফলাফল পাওয়া যায়নি @@ -2127,6 +2203,8 @@ পাঠান + + 00 একটি ইউজারনেম যোগ করুন @@ -2149,6 +2227,12 @@ বাদ দিয়ে যান শেষ + + এই ব্যবহারকারীর নামটি পাওয়া যাচ্ছে না, আরেকটি নম্বর দিয়ে চেষ্টা করুন। + + অকার্যকর ব্যবহারকারীর নাম, অন্তত %1$d-টি সংখ্যা লিখুন। + + অকার্যকর ব্যবহারকারীর নাম, অন্তত %1$d-টি সংখ্যা লিখুন। %1$d টি কন্ট্যাক্ট Signal ব্যবহার করছে! @@ -2346,6 +2430,10 @@ প্রক্রিয়া হচ্ছে… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2370,6 +2458,8 @@ সংযুক্ত ডিভাইসে উত্তর দেওয়া হয়েছে। সংযুক্ত ডিভাইসে অস্বীকার করা হয়েছে। সংযুক্ত ডিভাইসে ব্যাস্ত। + + ফ্লিপ ক্যামেরা সরিয়ে এখানে আনা হয়েছে, এটি ব্যবহার করে দেখতে আপনার ভিডিওতে ট্যাপ করুন কেউ একজন এই কলে এমন একটি নিরাপত্তা নাম্বার দিয়ে যোগ দিয়েছেন যা পরিবর্তিত হয়েছে। @@ -3925,14 +4015,16 @@ গ্রুপের সদস্য সরিয়ে ফেলতে ব্যর্থ হয়েছে। - সদস্য অনুরোধ আপনার কন্টাক্ট গ্রুপ থেকে সরিয়ে ফেলুন কন্টাক্ট হালনাগাদ ব্লক করুন মুছে ফেলুন - সাম্প্রতিককালে তাদের প্রোফাইল নাম %1$s থেকে %2$s তে পরিবর্তন করেছেন + + %1$s সম্প্রতি তার নাম %2$s থেকে %3$s-এ পরিবর্তন করেছেন। + + %1$s আপনার সিস্টেমের কন্টাক্টে আছেন %1$s যোগ দিয়েছেন @@ -4988,6 +5080,12 @@ প্রোফাইল চালু থাকা অবস্থায় আপনি যে সমস্ত ব্যক্তি ও গ্ৰুপের নোটিফিকেশন পেতে চান এবং কল করতে চান তাদের যোগ করুন ব্যক্তি অথবা গ্ৰুপ যোগ করুন + + প্রত্যাশা + + সকল কল অনুমোদন করুন + + সকল উল্লেখ অবহিত করুন যোগ করুন @@ -5602,8 +5700,8 @@ সকল কানেকশন রিভিউ করা হয়েছে, চালিয়ে যেতে সেন্ডে ট্যাপ করুন। - আপনার %1$dটি কানেকশন আছে, যারা হয়তো Signal আবার ইন্সটল করেছেন বা ডিভাইস পরিবর্তন করেছেন। তাদের সাথে আপনার স্টোরি শেয়ার করার আগে তাদের নিরাপত্তা নম্বরগুলো পর্যালোচনা করুন বা আপনার স্টোরি দেখা থেকে তাদের বাদ দিন। - আপনার %1$dটি কানেকশন আছে, যারা হয়তো Signal আবার ইনস্টল করেছেন বা ডিভাইস পরিবর্তন করেছেন। তাদের সাথে আপনার স্টোরি শেয়ার করার আগে তাদের নিরাপত্তা নম্বরগুলো পর্যালোচনা করুন বা আপনার স্টোরি দেখা থেকে তাদের বাদ দিন। + %1$d-টি কানেকশন হয়তো Signal পুনরায় ইনস্টল করেছেন বা ডিভাইস পরিবর্তন করেছেন। আপনি তার নিরাপত্তা নম্বর পর্যালোচনা করতে পারেন বা পাঠানো চালিয়ে যেতে পারেন। + %1$d-টি কানেকশন হয়তো Signal পুনরায় ইনস্টল করেছেন বা ডিভাইস পরিবর্তন করেছেন। আপনি তাদের নিরাপত্তা নম্বর পর্যালোচনা করতে পারেন বা পাঠানো চালিয়ে যেতে পারেন। নিরাপত্তা নাম্বার যাচাই করুন @@ -5961,12 +6059,18 @@ IBAN কান্ট্রি কোডটি সমর্থিত নয় অকার্যকর IBAN + + অন্তত 2 ক্যারেক্টার + + অকার্যকর ইমেইল ঠিকানা iDEAL আপনার ব্যাংকের নাম, আপনার নাম ও ইমেইল লিখুন। আপনার ডোনেশন সম্পর্কে আপডেট পাঠাতে Stripe এই ইমেইলটি ব্যবহার করে। %1$s + + আপনার ব্যাংকের তথ্য লিখুন। Signal আপনার ব্যক্তিগত তথ্য সংগ্রহ কিংবা সংরক্ষণ করে না। %1$s আরো জানুন @@ -6152,6 +6256,8 @@ আউটগোয়িং কল মিসড কল + + নোটিফিকেশন প্রোফাইল চালু থাকা অবস্থায় মিস হয়েছে যোগ দিন @@ -6259,6 +6365,8 @@ গ্রুপ কল মিসড গ্রুপ কল + + নোটিফিকেশন প্রোফাইল চালু থাকা অবস্থায় মিসড গ্রুপ কল ইনকামিং গ্রুপ কল diff --git a/app/src/main/res/values-bs/strings.xml b/app/src/main/res/values-bs/strings.xml index a267460c06..fa3bdb0922 100644 --- a/app/src/main/res/values-bs/strings.xml +++ b/app/src/main/res/values-bs/strings.xml @@ -325,8 +325,12 @@ Nije moguće preuzeti sliku. Morat ćete je poslati ponovo. Nije moguće preuzeti videozapis. Morat ćete ga poslati ponovo. - - Uređeno %1$s + + Uređeno sada + + Uređeno %1$s + + Uređeno %1$s Pridruži se pozivu @@ -364,6 +368,8 @@ Nezaštićen MMS Signal poruka + + Pošalji poruku Pređimo na Molly %1$s Molimo odaberite kontakt Veličina priloga veća je od dopuštene za vrstu poruke koju šaljete. @@ -1365,6 +1371,10 @@ Propušteni glasovni poziv Propušteni videopoziv + + Propušteni glasovni poziv dok je profil obavijesti uključen + + Propušteni glasovni poziv dok je profil obavijesti uključen Odbili ste glasovni poziv @@ -1643,7 +1653,7 @@ Želite li dopustiti da vam %1$s šalje poruke? Nećete primati poruke dok ih ne prestanete blokirati. Želite li primati ažuriranja i novosti od %1$s? Nećete primati nikakve novosti dok ovu osobu ne odblokirate. Continue your chat with this group and share your name and photo with its members? - This Legacy Group can no longer be used. Create a new group to activate new features like @mentions and admins. + Ova starija grupa se više ne može koristiti. Kreirajte novu grupu da aktivirate nove funkcije kao što su @spominjanja i administratori. Ova grupa više se ne može koristiti jer je prevelika. Maksimum za grupu iznosi %1$d. Nastavi chat sa %1$s i podijeli svoje ime i fotografiju s tom osobom? Pristupiti ovoj grupi i dopustiti članovima da vide Vaše ime i sliku? Oni neće znati da ste vidjeli njihove poruke dok ne prihvatite. @@ -1970,6 +1980,8 @@ Uključi/isključi kameru Uključi/isključi zvuk + + Dodatne radnje Završi razgovor @@ -1986,12 +1998,80 @@ Ikona koja predstavlja slušalicu uređaja. + + Podigni ruku + + Podigni ruku + + Spustiti ruku? + + Spusti ruku + + Otkaži + + Podigli ste ruku + + Pregled + + + + %1$s je podigao/la ruku + %1$s i još %2$d su podigli ruku + %1$s i još %2$d su podigli ruku + %1$s i još %2$d su podigli ruku + + + + + %1$s + %1$s +%2$d + %1$s +%2$d + %1$s +%2$d + + + + Proširite prikaz podignute ruke + + + + Signal veza + + %1$s je u vašim sistemskim kontaktima + + Nemate zajedničkih grupa + + Pažljivo pregledajte zahtjeve + + Zajedničke grupe: %1$d + + O kontaktu + + Vi + - - U ovom pozivu učestvuje· %1$d osoba - U ovom pozivu učestvuju %1$d osobe - U ovom pozivu učestvuje· %1$d osoba - U ovom pozivu učestvuje· %1$d osoba + + U ovom pozivu (%1$d) + U ovom pozivu (%1$d) + U ovom pozivu (%1$d) + U ovom pozivu (%1$d) + + + Signal će pozvati (%1$d) + Signal će pozvati (%1$d) + Signal će pozvati (%1$d) + Signal će pozvati (%1$d) + + + Signal će obavijestiti (%1$d) + Signal će obavijestiti (%1$d) + Signal će obavijestiti (%1$d) + Signal će obavijestiti (%1$d) + + + Podignute ruke (%1$d) + Podignute ruke (%1$d) + Podignute ruke (%1$d) + Podignuta ruka (%1$d) @@ -2107,6 +2187,12 @@ Viđeno Slike + + + Pronađen sukob imena + + Pregled + Nema rezultata za \'%1$s\' @@ -2273,6 +2359,8 @@ Šalji + + 00 Dodaj korisničko ime @@ -2295,6 +2383,12 @@ Preskoči Uredu + + Ovo korisničko ime nije dostupno, pokušajte s drugim brojem. + + Nevažeće korisničko ime, unesite najmanje %1$d cifre. + + Nevažeće korisničko ime, unesite najviše %1$d cifri. %1$d kontakt je na Signalu! @@ -2500,6 +2594,10 @@ Obrada… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2524,6 +2622,8 @@ Odgovoreno na povezanom uređaju. Odbijeno na povezanom uređaju. Zauzeto na povezanom uređaju. + + Opcija promjene kamere je premještena ovdje, dodirnite videozapis da je isprobate Pozivu se priključio neko čiji je sigurnosni broj promijenjen. @@ -4127,14 +4227,16 @@ Neuspjelo udaljavanje člana grupe. - Član Zahtjev Vaš kontakt Udalji iz grupe Ažuriraj kontakta Blokiraj Izbriši - Nedavno je promijenio/la ime na svom profilu iz %1$s u %2$s + + %1$s je nedavno promijenio/la ime svog profila iz %2$s u %3$s + + %1$s je u vašim sistemskim kontaktima %1$s se pridružio/la @@ -5208,6 +5310,12 @@ Dodajte osobe i grupe od kojih želite primati obavještenja kad je aktivan ovaj profil Dodaj osobe ili grupe + + Izuzeci + + Dopusti sve pozive + + Obavijesti svaki put kad me neko spomene Dodaj @@ -5856,10 +5964,10 @@ Sve veze su pregledane, dodirnite \"Pošalji\" da nastavite. - Imate %1$d kontakt koji je možda ponovo instalirao Signal ili je promijenio uređaj. Prije nego što podijelite priču s tom osobom, pregledajte njihove sigurnosne brojeve ili razmislite o tome da ih uklonite iz svoje priče. - Imate %1$d kontakta koja su možda ponovo instalirala Signal ili su promijenili uređaj. Prije nego što podijelite priču s tim osobama, pregledajte njihove sigurnosne brojeve ili razmislite o tome da ih uklonite iz svoje priče. - Imate %1$d kontakata koji su možda ponovo instalirali Signal ili su promijenili uređaj. Prije nego što podijelite priču s tim osobama, pregledajte njihove sigurnosne brojeve ili razmislite o tome da ih uklonite iz svoje priče. - Imate %1$d kontakata koja su možda ponovo instalirala Signal ili su promijenili uređaj. Prije nego što podijelite priču s tim osobama, pregledajte njihove sigurnosne brojeve ili razmislite o tome da ih uklonite iz svoje priče. + %1$d konekcija je možda ponovo instalirala Signal ili promijenila uređaje. Možete pregledati njihov sigurnosni broj ili nastaviti sa slanjem. + %1$d konekcije su možda ponovo instalirale Signal ili promijenile uređaje. Možete pregledati njihov sigurnosni broj ili nastaviti sa slanjem. + %1$d konekcija je možda ponovo instaliralo Signal ili promijenilo uređaje. Možete pregledati njihov sigurnosni broj ili nastaviti sa slanjem. + %1$d konekcija je možda ponovo instaliralo Signal ili promijenilo uređaje. Možete pregledati njihov sigurnosni broj ili nastaviti sa slanjem. Provjerite sigurnosni broj @@ -6225,12 +6333,18 @@ IBAN kȏd zemlje nije podržan Nevažeći IBAN + + Najmanje 2 znaka + + Pogrešna adresa e-pošte iDEAL Unesite svoju banku, ime i adresu e-pošte. Stripe koristi ovu e-poštu da vam šalje novosti o vašoj donaciji. %1$s + + Unesite bankovne podatke. Signal ne prikuplja niti pohranjuje vaše lične podatke. %1$s Saznaj više @@ -6418,6 +6532,8 @@ Odlazni Propušteni + + Propušteno dok je profil obavijesti uključen Pridruži se @@ -6531,6 +6647,8 @@ Grupni poziv Propušten grupni poziv + + Propušteni grupni poziv dok je profil obavijesti uključen Dolazni grupni poziv diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 30cf970d05..99662caeae 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -323,8 +323,12 @@ No es pot descarregar la imatge. Hauràs de tornar-la a enviar. No es pot descarregar el vídeo. Hauràs de tornar-lo a enviar. - - Editat fa %1$s + + Editat ara + + Editat fa %1$s + + Editat fa %1$s Unir-me a la trucada @@ -358,6 +362,8 @@ MMS no segur Missatge del Signal + + Envia el missatge Canviem al Molly, %1$s Trieu un contacte El fitxer adjunt excedeix la mida màxima per a aquest tipus de missatges. @@ -1277,6 +1283,10 @@ Trucada perduda Videotrucada perduda + + Trucada de veu perduda mentre els ajustos de notificació estaven activats + + Videotrucada perduda mentre els ajustos de notificació estaven activats Has rebutjat una trucada @@ -1828,6 +1838,8 @@ Canviar càmera Activa o desactiva silenci + + Accions addicionals Penja @@ -1844,10 +1856,68 @@ Icona representant auricular d\'un dispositiu. + + Aixecar la mà + + Aixecar la mà + + Baixar la mà? + + Baixar la mà + + Cancel·lar + + Has aixecat la mà + + Vista + + + + %1$s ha aixecat la mà + %1$s + %2$d han aixecat la mà + + + + + %1$s + %1$s +%2$d + + + + Ampliar la vista de mà aixecada + + + + Connexió de Signal + + %1$s és als contactes del teu sistema + + No teniu cap grup en comú + + Reviseu les sol·licituds amb atenció. + + %1$d grups en comú + + Detalls + + Vós + - - En aquesta trucada · %1$d persona - En aquesta trucada · %1$d persones + + En aquesta trucada (%1$d) + En aquesta trucada (%1$d) + + + Signal trucarà a (%1$d) + Signal trucarà a (%1$d) + + + Signal notificarà a (%1$d) + Signal notificarà a (%1$d) + + + Mà aixecada (%1$d) + Mans aixecades (%1$d) @@ -1961,6 +2031,12 @@ Vista Contingut + + + S\'ha trobat un conflicte en el nom + + Vista + No s\'ha trobat cap resultat per a «%1$s» @@ -2127,6 +2203,8 @@ Envia + + 00 Afegeix un nom d\'usuari @@ -2149,6 +2227,12 @@ Omet Fet + + Aquest nom d\'usuari no està disponible, prova un altre número. + + Nom d\'usuari no vàlid, introdueix %1$d dígits com a mínim. + + Nom d\'usuari no vàlid, introdueix %1$d dígits com a màxim. %1$d contacte és al Signal! @@ -2346,6 +2430,10 @@ Processant… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2370,6 +2458,8 @@ S\'ha respost en un dispositiu enllaçat. S\'ha rebutjat en un dispositiu enllaçat. Ocupat/da en un dispositiu enllaçat + + El botó per canviar de càmera s\'ha mogut aquí, toca el teu vídeo per provar-lo Algú s\'ha afegit a aquesta trucada amb un número de seguretat que ha canviat. @@ -3925,14 +4015,16 @@ No s\'ha pogut esborrar el membre del grup. - Membre Sol·licitud Contacte Esborrar del grup Actualitza el contacte Bloquejar Esborrar - Recentment han canviat el nom del perfil de %1$s a %2$s. + + %1$s ha canviat recentment el nom del perfil de %2$s a %3$s + + %1$s és als contactes del teu sistema %1$s s\'hi ha afegit. @@ -4988,6 +5080,12 @@ Afegiu persones i grups dels quals rebre notificacions i trucades quan aquest perfil estigui activat. Afegeix persones o grups + + Excepcions + + Permet totes les trucades + + Notifica totes les mencions Afegeix @@ -5602,8 +5700,8 @@ S\'han revisat tots els contactes, toca enviar per a continuar. - Tens %1$d contacte que pot ser que hagi reinstal·lat Signal o canviat de dispositiu. Pots revisar el seu número de seguretat abans d\'enviar. - Tens %1$d contactes que pot ser que hagin reinstal·lat Signal o canviat de dispositiu. Pots revisar els seus números de seguretat abans d\'enviar. + Pot ser que %1$d contacte hagi reinstal·lat Signal o canviat de dispositiu. Pots revisar el seu número de seguretat o continuar i enviar. + Pot ser que %1$d contactes hagin reinstal·lat Signal o canviat de dispositiu. Pots revisar els seus números de seguretat o continuar i enviar. Verifica el número de seguretat @@ -5961,12 +6059,18 @@ El codi de país de l\'IBAN no és compatible IBAN no vàlid + + Mínim 2 caràcters + + Adreça de correu electrònic no vàlida iDEAL Introdueix el teu banc, nom i correu electrònic. Stripe utilitza aquest correu electrònic per enviar-te actualitzacions sobre la teva donació. %1$s + + Introdueix els teus detalls bancaris. Signal no recopila ni emmagatzema la teva informació personal. %1$s Més informació @@ -6152,6 +6256,8 @@ Sortint Perduda + + Perduda mentre els ajustos de notificació estaven activats Unir-me @@ -6259,6 +6365,8 @@ Trucada de grup Trucada grupal perduda + + Trucada de grup perduda mentre els ajustos de notificació estaven activats Trucada grupal entrant diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 4b7458156d..05e987312a 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -325,8 +325,12 @@ Obrázek nelze stáhnout. Budete ho muset poslat znovu. Video nelze stáhnout. Budete ho muset poslat znovu. - - Upraveno %1$s + + Teď upraveno + + Upraveno %1$s + + Upraveno %1$s Připojit se k hovoru @@ -364,6 +368,8 @@ Nezabezpečená MMS Zpráva Signal + + Odeslat zprávu Přejít na Molly: %1$s Vyberte prosím kontakt Velikost přílohy překročila limit pro typ zprávy, kterou posíláte. @@ -1365,6 +1371,10 @@ Zmeškaný hlasový hovor Zmeškaný videohovor + + Zmeškaný hlasový hovor při zapnutém profilu oznámení + + Zmeškaný videohovor při zapnutém profilu oznámení Odmítli jste hlasový hovor @@ -1970,6 +1980,8 @@ Přepnout kameru Přepnout ztlumení zvuku + + Další možnosti Ukončit hovor @@ -1986,12 +1998,80 @@ Ikona znázorňující sluchátko zařízení. + + Zvednout ruku + + Zvednout ruku + + Dát ruku dolů? + + Dát ruku dolů + + Zrušit + + Máte zvednutou ruku + + Zobrazit + + + + %1$s má zvednutou ruku + %1$s a %2$d mají zvednutou ruku + %1$s a %2$d mají zvednutou ruku + %1$s a %2$d mají zvednutou ruku + + + + + %1$s + %1$s a %2$d + %1$s a %2$d + %1$s a %2$d + + + + Rozbalit zobrazení zvednutých rukou + + + + Spojení Signal + + %1$s je ve vašem seznamu kontaktů + + Nemáte žádné společné skupiny + + Požadavky pečlivě prověřte + + Počet společných skupin: %1$d + + O mně + + Vy + - - V tomto hovoru - %1$d člověk - V tomto hovoru - %1$d lidi - V tomto hovoru - %1$d lidi - V tomto hovoru - %1$d lidí + + V tomto hovoru (%1$d) + V tomto hovoru (%1$d) + V tomto hovoru (%1$d) + V tomto hovoru (%1$d) + + + Signal bude volat tohoto uživatele: (%1$d) + Signal bude volat tyto uživatele: (%1$d) + Signal bude volat tyto uživatele: (%1$d) + Signal bude volat tyto uživatele: (%1$d) + + + Signal upozorní tohoto uživatele: (%1$d) + Signal upozorní tyto uživatele: (%1$d) + Signal upozorní tyto uživatele: (%1$d) + Signal upozorní tyto uživatele: (%1$d) + + + Zvednutá ruka (%1$d) + Zvednuté ruce (%1$d) + Zvednuté ruce (%1$d) + Zvednuté ruce (%1$d) @@ -2107,6 +2187,12 @@ Zobrazeno Média + + + Zjištěn konflikt jmen + + Zobrazit + Pro \"%1$s\" nebylo nic nalezeno @@ -2273,6 +2359,8 @@ Odeslat + + 00 Přidat uživatelské jméno @@ -2295,6 +2383,12 @@ Přeskočit Hotovo + + Toto uživatelské jméno není dostupné, zkuste jiné číslo. + + Neplatné uživatelské jméno – zadejte alespoň %1$d číslice. + + Neplatné uživatelské jméno – zadejte alespoň %1$d číslic. %1$d kontakt má Signal! @@ -2500,6 +2594,10 @@ Probíhá zpracování… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2524,6 +2622,8 @@ Odpovězeno na propojeném zařízení. Odmítnuto na propojeném zařízení. Obsazeno na propojeném zařízení. + + Překlopení fotoaparátu bylo přesunuto sem – klepněte na video a vyzkoušejte to Někdo se přidal k tomuto hovoru s bezpečnostním číslem, které se změnilo. @@ -4127,14 +4227,16 @@ Člena skupiny se nepodařilo odebrat. - Člen Požadavek Váš kontakt Odebrat ze skupiny Aktualizovat kontakt Blokovat Odstranit - Nedávno změněno profilové jméno z %1$s na %2$s + + %1$s nedávno změnil/a jméno svého profilu z %2$s na %3$s + + %1$s je ve vašem seznamu kontaktů %1$s se připojil @@ -5208,6 +5310,12 @@ Přidání osob a skupin, od kterých chcete dostávat oznámení a hovory, když je tento profil zapnutý Přidat lidi nebo skupiny + + Výjimky + + Povolit všechna volání + + Upozornit na všechny zmínky Přidat @@ -5856,10 +5964,10 @@ Všechna spojení byla zkontrolována, pokračujte klepnutím na tlačítko odeslat. - Máte %1$d spojení, které si pravděpodobně přeinstalovalo aplikaci Signal nebo změnilo zařízení. Před sdílením příběhu s ním zkontrolujte jeho bezpečnostní číslo nebo zvažte jeho odebrání z příběhu. - Máte %1$d spojení, která si pravděpodobně přeinstalovala aplikaci Signal nebo změnila zařízení. Před sdílením příběhu s nimi zkontrolujte jejich bezpečnostní čísla nebo zvažte jejich odebrání z příběhu. - Máte %1$d spojení, která si pravděpodobně přeinstalovala aplikaci Signal nebo změnila zařízení. Před sdílením příběhu s nimi zkontrolujte jejich bezpečnostní čísla nebo zvažte jejich odebrání z příběhu. - Máte %1$d spojení, která si pravděpodobně přeinstalovala aplikaci Signal nebo změnila zařízení. Před sdílením příběhu s nimi zkontrolujte jejich bezpečnostní čísla nebo zvažte jejich odebrání z příběhu. + %1$d spojení si možná přeinstalovalo Signal nebo změnilo zařízení. Můžete zkontrolovat jeho bezpečnostní číslo nebo pokračovat v odesílání. + %1$d spojení si možná přeinstalovala Signal nebo změnila zařízení. Můžete zkontrolovat jejich bezpečnostní čísla nebo pokračovat v odesílání. + %1$d spojení si možná přeinstalovalo Signal nebo změnilo zařízení. Můžete zkontrolovat jejich bezpečnostní čísla nebo pokračovat v odesílání. + %1$d spojení si možná přeinstalovalo Signal nebo změnilo zařízení. Můžete zkontrolovat jejich bezpečnostní čísla nebo pokračovat v odesílání. Ověření bezpečnostního čísla @@ -6225,12 +6333,18 @@ Kód země IBAN není podporován Neplatné číslo IBAN + + Minimálně 2 znaky + + Neplatná e-mailová adresa iDEAL Zadejte název své banky, jméno a e-mailovou adresu. Společnost Stripe tento e-mail používá k zasílání aktuálních informací o vašem příspěvku. %1$s + + Zadejte své bankovní údaje. Signal neshromažďuje ani neukládá vaše osobní údaje. %1$s Více informací @@ -6418,6 +6532,8 @@ Odchozí Zmeškané + + Zmeškáno při zapnutém profilu oznámení Připojit se @@ -6531,6 +6647,8 @@ Skupinový hovor Zmeškaný skupinový hovor + + Zmeškaný skupinový hovor při zapnutém profilu oznámení Příchozí skupinový hovor diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index d2a34ecd5e..0ac6aab292 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -323,8 +323,12 @@ Billedet kan ikke downloades. Du skal sende det igen. Videoen kan ikke downloades. Du skal sende den igen. - - Redigeret %1$s + + Redigeret nu + + Redigerede %1$s + + Redigeret %1$s Deltag i opkald @@ -358,6 +362,8 @@ Usikker MMS Signal-besked + + Send besked Lad os bruge Molly %1$s? Vælg venligst en kontakt Vedhæftningen overskrider max. grænsen for filstørrelser, for den type af besked du sender. @@ -1277,6 +1283,10 @@ Ubesvaret stemmeopkald Ubesvaret videoopkald + + Ubesvaret taleopkald, mens notifikationsprofilen var aktiveret + + Ubesvaret videoopkald, mens notifikationsprofilen var aktiveret Du afviste et stemmeopkald @@ -1828,6 +1838,8 @@ Slå kamera til og fra Slå mikrofon til og fra + + Flere handlinger Afslut opkald @@ -1844,10 +1856,68 @@ Et ikon, der viser en enheds øretelefon. + + Ræk hånden op + + Ræk hånden op + + Tag hånden ned? + + Tag hånden ned + + Annuller + + Du har rakt hånden op + + Vis + + + + %1$s har rakt hånden op + %1$s + %2$d har rakt hånden op + + + + + %1$s + %1$s +%2$d + + + + Udvid visning af oprakte hænder + + + + Signal-forbindelse + + %1$s er i dine systemkontakter + + I har ingen grupper tilfælles + + Gennemgå anmodninger omhyggeligt + + %1$d grupper til fælles + + Om + + Dig + - - I dette opkald · %1$d person - I dette opkald · %1$d personer + + I dette opkald (%1$d) + I dette opkald (%1$d) + + + Signal ringer (%1$d) + Signal ringer (%1$d) + + + Signal sender en notifikation (%1$d) + Signal sender en notifikation (%1$d) + + + Oprakt hånd (%1$d) + Oprakte hænder (%1$d) @@ -1961,6 +2031,12 @@ Set Mediefil + + + Navnekonflikt fundet + + Vis + Ingen resultater fundet for \"%1$s\" @@ -2127,6 +2203,8 @@ Send + + 00 Tilføj et brugernavn @@ -2149,6 +2227,12 @@ Spring over Udført + + Dette brugernavn er ikke ledigt, prøv et andet nummer. + + Ugyldigt brugernavn, angiv min. %1$d cifre. + + Ugyldigt brugernavn, angiv maks. %1$d cifre. %1$d kontakt anvender Signal! @@ -2346,6 +2430,10 @@ Behandler… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2370,6 +2458,8 @@ Besvaret på en forbundet enhed. Afvist på en forbundet enhed. Optaget på en forbundet enhed + + Flip kamera er flyttet hertil. Tryk på din video for at prøve det Nogen har sluttet sig til dette opkald med et ændret sikkerhedsnummer. @@ -3925,14 +4015,16 @@ Fejl ved fjernelse af gruppemedlem. - Medlem Anmodning Din kontakt Fjern fra gruppe Opdatér kontakt Blokér Slet - Ændrede for nyligt sit profilnavn fra %1$s til %2$s + + %1$s har for nylig ændret deres profilnavn fra %2$s til %3$s + + %1$s er i dine systemkontakter %1$s sluttede sig til opkaldet @@ -4988,6 +5080,12 @@ Tilføj personer og grupper, som du vil have notifikationer og opkald fra, når denne profil er aktiveret Tilføj personer og grupper + + Undtagelser + + Tillad alle opkald + + Underret ved alle omtaler Tilføj @@ -5602,8 +5700,8 @@ Alle forbindelser er gennemgået. Tryk send for at fortsætte. - Du har %1$d kontakt, der muligvis har geninstalleret Signal eller skiftet enhed. Gennemgå vedkommendes sikkerhedsnummer, eller overvej at fjerne dem fra din historie, inden du deler din historie med dem. - Du har %1$d kontakter, der muligvis har geninstalleret Signal eller skiftet enhed. Gennemgå vedkommendes sikkerhedsnummer, eller overvej at fjerne dem fra din historie, inden du deler din historie med dem. + %1$d forbindelse har måske geninstalleret Signal eller ændret enhed. Du kan gennemgå deres sikkerhedsnummer eller fortsætte med at sende. + %1$d forbindelser har måske geninstalleret Signal eller ændret enhed. Du kan gennemgå deres sikkerhedsnumre eller fortsætte med at sende. Verificer sikkerhedsnummer @@ -5961,12 +6059,18 @@ IBAN-landekode understøttes ikke Ugyldigt IBAN + + Minimum 2 tegn + + Ugyldig mailadresse iDEAL Angiv din bank, dit navn og din mailadresse. Stripe bruger disse oplysninger til at sende opdateringer vedrørende din donation. %1$s + + Angiv dine bankoplysninger. Signal indsamler eller opbevarer ikke dine personlige oplysninger. %1$s Få mere at vide @@ -6152,6 +6256,8 @@ Udgående Ubesvaret + + Ubesvaret, mens notifikationsprofilen var aktiveret Deltag @@ -6259,6 +6365,8 @@ Gruppeopkald Ubesvaret gruppeopkald + + Ubesvaret gruppeopkald, mens notifikationsprofilen var aktiveret Indgående gruppeopkald diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 293afe97be..2a4179dc88 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -323,8 +323,12 @@ Bild kann nicht heruntergeladen werden. Du musst es erneut verschicken. Nachricht kann nicht heruntergeladen werden. Du musst es erneut verschicken. - - Bearbeitet %1$s + + Wurde eben bearbeitet + + Bearbeitet %1$s + + Bearbeitet %1$s Anruf beitreten @@ -358,6 +362,8 @@ Unverschlüsselte MMS Signal-Nachricht + + Nachricht senden Lass uns zu »Molly« wechseln: %1$s Bitte einen Kontakt auswählen Anhang zu groß für die zu sendende Nachrichtenart. @@ -1277,6 +1283,10 @@ Entgangener Sprachanruf Entgangener Videoanruf + + Entgangener Sprachanruf bei aktiviertem Benachrichtigungsprofil + + Verpasster Videoanruf bei aktiviertem Benachrichtigungsprofil Du hast einen Sprachanruf abgelehnt @@ -1828,6 +1838,8 @@ Kamerastatus ändern Stummschaltung ändern + + Weitere Aktionen Auflegen @@ -1844,10 +1856,68 @@ Ein Symbol, das Kopfhörer eines Geräts darstellt. + + Hand heben + + Hand heben + + Deine Hand senken? + + Hand senken + + Abbrechen + + Du hast deine Hand gehoben + + Anzeigen + + + + %1$s hat die Hand gehoben + %1$s + %2$d haben ihre Hand gehoben + + + + + %1$s + %1$s + %2$d + + + + »Gehobene Hand«-Ansicht vergrößern + + + + Signal-Verbindung + + %1$s ist in deinen Systemkontakten + + Ihr habt keine gemeinsamen Gruppen + + Prüfe Anfragen sorgfältig + + %1$d gemeinsame Gruppen + + Info + + Du + - - In diesem Anruf · %1$d Person - In diesem Anruf · %1$d Personen + + In diesem Anruf (%1$d) + In diesem Anruf (%1$d) + + + Signal ruft an (%1$d) + Signal ruft an (%1$d) + + + Signal sendet eine Nachricht (%1$d) + Signal sendet eine Nachricht (%1$d) + + + Gehobene Hand (%1$d) + Gehobene Hände (%1$d) @@ -1961,6 +2031,12 @@ Angesehen Medieninhalte + + + Namenskonflikt gefunden + + Anzeigen + Keine Ergebnisse für »%1$s« gefunden @@ -2127,6 +2203,8 @@ Senden + + 00 Einen Nutzernamen hinzufügen @@ -2149,6 +2227,12 @@ Überspringen Fertig + + Dieser Nutzername ist nicht verfügbar, versuche eine andere Nummer. + + Ungültiger Nutzername, bitte gib mindestens %1$d Ziffern ein. + + Ungültiger Nutzername, bitte gib maximal %1$d Ziffern ein. %1$d Kontakt ist bei Signal! @@ -2346,6 +2430,10 @@ Wird verarbeitet… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2370,6 +2458,8 @@ Auf einem gekoppelten Gerät angenommen. Auf einem gekoppelten Gerät abgelehnt. Auf einem gekoppelten Gerät besetzt. + + Die Schaltfläche »Kamera wechseln« wurde hierher verschoben. Tippe auf dein Video, um es auszuprobieren. Jemand mit einer geänderten Sicherheitsnummer ist diesem Anruf beigetreten. @@ -3925,14 +4015,16 @@ Gruppenmitglied konnte nicht entfernt werden. - Mitglied Anfrage Dein Kontakt Aus Gruppe entfernen Kontakt aktualisieren Blockieren Löschen - Profilname wurde kürzlich von %1$s zu %2$s geändert + + %1$s hat kürzlich den Profilnamen von %2$s zu %3$s geändert + + %1$s ist in deinen Systemkontakten %1$s ist beigetreten @@ -4988,6 +5080,12 @@ Füge Personen und Gruppen hinzu, von denen du Benachrichtigungen und Anrufe empfangen möchtest, wenn dieses Profil aktiviert ist Personen oder Gruppen hinzufügen + + Ausnahmen + + Alle Anrufe erlauben + + Bei jeder Erwähnung benachrichtigen Hinzufügen @@ -5602,8 +5700,8 @@ Alle Verbindungen wurden geprüft, tippe auf „Senden“, um fortzufahren. - Du hast %1$d Verbindung, die Signal möglicherweise erneut installiert oder das Gerät gewechselt hat. Optional kannst du seine/ihre Sicherheitsnummer vor dem Senden überprüfen. - Du hast %1$d Verbindungen, die Signal möglicherweise erneut installiert oder das Gerät gewechselt haben. Optional kannst du ihre Sicherheitsnummern vor dem Senden überprüfen. + %1$d Kontakt hat Signal möglicherweise erneut installiert oder das Gerät gewechselt. Optional kannst du dessen Sicherheitsnummer vor dem Senden überprüfen. + %1$d Kontakte haben Signal möglicherweise erneut installiert oder das Gerät gewechselt. Optional kannst du ihre Sicherheitsnummer vor dem Senden überprüfen. Sicherheitsnummer verifizieren @@ -5961,12 +6059,18 @@ Der IBAN-Ländercode wird nicht unterstützt IBAN ungültig + + Mindestens 2 Zeichen + + Ungültige E-Mail-Adresse iDEAL Gib deine Bank, deinen Namen und deine E-Mail Adresse ein. Stripe verwendet diese E-Mail-Adresse, um dir Updates zu deiner Spende zu schicken. %1$s + + Gib deine Bankverbindung ein. Deine persönlichen Daten werden von Signal nicht erfasst oder gespeichert. %1$s Mehr erfahren @@ -6152,6 +6256,8 @@ Ausgehend Verpasst + + Entgangen bei aktiviertem Benachrichtigungsprofil Beitreten @@ -6259,6 +6365,8 @@ Gruppenanruf Entgangener Gruppenanruf + + Entgangener Gruppenanruf bei aktiviertem Benachrichtigungsprofil Eingehender Gruppenanruf diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index bb1f95585a..0368e55d8a 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -323,8 +323,12 @@ Η λήψη της εικόνας απέτυχε. Πρέπει να τη στείλεις ξανά. Η λήψη του βίντεο απέτυχε. Πρέπει να το στείλεις ξανά. - - Έγινε επεξεργασία %1$s + + Έγινε επεξεργασία τώρα + + Έγινε επεξεργασία %1$s + + Έγινε επεξεργασία %1$s Είσοδος στην κλήση @@ -358,6 +362,8 @@ Μη ασφαλές MMS Μήνυμα Signal + + Αποστολή μηνύματος Έλα να χρησιμοποιήσουμε το Molly %1$s Επίλεξε μια επαφή Το συνημμένο υπερβαίνει τα όρια μεγέθους για τον τύπο μηνύματος που στέλνεις. @@ -1277,6 +1283,10 @@ Αναπάντητη κλήση Αναπάντητη βιντεοκλήση + + Αναπάντητη φωνητική κλήση ενώ είναι ενεργό το προφίλ ειδοποιήσεων + + Αναπάντητη βιντεοκλήση ενώ είναι ενεργό το προφίλ ειδοποιήσεων Απέρριψες μια κλήση @@ -1828,6 +1838,8 @@ Αλλαγή κάμερας Αλλαγή σίγασης + + Επιπλέον ενέργειες Τερματισμός κλήσης @@ -1844,10 +1856,68 @@ Ένα εικονίδιο που υποδεικνύει το ακουστικό μιας συσκευής. + + Σήκωσε το χέρι σου + + Σήκωσε το χέρι σου + + Θες να κατεβάσεις το χέρι σου; + + Κατέβασε το χέρι σου + + Ακύρωση + + Σήκωσες το χέρι σου + + Εμφάνιση + + + + Ο χρήστης %1$s σήκωσε χέρι + Οι χρήστες %1$s + %2$d σήκωσαν χέρι + + + + + %1$s + %1$s +%2$d + + + + Ανάπτυξη της προβολής σηκωμένου χεριού + + + + Επαφές Signal + + Ο χρήστης %1$s βρίσκεται στις επαφές του συστήματός σου + + Δεν έχετε κοινές ομάδες + + Επιθεώρησε τα αιτήματα προσεκτικά + + %1$d κοινές ομάδες + + Πληροφορίες + + Εσύ + - - Σε αυτή τη κλήση · %1$d άτομο - Σε αυτή τη κλήση · %1$d άτομα + + Στην κλήση (%1$d) + Στην κλήση (%1$d) + + + Το Signal θα κάνει κλήση (%1$d) + Το Signal θα κάνει κλήση (%1$d) + + + Το Signal θα ειδοποιήσει (%1$d) + Το Signal θα ειδοποιήσει (%1$d) + + + Σηκωμένο χέρι (%1$d) + Σηκωμένα χέρια (%1$d) @@ -1961,6 +2031,12 @@ Διαβάστηκε Πολυμέσα + + + Βρέθηκε σύγκρουση μεταξύ ονομάτων + + Εμφάνιση + Δε βρέθηκαν αποτελέσματα για \"%1$s\" @@ -2127,6 +2203,8 @@ Αποστολή + + 00 Προσθήκη ονόματος χρήστη @@ -2149,6 +2227,12 @@ Παράλειψη Τέλος + + Αυτό το όνομα χρήστη δεν είναι διαθέσιμο, δοκίμασε έναν άλλο αριθμό. + + Μη έγκυρο όνομα χρήστη, γράψε τουλάχιστον %1$d ψηφία. + + Μη έγκυρο όνομα χρήστη, γράψε έως %1$d ψηφία. %1$d επαφή είναι στο Signal! @@ -2346,6 +2430,10 @@ Επεξεργασία… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2370,6 +2458,8 @@ Απαντήθηκε από συνδεμένη συσκευή. Απορρίφθηκε απο συνδεμένη συσκευή. Απασχολημένος/η από συνδεμένη συσκευή. + + Η Εναλλαγή κάμερας μεταφέρθηκε εδώ, πάτα στο βίντεο για να τη δοκιμάσεις Κάποιος μπήκε στην κλήση με κάποιον αριθμό ασφαλείας που έχει αλλάξει. @@ -3925,14 +4015,16 @@ Αποτυχία αφαίρεσης μέλους της ομάδας. - Μέλος Αίτημα Επαφή σου Αφαίρεση απ\' την ομάδα Ανανέωση επαφής Αποκλεισμός Διαγραφή - Άλλαξε πρόσφατα το όνομα προφίλ του/της από %1$s σε %2$s + + Ο χρήστης %1$s άλλαξε πρόσφατα το όνομα του προφίλ του από %2$s σε %3$s + + Ο χρήστης %1$s βρίσκεται στις επαφές του συστήματός σου Ο/Η %1$s μπήκε @@ -4988,6 +5080,12 @@ Πρόσθεσε άτομα και ομάδες από τις οποίες θέλεις να λαμβάνεις ειδοποιήσεις όταν είναι ενεργό αυτό το προφίλ Προσθήκη ατόμων ή ομάδων + + Εξαιρέσεις + + Να επιτρέπονται όλες οι κλήσεις + + Ειδοποίηση για όλες τις αναφορές Προσθήκη @@ -5602,8 +5700,8 @@ Όλες οι επαφές έχουν ελεγχτεί. Πάτα Αποστολή για να συνεχίσεις. - Έχεις %1$d επαφή που μπορεί να έχει εγκαταστήσει εκ νέου το Signal ή να έχει αλλάξει συσκευή. Προτού μοιραστείς την ιστορία σου μαζί της, έλεγξε τους αριθμούς ασφαλείας της. Διαφορετικά, μπορείς να την αφαιρέσεις από την ιστορία σου. - Έχεις %1$d επαφές που μπορεί να έχουν εγκαταστήσει εκ νέου το Signal ή να έχουν αλλάξει συσκευές. Προτού μοιραστείς την ιστορία σου μαζί τους, έλεγξε τους αριθμούς ασφαλείας τους. Διαφορετικά, μπορείς να τις αφαιρέσεις από την ιστορία σου. + Η επαφή %1$d μπορεί να έχει εγκαταστήσει πάλι το Signal ή να έχει αλλάξει συσκευή. Μπορείς να ελέγξεις τον αριθμό ασφαλείας της ή να συνεχίσεις την αποστολή. + Οι επαφές %1$d μπορεί να έχουν εγκαταστήσει πάλι το Signal ή να έχουν αλλάξει συσκευή. Μπορείς να ελέγξεις τον αριθμό ασφαλείας τους ή να συνεχίσεις την αποστολή. Επαλήθευση αριθμού ασφαλείας @@ -5961,12 +6059,18 @@ Ο κωδικός χώρας IBAN δεν υποστηρίζεται Μη έγκυρο IBAN + + Τουλάχιστον 2 χαρακτήρες + + Μη έγκυρη διεύθυνση e-mail iDEAL Πρόσθεσε την τράπεζα, το όνομα και το email σου. Η Stripe χρησιμοποιεί αυτό το email για να σου στέλνει ενημερώσεις σχετικά με τη δωρεά σου. %1$s + + Γράψε τα τραπεζικά σου στοιχεία. Το Signal δεν συλλέγει ούτε αποθηκεύει τα προσωπικά στοιχεία σου. %1$s Μάθε περισσότερα @@ -6152,6 +6256,8 @@ Εξερχόμενη Αναπάντητη + + Αναπάντητη ενώ είναι ενεργό το προφίλ ειδοποιήσεων Σύνδεση @@ -6259,6 +6365,8 @@ Ομαδική κλήση Αναπάντητη ομαδική κλήση + + Αναπάντητη ομαδική κλήση ενώ είναι ενεργό το προφίλ ειδοποιήσεων Εισερχόμενη ομαδική κλήση diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index c7e8c86c53..0fabc88b8b 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -323,8 +323,12 @@ No se puede descargar la imagen. Tendrás que enviarlo de nuevo. No se puede descargar el vídeo Tendrás que enviarlo de nuevo. - - editado %1$s + + Editado ahora + + editado %1$s + + editado %1$s Unirse a la llamada @@ -358,6 +362,8 @@ MMS no seguro Mensaje de Signal + + Enviar mensaje Pasémonos a Molly: %1$s Por favor, selecciona una persona El adjunto excede los límites de tamaño para el mensaje. @@ -1277,6 +1283,10 @@ Llamada perdida Videollamada perdida + + Llamada de voz perdida mientras el perfil de notificación estaba activado + + Videollamada perdida mientras el perfil de notificación estaba activado Has rechazado una llamada @@ -1828,6 +1838,8 @@ Alternar cámara Modificar silenciado + + Acciones adicionales Finalizar llamada @@ -1844,10 +1856,68 @@ Icono que representa el auricular de un dispositivo. + + Levantar la mano + + Levantar la mano + + ¿Bajar la mano? + + Bajar la mano + + Cancelar + + Has levantado la mano + + Ver + + + + %1$s ha levantado la mano + %1$s + %2$d han levantado la mano + + + + + %1$s + %1$s +%2$d + + + + Ampliar vista de mano levantada + + + + Conexión de Signal + + %1$s está entre tus contactos del sistema + + No tienes grupos en común + + Revisa cuidadosamente las solicitudes + + %1$d grupos en común + + Biografía + + + - - En esta llamada · %1$d persona - En esta llamada · %1$d personas + + En esta llamada (%1$d) + En esta llamada (%1$d) + + + Signal llamará a (%1$d) + Signal llamará a (%1$d) + + + Signal notificará a (%1$d) + Signal notificará a (%1$d) + + + Mano levantada (%1$d) + Manos levantadas (%1$d) @@ -1961,6 +2031,12 @@ Visto Adjuntos + + + Se encontró conflicto de nombre + + Ver + No se encontraron resultados para «%1$s» @@ -2127,6 +2203,8 @@ Enviar + + 00 Añadir un alias @@ -2149,6 +2227,12 @@ Omitir Hecho + + Este alias no está disponible, prueba con otro número. + + Alias no válido, ingresa un mínimo de %1$d dígitos. + + Alias no válido, ingresa un máximo de %1$d dígitos. ¡%1$d contacto usa Signal! @@ -2346,6 +2430,10 @@ Procesando… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2370,6 +2458,8 @@ Atendida en dispositivo enlazado. Rechazada en dispositivo enlazado. Ocupado en dispositivo enlazado. + + El botón para cambiar de cámara se ha movido para aquí; pulsa tu vídeo para probarlo Las cifras de seguridad de alguien que se ha unido a esta llamada han cambiado. @@ -3925,14 +4015,16 @@ Fallo al expulsar a alguien del grupo. - Participante Solicitud Tu contacto Expulsar del grupo Actualizar contacto Bloquear Eliminar - Ha cambiado su nombre de perfil recientemente de %1$s a %2$s + + %1$s ha cambiado su nombre de perfil recientemente de %2$s a %3$s + + %1$s está entre tus contactos del sistema %1$s se ha unido @@ -4988,6 +5080,12 @@ Añade personas y grupos para recibir notificaciones con este perfil activo Añade personas o grupos + + Excepciones + + Permitir todas las llamadas + + Notificar al mencionarme Añadir @@ -5602,8 +5700,8 @@ Se han revisado todos los contactos. Toca enviar para continuar. - %1$d de tus contactos puede haber reinstalado Signal o cambiado de dispositivo. Antes de compartir tu historia con esta persona, revisa su número de seguridad o considera retirarle de tu historia. - %1$d de tus contactos pueden haber reinstalado Signal o cambiado de dispositivo. Antes de compartir tu historia con estas personas, revisa sus números de seguridad o considera retirarles de tu historia. + %1$d contacto puede haber reinstalado Signal o cambiado de dispositivo. Revisa su número de seguridad o continua con el envío. + %1$d contactos pueden haber reinstalado Signal o cambiado de dispositivo. Revisa sus números de seguridad o continua con el envío. Verificar cifras de seguridad @@ -5961,12 +6059,18 @@ El código de país en el IBAN no es compatible IBAN no válido + + Mínimo 2 caracteres + + Dirección de correo electrónico no válida iDEAL Introduce tu banco, nombre y correo electrónico. Stripe utiliza este correo electrónico para enviarte actualizaciones sobre tu donación. %1$s + + Introduce tus datos bancarios. Signal no recoge ni almacena tu información personal. %1$s Saber más @@ -6152,6 +6256,8 @@ Saliente Perdida + + Perdida mientras el perfil de notificación estaba activado Unirse @@ -6259,6 +6365,8 @@ Llamada en grupo Llamada grupal perdida + + Llamada de grupo perdida mientras el perfil de notificación estaba activado Llamada grupal entrante diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml index e9646d2207..4661ae56ed 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -310,7 +310,7 @@   Laadi rohkem alla   Ootel See sõnum kustutati. - Sina kustutasid selle teate. + Sa kustutasid selle sõnumi. Sõnumit ei saa alla laadida. %1$s peab selle uuesti saatma. @@ -323,8 +323,12 @@ Pilti ei saa alla laadida. Pead selle uuesti saatma. Videot ei saa alla laadida. Pead selle uuesti saatma. - - Muudetud %1$s eest + + Muudetud just nüüd + + Muudetud %1$s + + Muudetud %1$s Liitu kõnega @@ -358,6 +362,8 @@ Ebaturvaline MMS Signali sõnum + + Saada sõnum Lähme Mollyile üle %1$s Palun vali kontakt Manus ületab saadetava sõnumitüübi suuruspiiranguid. @@ -1277,6 +1283,10 @@ Vastamata häälkõne Vastamata videokõne + + Vastamata häälkõne teavituste profiili sisselülitatuse ajal + + Vastamata videokõne teavituste profiili sisselülitatuse ajal Sa keeldusid häälkõnest @@ -1285,7 +1295,7 @@ %1$s · %2$s %1$s uuendas gruppi. %1$s on Signalis! - Sa keelasid kaduvad sõnumid. + Sa lülitasid kaduvad sõnumid välja. %1$s keelas kaduvad sõnumid. Sa määrasid kaduvate sõnumite taimeriks %1$s. %1$s määras kaduvate sõnumite taimeriks %2$s. @@ -1828,6 +1838,8 @@ Kaamera lülitus Vaigistuse lülitus + + Lisategevused Lõpeta kõne @@ -1844,10 +1856,68 @@ Seadme kuularit tähistav ikoon. + + Tõsta käsi + + Tõsta käsi + + Kas soovid käe langetada? + + Langeta käsi + + Loobu + + Sa tõstsid käe + + Näita + + + + %1$s tõstis käe + %1$s + %2$d tõstsid käe + + + + + %1$s + %1$s + %2$d + + + + Laienda tõstetud käe vaadet + + + + Signali kontakt + + %1$s on sinu süsteemi kontaktide hulgas + + Teil ei ole ühiseid gruppe + + Vaata taotlused tähelepanelikult üle + + %1$d ühist gruppi + + Teave + + Sina + - - Selles kõnes · %1$d inimene - Selles kõnes · %1$d inimest + + Selles kõnes (%1$d) + Selles kõnes (%1$d) + + + Signal helistab isikule (%1$d) + Signal helistab isikutele (%1$d) + + + Signal teavitab isikut (%1$d) + Signal teavitab isikuid (%1$d) + + + Tõstetud käsi (%1$d) + Tõstetud käed (%1$d) @@ -1961,6 +2031,12 @@ Vaadatud Meedia + + + Leiti nimekonflikt + + Näita + Tulemusi ei leitud otsingule \'%1$s\' @@ -2064,7 +2140,7 @@ Korra vaatamise video Korra vaatamise meedia See sõnum kustutati. - Sina kustutasid selle teate. + Sa kustutasid selle sõnumi. Saatsid kutse maksete aktiveerimiseks @@ -2127,6 +2203,8 @@ Saada + + 00 Lisa kasutajanimi @@ -2149,6 +2227,12 @@ Jäta vahele Tehtud + + See kasutajanimi ei ole saadaval, proovi teist numbrit. + + Kehtetu kasutajanimi, sisesta vähemalt %1$d tähemärki. + + Kehtetu kasutajanimi, sisesta maksimaalselt %1$d tähemärki. %1$d kontakt on Signalis! @@ -2346,6 +2430,10 @@ Töötlemine… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2370,6 +2458,8 @@ Vastatud ühendatud seadmes. Keeldutud ühendatud seadmes. Hõivatud ühendatud seadmes. + + Tagakülje kaamera on nüüd siin - toksa oma videot, et seda järele proovida Keegi liitus sellesse kõnesse turvanumbriga, mida on muudetud. @@ -3925,14 +4015,16 @@ Grupi liikme eemaldamine ei õnnestunud. - Liige Taotlus Sinu kontakt Eemalda grupist Uuendatud kontakt Blokeeri Kustuta - Muutis hiljuti enda profiilinime %1$s -> %2$s. + + %1$s muutis hiljuti oma profiili nime %2$s nimeks %3$s + + %1$s on sinu süsteemi kontaktide hulgas %1$s liitus @@ -4988,6 +5080,12 @@ Lisa inimesed ja grupid, kelle teavitusi ja kõnesid tahad läbi lasta, kui see profiil aktiivne on. Lisa inimesi või gruppe + + Erandid + + Luba kõik kõned + + Teavita kõigi mainimiste korral Lisa @@ -5602,8 +5700,8 @@ Kõik kontaktid on kontrollitud, jätkamiseks toksa \"Saada\". - Sul on %1$d kontakt, kes võib olla Signali uuesti paigaldanud või oma seadme välja vahetanud. Enne temaga oma loo jagamist on sul võimalik kontrollida tema turvanumbrit või kaaluda tema eemaldamist oma lugudest. - Sul on %1$d kontakti, kes võivad olla Signali uuesti paigaldanud või oma seadme välja vahetanud. Enne nendega oma loo jagamist on sul võimalik kontrollida nende turvanumbreid või kaaluda nende eemaldamist oma lugudest. + Võimalik, et %1$d kontakt on Signali uuesti paigaldanud või oma seadet vahetanud. Sul on võimalik tema turvanumber üle kontrollida või saatmisega jätkata. + Võimalik, et %1$d kontakti on Signali uuesti paigaldanud või oma seadet vahetanud. Sul on võimalik nende turvanumbrid üle kontrollida või saatmisega jätkata. Kinnita turvanumbrit @@ -5961,12 +6059,18 @@ IBANi riigikoodi ei toetata Kehtetu IBAN + + Vähemalt kaks märki + + Vigane meiliaadress iDEAL Sisesta oma pank, nimi ja meiliaadress. Stripe kasutab seda meiliaadressi sinu annetuse kohta teavituste saatmiseks. %1$s + + Sisesta oma pangaandmed. Signal ei kogu ega salvesta sinu isikuandmeid. %1$s Rohkem teavet @@ -6152,6 +6256,8 @@ Väljuv Vastamata + + Vastamata teavituste profiili sisselülitatuse ajal Liitu @@ -6259,6 +6365,8 @@ Grupikõne Vastamata grupikõne + + Vastamata grupikõne teavituste profiili sisselülitatuse ajal Sissetulev grupikõne diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index 4fc22800b6..e1c24d9e00 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -323,8 +323,12 @@ Ezin da deskargatu irudia. Berriro bidali beharko duzu. Ezin da deskargatu bideoa. Berriro bidali beharko duzu. - - Editatuta (%1$s) + + Orain editatua + + Editatuta (%1$s) + + Editatuta (%1$s) Deian sartu @@ -358,6 +362,8 @@ MMS ez segurua Signal mezua + + Bidali mezua Mollyera aldatu dezagun %1$s Kontaktu bat aukeratu, mesedez Eranskina handiegia da bidaltzen ari zaren mezu motarako. @@ -1277,6 +1283,10 @@ Ahots-dei galdua Bideodei galdua + + Ahots-dei bat baztertu da, jakinarazpen-profila aktibatuta dagoelako + + Bideodei bat baztertu da, jakinarazpen-profila aktibatuta dagoelako Ahots-dei bat baztertu duzu @@ -1828,6 +1838,8 @@ Aktibatu/Desaktibatu kamera Aktibatu/Desaktibatu jakinarazpenak + + Ekintza gehigarriak Amaitu deia @@ -1844,10 +1856,68 @@ Gailu baten entzungailua adierazten duen ikonoa. + + Jaso eskua + + Jaso eskua + + Eskua jaitsi nahi duzu? + + Jaitsi eskua + + Utzi + + Eskua jaso duzu + + Ikusi + + + + %1$s erabiltzaileak eskua jaso du + %1$s + %2$d erabiltzailek eskua jaso dute + + + + + %1$s + %1$s + %2$d + + + + Zabaldu jasotako eskuen ikuspegia + + + + Signal konexioa + + %1$s zure sistemako kontaktuetan dago + + Ez duzue talderik komunean + + Berrikusi eskaerak kontu handiz + + %1$d talde komunean + + Honi buruz + + Zu + - - Dei honetan - pertsona %1$d - Dei honetan - %1$d pertsona + + Dei honetan (%1$d) + Dei honetan (%1$d) + + + Signal-ek tonua joko du (%1$d) + Signal-ek tonua joko du (%1$d) + + + Signal-ek jakinarazpen bat bidaliko du (%1$d) + Signal-ek jakinarazpen bat bidaliko du (%1$d) + + + Esku bat jaso da (%1$d) + Eskuak jaso dira (%1$d) @@ -1961,6 +2031,12 @@ Ikusita Multimedia + + + Arazo bat aurkitu da izenarekin + + Ikusi + Ez dago emaitzarik \'%1$s\' bilatuta @@ -2127,6 +2203,8 @@ Bidali + + 00 Gehitu erabiltzaile-izen bat @@ -2149,6 +2227,12 @@ Saltatu Eginda + + Erabiltzaile-izen hau ez dago erabilgarri; erabili beste zenbaki bat. + + Erabiltzaile-izenak ez du balio; idatzi gutxienez %1$d digitu. + + Erabiltzaile-izenak ez du balio; idatzi gehienez %1$d digitu. Kontaktu %1$d Signalen dago! @@ -2346,6 +2430,10 @@ Prozesatzen… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2370,6 +2458,8 @@ Erantzunda lotutako gailu batean. Ukatuta lotutako gailu batean. Okupatuta lotutako gailu batean. + + Kamera aldatzeko botoia hemen dago orain; probatzeko, sakatu bideoa Dei honetara sartu den norbaiten segurtasun zenbakia aldatu egin da. @@ -3925,14 +4015,16 @@ Ezin izan da kendu taldeko kidea. - Kidea Eskaera Zure kontaktua Kendu taldetik Eguneratu kontaktua Blokeatu Ezabatu - Duela gutxi bere profilaren izena aldatu du. Lehen %1$s zen eta orain %2$s da. + + %1$s erabiltzaileak %3$s ezarri du profileko izen gisa (lehen %2$s zen) + + %1$s zure sistemako kontaktuetan dago %1$s batu da @@ -4988,6 +5080,12 @@ Gehitu profila aktibatuta dagoenean jakinarazpenak eta deiak jaso nahi dituzun pertsonak eta taldeak Gehitu pertsonak edo taldeak + + Salbuespenak + + Baimendu dei guztiak + + Jakinarazi aipamen guztiengatik Gehitu @@ -5602,8 +5700,8 @@ Konexio guztiak berrikusi dira. Aurrera egiteko, sakatu Bidali. - Signal berriro instalatu duen edo gailuz aldatu den %1$d konexio duzu. Zure istorioa harekin partekatu aurretik, berrikusi haren segurtasun zenbakiak edo ken ezazu zure istoriotik. - Signal berriro instalatu duen edo gailuz aldatu den %1$d konexio dituzu. Zure istorioa haiekin partekatu aurretik, berrikusi haien segurtasun zenbakiak edo ken itzazu zure istoriotik. + Baliteke %1$d konexiok Signal berriro instalatu izatea edo gailuz aldatu izatea. Haren segurtasun-zenbakia berrikus dezakezu, edo bidalketarekin aurrera egin. + Baliteke %1$d konexiok Signal berriro instalatu izatea edo gailuz aldatu izatea. Haien segurtasun-zenbakiak berrikus ditzakezu, edo bidalketarekin aurrera egin. Segurtasun zenbakia baieztatu @@ -5961,12 +6059,18 @@ IBANaren herrialde-kodea ez da onartzen IBANak ez du balio + + Gutxienez 2 karaktere + + Helbide elektronikoak ez du balio iDEAL Sartu zure bankua, izena eta helbide elektronikoa. Stripe-k posta elektroniko hau erabiltzen du zure dohaintzari buruzko eguneraketak bidaltzeko. %1$s + + Idatzi bankuaren xehetasunak. Signal-ek ez du biltzen edo gordetzen zure informazio pertsonala. %1$s Informazio gehiago @@ -6152,6 +6256,8 @@ Irteerakoak Galduak + + Jakinarazpen-profilaren ondorioz baztertutakoak Batu @@ -6259,6 +6365,8 @@ Talde-deia Talde-deia galdua + + Taldeko dei bat baztertu da, jakinarazpen-profila aktibatuta dagoelako Sarrerako talde-deia diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index 52102124d6..1b2d0f25fd 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -323,8 +323,12 @@ تصویر بارگیری نمی‌شود. باید دوباره آن را ارسال کنید. ویدیو بارگیری نمی‌شود. باید دوباره آن را ارسال کنید. - - ویرایش‌شده در %1$s + + همین حالا ویرایش شد + + ویرایش‌شده در %1$s + + ویرایش‌شده در %1$s پیوستن به تماس @@ -358,6 +362,8 @@ پیام چندرسانه‌ای ناامن پیام سیگنال + + ارسال پیام بیایید از سیگنال استفاده کنیم: %1$s لطفاً یک مخاطب را انتخاب کنید حجم فایل پیوست شده بیش از اندازۀ مجاز برای این نوع پیامی است که در حال ارسال آن هستید. @@ -1277,6 +1283,10 @@ تماس صوتی ازدست‌رفته تماس تصویری ازدست‌رفته + + تماس صوتی ازدست‌رفته در حالی که نمایۀ اعلان فعال است + + تماس تصویری ازدست‌رفته در حالی که نمایۀ اعلان فعال است یک تماس صوتی را رد کردید @@ -1828,6 +1838,8 @@ تغییر دوربین تغییر حالت صدادار + + اقدامات بیشتر پایان دادن به تماس @@ -1844,10 +1856,68 @@ نماد نشان‌دهنده یک گوشی دستگاه. + + بالا بردن دست + + بالا بردن دست + + دستتان را پایین می‌آورید؟ + + پایین آوردن دست + + لغو + + شما دستتان را بالا بردید + + مشاهده + + + + %1$s دستش را بالا برده است + %1$s + %2$d دستشان را بالا برده‌اند + + + + + %1$s + %1$s +%2$d + + + + گسترش نمای دست‌های بالابرده + + + + آشنا در سیگنال + + %1$s در مخاطبان سیستم شما است + + گروه مشترکی ندارید + + درخواست‌ها را با دقت بازنگری کنید + + %1$d گروه مشترک + + درباره + + شما + - - در این تماس · %1$d نفر - در این تماس · %1$d نفر + + در این تماس (%1$d) + در این تماس (%1$d) + + + سیگنال زنگ خواهد زد (%1$d) + سیگنال زنگ خواهد زد (%1$d) + + + سیگنال اعلان خواهد فرستاد (%1$d) + سیگنال اعلان خواهد فرستاد (%1$d) + + + دستش را بالا برد (%1$d) + دستشان را بالا بردند (%1$d) @@ -1961,6 +2031,12 @@ مشاهده شد رسانه + + + تعارض نام وجود دارد + + مشاهده + هیچ نتیجه‌ای برای \'%1$s\' یافت نشد @@ -2127,6 +2203,8 @@ ارسال + + 00 یک نام کاربری اضافه کنید @@ -2149,6 +2227,12 @@ رد کردن تمام + + این نام کاربری دردسترس نیست، شمارۀ دیگری را امتحان کنید. + + نام کاربری نامعتبر است، حداقل %1$d رقم وارد کنید. + + نام کاربری نامعتبر است، حداکثر %1$d رقم وارد کنید. %1$d مخاطب در سیگنال هست! @@ -2346,6 +2430,10 @@ در حال پردازش… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2370,6 +2458,8 @@ برروی دستگاه پیوند داده شده پاسخ داده شد. بر روی دستگاه پیوند داده شده رد شد. بر روی دستگاه پیوند داده شده مشغول است. + + «برگرداندن دوربین» به اینجا منتقل شده است، برای امتحان کردن آن روی ویدیوی خود ضربه بزنید شخصی با یک شمارهٔ ایمنی تغییر یافته به این تماس پیوسته است. @@ -3925,14 +4015,16 @@ حذف عضو گروه ناموفق بود. - عضو درخواست مخاطب شما حذف از گروه به‌روزرسانی مخاطب مسدود کردن پاک کردن - به‌تازگی نام پروفایل خود را از %1$s به %2$s تغییر داد + + %1$s اخیراً نام پروفایل خود را از %2$s به %3$s تغییر داده است + + %1$s در مخاطبان سیستم شما است %1$s به تماس پیوست @@ -4988,6 +5080,12 @@ افراد و گروه‌هایی را که اعلان‌ها و تماس‌های آنان را هنگامی که این پروفایل روشن است، اضافه کنید افزودن افراد یا گروه‌ها + + موارد استثناء + + اجازه به همۀ تماس‌ها + + اعلان برای همۀ اشاره‌ها افزودن @@ -5602,8 +5700,8 @@ تمام آشنایان بررسی شده‌اند، برای ادامه روی ارسال ضربه بزنید. - شما %1$d آشنا دارید که ممکن است سیگنال را دوباره نصب کرده یا دستگاه خود را تغییر داده باشد. قبل از به اشتراک گذاشتن استوری خود با او، شماره ایمنی‌اش را بررسی کنید یا او را از استوری‌تان حذف کنید. - شما %1$d آشنا دارید که ممکن است سیگنال را دوباره نصب کرده یا دستگاه خود را تغییر داده باشند. قبل از هم‌رسانی استوری خود با آن‌ها، شماره ایمنی‌شان را بررسی کنید یا آن‌ها را از استوری‌تان حذف کنید. + ‏%1$d آشنا ممکن است سیگنال را بازنصب یا دستگاهش را عوض کرده باشد. می‌توانید شمارۀ ایمنی او را بررسی کنید یا به ارسال ادامه دهید. + ‏%1$d آشنا ممکن است سیگنال را بازنصب یا دستگاهشان را عوض کرده باشند. می‌توانید شماره ایمنی آن‌ها را بررسی کنید یا به ارسال ادامه دهید. وارسی شمارهٔ ایمنی @@ -5961,12 +6059,18 @@ کد کشور IBAN پشتیبانی نمی‌شود شمارۀ IBAN نامعتبر است + + حداقل ۲ نویسه + + آدرس ایمیل نامعتبر iDEAL بانک، نام و ایمیل خود را وارد کنید. Stripe برای ارسال به‌روزرسانی‌ها درباره کمک مالی‌تان، از این ایمیل استفاده می‌کند. %1$s + + مشخصات بانکی خود را وارد کنید. سیگنال اطلاعات شخصی شما را جمع آوری یا ذخیره نمی‌کند. %1$s اطلاعات بیشتر @@ -6152,6 +6256,8 @@ خروجی ازدست‌رفته + + ازدست‌رفته درحالی که نمایۀ اعلان فعال است پیوستن @@ -6259,6 +6365,8 @@ تماس گروهی تماس گروهی ازدست‌رفته + + تماس گروهی ازدست‌رفته درحالی که نمایۀ اعلان فعال است تماس گروهی ورودی diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 1de7cda593..71808fd9ef 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -323,8 +323,12 @@ Kuvaa ei voi ladata. Sinun on lähetettävä se uudelleen. Videota ei voi ladata. Sinun on lähetettävä se uudelleen. - - Muokattu %1$s + + Muokattu nyt + + Muokattu %1$s + + Muokattu %1$s Liity puheluun @@ -358,6 +362,8 @@ Salaamaton MMS-viesti Signal-viesti + + Lähetä viesti Hei, vaihdetaan Mollyiin %1$s Valitse yhteystieto Liitetiedosto ylittää lähettämäsi viestityypin kokorajoituksen. @@ -1277,6 +1283,10 @@ Vastaamaton äänipuhelu Vastaamaton videopuhelu + + Vastaamaton äänipuhelu ilmoitusprofiilin ollessa käytössä + + Vastaamaton videopuhelu ilmoitusprofiilin ollessa käytössä Hylkäsit äänipuhelun @@ -1828,6 +1838,8 @@ Kamera käyttöön / pois käytöstä Mykistys käyttöön / pois käytöstä + + Lisää toimintoja Lopeta puhelu @@ -1844,10 +1856,68 @@ Laitteen kuuloketta esittävä kuvake. + + Nosta käsi + + Nosta käsi + + Laske käsi? + + Laske käsi + + Peruuta + + Nostit kätesi + + Näytä + + + + %1$s nosti kätensä + %1$s ja %2$d nostivat kätensä + + + + + %1$s + %1$s + %2$d + + + + Laajenna nostettujen käsien näkymä + + + + Signal-kontaktit + + %1$s on järjestelmän yhteystiedoissa + + Teillä ei ole yhteisiä ryhmiä + + Tarkista pyynnöt huolellisesti + + %1$d yhteistä ryhmää + + Tiedot + + Sinä + - - Tässä puhelussa · %1$d henkilö - Tässä puhelussa · %1$d henkilöä + + Tässä puhelussa (%1$d) + Tässä puhelussa (%1$d) + + + Signal soittaa käyttäjälle (%1$d) + Signal soittaa käyttäjille (%1$d) + + + Signaali ilmoittaa käyttäjälle (%1$d) + Signaali ilmoittaa käyttäjille (%1$d) + + + Käsi nostettu: (%1$d) + Käsiä nostettu: (%1$d) @@ -1961,6 +2031,12 @@ Katsottu Media + + + Löytyi nimiristiriita + + Näytä + Ei tuloksia haulle %1$s @@ -2127,6 +2203,8 @@ Lähetä + + 00 Lisää käyttäjänimi @@ -2149,6 +2227,12 @@ Ohita Valmis + + Tämä käyttäjänimi ei ole saatavilla. Kokeile toista numeroa. + + Virheellinen käyttäjänimi, anna vähintään %1$d numeroa. + + Virheellinen käyttäjänimi, anna vähintään %1$d numeroa. Signalissa on %1$d kontakti! @@ -2346,6 +2430,10 @@ Käsitellään… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2370,6 +2458,8 @@ Vastattu yhdistetyllä laitteella. Hylätty yhdistetyllä laitteella. Varattu yhdistetyllä laitteella. + + Kameran vaihto on nyt täällä. Kokeile napauttamalla videota Joku on liittynyt tähän puheluun vaihtuneen turvanumeron kanssa. @@ -3925,14 +4015,16 @@ Ryhmän jäsenen poisto epäonnistui. - Jäsen Pyyntö Yhteystietosi Poista ryhmästä Päivitä yhteystieto Estä Poista - Muutti äskettäin profiilinimensä %1$s nimeksi %2$s. + + %1$s on äskettäin vaihtanut profiilinsa nimen, ennen: %2$s, nyt: %3$s + + %1$s on järjestelmän yhteystiedoissa %1$s liittyi @@ -4988,6 +5080,12 @@ Lisää yhteystietoja ja ryhmiä, joilta haluat ilmoituksia ja puheluita, kun tämä profiili on aktiivinen. Lisää yhteystietoja tai ryhmiä + + Poikkeukset + + Salli kaikki puhelut + + Ilmoita kaikista maininnoista Lisää @@ -5602,8 +5700,8 @@ Kaikki kontaktit on tarkistettu. Jatka napauttamalla. - Sinulla on %1$d kontakti, joka on ehkä asentanut Signalin uudelleen tai vaihtanut käyttämäänsä laitetta. Voit halutessasi tarkistaa hänen turvanumeronsa ennen lähettämistä. - Sinulla on %1$d kontaktia, jotka ovat ehkä asentaneet Signalin uudelleen tai vaihtaneet käyttämäänsä laitetta. Voit halutessasi tarkistaa heidän turvanumeronsa ennen lähettämistä. + %1$d kontakti on ehkä asentanut Signalin uudelleen tai vaihtanut käyttämäänsä laitetta. Voit tarkistaa hänen turvanumeronsa ennen lähettämistä. + %1$d kontaktia on ehkä asentanut Signalin uudelleen tai vaihtanut käyttämiänsä laitteita. Voit tarkistaa heidän turvanumeronsa ennen lähettämistä. Varmenna turvanumero @@ -5961,12 +6059,18 @@ IBAN-maakoodia ei tueta Virheellinen IBAN-tilinumero + + Vähintään 2 merkkiä + + Virheellinen sähköpostiosoite iDEAL Anna pankkitietosi, nimesi ja sähköpostiosoitteesi. Stripe käyttää tätä sähköpostiosoitetta lähettääkseen sinulle päivityksiä lahjoituksestasi. %1$s + + Anna pankkitietosi. Signal ei kerää tai tallenna henkilötietojasi. %1$s Lue lisää @@ -6152,6 +6256,8 @@ Lähtevä Vastaamaton + + Jäi huomaamatta ilmoitusprofiilin ollessa käytössä Liity @@ -6259,6 +6365,8 @@ Ryhmäpuhelu Vastaamaton ryhmäpuhelu + + Vastaamaton ryhmäpuhelu ilmoitusprofiilin ollessa käytössä Saapuva ryhmäpuhelu diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index c864225cfd..575ff31de5 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -323,8 +323,12 @@ Impossible de télécharger l’image. {0} doit l’envoyer à nouveau. Impossible de télécharger la vidéo. {0} doit l’envoyer à nouveau. - - Modifié il y a %1$s + + Modifié à l\'instant + + Modifié il y a %1$s minutes + + Modifié à %1$s Me joindre à l’appel @@ -358,6 +362,8 @@ Message multimédia non sécurisé Message Signal + + Envoyer un message Passons à Molly %1$s Veuillez choisir un contact Le fichier joint dépasse la limite de taille pour le type de message que vous envoyez. @@ -1277,6 +1283,10 @@ Appel vocal manqué Appel vidéo manqué + + Appel vocal manqué lorsque le profil de notification était activé + + Appel vidéo manqué lorsque le profil de notification était activé Vous avez refusé un appel vocal @@ -1828,6 +1838,8 @@ Activer/désactiver l’appareil photo Activer/désactiver le son + + Actions supplémentaires Raccrocher @@ -1844,10 +1856,68 @@ L’icône de l’écouteur d’un appareil. + + Lever la main + + Lever la main + + Baisser la main ? + + Baisser la main + + Annuler + + Vous avez levé la main + + Afficher + + + + %1$s a levé la main + %1$s + %2$d ont levé la main + + + + + %1$s + %1$s + %2$d + + + + Agrandir lla liste des personnes ayant levé la main + + + + Connexions Signal + + %1$s se trouve dans vos contacts + + Aucun groupe en commun + + Examinez les demandes avec attention + + %1$d groupes en commun + + À propos + + Vous + - - Dans cet appel · %1$d personne - Dans cet appel · %1$d personnes + + Participant (%1$d) + Participants (%1$d) + + + Signal appellera (%1$d) + Signal appellera (%1$d) + + + Signal notifiera (%1$d) + Signal notifiera (%1$d) + + + Main levée (%1$d) + Mains levées (%1$d) @@ -1961,6 +2031,12 @@ Visualisé Médias + + + Conflit de noms trouvé + + Afficher + Aucun résultat n’a été trouvé pour « %1$s » @@ -2127,6 +2203,8 @@ Envoyer + + 00 Ajouter un nom d\'utilisateur @@ -2149,6 +2227,12 @@ Ignorer Terminé + + Nom d’utilisateur indisponible, veuillez essayer un autre numéro. + + Nom d’utilisateur non valide. Veuillez saisir au moins %1$d chiffres. + + Nom d’utilisateur non valide. Veuillez saisir %1$d chiffres maximum. %1$d contact est sur Signal ! @@ -2346,6 +2430,10 @@ Compression en cours… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2370,6 +2458,8 @@ L’appel a été pris sur un appareil relié. L’appel a été refusé sur un appareil relié. Occupé sur un appareil relié. + + Le bouton pour changer de caméra se trouve désormais ici. Appuyez-le pour l’essayer. Quelqu’un s’est joint à cet appel avec un numéro de sécurité qui a changé. @@ -3925,14 +4015,16 @@ Échec du retrait du membre du groupe. - Membre Demande Votre contact Retirer du groupe Mettre le contact à jour Bloquer Supprimer - A récemment changé son nom de profil de %1$sà %2$s + + %1$s a récemment changé son nom de profil de %2$s à %3$s + + %1$s se trouve dans vos contacts %1$s s’est joint @@ -4988,6 +5080,12 @@ Ajoutez les personnes et les groupes dont vous souhaitez recevoir des notifications et des appels quand ce profil est activé Ajouter des personnes ou des groupes + + Exceptions + + Autoriser tous les appels + + Notifier de toutes les mentions Ajouter @@ -5602,8 +5700,8 @@ Toutes les connexions ont été revues, appuyez sur envoyer pour continuer. - %1$d de vos contacts a sûrement réinstallé Signal ou changé d’appareil. Avant de partager votre Story avec ces personnes, vérifiez leurs numéros de sécurité ou excluez-les de votre partage. - %1$d de vos contacts ont sûrement réinstallé Signal ou changé d’appareil. Avant de partager votre Story avec ces personnes, vérifiez leurs numéros de sécurité ou excluez-les de votre partage. + %1$d contact a peut-être réinstallé Signal ou changé d’appareil. Vérifiez son numéro de sécurité ou procédez à l’envoi. + %1$d contacts ont peut-être réinstallé Signal ou changé d’appareil. Vérifiez leur numéro de sécurité ou procédez à l’envoi. Confirmer le numéro de sécurité @@ -5961,12 +6059,18 @@ Code pays de l’IBAN non pris en charge IBAN invalide + + 2 caractères minimum + + Adresse e-mail invalide iDEAL Saisissez vos coordonnées bancaires, votre nom et votre adresse e-mail. Stripe utilisera cette adresse e-mail pour vous tenir informé du statut de votre don. %1$s + + Veuillez saisir vos coordonnées bancaires. Signal ne collecte ni ne sauvegarde vos informations personnelles. %1$s En savoir plus @@ -6152,6 +6256,8 @@ Sortant Manqué + + Appel manqué lorsque le profil de notification était activé Rejoindre @@ -6259,6 +6365,8 @@ Appel de groupe Appel de groupe manqué + + Appel de groupe manqué lorsque le profil de notification était activé Appel de groupe entrant diff --git a/app/src/main/res/values-ga/strings.xml b/app/src/main/res/values-ga/strings.xml index 6d213ea13d..dbe570e10f 100644 --- a/app/src/main/res/values-ga/strings.xml +++ b/app/src/main/res/values-ga/strings.xml @@ -75,7 +75,7 @@ (físeán) (suíomh) (freagair) - (Teachtaireacht bhéil) + (Glórphost) Gailearaí @@ -326,8 +326,12 @@ Ní féidir an íomhá a íoslódáil. Beidh ort í a sheoladh arís. Ní féidir an físeán a íoslódáil. Beidh ort é a sheoladh arís. - - Curtha in eagar %1$s + + Curtha in eagar Anois + + Curtha in eagar %1$s + + Curtha in eagar %1$s Téigh isteach sa ghlao @@ -367,6 +371,8 @@ MMS easlán Signal + + Seol teachtaireacht Téimís i muinín Molly %1$s Roghnaigh teagmhálaí Tá an ceangaltán ró-mhór don teachtaireacht atá á sheoladh agat. @@ -1409,6 +1415,10 @@ Guthghlao caillte Físghlao caillte + + Guthghlao caillte agus an phróifíl fógraí casta uirthi + + Físghlao caillte agus an phróifíl fógraí casta uirthi Dhiúltaigh tú do ghuthghlao @@ -1697,7 +1707,7 @@ Lig do %1$s teachtaireachtaí a sheoladh chugat? Ní bhfaighidh tú aon teachtaireachtaí go dtí go mbainfidh tú an bac de/di. Faigh nuashonruithe agus nuacht ó %1$s? Ní bhfaighidh tú aon nuashonruithe go dtí go mbainfidh tú an bac de/di. Continue your chat with this group and share your name and photo with its members? - This Legacy Group can no longer be used. Create a new group to activate new features like @mentions and admins. + Ní féidir an Grúpa Oidhreachta seo a úsáid a thuilleadh. Cruthaigh grúpa nua le gnéithe nua a ghníomhachtú, amhail @tráchtanna agus riarthóirí. Ní féidir an seanleagan seo den Ghrúpa a úsáid a thuilleadh toisc go bhfuil sé rómhór. Is í %1$d uasmhéid an ghrúpa. Lean le do chomhrá le %1$s agus comhroinn d\'ainm agus grianghraf leis an duine sin? Téigh isteach sa ghrúpa seo agus comhroinn d\'ainm agus grianghraf lena bhaill? Ní bheidh a fhios acu go bhfaca tú na teachtaireachtaí go dtí go nglacfaidh tú. @@ -2041,6 +2051,8 @@ Scoránaigh ceamara Scoránaigh balbhú + + Gníomhartha breise Cuir deireadh leis an nglao @@ -2057,13 +2069,86 @@ Deilbhín a léiríonn cluaisín gléis. + + Ardaigh Lámh + + Ardaigh Lámh + + Ísligh do lámh? + + Ísligh Lámh + + Cuir ar ceal + + D\'ardaigh tú do lámh + + AMHARC + + + + D\'ardaigh %1$s lámh + D\'ardaigh %1$s + %2$d lámh + D\'ardaigh %1$s + %2$d lámh + D\'ardaigh %1$s + %2$d lámh + D\'ardaigh %1$s + %2$d lámh + + + + + %1$s + %1$s + %2$d + %1$s + %2$d + %1$s + %2$d + %1$s + %2$d + + + + Fairsingigh amharc ar lámha ardaithe + + + + Nasc Signal + + Tá %1$s i dteagmhálaithe do chórais + + Níl aon ghrúpaí i bpáirt agat + + Déan iarratais a athbhreithniú go cúramach + + %1$d grúpa i bpáirt + + Maidir le + + Tusa + - - Sa ghlao seo · %1$d duine - Sa ghlao seo · %1$d dhuine - Sa ghlao seo · %1$d dhuine - Sa ghlao seo · %1$d nduine - Sa ghlao seo · %1$d duine + + Sa ghlao seo (%1$d) + Sa ghlao seo (%1$d) + Sa ghlao seo (%1$d) + Sa ghlao seo (%1$d) + Sa ghlao seo (%1$d) + + + Cuirfidh Signal glao ar (%1$d) + Cuirfidh Signal glao ar (%1$d) + Cuirfidh Signal glao ar (%1$d) + Cuirfidh Signal glao ar (%1$d) + Cuirfidh Signal glao ar (%1$d) + + + Cuirfidh Signal (%1$d) ar an eolas + Cuirfidh Signal (%1$d) ar an eolas + Cuirfidh Signal (%1$d) ar an eolas + Cuirfidh Signal (%1$d) ar an eolas + Cuirfidh Signal (%1$d) ar an eolas + + + Lámh ardaithe (%1$d) + Lámha ardaithe (%1$d) + Lámha ardaithe (%1$d) + Lámha ardaithe (%1$d) + Lámha ardaithe (%1$d) @@ -2180,6 +2265,12 @@ Feicthe Meáin + + + Coimhlint in ainm aimsithe + + AMHARC + Níor aimsíodh aon torthaí le haghaidh \'%1$s\' @@ -2306,7 +2397,7 @@ Tá an duine sin folaithe agat, seol teachtaireacht arís le cur ar ais ar do liosta é/í. Grianghraf GIF - Teachtaireacht Ghutha + Glórphost Comhad Físeán Chat session refreshed @@ -2346,6 +2437,8 @@ Seol + + 00 Cuir ainm úsáideora leis @@ -2368,6 +2461,12 @@ Léim thar seo Déanta + + Níl an t-ainm úsáideora seo ar fáil, triail uimhir eile. + + Ainm úsáideora neamhbhailí, cuir isteach íoslíon de %1$d dhigit. + + Ainm úsáideora neamhbhailí, cuir isteach uaslíon de %1$d dhigit. Tá %1$d teagmhálaí ar Signal! @@ -2577,6 +2676,10 @@ Próiseáil… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2601,6 +2704,8 @@ Freagartha ar ghléas nasctha. Diúltaithe dó ar ghléas nasctha. Gnóthach ar ghléas nasctha. + + Bogadh an Smeach-cheamara anseo, tapáil d\'fhíseán lena thriail Someone has joined this call with a safety number that has changed. @@ -4193,7 +4298,7 @@ Theip ar sheinm an ghlórphoist - Teachtaireacht bhéil · %1$s + Glórphost · %1$s %1$s chuig %2$s @@ -4228,14 +4333,16 @@ Theip ar bhaint baill den ghrúpa. - Ball Iarratas Do theagmhálaí Bain den ghrúpa Nuashonraigh an teagmhálaí Cuir bac Scrios - D\'athraigh sé/sí a ainm/hainm próifíle ó %1$s go %2$s. + + D\'athraigh %1$s ainm próifíle ó %2$s go %3$s le déanaí + + Tá %1$s i dteagmhálaithe do chórais Chuaigh %1$s isteach ann @@ -5318,6 +5425,12 @@ Cuir leis daoine agus grúpaí is mian leat fógraí agus glaonna a fháil uathu agus an phróifíl seo air Cuir daoine nó grúpaí leis + + Eisceachtaí + + Ceadaigh gach glao + + Cuir in iúl do gach trácht Cuir leis @@ -5983,11 +6096,11 @@ Rinneadh athbhreithniú ar na teagmhálaithe go léir, tapáil Seol chun leanúint ar aghaidh. - Tá %1$d teagmhálaí amháin agat a bhfuil seans ann gur athshuiteáil sé/sí Signal nó gur athraigh sé/sí an gléas. Sula gcomhroinneann tú do scéal, déan athbhreithniú ar uimhreacha sábháilteachta an duine sin nó smaoinigh ar an duine sin a bhaint de do scéal. - Tá %1$d theagmhálaí agat a bhfuil seans ann gur athshuiteáil siad Signal nó gur athraigh siad an gléas. Sula gcomhroinneann tú do scéal, déan athbhreithniú ar uimhreacha sábháilteachta na ndaoine sin nó smaoinigh ar na daoine sin a bhaint de do scéal. - Tá %1$d theagmhálaí agat a bhfuil seans ann gur athshuiteáil siad Signal nó gur athraigh siad an gléas. Sula gcomhroinneann tú do scéal, déan athbhreithniú ar uimhreacha sábháilteachta na ndaoine sin nó smaoinigh ar na daoine sin a bhaint de do scéal. - Tá %1$d dteagmhálaí agat a bhfuil seans ann gur athshuiteáil siad Signal nó gur athraigh siad an gléas. Sula gcomhroinneann tú do scéal, déan athbhreithniú ar uimhreacha sábháilteachta na ndaoine sin nó smaoinigh ar na daoine sin a bhaint de do scéal. - Tá %1$d teagmhálaí agat a bhfuil seans ann gur athshuiteáil siad Signal nó gur athraigh siad an gléas. Sula gcomhroinneann tú do scéal, déan athbhreithniú ar uimhreacha sábháilteachta na ndaoine sin nó smaoinigh ar na daoine sin a bhaint de do scéal. + Is féidir go ndearna %1$d teagmhálaí Signal a athshuiteáil nó gléas a athrú. Is féidir leat an uimhir sábháilteachta a athbhreithniú nó leanúint leis an seoladh. + Is féidir go ndearna %1$d theagmhálaí Signal a athshuiteáil nó gléas a athrú. Is féidir leat na huimhreacha sábháilteachta a athbhreithniú nó leanúint leis an seoladh. + Is féidir go ndearna %1$d theagmhálaí Signal a athshuiteáil nó gléas a athrú. Is féidir leat na huimhreacha sábháilteachta a athbhreithniú nó leanúint leis an seoladh. + Is féidir go ndearna %1$d dteagmhálaí Signal a athshuiteáil nó gléas a athrú. Is féidir leat na huimhreacha sábháilteachta a athbhreithniú nó leanúint leis an seoladh. + Is féidir go ndearna %1$d teagmhálaí Signal a athshuiteáil nó gléas a athrú. Is féidir leat na huimhreacha sábháilteachta a athbhreithniú nó leanúint leis an seoladh. Fíordheimhnigh an uimhir shábháilteachta @@ -6357,12 +6470,18 @@ Cód tíortha IBAN gan tacú IBAN neamhbhailí + + 2 charachtar ar a laghad + + Seoladh ríomhphoist neamhbhailí iDEAL Cuir isteach do bhanc, d\'ainm agus do sheoladh ríomhphoist. Úsáideann Stripe an seoladh ríomhphoist seo le nuashonruithe a sheoladh chugat maidir le do thabhartas. %1$s + + Cuir isteach do shonraí bainc. Ní dhéanann Signal d\'fhaisnéis phearsanta a bhailiú ná a stóráil. %1$s Tuilleadh faisnéise @@ -6551,6 +6670,8 @@ Amach Caillte + + Caillte agus an phróifíl fógraí casta uirthi Téigh isteach ann @@ -6667,6 +6788,8 @@ Glao grúpa Grúpghlao caillte + + Grúpghlao caillte agus an phróifíl fógraí casta uirthi Grúpghlao isteach diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index c0b68f3824..3e2bf57d50 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -323,8 +323,12 @@ Non se pode descargar a imaxe. Terás que reenviala. Non se pode descargar o vídeo. Terás que reenvialo. - - Editada hai %1$s + + Editada agora + + Editada hai %1$s + + Editada hai %1$s Unirse á chamada @@ -358,6 +362,8 @@ MMS insegura Mensaxe de Signal + + Enviar mensaxe Conversa comigo en Molly %1$s Por favor, elixe un contacto O anexo excede o límite para o tipo de mensaxe que estás a enviar. @@ -1277,6 +1283,10 @@ Chamada de voz perdida Chamada de vídeo perdida + + Chamada de voz perdida mentres o perfil de notificación estaba activado + + Videochamada perdida mentres o perfil de notificación estaba activado Rexeitaches unha chamada de voz @@ -1828,6 +1838,8 @@ Activar cámara Activar modo sen son + + Accións adicionais Finalizar chamada @@ -1844,10 +1856,68 @@ Unha icona que representa un dispositivo con auricular. + + Levantar man + + Levantar man + + Queres baixar a man? + + Baixar a man + + Cancelar + + Alzaches a man + + Ver + + + + %1$s alzou a man + %1$s + %2$d alzaron a man + + + + + %1$s + %1$s + %2$d + + + + Ampliar a lista de mans levantadas + + + + Conexións de Signal + + %1$s está na túa lista de contactos + + Non tedes grupos en común + + Revisa as solicitudes con atención + + %1$d grupos en común + + Acerca de + + Ti + - - Nesta chamada · %1$d persoa - Nesta chamada · %1$d persoas + + Nesta chamada (%1$d) + Nesta chamada (%1$d) + + + Signal chamará a (%1$d) + Signal chamará a (%1$d) + + + Signal notificará a (%1$d) + Signal notificará a (%1$d) + + + Mans alzadas (%1$d) + Mans alzadas (%1$d) @@ -1961,6 +2031,12 @@ Vista Ficheiros multimedia + + + Atopouse un conflito de nomes + + Ver + Ningún resultado atopado para \'%1$s\' @@ -2127,6 +2203,8 @@ Enviar + + 00 Engadir un nome de usuario @@ -2149,6 +2227,12 @@ Omitir Feito + + Este nome de usuario non está dispoñible, tenta con outro número. + + Nome de usuario non válido, insire un mínimo de %1$d díxitos. + + Nome de usuario non válido, insire un máximo de %1$d díxitos. %1$dcontacto está en Signal! @@ -2346,6 +2430,10 @@ Procesando… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2370,6 +2458,8 @@ Respondido nun dispositivo ligado. Rexeitado nun dispositivo ligado. Ocupado desde un dispositivo ligado. + + «Cambiar cámara» agora está aquí, preme na túa ventá para probalo Alguén xuntouse nesta chamada cun número de seguridade que cambiou. @@ -3925,14 +4015,16 @@ Erro ao eliminar un membro do grupo. - Membro Solicitude O teu contacto Eliminar do grupo Actualizar contacto Bloquear Eliminar - Cambiou recentemente o seu nome de perfil de %1$s a %2$s + + %1$s cambiou recentemente o seu nome de perfil de %2$s a %3$s + + %1$s está na túa lista de contactos %1$suniuse @@ -4988,6 +5080,12 @@ Engade persoas e grupos dos que queiras recibir notificacións e chamadas cando este perfil estea activo Engadir persoas ou grupos + + Agás + + Permitir todas as chamadas + + Notificar todas as mencións Engadir @@ -5602,8 +5700,8 @@ Revisáronse todas as conexións, preme «Enviar» para continuar. - Tes %1$d conexión que pode que volvese instalar Signal ou que cambiase de dispositivo. Se queres, podes revisar o seu número de seguranza antes de enviar nada. - Tes %1$d conexións que pode que volvesen instalar Signal ou que cambiasen de dispositivos. Se queres, podes revisar os seus números de seguranza antes de enviar nada. + Tes %1$d conexión que pode que volvese instalar Signal ou que cambiase de dispositivo. Se queres, podes revisar o seu número de seguranza ou continuar co envío. + Tes %1$d conexións que pode que volvesen instalar Signal ou que cambiasen de dispositivo. Se queres, podes revisar o seus números de seguranza ou continuar co envío. Comprobar número de seguranza @@ -5961,12 +6059,18 @@ O código de país do IBAN non se admite IBAN incorrecto + + Mínimo 2 caracteres + + Enderezo electrónico incorrecto iDEAL Insire o teu banco, nome e enderezo electrónico. Stripe emprega ese enderezo electrónico para enviarche actualizacións da túa doazón. %1$s + + Insire os teus datos bancarios. Signal non recolle nin almacena a túa información persoal. %1$s Máis información @@ -6152,6 +6256,8 @@ Saínte Perdida + + Perdida mentres o perfil de notificación estaba activado Unirse @@ -6259,6 +6365,8 @@ Chamada en grupo Chamada de grupo perdida + + Chamada de grupo perdida mentres o perfil de notificación estaba activado Chamada de grupo entrante diff --git a/app/src/main/res/values-gu/strings.xml b/app/src/main/res/values-gu/strings.xml index 65145e829d..ce55ebab07 100644 --- a/app/src/main/res/values-gu/strings.xml +++ b/app/src/main/res/values-gu/strings.xml @@ -323,8 +323,12 @@ ઇમેજ ડાઉનલોડ કરી શકતા નથી. તમારે તેને ફરીથી મોકલવાની જરૂર છે. વીડિયો ડાઉનલોડ કરી શકતા નથી. તમારે તેને ફરીથી મોકલવાની જરૂર છે. - - %1$s વાગ્યે ફેરફાર કર્યો + + હમણાં જ ફેરફાર કરેલ + + %1$s વાગ્યે ફેરફાર કર્યો + + %1$s વાગ્યે ફેરફાર કર્યો કૉલમાં જોડાઓ @@ -358,6 +362,8 @@ અસુરક્ષિત MMS Signal મેસેજ + + મેસેજ મોકલો ચાલો સિગ્નલ પર સ્વિચ કરીએ %1$s કૃપા કરીને સંપર્ક પસંદ કરો તમે મોકલો છો તે મેસેજ ના પ્રકાર માટે જોડાણ કદની મર્યાદાથી વધુ છે. @@ -1277,6 +1283,10 @@ મિસ્ડ વૉઇસ કૉલ મિસ્ડ વિડિયો કૉલ + + નોટિફિકેશન પ્રોફાઇલ ચાલુ હતી ત્યારે વૉઇસ કૉલ મિસ થયો + + નોટિફિકેશન પ્રોફાઇલ ચાલુ હતી ત્યારે વીડિયો કૉલ મિસ થયો તમે વૉઇસ કૉલ નકાર્યો @@ -1828,6 +1838,8 @@ કૅમેરા ચાલુ-બંધ કરો મ્યૂટ ચાલુ-બંધ કરો + + વધારાની ક્રિયાઓ કૉલ પૂરો કરો @@ -1844,10 +1856,68 @@ ડિવાઇસના ઇયરપીસને દર્શાવતું આઇકન. + + હાથ ઊંચો કરો + + હાથ ઊંચો કરો + + તમારો હાથ નીચો કરવો છે? + + હાથ નીચે કરો + + રદ કરો + + તમે તમારો હાથ ઊંચો કર્યો + + જુઓ + + + + %1$sએ હાથ ઊંચો કર્યો + %1$s + %2$d એ હાથ ઊંચા કર્યા + + + + + %1$s + %1$s +%2$d + + + + હાથ ઊંચા કરેલા વ્યૂને વિસ્તૃત કરો + + + + Signal કનેક્શન + + %1$s તમારા સિસ્ટમના સંપર્કોમાં છે + + તમે કોઈ ગ્રૂપમાં સાથે નથી + + વિનંતીઓની કાળજીપૂર્વક રિવ્યુ કરો + + %1$d ગ્રુપ એકબીજામાં સમાન છે + + પરિચય + + તમે + - - આ કૉલમાં · %1$d લોકો - આ કૉલમાં · %1$d લોકો + + આ કૉલમાં (%1$d) + આ કૉલમાં (%1$d) + + + Signal (%1$d)ને રિંગ કરશે + Signal (%1$d)ને રિંગ કરશે + + + Signal (%1$d)ને સૂચિત કરશે + Signal (%1$d)ને સૂચિત કરશે + + + હાથ ઊંચો કર્યો (%1$d) + હાથ ઊંચા કર્યા (%1$d) @@ -1961,6 +2031,12 @@ જોયેલુ મીડિયા + + + નામનો વિરોધાભાસ મળ્યો + + જુઓ + \'%1$s\' માટે કોઈ પરિણામ મળ્યું નથી @@ -2127,6 +2203,8 @@ મોકલો + + 00 વપરાશકર્તાનામ ઉમેરો @@ -2149,6 +2227,12 @@ અવગણો થઈ ગયું + + આ યુઝરનેમ ઉપલબ્ધ નથી, અન્ય નંબર અજમાવી જુઓ. + + અમાન્ય યુઝરનેમ, ઓછામાં ઓછા %1$d અંકો દાખલ કરો. + + અમાન્ય યુઝરનેમ, વધુમાં વધુ %1$d અંકો દાખલ કરો. %1$dસંપર્ક Signal પર છે! @@ -2346,6 +2430,10 @@ પ્રક્રિયા કરી રહ્યા છીએ… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2370,6 +2458,8 @@ લિંક્ડ ડિવાઇસ પર જવાબ આપ્યો. લિંક્ડ ડિવાઇસ પર નકારી. લિંક્ડ ડિવાઇસ પર વ્યસ્ત. + + ફ્લિપ કૅમેરા અહીં ખસેડવામાં આવ્યો છે, તેને અજમાવવા માટે તમારી વીડિયોને ટેપ કરો કોઈએ આ કૉલને સલામતી નંબર સાથે જોડ્યો છે જે બદલાઈ ગયો છે. @@ -3925,14 +4015,16 @@ ગ્રુપ સભ્યને દૂર કરવામાં નિષ્ફળ. - સભ્ય વિનંતી તમારા સંપર્ક ગ્રુપમાંથી દૂર કરો સંપર્કને અપડેટ કરો બ્લૉક કરો ડિલીટ કરો - તાજેતરમાં જ તેમનું પ્રોફાઇલ નામ %1$s માંથી %2$s માં બદલ્યું. + + %1$sએ તાજેતરમાં તેમનું પ્રોફાઇલ નામ %2$s થી બદલીને %3$s કર્યું + + %1$s તમારા સિસ્ટમના સંપર્કોમાં છે %1$s જોડાયા @@ -4988,6 +5080,12 @@ જ્યારે આ પ્રોફાઇલ ચાલુ હોય ત્યારે જે લોકો અને ગ્રુપ પાસેથી તમને સૂચનો જોઈતી હોય તેમને ઉમેરો લોકો અથવા ગ્રુપ ઉમેરો + + અપવાદો + + બધા કૉલને મંજૂરી આપો + + તમામ ઉલ્લેખો માટે સૂચિત કરો ઉમેરો @@ -5602,8 +5700,8 @@ બધા જ કનેક્શનની સમીક્ષા થઈ ચૂકી છે, ચાલુ રાખવા માટે મોકલો પર ટૅપ કરો. - તમારી પાસે %1$d કનેક્શન એવું છે જેણે Signal ફરીથી ઇન્સ્ટોલ કર્યું હોઈ શકે અથવા ડિવાઇસ બદલ્યું હોઈ શકે છે. તેમની સાથે તમારી સ્ટોરી શેર કરતાં પહેલાં તેમના સેફ્ટી નંબરને રિવ્યૂ કરો અથવા તેમને તમારી સ્ટોરીમાંથી દૂર કરવાનું ધ્યાને લો. - તમારી પાસે %1$d કનેક્શન એવા છે જેમણે Signal ફરીથી ઇન્સ્ટોલ કર્યું હોઈ શકે અથવા ડિવાઇસ બદલ્યા હોઈ શકે છે. તેમની સાથે તમારી સ્ટોરી શેર કરતાં પહેલાં તેમના સેફ્ટી નંબરને રિવ્યૂ કરો અથવા તેમને તમારી સ્ટોરીમાંથી દૂર કરવાનું ધ્યાને લો. + %1$d કનેક્શને Signal ફરીથી ઇન્સ્ટોલ કર્યું હોઈ શકે ડિવાઇસ બદલ્યું હોઈ શકે છે. તમે તેમના સેફ્ટી નંબરને રિવ્યૂ કરી શકો છો અથવા મોકલવાનું ચાલુ રાખી શકો છો. + %1$d કનેક્શનોએ Signal ફરીથી ઇન્સ્ટોલ કર્યું હોઈ શકે ડિવાઇસ બદલ્યા હોઈ શકે છે. તમે તેમના સેફ્ટી નંબરને રિવ્યૂ કરી શકો છો અથવા મોકલવાનું ચાલુ રાખી શકો છો. સલામતી નંબર ચકાસો @@ -5961,12 +6059,18 @@ IBAN દેશ કોડ સપોર્ટેડ નથી અમાન્ય IBAN + + ઓછામાં ઓછા 2 અક્ષરો + + અમાન્ય ઇમેઇલ એડ્રેસ iDEAL તમારી બેંક, નામ અને ઇમેઇલ દાખલ કરો. Stripe તમને તમારા દાન વિશે અપડેટ્સ મોકલવા માટે આ ઇમેઇલનો ઉપયોગ કરે છે. %1$s + + તમારી બેંકની વિગતો દાખલ કરો. Signal તમારી અંગત માહિતી એકત્રિત કે સંગ્રહિત કરતું નથી. %1$s વધુ જાણો @@ -6152,6 +6256,8 @@ આઉટગોઇંગ મિસ્ડ કૉલ + + નોટિફિકેશન પ્રોફાઇલ ચાલુ હતી ત્યારે મિસ થયો જોડાઓ @@ -6259,6 +6365,8 @@ ગ્રુપ કૉલ મિસ્ડ ગ્રૂપ કૉલ + + નોટિફિકેશન પ્રોફાઇલ ચાલુ હતી ત્યારે ગ્રૂપ કૉલ મિસ થયો ઇનકમિંગ ગ્રૂપ કૉલ diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index 5adfc08ab2..554d2193b4 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -323,8 +323,12 @@ इमेज डाउनलोड नहीं किया जा सकता। आपको इसे फिर से भेजना होगा। वीडियो डाउनलोड नहीं किया जा सकता। आपको इसे फिर से भेजना होगा। - - %1$s संपादित किया गया + + अब संपादित किया गया + + %1$s संपादित किया गया + + %1$s संपादित किया गया कॉल से जुड़ें @@ -358,6 +362,8 @@ असुरक्षित MMS Signal मेसेज + + संदेश भेजें %1$s चलो Molly पर स्विच करें कृपया एक संपर्क चुनें आपके द्वारा भेजे जा रहे मेसेज के प्रकार के लिए अनुलग्नक आकार सीमा से अधिक है। @@ -1277,6 +1283,10 @@ छूटी हुई वॉयस कॉल छूटी हुई वीडियो कॉल + + नोटिफ़िकेशन प्रोफ़ाइल चालू होने पर वॉयस कॉल छूट गई + + नोटिफ़िकेशन प्रोफ़ाइल चालू होने पर वीडियो कॉल छूट गई आपने एक वॉयस कॉल काट दी @@ -1828,6 +1838,8 @@ कैमरा टॉगल करें म्यूट टॉगल करें + + अतिरिक्त कार्रवाइयां कॉल बंद @@ -1844,10 +1856,68 @@ डिवाइस के ईयरपीस का प्रतिनिधित्व करने वाला आइकन। + + हाथ बढ़ाएं + + हाथ बढ़ाएं + + अपना हाथ नीचे करें? + + हाथ नीचे करें + + रद्द करें + + आपने अपना हाथ उठाया है + + देखना + + + + %1$s ने हाथ उठाया है + %1$s + %2$d ने हाथ उठाया है + + + + + %1$s + %1$s +%2$d + + + + उठे हुए हाथ के व्यू को एक्सपैंड करें + + + + Signal कनेक्शन + + %1$s आपके सिस्टम संपर्कों में है + + आपके बीच कोई समान समूह नहीं है + + अनुरोधों की सावधानीपूर्वक समीक्षा करें + + %1$d साझा समूह + + हमारे बारे में + + आप + - - इस कॉल में · %1$d व्यक्ति - इस कॉल में · %1$d लोग + + इस कॉल में (%1$d) + इस कॉल में (%1$d) + + + Signal (%1$d) को रिंग करेगा + Signal (%1$d) को रिंग करेगा + + + Signal (%1$d) को सूचित करेगा + Signal (%1$d) को सूचित करेगा + + + हाथ उठाया (%1$d) + हाथ उठाए (%1$d) @@ -1934,7 +2004,7 @@ फोन नंबर देश कोड - कोल + कॉल सत्यापन कोड कोड फ़िर से भेजें @@ -1961,6 +2031,12 @@ देखा गया मीडिया + + + नाम संबंधी विवाद पाया गया + + देखना + \'%1$s\' के लिए कोई परिणाम नहीं मिला @@ -2127,6 +2203,8 @@ भेजें + + 00 कोई यूज़रनेम जोड़ें @@ -2149,6 +2227,12 @@ छोड़ दे पूर्ण + + यह यूज़रनेम उपलब्ध नहीं है, कोई अन्य नंबर आज़माएँ। + + अमान्य यूज़रनेम, न्यूनतम %1$d अंक दर्ज करें। + + अमान्य यूज़रनेम, अधिकतम %1$d अंक दर्ज करें। %1$d संपर्क सिग्नल पर है! @@ -2346,6 +2430,10 @@ प्रोसेस हो रहा है + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2370,6 +2458,8 @@ एक लिंक किए हुए डिवाइस पर जवाब दिया गया। एक लिंक किए हुए डिवाइस पर रद्द किया गया। एक लिंक किए डिवाइस पर व्यस्त हैं। + + फ्लिप कैमरे को यहां लाया गया है, इसे आज़माने के लिए अपने वीडियो पर टैप करें कोई एक बदल चुके सुरक्षा नंबर के साथ इस कॉल में जुड़ चुका है। @@ -3925,14 +4015,16 @@ समूह सदस्य को हटाने में असफलता। - सदस्य निवेदन आपकी संपर्क जानकारी ग्रूप से हटाएँ संपर्क जानकारी अपडेट करें ब्लॉक करें डिलीट करें - हाल ही में अपना प्रोफ़ाइल नाम %1$s से बदलकर %2$s रखा + + हाल ही में %1$s अपना प्रोफ़ाइल नाम %2$s से %3$s में बदला है + + %1$s आपके सिस्टम संपर्कों में है %1$s समूह में शामिल हुए @@ -4988,6 +5080,12 @@ इस प्रोफ़ाइल के ऑन होने पर जिन लोगों और ग्रुप्स से आप नोटिफ़िकेशन पाना चाहते हैं उन्हें जोड़ें लोग या ग्रुप जोड़ें + + अपवाद + + सभी कॉल अनुमत करें + + सभी उल्लेख हेतु अधिसूचित करें जोड़ें @@ -5602,8 +5700,8 @@ सभी कनेक्शन की समीक्षा कर ली गई है, जारी रखने के लिए भेजें पर टैप करें। - आपका %1$d कनेक्शन है जिसने शायद Signal फिर से इंस्टॉल किया हो या डिवाइस बदले हों। आप उसे भेजने से पहले वैकल्पिक तौर पर उनकी सुरक्षा संख्याओं की समीक्षा कर सकते हैं या उन्हें स्टोरी से हटा सकते हैं। - आपके %1$d कनेक्शन हैं जिन्होंने शायद Signal फिर से इंस्टॉल किया हो या डिवाइस बदले हों। आप उन्हें भेजने से पहले वैकल्पिक तौर पर उनकी सुरक्षा संख्याओं की समीक्षा कर सकते हैं या उन्हें स्टोरी से हटा सकते हैं। + हो सकता है कि %1$d कनेक्शन ने सिग्नल को पुनः इंस्टॉल कर दिया हो या डिवाइस बदल दिए हों। आप उनके सुरक्षा नंबर की समीक्षा कर सकते हैं या भेजना जारी रख सकते हैं। + हो सकता है कि %1$d कनेक्शन ने सिग्नल को पुनः इंस्टॉल कर दिया हो या डिवाइस बदल दिए हों। आप उनके सुरक्षा नंबर की समीक्षा कर सकते हैं या भेजना जारी रख सकते हैं। सुरक्षा संख्या सत्यापित करें @@ -5961,12 +6059,18 @@ IBAN देश कोड समर्थित नहीं है अमान्य IBAN + + न्यूनतम 2 अक्षर + + अमान्य ईमेल iDEAL अपना बैंक, नाम और ईमेल दर्ज करें। स्ट्राइप इस ईमेल का इस्तेमाल आपको आपके दान के बारे में अपडेट भेजने के लिए करता है। %1$s + + अपनी बैंक जानकारी दर्ज करें। Signal आपकी व्यक्तिगत जानकारी एकत्र या स्टोर नहीं करता है। %1$s अधिक जानें @@ -6152,6 +6256,8 @@ आउटगोइंग मिस्ड + + नोटिफ़िकेशन प्रोफ़ाइल चालू होने पर चूक गया जुड़ें @@ -6259,6 +6365,8 @@ ग्रुप कॉल मिस्ड ग्रुप कॉल + + नोटिफ़िकेशन प्रोफ़ाइल चालू होने पर समूह कॉल छूट गई इनकमिंग ग्रुप कॉल diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 3abeb24175..bcbfb05d86 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -325,8 +325,12 @@ Nije moguće preuzeti fotografiju. Morat ćete ju poslati ponovno. Nije moguće preuzeti videozapis. Morat ćete ga poslati ponovno. - - Uređeno %1$s + + Uređeno sada + + Uređeno %1$s + + Uređeno %1$s Pridruži se pozivu @@ -364,6 +368,8 @@ Nesigurni MMS Signal poruka + + Pošalji poruku Prebaci se na Molly %1$s Odaberite kontakt Privitak premašuje ograničenja veličine za vrstu poruke koju šaljete. @@ -1365,6 +1371,10 @@ Propušteni glasovni poziv Propušteni videopoziv + + Propustili ste glasovni poziv dok je profil obavijesti bio uključen + + Propustili ste videopoziv dok je profil obavijesti bio uključen Odbili ste glasovni poziv @@ -1970,6 +1980,8 @@ Uključi/isključi kameru Uključi/isključi zvuk + + Dodatne radnje Završi poziv @@ -1986,12 +1998,80 @@ Ikona koja predstavlja slušalicu uređaja. + + Podigni ruku + + Podigni ruku + + Želite spustiti ruku? + + Spusti ruku + + Poništi + + Podigli ste ruku + + Prikaz + + + + %1$s podiže ruku + %1$s i još %2$d korisnika podižu ruku + %1$s i još %2$d korisnika podižu ruku + %1$s i još %2$d korisnika podižu ruku + + + + + %1$s + %1$s +%2$d + %1$s +%2$d + %1$s +%2$d + + + + Pogledaj podignute ruke + + + + Signal kontakt + + %1$s je u kontaktima vašeg sustava + + Nemate nijednu zajedničku grupu + + Pažljivo pregledajte zahtjeve + + %1$d zajedničkih grupa + + Opis + + Vi + - - U ovom pozivu · %1$d osoba - U ovom pozivu · %1$d osobe - U ovom pozivu · %1$d osoba - U ovom pozivu · %1$d osoba + + Na ovom pozivu (%1$d) + Na ovom pozivu (%1$d) + Na ovom pozivu (%1$d) + Na ovom pozivu (%1$d) + + + Signal će uputiti poziv korisniku (%1$d) + Signal će uputiti poziv korisnicima (%1$d) + Signal će uputiti poziv korisnicima (%1$d) + Signal će uputiti poziv korisnicima (%1$d) + + + Signal će obavijestiti korisnika (%1$d) + Signal će obavijestiti korisnike (%1$d) + Signal će obavijestiti korisnike (%1$d) + Signal će obavijestiti korisnike (%1$d) + + + Podignute ruke (%1$d) + Podignute ruke (%1$d) + Podignute ruke (%1$d) + Podignute ruke (%1$d) @@ -2107,6 +2187,12 @@ Pregledano Medijski zapis + + + Naišli smo na dvojbu oko imena + + Prikaz + Nema rezultata za \'%1$s\' @@ -2273,6 +2359,8 @@ Pošalji + + 00 Dodajte korisničko ime @@ -2295,6 +2383,12 @@ Preskoči Gotovo + + Korisničko ime nije dostupno, pokušajte odabrati drugi broj. + + Korisničko ime nije valjano, unesite najmanje %1$d znamenke. + + Korisničko ime nije valjano, unesite najmanje %1$d znamenki. %1$d kontakt je na Signalu! @@ -2500,6 +2594,10 @@ Obrada u tijeku… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2524,6 +2622,8 @@ Odgovoreno na povezanom uređaju. Odbijeno na povezanom uređaju. Zauzeto na povezanom uređaju. + + Gumb za promjenu smjera kamere premješten je ovdje, dodirnite ga i isprobajte Netko se pridružio ovom pozivu sa sigurnosnim brojem koji se promijenio. @@ -4127,14 +4227,16 @@ Uklanjanje člana grupe nije uspjelo. - Član Zahtjev Vaši kontakti Ukloni iz grupe Ažuriraj kontakt Blokiraj Izbriši - Nedavno su promijenili ime svog profila iz %1$s u %2$s + + Korisnik %1$s nedavno je promijenio ime profila iz %2$s u %3$s + + %1$s je u kontaktima vašeg sustava %1$s se pridružio/la @@ -5208,6 +5310,12 @@ Dodajte osobe i grupe od kojih želite primati obavijesti i pozive kada je ovaj profil uključen Dodaj ljude ili grupe + + Iznimke + + Dopusti sve pozive + + Pošalji obavijesti za sva spominjanja Dodaj @@ -5856,10 +5964,10 @@ Svi kontakti su pregledani, dodirnite Pošalji za nastavak. - %1$d osoba među vašim kontaktima je ponovno instalirala Signal ili promijenila uređaj. Prije nego što s njom podijelite vaše priče, pregledajte njezine sigurnosne brojeve ili razmislite o tome da je uklonite iz gledatelja vaših priča. - %1$d osobe među vašim kontaktima su ponovno instalirale Signal ili promijenile uređaj. Prije nego što s njima podijelite vaše priče, pregledajte njihove sigurnosne brojeve ili razmislite o tome da ih uklonite iz gledatelja vaših priča. - %1$d osoba među vašim kontaktima je ponovno instaliralo Signal ili promijenilo uređaj. Prije nego što s njima podijelite vaše priče, pregledajte njihove sigurnosne brojeve ili razmislite o tome da ih uklonite iz gledatelja vaših priča. - %1$d osoba među vašim kontaktima je ponovno instaliralo Signal ili promijenilo uređaj. Prije nego što s njima podijelite vaše priče, pregledajte njihove sigurnosne brojeve ili razmislite o tome da ih uklonite iz gledatelja vaših priča. + Imate %1$d kontakt koji je možda ponovno instalirao Signal ili promijenio uređaje. Možete pregledati njegov sigurnosni broj ili nastaviti sa slanjem. + Imate %1$d kontakta koja su možda ponovno instalirala Signal ili promijenila uređaje. Možete pregledati njihove sigurnosne brojeve ili nastaviti sa slanjem. + Imate %1$d kontakata koji su možda ponovno instalirali Signal ili promijenili uređaje. Možete pregledati njihove sigurnosne brojeve ili nastaviti sa slanjem. + Imate %1$d kontakata koji su možda ponovno instalirali Signal ili promijenili uređaje. Možete pregledati njihove sigurnosne brojeve ili nastaviti sa slanjem. Potvrdi sigurnosni broj @@ -6225,12 +6333,18 @@ Navedeni državni kôd IBAN-a nije podržan Navedeni IBAN nije važeći + + Minimalno 2 znaka + + Nevažeća adresa e-pošte iDEAL Unesite ime banke, vaše ime i adresu e-pošte. Stripe će koristiti vašu adresu e-pošte za slanje ažuriranja o vašoj donaciji. %1$s + + Unesite svoje bankovne podatke. Signal ne prikuplja niti pohranjuje vaše osobne podatke. %1$s Saznajte više @@ -6418,6 +6532,8 @@ Odlazni Propušteni + + Propušteno dok je profil obavijesti bio uključen Pridruži se @@ -6531,6 +6647,8 @@ Grupni poziv Propušten grupni poziv + + Propustili ste grupni poziv dok je profil obavijesti bio uključen Dolazni grupni poziv diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 97a80c191c..69d18de249 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -323,8 +323,12 @@ A kép nem tölthető le. Újra el kell küldened. A videó nem tölthető le. Újra el kell küldened. - - Szerkesztve: %1$s + + Most szerkesztve + + Szerkesztve: %1$s + + Szerkesztve: %1$s Belépés a hívásba @@ -358,6 +362,8 @@ Titkosítatlan MMS Signal üzenet + + Üzenet küldése Váltsunk Mollyra: %1$s Kérlek válassz egy kontaktot A melléklet mérete meghaladja az adott típushoz tartozó mérethatárt. @@ -1277,6 +1283,10 @@ Nem fogadott hanghívás Nem fogadott videóhívás + + Nem fogadott hanghívás, miközben az értesítési profil be van kapcsolva + + Nem fogadott videohívás, miközben az értesítési profil be van kapcsolva Elutasítottál egy hanghívást @@ -1828,6 +1838,8 @@ Kamera ki-/bekapcsolása Némítás ki-/bekapcsolása + + További műveletek Hívás befejezése @@ -1844,10 +1856,68 @@ Egy eszköz fülhallgatóját jelképező ikon. + + Kéz felemelése + + Kéz felemelése + + Leteszed a kezed? + + Kéz letétele + + Mégse + + Felemelted a kezed + + Megtekintés + + + + %1$s felemelte a kezét + %1$s + %2$d felemelte a kezét + + + + + %1$s + %1$s + %2$d + + + + A felemelt kéz nézet megnyitása + + + + Signal Névjegyek + + %1$s szerepel a rendszernévjegyeid között + + Nincsenek közös csoportjaitok + + Gondosan nézd át a kéréseket + + %1$d db közös csoport + + Névjegy + + Te + - - Ebben a hívásban · %1$d személy - Ebben a hívásban · %1$d személy + + Ebben a hívásban (%1$d) + Ebben a hívásban (%1$d) + + + A Signal felhívja (%1$d) + A Signal felhívja (%1$d) + + + A Signal értesíti (%1$d) + A Signal értesíti (%1$d) + + + Felemelt kéz (%1$d) + Felemelt kéz (%1$d) @@ -1961,6 +2031,12 @@ Megtekintve Média + + + Névütközés található + + Megtekintés + Nincs találat a \'%1$s\' kifejezésre @@ -2127,6 +2203,8 @@ Küldés + + 00 Felhasználónév hozzáadása @@ -2149,6 +2227,12 @@ Kihagyás Befejezés + + Ez a felhasználónév nem érhető el, próbálkozz egy másik számmal. + + Érvénytelen felhasználónév, adj meg legalább %1$d számjegyet. + + Érvénytelen felhasználónév, legfeljebb %1$d számjegyet adj meg. %1$d ismerős már elérhető Signalon! @@ -2346,6 +2430,10 @@ Feldolgozás… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2370,6 +2458,8 @@ Fogadva egy társított eszközön. Elutasítva egy társított eszközön. Foglalt jelzés egy társított eszközön. + + A Flip Camera átkerült ide. Koppints a videódra a kipróbáláshoz Valaki olyan csatlakozott a híváshoz, akinek biztonsági azonosítója megváltozott. @@ -3925,14 +4015,16 @@ Nem sikerült eltávolítani a csoporttagot. - Tag Kérés Saját profil Eltávolítás a csoportból Profil frissítése Letiltás Törlés - Nemrég megváltoztatta profilnevét erről: %1$s erre: %2$s + + %1$s a közelmúltban megváltoztatta a profilnevét. Régi név: %2$s, új név: %3$s + + %1$s szerepel a rendszernévjegyeid között %1$s csatlakozott @@ -4988,6 +5080,12 @@ Add hozzá azokat azokat a személyeket és csoportokat, akiktől szeretnél értesítéseket és hívásokat fogadni olyankor, amikor a profil aktív! Személyek vagy csoportok hozzáadása + + Kivételek + + Összes hívás engedélyezése + + Értesítsen az összes említés esetén Hozzáadás @@ -5602,8 +5700,8 @@ Minden névjegy ellenőrzése megtörtént, a folytatáshoz koppints a Küldés gombra. - Van %1$d olyan névjegyed, aki újratelepítette a Signalt, vagy eszközt cserélt. Mielőtt megosztanád velük a Történeted, vess egy pillantást a biztonsági számukra, vagy akár ki is zárhatod őket a Történet megtekintéséből. - %1$d olyan névjegyed van, aki újratelepítette a Signalt vagy eszközt cserélt. Mielőtt megosztanád velük a Történeted, vess egy pillantást a biztonsági számukra, vagy akár ki is zárhatod őket a Történet megtekintéséből. + Lehet, hogy %1$d névjegyed újratelepítette a Signalt, vagy eszközt cserélt. Megnézheted a biztonsági számát, vagy folytathatod a küldést. + Lehet, hogy %1$d névjegyed újratelepítette a Signalt, vagy eszközt cserélt. Megnézheted a biztonsági számát, vagy folytathatod a küldést. Bizt. szám ellenőrzése @@ -5961,12 +6059,18 @@ Az IBAN-országkód nem támogatott Érvénytelen IBAN + + Minimum 2 karakter + + Érvénytelen e-mail-cím iDEAL Add meg a bankod, a neved és az e-mail-címed. A Stripe ezt az e-mailt használja fel arra, hogy frissítéseket küldjön neked az adományoddal kapcsolatban. %1$s + + Add meg a banki adataid. A Signal nem gyűjti és nem tárolja a személyes adataidat. %1$s Tudj meg többet @@ -6152,6 +6256,8 @@ Kimenő Nem fogadott + + Nem fogadott, miközben az értesítési profil be van kapcsolva Csatlakozás @@ -6259,6 +6365,8 @@ Csoporthívás Nem fogadott csoporthívás + + Nem fogadott csoporthívás, miközben az értesítési profil be van kapcsolva Bejövő csoportos hívás diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 5a7fd25f08..b9fd5631b7 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -322,8 +322,12 @@ Tidak dapat mengunduh gambar. Anda harus mengirimnya lagi. Tidak dapat mengunduh video. Anda harus mengirimnya lagi. - - Diedit %1$s + + Baru Diedit + + Diedit %1$s + + Diedit %1$s Gabung ke panggilan @@ -355,6 +359,8 @@ MMS tidak terenkripsi Pesan Signal + + Kirim pesan Ayo pindah ke Molly %1$s Silahkan pilih kontak Lampiran melebihi batas ukuran untuk tipe pesan yang Anda kirimkan. @@ -1233,6 +1239,10 @@ Panggilan suara tidak terjawab Panggilan video tidak terjawab + + Panggilan suara tidak terjawab saat profil notifikasi aktif + + Panggilan video tidak terjawab saat profil notifikasi aktif Anda menolak panggilan suara @@ -1757,6 +1767,8 @@ Ubah status kamera Ubah status bisukan + + Tindakan lain Akhiri panggilan @@ -1773,9 +1785,62 @@ Ikon yang merepresentasikan earpiece perangkat. + + Angkat Tangan + + Angkat Tangan + + Turunkan tangan? + + Turunkan Tangan + + Batal + + Anda angkat tangan + + Lihat + + + + %1$s + %2$d angkat tangan + + + + + %1$s +%2$d + + + + Perluas tampilan info angkat tangan + + + + Koneksi Signal + + %1$s ada di kontak sistem Anda + + Tidak punya grup yang sama + + Tinjau permintaan dengan hati-hati + + %1$d grup yang sama + + Tentang + + Anda + - - Di panggilan ini · %1$d orang + + Dalam panggilan ini (%1$d) + + + Signal akan Menampilkan Layar Dering untuk (%1$d) + + + Signal akan Kirim Notifikasi ke (%1$d) + + + Angkat tangan (%1$d) @@ -1888,6 +1953,12 @@ Dilihat Media + + + Ditemukan permasalahan nama + + Lihat + Tidak ditemukan hasil untuk \'%1$s\' @@ -2054,6 +2125,8 @@ Kirim + + 00 Tambahkan nama pengguna @@ -2076,6 +2149,12 @@ Lewati Selesai + + Nama pengguna ini tidak tersedia, coba nomor lain. + + Nama pengguna tidak valid, masukkan minimal %1$d digit. + + Nama pengguna tidak valid, masukkan maksimal %1$d digit. %1$d kontak ada di Signal! @@ -2269,6 +2348,10 @@ Memproses… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2293,6 +2376,8 @@ Dijawab dari perangkat terhubung. Ditolak dari perangkat terhubung. Sibuk di perangkat tersambung. + + Fitur Balik Kamera telah dipindahkan ke sini, ketuk video untuk mencobanya Seseorang telah bergabung dalam panggilan ini dengan nomor keamanan yang berubah. @@ -3824,14 +3909,16 @@ Gagal menghapus anggota. - Anggota Permintaan Kontak Anda Keluarkan dari grup Perbarui kontak Blokir Hapus - Baru saja mengganti nama profilnya dari %1$s menjadi %2$s + + %1$s baru-baru ini mengubah nama profilnya dari %2$s jadi %3$s + + %1$s ada di kontak sistem Anda %1$s bergabung @@ -4878,6 +4965,12 @@ Tambahkan orang dan grup yang Anda ingin dapatkan notifikasi dan panggilannya saat profil ini aktif Tambahkan orang atau grup + + Pengecualian + + Izinkan semua panggilan + + Notifikasi untuk semua penyebutan Tambahkan @@ -5475,7 +5568,7 @@ Semua koneksi telah ditinjau, ketuk kirim untuk melanjutkan. - Anda punya %1$d koneksi yang mungkin telah menginstal ulang Signal atau berganti perangkat. Sebelum berbagi cerita dengan mereka, tinjau nomor keamanan mereka atau pertimbangkan untuk menghapus mereka dari cerita Anda. + %1$d koneksi mungkin telah menginstal ulang Signal atau mengganti perangkat mereka. Anda dapat meninjau nomor keamanan mereka atau melanjutkan pengiriman. Verifikasi nomor keamanan @@ -5829,12 +5922,18 @@ Kode negara IBAN tidak didukung IBAN tidak valid + + Minimal 2 karakter + + Alamat email salah iDEAL Masukkan detail bank, nama, dan email Anda. Stripe menggunakan email ini untuk mengirimkan info terbaru tentang donasi Anda. %1$s + + Masukkan detail bank Anda. Signal tidak mengumpulkan atau menyimpan informasi pribadi Anda. %1$s Pelajari selengkapnya @@ -6019,6 +6118,8 @@ Panggilan Keluar Panggilan Tidak Terjawab + + Tidak terjawab saat profil notifikasi aktif Gabung @@ -6123,6 +6224,8 @@ Panggilan grup Panggilan grup tak terjawab + + Panggilan grup tidak terjawab saat profil notifikasi aktif Panggilan grup masuk diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index c242659ba8..286ea5b6fb 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -323,8 +323,12 @@ Impossibile scaricare l\'immagine. Dovrai inviarla di nuovo. Impossibile scaricare il video. Dovrai inviarlo di nuovo. - - Modificato %1$s + + Modificato ora + + Modificato %1$s + + Modificato %1$s Unisciti alla chiamata @@ -358,6 +362,8 @@ MMS non sicuro Messaggio Signal + + Invia messaggio Passiamo a Molly %1$s Scegli un contatto L\'allegato che stai cercando di inviare supera le dimensioni consentite. @@ -1277,6 +1283,10 @@ Chiamata persa Videochiamata persa + + Chiamata vocale persa per lo stato del profilo + + Videochiamata persa per lo stato del profilo Hai rifiutato una chiamata @@ -1535,7 +1545,7 @@ Vuoi consentire a %1$s di inviarti messaggi? Non riceverai messaggi da parte sua finché non sbloccherai il contatto. Vuoi ricevere aggiornamenti e notizie da %1$s? Non riceverai aggiornamenti finché non sbloccherai il contatto. Continuare la tua chat con questo gruppo e condividere il tuo nome e la tua foto con i suoi membri? - This Legacy Group can no longer be used. Create a new group to activate new features like @mentions and admins. + Questo Gruppo Legacy non può essere più usato. Crea un altro gruppo per attivare nuove funzioni come le @menzioni e gli admin. Questo Gruppo Legacy non può più essere usato perché è troppo grande. La dimensione massima del gruppo è %1$d. Vuoi continuare questa chat con %1$s e condividere il tuo nome e la tua foto con questo utente? Unirsi a questo gruppo e condividere il tuo nome e la tua foto con i suoi membri? Non sapranno che hai visto i loro messaggi finché non accetti. @@ -1828,6 +1838,8 @@ Attiva fotocamera Attiva silenzioso + + Altre azioni Termina chiamata @@ -1844,10 +1856,68 @@ Icona che rappresenta gli auricolari di un dispositivo. + + Alza la mano + + Alza la mano + + Vuoi abbassare la mano? + + Abbassa la mano + + Annulla + + Hai alzato la mano + + Mostra + + + + %1$s ha alzato la mano + %1$s + %2$d hanno alzato la mano + + + + + %1$s + %1$s +%2$d + + + + Espandi la lista di persone che hanno alzato la mano + + + + Amicizia di Signal + + %1$s è tra i contatti del tuo telefono + + Non avete gruppi in comune + + Controlla attentamente le richieste + + %1$d gruppi in comune + + Info + + Tu + - - In questa chiamata · %1$d persona - In questa chiamata · %1$d persone + + In questa chiamata (%1$d) + In questa chiamata (%1$d) + + + Signal chiamerà (%1$d) + Signal chiamerà (%1$d) + + + Signal avviserà (%1$d) + Signal avviserà (%1$d) + + + Mano alzata (%1$d) + Mani alzate (%1$d) @@ -1961,6 +2031,12 @@ Visualizzato Media + + + Riscontrato un conflitto tra nomi uguali + + Mostra + Nessun risultato trovato per \'%1$s\' @@ -2127,6 +2203,8 @@ Invia + + 00 Aggiungi un nome utente @@ -2149,6 +2227,12 @@ Salta Fatto + + Questo nome utente non è disponibile, provane un altro. + + Nome utente non valido, inserisci almeno %1$d caratteri. + + Nome utente non valido, inserisci al massimo %1$d caratteri. %1$d contatto è su Signal! @@ -2346,6 +2430,10 @@ Elaborazione in corso… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2370,6 +2458,8 @@ Hai risposto su un dispositivo collegato. Hai rifiutato su un dispositivo collegato. Occupato su un dispositivo collegato. + + Ora puoi ruotare la camera da qui: tocca per provare! Qualcuno si è unito a questa chiamata con un codice di sicurezza che è cambiato. @@ -3925,14 +4015,16 @@ Impossibile rimuovere il membro del gruppo. - Membro Richiesta Tuo contatto Rimuovi dal gruppo Aggiorna contatto Blocca Elimina - Ha recentemente cambiato il suo nome profilo da %1$s a %2$s + + %1$s ha cambiato da poco il suo nome profilo da %2$s a %3$s + + %1$s è tra i contatti del tuo telefono %1$s si è unito @@ -4988,6 +5080,12 @@ Aggiungi persone e gruppi da cui vuoi ricevere notifiche e chiamate quando questo profilo è attivo Aggiungi persone e gruppi + + Eccezioni + + Consenti tutte le chiamate + + Notifica per tutte le menzioni Aggiungi @@ -5602,8 +5700,8 @@ I codici di sicurezza di tutte le Amicizie di Signal sono stati verificati: tocca Invia per continuare. - Hai %1$d contatto che potrebbe aver reinstallato Signal oppure cambiato dispositivo. Prima di condividere la tua Storia, puoi verificare i suoi codici di sicurezza (oppure rimuovere questa persona dalla condivisione della Storia). - Hai %1$d contatti che potrebbero aver reinstallato Signal oppure cambiato dispositivo. Prima di condividere la tua Storia, puoi verificare i loro codici di sicurezza (oppure rimuovere queste persone dalla condivisione della Storia). + Hai %1$d Amicizia di Signal che potrebbe aver reinstallato l\'app oppure cambiato dispositivo. Puoi verificare i suoi codici di sicurezza (oppure inviare lo stesso). + Hai %1$d Amicizie di Signal che potrebbero aver reinstallato l\'app oppure cambiato dispositivo. Puoi verificare i loro codici di sicurezza (oppure inviare lo stesso). Verifica numero sicuro @@ -5961,12 +6059,18 @@ Codice identificazione Paese dell\'IBAN non supportato IBAN non valido + + Minimo 2 caratteri + + Indirizzo email non valido iDEAL Inserisci i dettagli del tuo conto corrente, il tuo nome e la tua email. Stripe utilizzerà il tuo indirizzo email per inviarti aggiornamenti relativi alla donazione. %1$s + + Inserisci i dettagli del tuo conto. Signal non raccoglie e non conserva le tue informazioni personali. %1$s Scopri di più @@ -6152,6 +6256,8 @@ In uscita Chiamata persa + + Chiamata persa per lo stato del profilo Unisciti @@ -6259,6 +6365,8 @@ Chiamata di gruppo Chiamata di gruppo persa + + Chiamata di gruppo persa per lo stato del profilo Chiamata di gruppo in entrata diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 9295b22845..762bdf1797 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -325,8 +325,12 @@ הורדת התמונה נכשלה. עליך לשלוח אותה שוב. הורדת הסרטון נכשלה. עליך לשלוח אותו שוב. - - נערך %1$s + + נערך עכשיו + + נערך %1$s + + נערך %1$s הצטרפות לשיחה @@ -364,6 +368,8 @@ MMS בלתי מאובטח הודעת Signal + + שלח הודעה בוא נחליף אל Molly %1$s אנא בחר איש קשר הצרופה חורגת ממגבלות הגודל עבור הסוג של ההודעה שאתה שולח. @@ -1365,6 +1371,10 @@ שיחה קולית שלא נענתה שיחת וידאו שלא נענתה + + שיחה קולית שלא נענתה בזמן שפרופיל ההתראות פועל + + שיחת וידאו שלא נענתה בזמן שפרופיל ההתראות פועל דחית שיחה קולית @@ -1970,6 +1980,8 @@ שינוי מצב מצלמה שינוי מצב השתקה + + פעולות נוספות סיים שיחה @@ -1986,12 +1998,80 @@ סמל שמייצג אוזנייה של מכשיר. + + הרמת יד + + הרמת יד + + להוריד את היד? + + הורדת יד + + ביטול + + הרמת יד + + הצגה + + + + %1$s הרים/ה יד + %1$s + %2$d הרימו יד + %1$s + %2$d הרימו יד + %1$s + %2$d הרימו יד + + + + + %1$s + %1$s +%2$d + %1$s +%2$d + %1$s +%2$d + + + + להרחיב תצוגת הרמת ידיים + + + + חבר/ת Signal + + %1$s נמצא/ת באנשי הקשר של המערכת שלך + + אין קבוצות משותפות + + סקור בקשות בזהירות + + %1$d קבוצות במשותף + + אודות + + אתה + - - בשיחה זו · איש %1$d - בשיחה זו · %1$d אנשים - בשיחה זו · %1$d אנשים - בשיחה זו · %1$d אנשים + + בשיחה הזו (%1$d) + בשיחה הזו (%1$d) + בשיחה הזו (%1$d) + בשיחה הזו (%1$d) + + + Signal תצלצל אל (%1$d) + Signal תצלצל אל (%1$d) + Signal תצלצל אל (%1$d) + Signal תצלצל אל (%1$d) + + + Signal תודיע ל(%1$d) + Signal תודיע ל(%1$d) + Signal תודיע ל(%1$d) + Signal תודיע ל(%1$d) + + + יד מורמת (%1$d) + ידיים מורמות (%1$d) + ידיים מורמות (%1$d) + ידיים מורמות (%1$d) @@ -2107,6 +2187,12 @@ הוצג מדיה + + + נמצאה התנגשות שמות + + הצגה + תוצאות לא נמצאו עבור \'%1$s\' @@ -2273,6 +2359,8 @@ שלח + + 00 הוספת שם משתמש @@ -2295,6 +2383,12 @@ דלג סיים + + שם המשתמש הזה לא זמין, כדאי לנסות מספר אחר. + + שם משתמש לא חוקי, יש להזין לפחות %1$d ספרות. + + שם משתמש לא חוקי, יש להזין עד %1$d ספרות. איש קשר %1$d ב־Signal! @@ -2500,6 +2594,10 @@ מעבדים… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2524,6 +2622,8 @@ נענתה על מכשיר מקושר. נדחתה על מכשיר מקושר. תפוס על מכשיר מקושר. + + כפתור הפיכת כיוון המצלמה עבר לכאן, אפשר ללחוץ על הוידאו שלך כדי לנסות אותו מישהו הצטרף אל שיחה זו עם מספר ביטחון שהשתנה. @@ -4127,14 +4227,16 @@ הסרת חבר/ת קבוצה נכשלה. - חבר קבוצה בקשה איש הקשר שלך הסרה מהקבוצה עדכן איש קשר חסימה מחיקה - שינה/שינתה לאחרונה את שם הפרופיל מן %1$s אל %2$s + + %1$s החליף/ה לאחרונה את שם הפרופיל מ%2$s ל%3$s + + %1$s נמצא/ת באנשי הקשר של המערכת שלך %1$s הצטרף/ה @@ -4616,7 +4718,7 @@ מוצק - שיפוע + גרדיאנט גוון רוויה @@ -4647,7 +4749,7 @@ אפס שמור מתאים באופן אוטומטי את הצבע אל הטפט - גרור את השינוי אל כיוון השיפוע + יש לגרור כדי לשנות את כיוון שינוי הצבע בגרדיאנט הוסף תמונת פרופיל @@ -5208,6 +5310,12 @@ הוסף אנשים וקבוצות שאתה רוצה לקבל מהם התראות ושיחות כאשר הפרופיל הזה מופעל הוסף אנשים או קבוצות + + יוצאי דופן + + לאפשר את כל השיחות + + להתריע על כל האזכורים הוסף @@ -5856,10 +5964,10 @@ כל חברי ה-Signal נסקרו, יש ללחוץ על שליחה כדי להמשיך. - יש לך חבר או חברת Signal %1$d שיתכן והתקינו מחדש את Signal או החליפו מכשירים. כדאי לסקור את מספרי הבטיחות שלהם לפני שיתוף הסטורי, או להסיר אותם מהסטורי במידת הצורך. - יש לך %1$d חברי Signal שיתכן והתקינו מחדש את Signal או החליפו מכשירים. כדאי לסקור את מספרי הבטיחות שלהם לפני שיתוף הסטורי, או להסיר אותם מהסטורי במידת הצורך. - יש לך %1$d חברי Signal שיתכן והתקינו מחדש את Signal או החליפו מכשירים. כדאי לסקור את מספרי הבטיחות שלהם לפני שיתוף הסטורי, או להסיר אותם מהסטורי במידת הצורך. - יש לך %1$d חברי Signal שיתכן והתקינו מחדש את Signal או החליפו מכשירים. כדאי לסקור את מספרי הבטיחות שלהם לפני שיתוף הסטורי, או להסיר אותם מהסטורי במידת הצורך. + יתכן שחבר או חברת Signal %1$d התקינו מחדש את Signal או החליפו מכשירים. יש לך אפשרות לסקור את מספר הבטיחות שלהם או להמשיך עם השליחה. + יתכן ש–%1$d חברי Signal התקינו מחדש את Signal או החליפו מכשירים. יש לך אפשרות לסקור את מספרי הבטיחות שלהם או להמשיך עם השליחה. + יתכן ש–%1$d חברי Signal התקינו מחדש את Signal או החליפו מכשירים. יש לך אפשרות לסקור את מספרי הבטיחות שלהם או להמשיך עם השליחה. + יתכן ש–%1$d חברי Signal התקינו מחדש את Signal או החליפו מכשירים. יש לך אפשרות לסקור את מספרי הבטיחות שלהם או להמשיך עם השליחה. אימות מספר בטיחות @@ -6225,12 +6333,18 @@ קוד המדינה של ה‑IBAN אינו נתמך מספר IBAN לא תקין + + מינימום 2 תווים + + כתובת אימייל לא חוקית iDEAL יש להזין את הבנק, השם והאימייל שלך. Stripe משתמשת באימייל שלך כדי לשלוח לך עדכונים על התרומה שלך. %1$s + + יש להזין את פרטי הבנק שלך. Signal לא אוספת או מאחסנת את המידע האישי שלך. %1$s למידע נוסף @@ -6418,6 +6532,8 @@ שיחה יוצאת שיחה שלא נענתה + + שיחות שלא נענו בזמן שפרופיל ההתראות פועל הצטרפות @@ -6531,6 +6647,8 @@ שיחה קבוצתית שיחה קבוצתית שלא נענתה + + שיחה קבוצתית שלא נענתה בזמן שפרופיל ההתראות פועל שיחה קבוצתית נכנסת diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 5c1bfb72c4..76b6a14961 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -322,8 +322,12 @@ 画像をダウンロードできません。もう一度送信する必要があります。 動画をダウンロードできません。もう一度送信する必要があります。 - - 編集済み %1$s + + 編集済み たった今 + + 編集済み %1$s + + 編集済み %1$s 通話に参加する @@ -355,6 +359,8 @@ 安全でないMMS Signalメッセージ + + メッセージを送信 メッセージアプリをMollyにしましょう %1$s 連絡先を選択してください 添付ファイルのサイズが、送信するメッセージタイプに応じた制限を超えています。 @@ -1233,6 +1239,10 @@ 音声通話着信あり ビデオ通話着信あり + + 通知プロファイルがオンのときに不在着信になった音声通話がありました + + 通知プロファイルがオンのときに不在着信になったビデオ通話がありました 音声通話を拒否しました @@ -1757,6 +1767,8 @@ カメラを切り替える ミュートを切り替える + + その他の操作 通話終了 @@ -1773,9 +1785,62 @@ 端末のイヤホンを表すアイコン。 + + 手を挙げる + + 手を挙げる + + 手をおろしますか? + + 手を降ろす + + キャンセル + + あなたは手を挙げています + + 表示する + + + + %1$s と %2$d 人が手を挙げています + + + + + %1$s + %2$d + + + + 挙手の表示を拡大する + + + + Signalコネクション + + %1$s はシステムの連絡先に含まれています + + 共通のグループはありません + + 慎重に申請を確認してください + + 共通のグループ %1$d件 + + プロフィール + + あなた + - - 参加者 · %1$d人 + + 通話参加者(%1$d) + + + Signalは(%1$d)を呼び出します + + + Signalは(%1$d)に通知を送信します + + + 手を挙げている人(%1$d) @@ -1888,6 +1953,12 @@ 閲覧済み メディア + + + 同名のメンバーがいます + + 表示する + 「%1$s」の検索結果はありません @@ -2054,6 +2125,8 @@ 送信する + + 00 ユーザー名を追加する @@ -2076,6 +2149,12 @@ スキップ 完了 + + このユーザーネームは使用できません。別の数字をお試し下さい。 + + ユーザーネームが無効です。数字は%1$d以上入力してください。 + + ユーザーネームが無効です。入力できる数字は、最大で%1$d桁までです。 %1$d件の連絡先がSignalを使用しています! @@ -2269,6 +2348,10 @@ 処理中… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2293,6 +2376,8 @@ リンクした端末で応答しました。 リンクした端末で拒否しました。 リンクした端末で通話中です。 + + カメラアイコンがここへ移動しました。画面をタップして試してみてください 安全番号を変更したメンバーが、この通話に参加しています。 @@ -3824,14 +3909,16 @@ グループメンバーの削除に失敗しました。 - メンバー 申請 あなたの連絡先 グループから削除する 連絡先をアップデートする ブロックする 消去する - 最近、プロフィール名を %1$s から %2$s に変更しました。 + + %1$s は、最近プロフィール名を %2$s から %3$s に変更しました + + %1$s はシステムの連絡先に含まれています %1$s が参加しました @@ -4225,7 +4312,7 @@ ブロック済 - %1$d件 + %1$d人 メッセージ送受信 消えるメッセージ アプリのセキュリティ @@ -4878,6 +4965,12 @@ このプロファイルの有効時に通知や通話着信を受け取りたい、人やグループを選択してください。 人またはグループを追加 + + 例外 + + すべての通話を許可する + + すべてのメンションを通知する 追加する @@ -5475,7 +5568,7 @@ すべてのコネクションが確認されました。続行するには送信をタップしてください。 - %1$d 人のコネクションがSignalを再インストールしたか端末を変更した可能性があります。ストーリーを彼らと共有する前に、彼らの安全番号を確認するか、あなたのストーリーから彼らを削除することを検討してください。 + %1$d人のコネクションがSignalを再インストールしたか端末を変更した可能性があります。安全番号を確認するか、送信を続行してください。 安全番号を確認 @@ -5829,12 +5922,18 @@ この国コードはIBANを採用していません IBANが無効です + + 2文字以上入力 + + 無効なEメールアドレスです iDEAL 銀行名、氏名、メールアドレスを入力してください。入力いただいたメールアドレスは、Stripeがお客様の寄付に関するお知らせをお送りするために使用されます。%1$s + + 口座情報を入力してください。Signalがお客様の個人情報を収集したり保存したりすることはありません。%1$s 詳しく見る @@ -6019,6 +6118,8 @@ 発信 不在着信 + + 通知プロファイルがオンのときの不在着信がありました 参加する @@ -6123,6 +6224,8 @@ グループ通話 グループ通話着信あり + + 通知プロファイルがオンのときに不在着信になったグループ通話がありました グループ通話着信 diff --git a/app/src/main/res/values-ka/strings.xml b/app/src/main/res/values-ka/strings.xml index 4b6f20d3c1..0fb5342a84 100644 --- a/app/src/main/res/values-ka/strings.xml +++ b/app/src/main/res/values-ka/strings.xml @@ -323,8 +323,12 @@ სურათის გადმოწერა ვერ მოხერხდა. მისი თავიდან გაგზავნა მოგიწევს. ვიდეოს გადმოწერა ვერ მოხერხდა. მისი თავიდან გაგზავნა მოგიწევს. - - რედაქტირებულია %1$s + + რედაქტირდა ახლა + + რედაქტირებულია %1$s + + რედაქტირებულია %1$s ზარზე შესვლა @@ -358,6 +362,8 @@ დაუცველი MMS Signal-ის შეტყობინება + + შეტყობინების გაგზავნა მოდი, გადავერთოთ Molly-ზე %1$s გთხოვთ აირჩიო კონტაქტი მიმაგრებული ფაილი აღემატება იმ შეტყობინების ზომების ლიმიტებს, რომლის გაგზავნასაც შენ ცდილობ. @@ -1277,6 +1283,10 @@ გამოტოვებული ხმოვანი ზარი გამოტოვებული ვიდეო ზარი + + გამოტოვებული ხმოვანი ზარი ჩართული შეტყობინების პროფილის დროს + + გამოტოვებული ვიდეო ზარი ჩართული შეტყობინების პროფილის დროს შენ უარყავი ხმოვანი ზარი @@ -1828,6 +1838,8 @@ კამერის გადართვა დადუმებული რეჟიმის გადართვა + + დამატებითი მოქმედებები ზარის დასრულება @@ -1844,10 +1856,68 @@ ხატულა, რომელიც მოწყობილობის ყურსასმენს წარმოადგენს. + + ხელის აწევა + + ხელის აწევა + + გსურს, ხელი ჩამოწიო? + + ხელის ჩაწევა + + გაუქმება + + შენ აიწიე ხელი + + View + + + + %1$s-მა ხელი აიწია + %1$s + %2$d-მა ხელი აიწიეს + + + + + %1$s + %1$s +%2$d + + + + აწეული ხელის ვიზუალის გადიდება + + + + Signal-ის კონტაქტები + + %1$s შენი სისტემის კონტაქტებშია + + თქვენ საერთო ჯგუფები არ გაქვთ + + ყურადღებით გადახედე მოთხოვნებს + + %1$d საერთო ჯგუფი + + შესახებ + + შენ + - - ამ ზარში · %1$d ადამიანი - ამ ზარზე · %1$d ადამიანი + + ამ ზარზე (%1$d) + ამ ზარზე (%1$d) + + + Signal-ი დაურეკავს (%1$d) + Signal-ი დაურეკავს (%1$d) + + + Signal-ი შეატყობინებს (%1$d)-ს + Signal-ი შეატყობინებს (%1$d)-ს + + + (%1$d) აწეული ხელი + (%1$d) აწეული ხელი @@ -1961,6 +2031,12 @@ ნანახია მედია-ფაილები + + + ნაპოვნია სახელების კონფლიქტი + + View + \'%1$s\'-ზე ვერაფერ ვიპოვეთ @@ -2127,6 +2203,8 @@ გაგზავნა + + 00 დაამატე მომხმარებლის სახელი @@ -2149,6 +2227,12 @@ გამოტოვება შესრულებულია + + მომხმარებლის სახელი არაა ხელმისაწვდომი, სხვა ნომერი სცადე. + + მომხმარებლის სახელი არასწორია, შეიყვანე მინიმუმ %1$d ციფრი. + + მომხმარებლის სახელი არასწორია, შეიყვანე მინიმუმ %1$d ციფრი. %1$d კონტაქტი Signal-ზეა! @@ -2346,6 +2430,10 @@ მუშავდება… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2370,6 +2458,8 @@ ზარი მიღებულია დაკავშირებულ მოწყობილობაზე. ზარი უარყოფილია დაკავშირებულ მოწყობილობაზე. დაკავებაა დაკავშირებულ მოწყობილობაზე. + + კამერის გადართვა აქ გადმოვიტანეთ, დააჭირე შენს ვიდეოს, რომ გამოსცადო ვიღაც შეუერთდა ამ ზარს უსაფრთხოების ნომრით, რომელიც შეიცვალა. @@ -3925,14 +4015,16 @@ ჯგუფის წევრის წაშლა ვერ მოხერხდა. - წევრი მოთხოვნა შენი კონტაქტი ჯგუფიდან წაშლა კონტაქტის განახლება დაბლოკვა წაშლა - ახლახანს შეცვალა პროფილის სახელი %1$s %2$s-ით + + %1$s-მა პროფილის სახელი %2$s %3$s-ით ახლახანს შეცვალა + + %1$s შენი სისტემის კონტაქტებშია %1$s შემოგიერთდათ @@ -4988,6 +5080,12 @@ დაამატე ადამიანები და ჯგუფები, რომლებისგანაც გსურს შეტყობინებებისა და ზარების მიღება, როდესაც ეს პროფილი ჩართულია ადამიანების ან ჯგფების დამატება + + გამონაკლისები + + ყველა ზარის დაშვება + + ყველა მონიშვნაზე მამცნობეთ დამატება @@ -5602,8 +5700,8 @@ ყველა კონტაქტი განხილულია, გასაგრძელებლად დააჭირე გაგზავნას. - შენ გყავს %1$d კონტაქტი, რომელმაც შესაძლოა გადააყენა Signal-ი ან შეცვალა მოწყობილობა. სანამ მას შენს Story-ის გაუზიარებდე, გადახედე მის უსაფრთხოების ნომერს ან იქნებ შენი Story-იდან გინდა მისი წაშლა. - შენ გყავს %1$d კონტაქტი, რომელთაც შესაძლოა გადააყენეს Signal-ი ან შეცვალეს მოწყობილობები. სანამ მათ შენს Story-ის გაუზიარებდე, გადახედე მათი უსაფრთხოების ნომრებს ან იქნებ შენი Story-იდან გინდა მათი წაშლა. + %1$d კონტაქტმა შესაძლოა გადააყენა Signal-ი ან მოწყობილობა შეცვალა. შეგიძლია გადახედო მის უსაფრთხოების ნომერს ან გაგზავნა განაგრძო. + %1$d კონტაქტმა შესაძლოა გადააყენა Signal-ი ან მოწყობილობა შეცვალა. შეგიძლია გადახედო მის უსაფრთხოების ნომერს ან გაგზავნა განაგრძო. უსაფრთხოების ნომრის ვერიფიცირება @@ -5961,12 +6059,18 @@ IBAN-ის ქვეყნის კოდს მხარდაჭერა არ აქვს არასწორი IBAN ნომერი + + მინიმუმ 2 სიმბოლო + + ელ-ფოსტის არასწორი მისამართი iDEAL შეიყვანე შენი ბანკი, სახელი და ელ-ფოსტა. Stripe-ი ამას შენი დონაციის შესახებ განახლებების გამოსაგზავნად გამოიყენებს. %1$s + + შეიყვანე შენი საბანკო დეტალები. Signal-ი არ აგროვებს და არ ინახავს შენს პერსონალურ მონაცემებს. %1$s გაიგე მეტი @@ -6152,6 +6256,8 @@ გამავალი გამოტოვებული + + გამოტოვებულია ჩართული შეტყობინების პროფილის დროს შეუერთდი @@ -6259,6 +6365,8 @@ ჯგუფური ზარი ჯგუფის გამოტოვებული ზარი + + ჩართული შეტყობინების პროფილის დროს გამოტოვებულია ჯგუფის ზარი ჯგუფის შემომავალი ზარი diff --git a/app/src/main/res/values-kk/strings.xml b/app/src/main/res/values-kk/strings.xml index 302b3023e9..4d6a6c26e3 100644 --- a/app/src/main/res/values-kk/strings.xml +++ b/app/src/main/res/values-kk/strings.xml @@ -323,8 +323,12 @@ Суретті жүктеп алу мүмкін емес. Сіз оны қайта жіберуіңіз керек. Видеоны жүктеп алу мүмкін емес. Сіз оны қайта жіберуіңіз керек. - - %1$s өңделді + + Енді өзгертілді + + %1$s өңделді + + %1$s өңделді Қоңырауға қосылу @@ -358,6 +362,8 @@ Қорғалмаған MMS Signal хаты + + Хат жіберу Molly %1$s қолданбасына ауысайық Контактіні таңдаңыз Сіз жіберіп жатқан хат түрі үшін тіркеменің өлшемі бекітілген шектен асып кетті. @@ -1277,6 +1283,10 @@ Қабылданбаған дауыстық қоңырау Қабылданбаған видеоқоңырау + + Хабарландыру профилі ашылып тұрғанда, дауыстық қоңырау қабылданбады + + Хабарландыру профилі ашылып тұрғанда, видеоқоңырау қабылданбады Дауыстық қоңырауды көтермедіңіз @@ -1828,6 +1838,8 @@ Камераны ауыстыру Дыбысты ауыстыру + + Қосымша әрекеттер Қоңырауды аяқтау @@ -1844,10 +1856,68 @@ Құрылғының құлаққабын білдіретін белгіше. + + Қолды көтеру + + Қолды көтеру + + Қолыңызды түсіресіз бе? + + Қолды түсіру + + Бас тарту + + Қолыңызды көтердіңіз + + View + + + + %1$s қолын көтерді + %1$s + %2$d қолдарын көтерді + + + + + %1$s + %1$s +%2$d + + + + Қол көтерген көріністі кеңейту + + + + Signal-мен байланыс + + %1$s сіздің жүйелік контактілеріңізде бар + + Сіздерде ортақ топ жоқ + + Өтініштерді мұқият қарап шығыңыз + + %1$d ортақ топ бар + + Мағлұмат + + Сіз + - - Бұл қоңырауда · %1$d адам - Бұл қоңырауда · %1$d адам + + Бұл қоңырауда (%1$d) + Бұл қоңырауда (%1$d) + + + Signal қоңырау шалады (%1$d) + Signal қоңырау шалады (%1$d) + + + Signal хабарландыру жібереді (%1$d) + Signal хабарландыру жібереді (%1$d) + + + Қол көтерді (%1$d) + Қол көтерді (%1$d) @@ -1961,6 +2031,12 @@ Қаралды Ақпарат құралдары + + + Аттар сәйкессіздігі орын алды + + View + \"%1$s\" бойынша бірде-бір нәтиже табылмады @@ -2127,6 +2203,8 @@ Жіберу + + 00 Пайдаланушы атын қосу @@ -2149,6 +2227,12 @@ Өткізіп жіберу Дайын + + Бұл пайдаланушы аты қолжетімді емес, басқа нөмірді қолданып көріңіз. + + Пайдаланушы аты дұрыс емес, кемінде %1$d сан енгізіңіз. + + Пайдаланушы аты дұрыс емес, ең көбі %1$d сан енгізіңіз. Signal-да %1$d контакт бар! @@ -2346,6 +2430,10 @@ Өңделуде… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2370,6 +2458,8 @@ Байланыстырылған құрылғыда жауап берілді. Байланыстырылған құрылғыда қабылданбады. Байланыстырылған құрылғыда бос емес. + + Камераны аудару функциясы осы жерге ауысты, видеоңызды түртіп, оны қолданып көріңіз Әлдебіреу осы қоңырауға өзгерген қауіпсіздік нөмірімен қосылды. @@ -3925,14 +4015,16 @@ Топ мүшесін өшіру мүмкін болмады. - Мүше Өтініш Контактіңіз Топтан өшіру Контактіні жаңарту Блоктау Жою - Жақында профиль атауын (%1$s) %2$s деп өзгертті + + %1$s жақында өз профилінің %2$s деген атын %3$s деп өзгертті + + %1$s сіздің жүйелік контактілеріңізде бар %1$s қосылды @@ -4988,6 +5080,12 @@ Осы профиль қосылып тұрғанда, сіз хабарландырулар мен қоңыраулар алғыңыз келетін адамдарды және топтарды қосыңыз Адамдарды немесе топтарды қосу + + Ерекше жағдайлар + + Барлық қоңырауға рұқсат ету + + Барлық атап өтілген жағдайда хабарлау Қосу @@ -5602,8 +5700,8 @@ Барлық байланыс тексерілді, жалғастыру үшін жіберу түймесін басыңыз. - Сізде Signal қолданбасын қайта орнатқан немесе құрылғысын ауыстырған %1$d контакт бар. Сториспен бөлісер алдында олардың қауіпсіздік нөмірлерін тексеріп алыңыз немесе оларды стористен өшіріңіз. - Сізде Signal қолданбасын қайта орнатқан немесе құрылғыларды өзгерткен %1$d контакт бар. Сториспен бөлісер алдында олардың қауіпсіздік нөмірлерін тексеріп алыңыз немесе оларды стористен өшіріңіз. + %1$d контакт Signal-ды қайта орнатқан немесе құрылғыларды өзгерткен болуы мүмкін. Оның қауіпсіздік нөмірін көруіңізге немесе жібере беруіңізге болады. + %1$d контакт Signal-ды қайта орнатқан немесе құрылғыларды өзгерткен болуы мүмкін. Олардың қауіпсіздік нөмірін көруіңізге немесе жібере беруіңізге болады. Қауіпсіздік нөмірін тексеру @@ -5961,12 +6059,18 @@ IBAN ел кодына қолдау көрсетілмейді IBAN нөмірі жарамсыз + + Минимум 2 таңба + + Электрондық пошта мекенжайы дұрыс емес iDEAL Банкіңіздің атын, аты-жөніңізді және электрондық поштаңызды енгізіңіз. Stripe осы электрондық поштаны пайдаланып, сіз жасаған демеушілік туралы ақпарат жіберіп отырады. %1$s + + Банк реквизиттеріңізді енгізіңіз. Signal сіздің жеке басыңызға қатысты ақпаратты жинамайды немесе сақтамайды. %1$s Толық ақпарат @@ -6152,6 +6256,8 @@ Шығыс Қабылданбаған + + Хабарландыру профилі ашылып тұрғанда, қабылданбады Қосылу @@ -6259,6 +6365,8 @@ Топтық қоңырау Қабылданбаған топтық қоңырау + + Хабарландыру профилі ашылып тұрғанда, топтық қоңырау қабылданбады Кіріс топтық қоңырау diff --git a/app/src/main/res/values-km/strings.xml b/app/src/main/res/values-km/strings.xml index e8ff0f406f..13f738420d 100644 --- a/app/src/main/res/values-km/strings.xml +++ b/app/src/main/res/values-km/strings.xml @@ -322,8 +322,12 @@ មិនអាចទាញយករូបភាពទេ។ អ្នកនឹងត្រូវផ្ញើវាម្តងទៀត។ មិនអាចទាញយកវីដេអូទេ។ អ្នកនឹងត្រូវផ្ញើវាម្តងទៀត។ - - បានកែ %1$s + + បានកែឥឡូវនេះ + + បានកែ %1$s + + បានកែ %1$s ចូលរួមការហៅ @@ -355,6 +359,8 @@ MMS មិនមានសុវត្ថិភាព សារ Signal + + ផ្ញើសារ តោះដូរទៅកាន់ Molly %1$s សូមជ្រើសរើសលេខទំនាក់ទំនង១ ឯកសារភ្ជាប់លើសទំហំកំណត់ សម្រាប់ប្រភេទសារដែលអ្នកកំពុងផ្ញើ។ @@ -1233,6 +1239,10 @@ ការហៅជាសំឡេងខកមិនបានទទួល ការហៅជាវីដេអូខកមិនបានទទួល + + ខកមិនបានទទួលការហៅជាសំឡេង ពេលបើកប្រូហ្វាល់ជូនដំណឹង + + ខកមិនបានទទួលការហៅជាវីដេអូ ពេលបើកប្រូហ្វាល់ជូនដំណឹង អ្នកមិនព្រមទទួលការហៅជាសំឡេង @@ -1757,6 +1767,8 @@ បិទ/បើកកាមេរ៉ា បិទ/បើកសំឡេង + + សកម្មភាពបន្ថែម បិទការហៅ @@ -1773,9 +1785,62 @@ រូបតំណាងសម្រាប់កាសរបស់ឧបករណ៍។ + + លើកដៃ + + លើកដៃ + + ដាក់ដៃរបស់អ្នកចុះឬ? + + ដាក់ដៃចុះ + + បោះបង់ + + អ្នកបានលើកដៃ + + បង្ហាញ + + + + %1$s + %2$d បានលើកដៃ + + + + + %1$s +%2$d + + + + ពង្រីកមើលអ្នកលើកដៃ + + + + អ្នកភ្ជាប់ទំនាក់ទំនងក្នុង Signal + + %1$s ស្ថិតនៅក្នុងប្រព័ន្ធទំនាក់ទំនងរបស់អ្នក + + អ្នកមិនមានក្រុមរួមទេ + + ពិនិត្យការស្នើសុំដោយប្រុងប្រយ័ត្ន + + %1$d ក្រុមរួមគ្នា + + អំពី + + អ្នក + - - ក្នុងការហៅនេះ · %1$d នាក់ + + នៅក្នុងការហៅនេះ (%1$d) + + + Signal នឹងហៅទៅ (%1$d) + + + Signal នឹងជូនដំណឹងទៅ (%1$d) + + + បានលើកដៃ (%1$d) @@ -1888,6 +1953,12 @@ បានបើកមើល ព័ត៌មាន + + + រកឃើញឈ្មោះជាន់គ្នា + + បង្ហាញ + រកមិនឃើញលទ្ធផលសម្រាប់ \'%1$s\' @@ -2054,6 +2125,8 @@ ផ្ញើ + + 00 បញ្ចូលឈ្មោះអ្នកប្រើ @@ -2076,6 +2149,12 @@ រំលង បញ្ចប់ + + ឈ្មោះអ្នកប្រើនេះមិនអាចប្រើបានទេ។ សូមសាកល្បងលេខផ្សេងទៀត។ + + ឈ្មោះអ្នកប្រើមិនត្រឹមត្រូវ។ សូមបញ្ចូលលេខយ៉ាងតិចបំផុត %1$d ខ្ទង់។ + + ឈ្មោះអ្នកប្រើមិនត្រឹមត្រូវ។ សូមបញ្ចូលលេខយ៉ាងច្រើនបំផុត %1$d ខ្ទង់។ លេខទំនាក់ទំនង%1$dនៅលើ Signal! @@ -2269,6 +2348,10 @@ កំពុងដំណើរការ… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2293,6 +2376,8 @@ បានឆ្លើយលើឧបករណ៍ដែលបានតភ្ជាប់។ បានបដិសេធលើឧបករណ៍ដែលបានតភ្ជាប់។ ជាប់រវល់លើឧបករណ៍ដែលបានតភ្ជាប់។ + + មុខងារត្រឡប់កាមេរ៉ាត្រូវបានផ្លាស់ទីមកត្រង់នេះ។ ចុចលើវីដេអូរបស់អ្នក ដើម្បីសាកល្បងប្រើវា នរណាម្នាក់បានចូលរួមការហៅនេះ ជាមួយលេខសុវត្ថិភាពដែលបានផ្លាស់ប្តូរ។ @@ -3824,14 +3909,16 @@ មិនអាចដកសមាជិកចេញពីក្រុមបាន។ - សមាជិក ស្នើសុំ លេខទំនាក់ទំនងរបស់អ្នក ដកចេញពីក្រុម ធ្វើបច្ចុប្បន្នភាពលេខទំនាក់ទំនង ទប់ស្កាត់ លុប - ថ្មីៗនេះ %1$sបានប្ដូរឈ្មោះគណនីទៅ %2$s + + %1$s ទើបតែប្តូរឈ្មោះប្រូហ្វាល់របស់គាត់ពី %2$s ទៅជា %3$s + + %1$s ស្ថិតនៅក្នុងប្រព័ន្ធទំនាក់ទំនងរបស់អ្នក %1$s បានចូលរួម @@ -4878,6 +4965,12 @@ បន្ថែមមនុស្ស ឬក្រុមដែលអ្នកចង់ទទួលសារជូនដំណឹង និងការហៅចូលនៅពេលគណនីដំណើរការ បន្ថែមមនុស្ស និងក្រុម + + លើកលែង + + អនុញ្ញាតការហៅទាំងអស់ + + ជូនដំណឹងអ្នកដែលត្រូវបានលើកឈ្មោះទាំងអស់ បន្ថែម @@ -5475,7 +5568,7 @@ អ្នកភ្ជាប់ទំនាក់ទំនងទាំងអស់ត្រូវបានពិនិត្យមើល។ សូមចុចផ្ញើដើម្បីបន្ត។ - អ្នកមានអ្នកភ្ជាប់ទំនាក់ទំនង %1$d នាក់ដែលប្រហែលជាបានដំឡើង Signal ឡើងវិញ ឬផ្លាស់ប្តូរឧបករណ៍។ មុនពេលចែករំលែករឿងរ៉ាវរបស់អ្នកជាមួយពួកគេ សូមពិនិត្យមើលលេខសុវត្ថិភាពរបស់ពួកគេ ឬពិចារណាដកពួកគេចេញពីរឿងរ៉ាវរបស់អ្នក។ + អ្នកភ្ជាប់ទំនាក់ទំនង %1$d នាក់ប្រហែលជាបានដំឡើង Signal ឡើងវិញ ឬផ្លាស់ប្តូរឧបករណ៍។ អ្នកអាចពិនិត្យមើលលេខសុវត្ថិភាពរបស់ពួកគេ ឬបន្តជាមួយការផ្ញើ។ ផ្ទៀងផ្ទាត់លេខសុវត្ថិភាព @@ -5829,12 +5922,18 @@ មិនស្គាល់លេខកូដប្រទេស IBAN លេខ IBAN មិនត្រឹមត្រូវ + + យ៉ាងតិចណាស់ 2 តួអក្សរ + + អាសយដ្ឋានអ៊ីមែលមិនត្រឹមត្រូវ iDEAL បញ្ចូលធនាគារ ឈ្មោះ និងអ៊ីមែលរបស់អ្នក។ Stripe ប្រើអ៊ីមែលនេះដើម្បីផ្ញើព័ត៌មានថ្មីៗមកអ្នកអំពីការបរិច្ចាគរបស់អ្នក។ %1$s + + បញ្ចូលព័ត៌មានលម្អិតនៃធនាគាររបស់អ្នក។ Signal មិនប្រមូល ឬរក្សាទុកព័ត៌មានផ្ទាល់ខ្លួនរបស់អ្នកទេ។ %1$s ស្វែងយល់បន្ថែម @@ -6019,6 +6118,8 @@ ហៅចេញ ខកមិនបានទទួល + + ខកមិនបានទទួល ពេលបើកប្រូហ្វាល់ជូនដំណឹង ចូលរួម @@ -6123,6 +6224,8 @@ ការហៅជាក្រុម ខកមិនបានទទួលការហៅជាក្រុម + + ខកមិនបានទទួលការហៅជាក្រុម ពេលបើកប្រូហ្វាល់ជូនដំណឹង ការហៅចូលជាក្រុម diff --git a/app/src/main/res/values-kn/strings.xml b/app/src/main/res/values-kn/strings.xml index fc81983c14..00f6e78f08 100644 --- a/app/src/main/res/values-kn/strings.xml +++ b/app/src/main/res/values-kn/strings.xml @@ -323,8 +323,12 @@ ಚಿತ್ರವನ್ನು ಡೌನ್‌ಲೋಡ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ. ನೀವು ಇದನ್ನು ಮತ್ತೆ ಕಳುಹಿಸಬೇಕಾಗಿದೆ. ವೀಡಿಯೋವನ್ನು ಡೌನ್‌ಲೋಡ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ. ನೀವು ಇದನ್ನು ಮತ್ತೆ ಕಳುಹಿಸಬೇಕಾಗಿದೆ. - - ಎಡಿಟ್ ಮಾಡಲಾದ ಸಮಯ %1$s + + ಈಗ ಎಡಿಟ್ ಮಾಡಲಾಗಿದೆ + + ಎಡಿಟ್ ಮಾಡಲಾದ ಸಮಯ %1$s + + ಎಡಿಟ್ ಮಾಡಲಾದ ಸಮಯ %1$s ಕರೆಗೆ ಸೇರಿ @@ -358,6 +362,8 @@ ಅಸುರಕ್ಷಿತ ಎಂಎಂಎಸ್ Signal ಸಂದೇಶ + + ಸಂದೇಶ ಕಳುಹಿಸಿ Molly %1$s ಗೆ ಬದಲಾಯಿಸೋಣ ದಯವಿಟ್ಟು ಒಂದು ಸಂಪರ್ಕವನ್ನು ಆರಿಸಿಕೊಳ್ಳಿ ನೀವು ಕಳುಹಿಸುತ್ತಿರುವ ಲಗತ್ತು ಸಂದೇಶದ ವಿಧವು ಗಾತ್ರದ ಮಿತಿಗಳನ್ನು ಮೀರಿದೆ. @@ -1277,6 +1283,10 @@ ಮಿಸ್ಡ್ ವಾಯ್ಸ್ ಕಾಲ್ ಮಿಸ್ಡ್ ವೀಡಿಯೊ ಕಾಲ್ + + ಅಧಿಸೂಚನೆ ಪ್ರೊಫೈಲ್ ಆನ್ ಆಗಿರುವಾಗ ತಪ್ಪಿಹೋದ ಧ್ವನಿ ಕರೆ + + ಅಧಿಸೂಚನೆ ಪ್ರೊಫೈಲ್ ಆನ್ ಆಗಿರುವಾಗ ತಪ್ಪಿಹೋದ ವೀಡಿಯೊ ಕರೆ ನೀವು ವಾಯ್ಸ್ ಕಾಲ್ ಅನ್ನು ನಿರಾಕರಿಸಿದ್ದೀರಿ @@ -1828,6 +1838,8 @@ ಕ್ಯಾಮೆರಾ ಟಾಗಲ್ ಮಾಡಿ ಮ್ಯೂಟ್ ಟಾಗಲ್ ಮಾಡಿ + + ಹೆಚ್ಚುವರಿ ಕ್ರಮಗಳು ಕರೆ ಅಂತ್ಯಗೊಳಿಸಿ @@ -1844,10 +1856,68 @@ ಸಾಧನದ ಇಯರ್‌‍ಪೀಸ್ ಅನ್ನು ಪ್ರತಿನಿಧಿಸುವ ಐಕಾನ್. + + ಕೈ ಎತ್ತಿ + + ಕೈ ಎತ್ತಿ + + ನಿಮ್ಮ ಕೈಯನ್ನು ಕೆಳಗಿಳಿಸಬೇಕೇ? + + ಕೈ ಕೆಳಗಿಳಿಸಿ + + ರದ್ದುಮಾಡಿ + + ನೀವು ಕೈ ಎತ್ತಿದ್ದೀರಿ + + ತೋರಿಸು + + + + %1$s ಕೈ ಎತ್ತಿದ್ದಾರೆ + %1$s + %2$d ಕೈ ಎತ್ತಿದ್ದಾರೆ + + + + + %1$s + %1$s +%2$d + + + + ಎತ್ತಿದ ಕೈ ವೀಕ್ಷಣೆಯನ್ನು ವಿಸ್ತರಿಸಿ + + + + Signal ಸಂಪರ್ಕ + + %1$s ಅವರು ನಿಮ್ಮ ಸಿಸ್ಟಂ ಸಂಪರ್ಕಗಳಲ್ಲಿದ್ದಾರೆ + + ನೀವು ಯಾವುದೇ ಸಾಮಾನ್ಯ ಗುಂಪುಗಳನ್ನು ಹೊಂದಿಲ್ಲ + + ವಿನಂತಿಗಳನ್ನು ಕಾಳಜಿಯಿಂದ ಪರಿಶೀಲಿಸಿ + + %1$d ಗುಂಪುಗಳಲ್ಲಿ ನೀವಿಬ್ಬರೂ ಇರುವಿರಿ + + ಬಗ್ಗೆ + + ನೀವು + - - ಈ ಕರೆಯಲ್ಲಿ · %1$d ಜನರು - ಈ ಕರೆಯಲ್ಲಿ · %1$d ಜನರು + + ಈ ಕರೆಯಲ್ಲಿ (%1$d) + ಈ ಕರೆಯಲ್ಲಿ (%1$d) + + + Signal (%1$d) ಅವರಿಗೆ ರಿಂಗ್ ಮಾಡುತ್ತದೆ + Signal (%1$d) ಅವರಿಗೆ ರಿಂಗ್ ಮಾಡುತ್ತದೆ + + + Signal (%1$d) ಅವರಿಗೆ ತಿಳಿಸುತ್ತದೆ + Signal (%1$d) ಅವರಿಗೆ ತಿಳಿಸುತ್ತದೆ + + + ಕೈ ಎತ್ತಿದ್ದಾರೆ (%1$d) + ಕೈಗಳನ್ನು ಎತ್ತಿದ್ದಾರೆ (%1$d) @@ -1961,6 +2031,12 @@ ವೀಕ್ಷಿಸಲಾಗಿದೆ ಮೀಡಿಯಾ + + + ಹೆಸರಿನ ಸಂಘರ್ಷ ಕಂಡುಬಂದಿದೆ + + ತೋರಿಸು + %1$sಗೆ ಫಲಿತಾಂಶಗಳು ಲಭ್ಯವಿಲ್ಲ @@ -2127,6 +2203,8 @@ ಕಳುಹಿಸು + + 00 ಯೂಸರ್‌ನೇಮ್ ಸೇರಿಸಿ @@ -2149,6 +2227,12 @@ ಬಿಟ್ಟು ಮುಂದುವರಿ ಮುಗಿದಿದೆ + + ಈ ಯೂಸರ್‌ನೇಮ್ ಲಭ್ಯವಿಲ್ಲ, ಇನ್ನೊಂದು ಸಂಖ್ಯೆಯನ್ನು ಪ್ರಯತ್ನಿಸಿ. + + ಅಮಾನ್ಯ ಯೂಸರ್‌ನೇಮ್, ಕನಿಷ್ಠ %1$d ಅಂಕಿಗಳನ್ನು ನಮೂದಿಸಿ. + + ಅಮಾನ್ಯ ಯೂಸರ್‌ನೇಮ್, ಗರಿಷ್ಠ %1$d ಅಂಕಿಗಳನ್ನು ನಮೂದಿಸಿ. %1$d ಸಂಪರ್ಕಗಳು Signal ನಲ್ಲಿವೆ! @@ -2346,6 +2430,10 @@ ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲಾಗುತ್ತಿದೆ… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2370,6 +2458,8 @@ ಲಿಂಕ್ ಮಾಡಿದ ಸಾಧನದಲ್ಲಿ ಉತ್ತರಿಸಲಾಗಿದೆ. ಲಿಂಕ್ ಮಾಡಿದ ಸಾಧನದಲ್ಲಿ ತಿರಸ್ಕರಿಸಲಾಗಿದೆ. ಲಿಂಕ್ ಮಾಡಿದ ಸಾಧನದಲ್ಲಿ ಕಾರ್ಯನಿರತವಾಗಿದೆ. + + ಫ್ಲಿಪ್ ಕ್ಯಾಮೆರಾವನ್ನು ಇಲ್ಲಿಗೆ ಸರಿಸಲಾಗಿದೆ, ಅದನ್ನು ಪ್ರಯತ್ನಿಸಲು ನಿಮ್ಮ ವೀಡಿಯೊವನ್ನು ತಟ್ಟಿ ಬದಲಾವಣೆಯಾಗಿರುವ ಸುರಕ್ಷತಾ ಸಂಖ್ಯೆಯೊಂದಿಗೆ ಈ ಕರೆಗೆ ಯಾರೋ ಸೇರಿದ್ದಾರೆ. @@ -3925,14 +4015,16 @@ ಗ್ರೂಪಿನ ಸದಸ್ಯರನ್ನು ತೆಗೆದುಹಾಕಲು ವಿಫಲವಾಗಿದೆ. - ಸದಸ್ಯ ಕೋರಿಕೆ ನಿಮ್ಮ ಸಂಪರ್ಕ ಗ್ರೂಪ್‌ನಿಂದ ತೆಗೆದುಹಾಕಿ ಸಂಪರ್ಕ ನವೀಕರಿಸಿ ನಿರ್ಬಂಧಿಸಿ ಅಳಿಸಿ - ಇತ್ತೀಚೆಗೆ ಅವರ ಪ್ರೊಫೈಲ್ ಹೆಸರನ್ನು %1$s ರಿಂದ %2$s ಕ್ಕೆ ಬದಲಾಯಿಸಲಾಗಿದೆ + + %1$s ಅವರು ಇತ್ತೀಚೆಗೆ ತಮ್ಮ ಪ್ರೊಫೈಲ್ ಹೆಸರನ್ನು %2$s ನಿಂದ %3$sಗೆ ಬದಲಾಯಿಸಿದ್ದಾರೆ + + %1$s ಅವರು ನಿಮ್ಮ ಸಿಸ್ಟಂ ಸಂಪರ್ಕಗಳಲ್ಲಿದ್ದಾರೆ %1$s ಸೇರಿಕೊಂಡಿದ್ದಾರೆ @@ -4988,6 +5080,12 @@ ಈ ಪ್ರೊಫೈಲ್‌ ಆನ್ ಇದ್ದಾಗ ಯಾರ ಅಧಿಸೂಚನೆಗಳು ಮತ್ತು ಕರೆಗಳನ್ನು ಬಯಸುತ್ತೀರೋ ಆ ಜನರು ಮತ್ತು ಗ್ರೂಪ್‌ಗಳನ್ನು ಸೇರಿಸಿ ಜನರು ಅಥವಾ ಗ್ರೂಪ್‌ಗಳನ್ನು ಸೇರಿಸಿ + + ವಿನಾಯಿತಿಗಳು + + ಎಲ್ಲಾ ಕರೆಗಳನ್ನು ಅನುಮತಿಸಿ + + ಎಲ್ಲಾ ಉಲ್ಲೇಖಗಳನ್ನು ತಿಳಿಸಿ ಸೇರಿಸಿ @@ -5602,8 +5700,8 @@ ಎಲ್ಲಾ ಸಂಪರ್ಕಗಳನ್ನು ಪರಿಶೀಲಿಸಲಾಗಿದೆ, ಮುಂದುವರಿಸಲು send ಅನ್ನು ಟ್ಯಾಪ್ ಮಾಡಿ. - Signal ಅನ್ನು ಪುನಃ ಇನ್‍ಸ್ಟಾಲ್ ಮಾಡಿರಬಹುದಾದ ಅಥವಾ ಸಾಧನಗಳನ್ನು ಬದಲಿಸಿರಬಹುದಾದ %1$d ಸಂಪರ್ಕವನ್ನು ನೀವು ಹೊಂದಿದ್ದೀರಿ. ನಿಮ್ಮ ಸ್ಟೋರಿಯನ್ನು ಅವರೊಂದಿಗೆ ಹಂಚಿಕೊಳ್ಳುವ ಮುನ್ನ ಅವರ ಸುರಕ್ಷಾ ಸಂಖ್ಯೆಗಳನ್ನು ಪರಿಶೀಲಿಸಿ ಅಥವಾ ಅವರನ್ನು ನಿಮ್ಮ ಸ್ಟೋರಿಯಿಂದ ತೆಗೆದುಹಾಕುವುದನ್ನು ಪರಿಗಣಿಸಿ. - Signal ಅನ್ನು ಪುನಃ ಇನ್‍ಸ್ಟಾಲ್ ಮಾಡಿರಬಹುದಾದ ಅಥವಾ ಸಾಧನಗಳನ್ನು ಬದಲಿಸಿರಬಹುದಾದ %1$d ಸಂಪರ್ಕಗಳನ್ನು ನೀವು ಹೊಂದಿದ್ದೀರಿ. ನಿಮ್ಮ ಸ್ಟೋರಿಯನ್ನು ಅವರೊಂದಿಗೆ ಹಂಚಿಕೊಳ್ಳುವ ಮುನ್ನ ಅವರ ಸುರಕ್ಷಾ ಸಂಖ್ಯೆಗಳನ್ನು ಪರಿಶೀಲಿಸಿ ಅಥವಾ ಅವರನ್ನು ನಿಮ್ಮ ಸ್ಟೋರಿಯಿಂದ ತೆಗೆದುಹಾಕುವುದನ್ನು ಪರಿಗಣಿಸಿ. + %1$d ಸಂಪರ್ಕವು ಮರುಸ್ಥಾಪಿಸಿದ Signal ಅನ್ನು ಅಥವಾ ಬದಲಾದ ಸಾಧನಗಳನ್ನು ಹೊಂದಿರಬಹುದು. ನೀವು ಅವರ ಸುರಕ್ಷತಾ ಸಂಖ್ಯೆಯನ್ನು ಪರಿಶೀಲಿಸಬಹುದು ಅಥವಾ ಕಳುಹಿಸುವುದನ್ನು ಮುಂದುವರಿಸಬಹುದು. + %1$d ಸಂಪರ್ಕಗಳು ಮರುಸ್ಥಾಪಿಸಿದ Signal ಅನ್ನು ಅಥವಾ ಬದಲಾದ ಸಾಧನಗಳನ್ನು ಹೊಂದಿರಬಹುದು. ನೀವು ಅವರ ಸುರಕ್ಷತಾ ಸಂಖ್ಯೆಗಳನ್ನು ಪರಿಶೀಲಿಸಬಹುದು ಅಥವಾ ಕಳುಹಿಸುವುದನ್ನು ಮುಂದುವರಿಸಬಹುದು. ಸುರಕ್ಷತಾ ಸಂಖ್ಯೆ ದೃಢೀಕರಿಸಿ @@ -5961,12 +6059,18 @@ IBAN ದೇಶದ ಕೋಡ್ ಬೆಂಬಲಿತವಾಗಿಲ್ಲ ಅಮಾನ್ಯ IBAN + + ಕನಿಷ್ಠ 2 ಕ್ಯಾರಕ್ಟರ್‌ಗಳು + + ಅಮಾನ್ಯ ಇಮೇಲ್ ವಿಳಾಸ iDEAL ನಿಮ್ಮ ಬ್ಯಾಂಕ್, ಹೆಸರು ಮತ್ತು ಇಮೇಲ್ ನಮೂದಿಸಿ. ನಿಮ್ಮ ದೇಣಿಗೆಯ ಕುರಿತು ನಿಮಗೆ ಅಪ್‌ಡೇಟ್‌ಗಳನ್ನು ಕಳುಹಿಸಲು ಈ ಇಮೇಲ್ ಅನ್ನು Stripe ಬಳಸುತ್ತದೆ. %1$s + + ನಿಮ್ಮ ಬ್ಯಾಂಕ್ ವಿವರಗಳನ್ನು ನಮೂದಿಸಿ. ನಿಮ್ಮ ವೈಯಕ್ತಿಕ ಮಾಹಿತಿಯನ್ನು Signal ಸಂಗ್ರಹಿಸುವುದಿಲ್ಲ ಅಥವಾ ಶೇಖರಿಸುವುದಿಲ್ಲ. %1$s ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ @@ -6152,6 +6256,8 @@ ಹೊರಹೋಗುವ ತಪ್ಪಿಹೋದ + + ಅಧಿಸೂಚನೆ ಪ್ರೊಫೈಲ್ ಆನ್ ಆಗಿರುವಾಗ ತಪ್ಪಿಹೋಗಿದೆ ಸೇರಿ @@ -6259,6 +6365,8 @@ ಗ್ರೂಪ್ ಕಾಲ್ ತಪ್ಪಿದ ಗ್ರೂಪ್ ಕರೆ + + ಅಧಿಸೂಚನೆ ಪ್ರೊಫೈಲ್ ಆನ್ ಆಗಿರುವಾಗ ತಪ್ಪಿಹೋದ ಗ್ರೂಪ್ ಕರೆ ಒಳಬರುವ ಗ್ರೂಪ್ ಕರೆ diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 50acaa443d..b1b7be393f 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -322,8 +322,12 @@ 이미지를 다운로드할 수 없습니다. 다시 공유해 주세요. 동영상을 다운로드할 수 없습니다. 다시 공유해 주세요. - - %1$s 전 수정함 + + 방금 수정함 + + %1$s에 수정함 + + %1$s에 수정함 통화 참여 @@ -355,6 +359,8 @@ 비보안 MMS Signal 메시지 + + 문자 보내기 Molly로 대화해요! %1$s 연락처를 선택해 주세요. 첨부 파일이 전송 메시지의 사이즈 제한을 초과합니다. @@ -1233,6 +1239,10 @@ 부재중 음성 통화 부재중 영상 통화 + + 알림 프로필을 사용하는 동안 받지 못한 전화가 있습니다. + + 알림 프로필을 사용하는 동안 받지 못한 화상 통화가 있습니다. 음성 통화를 거부했습니다. @@ -1481,7 +1491,7 @@ %1$s 님이 내게 메시지를 보내도록 허용할까요? 차단을 해제할 때까지 모든 메시지를 차단합니다. %1$s의 업데이트와 메시지를 받아보시겠어요? 차단을 해제할 때까지 모든 업데이트를 차단합니다. 이 그룹과 대화를 계속하고 멤버들에게 이름과 사진을 공유하시겠어요? - This Legacy Group can no longer be used. Create a new group to activate new features like @mentions and admins. + 이 레거시 그룹은 더 이상 사용할 수 없습니다. 새 그룹을 만들어 @멘션 및 관리자와 같은 새로운 기능을 활성화하세요. 이 구형 그룹의 규모가 너무 커서 더 이상 사용할 수 없습니다. 최대 그룹 규모는 %1$d입니다. %1$s 님과 대화를 계속하고 이름과 사진을 공유하시겠어요? 그룹에 가입하고 멤버들과 이름과 사진을 공유하시겠습니까? 귀하가 수락할 때까지 귀하가 자신의 메시지를 본 것을 알지 못합니다. @@ -1757,6 +1767,8 @@ 카메라 토글 알림 토글 + + 작업 더 보기 전화 종료 @@ -1773,9 +1785,62 @@ 기기 이어폰을 나타내는 아이콘입니다. + + 손 들기 + + 손 들기 + + 손을 내릴까요? + + 손 내리기 + + 취소 + + 손을 들었습니다. + + 보기 + + + + %1$s + %2$d명이 손을 들었습니다. + + + + + %1$s +%2$d명 + + + + 손 든 사람 보기 확장 + + + + Signal 커넥션 + + %1$s 님이 내 시스템 연락처에 있습니다. + + 공통 그룹이 없습니다. + + 신중하게 요청을 검토하세요. + + 공통 그룹 %1$d개 + + 정보 + + + - - 통화 참여자: %1$d명 + + 통화 참가자 수(%1$d) + + + Signal에서 전화벨을 울립니다(%1$d). + + + Signal에서 알림을 보냅니다(%1$d). + + + 손 든 사람(%1$d명) @@ -1888,6 +1953,12 @@ 조회함 미디어 + + + 이름 중복 문제를 발견했습니다. + + 보기 + \'%1$s\'에 대한 검색 결과 없음 @@ -2054,6 +2125,8 @@ 보내기 + + 00 사용자 이름 추가 @@ -2076,6 +2149,12 @@ 건너뛰기 확인 + + 이 사용자 이름은 사용할 수 없습니다. 다른 번호를 사용해 보세요. + + 사용자 이름이 잘못되었습니다. %1$d자 이상으로 입력해 주세요. + + 사용자 이름이 잘못되었습니다. %1$d자 이하로 입력해 주세요. Signal에 연락처가 %1$d개 있습니다! @@ -2269,6 +2348,10 @@ 처리 중… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2293,6 +2376,8 @@ 연결된 기기에서 응답했습니다. 연결된 기기에서 거부했습니다. 연결된 기기에서 사용 중입니다. + + 카메라 전환이 여기로 이동했습니다. 영상을 탭하여 사용해 보세요. 누군가 변경된 안전 번호로 통화에 참여했습니다. @@ -3824,14 +3909,16 @@ 그룹 멤버를 제거하지 못했습니다. - 멤버 요청 연락처 그룹에서 제거 연락처 업데이트 차단 삭제 - 최근 프로필 이름을 %1$s에서 %2$s(으)로 변경했습니다. + + %1$s 님이 최근 프로필 이름을 %2$s에서 %3$s(으)로 변경했습니다. + + %1$s 님이 내 시스템 연락처에 있습니다. %1$s 가입함 @@ -4878,6 +4965,12 @@ 이 프로필이 켜져 있을 때 알림과 통화를 받을 사용자와 그룹을 추가합니다. 사용자 또는 그룹 추가 + + 예외 + + 모든 통화 허용 + + 모든 멘션 알림 추가 @@ -5475,7 +5568,7 @@ 모든 커넥션을 검토했습니다. 보내기를 탭하여 계속하세요. - Signal을 다시 설치하거나 기기를 변경했을 수 있는 커넥션이 %1$d개 있습니다. 스토리를 공유하기 전에 해당 사용자의 안전 번호를 검토하거나, 스토리에서 제거하는 것이 좋습니다. + 커넥션 %1$d개가 Signal을 다시 설치했거나 기기를 변경했을 수 있습니다. 해당 사용자의 안전 번호를 검토하거나 계속 보내세요. 안전 번호 확인 @@ -5829,12 +5922,18 @@ IBAN 국가 코드가 지원되지 않습니다. 유효하지 않은 IBAN + + 이름은 2자 이상이어야 합니다. + + 잘못된 이메일 주소입니다. iDEAL 은행과 이름, 이메일 주소를 입력하세요. Stripe는 이 이메일 주소로 기부에 관한 업데이트를 보내드립니다. %1$s + + 은행 세부 정보를 입력하세요. Signal은 개인 정보를 수집하거나 저장하지 않습니다. %1$s 자세히 알아보기 @@ -6019,6 +6118,8 @@ 발신 부재중 + + 알림 프로필 사용 중 부재중 전화 참가 @@ -6123,6 +6224,8 @@ 그룹 통화 부재중 그룹 통화 + + 알림 프로필을 사용하는 동안 받지 못한 그룹 통화가 있습니다. 수신 그룹 통화 diff --git a/app/src/main/res/values-ky/strings.xml b/app/src/main/res/values-ky/strings.xml index ad68e50105..3b92230ca6 100644 --- a/app/src/main/res/values-ky/strings.xml +++ b/app/src/main/res/values-ky/strings.xml @@ -322,8 +322,12 @@ Сүрөт жүктөлүп алынган жок. {0} кайра жөнөтүшү керек. Видео жүктөлүп алынган жок. {0} кайра жөнөтүшү керек. - - Качан оңдолду: %1$s + + Азыр Оңдолду + + Качан оңдолду: %1$s + + Качан оңдолду: %1$s Чалууга кошулуу @@ -355,6 +359,8 @@ Корголбогон MMS Signal билдирүүсү + + Билдирүү жөнөтүү Molly\'га өтүп алыңыз: %1$s Байланыш тандаңыз Тиркеменин көлөмү жөнөтүлүп жаткан билдирүү үчүн белгиленген чектен ашып кетти. @@ -1233,6 +1239,10 @@ Кабыл алынбаган аудио чалуу Кабыл алынбаган аудио чалуу + + Билдирмелер профили иштеп турганда кабыл алынбай калган аудио чалуу + + Билдирмелер профили иштеп турганда кабыл алынбай калган видео чалуу Аудио чалуудан баш тарттыңыз @@ -1757,6 +1767,8 @@ Камераны өзгөртүү Үнсүз абалды өзгөртүү + + Кошумча аракеттер Чалууну бүтүрүү @@ -1773,9 +1785,62 @@ Түзмөктүн кулакчынынын сүрөтчөсү. + + Колду көтөрүү + + Колду көтөрүү + + Колуңузду түшүрөсүзбү? + + Колду түшүрүү + + Жок + + Колуңузду көтөрдүңүз + + Карап көрүү + + + + %1$s жана дагы %2$d колдорун көтөрүштү + + + + + %1$s жана дагы %2$d + + + + Көтөрүлгөн колдун көрүнүшүн чоңойтуу + + + + Signal байланышы + + %1$s системаңыздагы байланыштардын бири + + Жалпы топторуңуз жок + + Сурамдарды этияттап караңыз + + %1$d жалпы топ + + Учкай маалымат + + Сиз + - - Бул чалууда · %1$d адам + + Бул чалууда (%1$d) + + + Signal шыңгырайт (%1$d) + + + Signal кабарлайт (%1$d) + + + Канча адам кол көтөрдү (%1$d) @@ -1888,6 +1953,12 @@ Көрүлдү Медиа + + + Мындай ысым бар + + Карап көрүү + \'%1$s\' боюнча эч нерсе табылган жок @@ -2054,6 +2125,8 @@ Жөнөтүү + + 00 Колдонуучу аты атын кошуу @@ -2076,6 +2149,12 @@ Өткөрүп жиберүү Бүттү + + Бул колдонуучу аты жеткиликсиз, башкасын колдонуп көрүңүз. + + Колдонуучунун аты жарабайт, кеминде %1$d сан киргизиңиз. + + Колдонуучунун аты жарабайт, сандар %1$d ашпашы керек. Signal\'да %1$dбайланышыңыз бар! @@ -2269,6 +2348,10 @@ Аткарылууда… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2293,6 +2376,8 @@ Байланышкан түзмөктө жооп берилди. Байланышкан түзмөктө четке кагылды. Байланышкан түзмөктө бош эмес. + + Flip Camera бул жерге жылдырылды, аны байкап көрүү үчүн видеоңузду басыныз Коопсуздук коду өзгөргөн кимдир бирөө чалууга кошулду. @@ -3824,14 +3909,16 @@ Топтун мүчөсүн чыгаруу ишке ашкан жок. - Мүчө Өтүнүч Сиздин байланышыңыз Топтон алып салуу Байланышты жаңыртуу Бөгөттөө Өчүрүү - Жакында өзүнүн профиль атын %1$s ордуна %2$s деп өзгөрттү + + %1$s жакында профилинин аталышын өзгөрттү. Мурун %2$s болсо, эми %3$s болуп калды + + %1$s системаңыздагы байланыштардын бири %1$s кошулду @@ -4878,6 +4965,12 @@ Бул профиль иштетилгенде билдирмелерди жана чалууларды алууну каалаган адамдарды жана топторду кошуңуз Адамдарды же топторду кошуу + + Өзгөчө учурлар + + Бардык чалууларга уруксат берүү + + Бардык эскертмелер жөнүндө кабарлоо Кошуу @@ -5475,7 +5568,7 @@ Бардык байланыштар текшерилди, улантуу үчүн жөнөтүү дегенди басыңыз. - Signal\'ын кайра орноткон же түзмөктөрүн алмаштырган %1$d байланышыңыз бар. Жөнөтүүдөн мурун коопсуздук номерлерин текшерип койсоңуз болот. + %1$d байланыштары Signal\'ды кайра орнотушу же түзмөктөрдү өзгөртүшү мүмкүн. Алардын коопсуздук номерлерин карап көрүңүз же жөнөтүп улантыңыз. Коопсуздук кодун ырастоо @@ -5829,12 +5922,18 @@ Өлкөнүн IBAN коду колдоого алынбайт IBAN туура эмес + + Кеминде 2 символ + + Электрондук почта дареги жараксыз iDEAL Банкыңызды, атыңызды жана электрондук почта дарегиңизди көрсөтүңүз. Stripe кызматы ушул электрондук почта дареги аркылуу салымыңызга байланыштуу жаңылыктарды кабарлап турат. %1$s + + Банк эсебиңиздин чоо-жайын киргизиңиз. Signal жеке маалыматыңызды чогултпайт же сактабайт. %1$s Кененирээк маалымат @@ -6019,6 +6118,8 @@ Чыккан чалуулар Жооп берилбеген + + Билдирмелер профили иштеп турганда кабыл алынбай калды Кошулуу @@ -6123,6 +6224,8 @@ Топтук чалуу Жооп берилбеген топтук чалуу + + Билдирмелер профили иштеп турганда кабыл алынбай калган топтук чалуу Келүүчү топтук чалуу diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index 900795e6da..8234a5c92b 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -325,8 +325,12 @@ Nepavyksta atsisiųsti vaizdo. Turėsi dar kartą ją išsiųsti. Nepavyksta atsisiųsti vaizdo įrašo. Turėsi dar kartą ją išsiųsti. - - Redaguota %1$s + + Redaguota dabar + + Redaguota %1$s + + Redaguota %1$s Prisijungti prie skambučio @@ -364,6 +368,8 @@ Nesaugi MMS Signal žinutė + + Siųsti žinutę Bendraukime per Molly %1$s Pasirinkite adresatą Priedas viršija jūsų siunčiamos žinutės tipui leidžiamą dydį. @@ -1365,6 +1371,10 @@ Praleistas balso skambutis Praleistas vaizdo skambutis + + Missed voice call while notification profile on + + Missed video call while notification profile on Tu atmetei balso skambutį @@ -1970,6 +1980,8 @@ Perjungti kamerą Perjungti nutildymą + + Papildomi veiksmai Baigti skambutį @@ -1986,12 +1998,80 @@ Piktograma, vaizduojanti įrenginio ausinę. + + Pakelti ranką + + Pakelti ranką + + Nuleisti ranką? + + Nuleisti ranką + + Atšaukti + + Pakėlėte ranką + + Rodyti + + + + Rankas pakėlė %1$s + Rankas pakėlė %1$s + %2$d + Rankas pakėlė %1$s + %2$d + Rankas pakėlė %1$s + %2$d + + + + + %1$s + %1$s + %2$d + %1$s + %2$d + %1$s + %2$d + + + + Išplėsti pakeltos rankos rodinį + + + + „Signal“ ryšys + + %1$s yra jūsų sistemos kontaktuose + + Neturi bendrų grupių + + Atidžiai peržiūrėkite prašymus + + Bendrų grupių: %1$d + + Apie + + Jūs + - - Šiame skambutyje · %1$d žmogus - Šiame skambutyje · %1$d žmonės - Šiame skambutyje · %1$d žmonių - Šiame skambutyje · %1$d žmogus + + Šiame skambutyje (%1$d) + Šiame skambutyje (%1$d) + Šiame skambutyje (%1$d) + Šiame skambutyje (%1$d) + + + „Signal“ paskambins (%1$d) + „Signal“ paskambins (%1$d) + „Signal“ paskambins (%1$d) + „Signal“ paskambins (%1$d) + + + „Signal“ praneš (%1$d) + „Signal“ praneš (%1$d) + „Signal“ praneš (%1$d) + „Signal“ praneš (%1$d) + + + Pakelta ranka (%1$d) + Pakeltos rankos (%1$d) + Pakelta rankų (%1$d) + Pakelta rankų (%1$d) @@ -2107,6 +2187,12 @@ Peržiūrėta Medija + + + Rastas vardų konfliktas + + Rodyti + Nerasta jokių rezultatų, atitinkančių „%1$s“ @@ -2273,6 +2359,8 @@ Siųsti + + 00 Pridėti naudotojo vardą @@ -2295,6 +2383,12 @@ Praleisti Atlikta + + This username is not available, try another number. + + Invalid username, enter a minimum of %1$d digits. + + Invalid username, enter a maximum of %1$d digits. %1$d adresatas naudoja Signal! @@ -2500,6 +2594,10 @@ Apdorojama… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2524,6 +2622,8 @@ Atsiliepta susietame įrenginyje. Atmesta susietame įrenginyje. Užimta susietame įrenginyje. + + Funkcija „Apversti kamerą“ perkelta čia – palieskite savo vaizdą, kad išbandytumėte Prie šio skambučio prisijungė kažkas su pasikeitusiu saugumo numeriu. @@ -4127,14 +4227,16 @@ Nepavyko pašalinti grupės nario. - Narys Prašyti Jūsų adresatas Šalinti iš grupės Atnaujinti adresatą Užblokuoti Ištrinti - Neseniai pasikeitė profilio vardą iš %1$s į %2$s + + %1$s neseniai pasikeitė profilio vardą iš %2$s į %3$s + + %1$s yra jūsų sistemos kontaktuose Prisijungė %1$s @@ -5208,6 +5310,12 @@ Pridėkite žmones ir grupes iš kurių, įjungus šį profilį, norite gauti pranešimus ir skambučius Pridėti žmones ar grupes + + Išimtys + + Leisti visus skambučius + + Pranešti apie visus paminėjimus Pridėti @@ -5856,10 +5964,10 @@ Visi kontaktai peržiūrėti; bakstelėkite „Siųsti“, jei norite tęsti. - Turi %1$d kontaktą, kuris galbūt iš naujo įdiegė „Signal“ arba pakeitė įrenginį. Prieš savo istorijos bendrinimą peržiūrėk jo saugumo numerius ir arba apsvarstyk galimybę pašalinti iš savo istorijos. - Turi %1$d kontaktus, kurie galbūt iš naujo įdiegė „Signal“ arba pakeitė įrenginius. Prieš savo istorijos bendrinimą peržiūrėk jų saugumo numerius ir arba apsvarstyk galimybę pašalinti iš savo istorijos. - Turi %1$d kontakto, kurie galbūt iš naujo įdiegė „Signal“ arba pakeitė įrenginius. Prieš savo istorijos bendrinimą peržiūrėk jų saugumo numerius ir arba apsvarstyk galimybę pašalinti iš savo istorijos. - Turi %1$d kontaktų, kurie galbūt iš naujo įdiegė „Signal“ arba pakeitė įrenginius. Prieš savo istorijos bendrinimą peržiūrėk jų saugumo numerius ir arba apsvarstyk galimybę pašalinti iš savo istorijos. + %1$d connection may have reinstalled Signal or changed devices. You may review their safety number or continue with the send. + %1$d connections may have reinstalled Signal or changed devices. You may review their safety numbers or continue with the send. + %1$d connections may have reinstalled Signal or changed devices. You may review their safety numbers or continue with the send. + %1$d connections may have reinstalled Signal or changed devices. You may review their safety numbers or continue with the send. Patvirtinti saugumo numerį @@ -6225,12 +6333,18 @@ IBAN šalies kodas nepalaikomas Neteisingas IBAN + + Reikia bent 2 simbolių + + Netinkamas el. pašto adresas iDEAL Įvesk savo banko duomenis, vardą ir el. pašto adresą. „Stripe“ šiuo el. pašto adresu siųs naujienas apie skirtą paramą. %1$s + + Įveskite savo banko duomenis. Jūsų asmeninės informacijos „Signal“ nerenka ir nekaupia. %1$s Sužinoti daugiau @@ -6418,6 +6532,8 @@ Išsiunčiamieji Praleisti + + Missed while notification profile on Prisijungti @@ -6531,6 +6647,8 @@ Grupės skambutis Praleistas grupės skambutis + + Missed group call while notification profile on Gaunamasis grupės skambutis diff --git a/app/src/main/res/values-lv/strings.xml b/app/src/main/res/values-lv/strings.xml index af55fc3515..2cf52a0f9b 100644 --- a/app/src/main/res/values-lv/strings.xml +++ b/app/src/main/res/values-lv/strings.xml @@ -324,8 +324,12 @@ Nevar lejupielādēt attēlu. Jums tas jānosūta vēlreiz. Nevar lejupielādēt video. Jums tas jānosūta vēlreiz. - - Rediģēta %1$s + + Rediģēts tikko + + Rediģēts pirms %1$s + + Rediģēts plkst. %1$s Pievienoties sarunai @@ -361,6 +365,8 @@ Nedrošs MMS Signal ziņa + + Nosūtīt ziņu Pārslēdzamies uz Molly %1$s! Lūdzu, izvēlieties kontaktu! Pielikuma izmērs pārsniedz ierobežojumu šāda veida ziņai, kuru sūtāt. @@ -1321,6 +1327,10 @@ Neatbildēts balss zvans Neatbildēts videozvans + + Neatbildēts balss zvans, kamēr bija aktivizēts paziņojumu profils + + Neatbildēts video zvans, kamēr bija aktivizēts paziņojumu profils Jūs noraidījāt balss zvanu @@ -1899,6 +1909,8 @@ Kameras ieslēgšana/izslēgšana Skaņas ieslēgšana/izslēgšana + + Papildu darbības Beigt zvanu @@ -1915,11 +1927,74 @@ Ikona, kas attēlo ierīces austiņu. + + Pacelt roku + + Pacelt roku + + Nolaist roku? + + Nolaist roku + + Atcelt + + Jūs pacēlāt roku + + Skatīt + + + + %1$s +%2$d pacēla roku + %1$s pacēla roku + %1$s +%2$d pacēla roku + + + + + %1$s +%2$d + %1$s + %1$s +%2$d + + + + Izvērst pacelto roku skatu + + + + Signal kontaktpersona + + %1$s ir kontaktpersona jūsu sistēmā + + Jums nav kopīgu grupu + + Pārskatiet pieprasījumus rūpīgi + + %1$d kopīgas grupas + + Par + + Jūs + - - Šajā zvanā · %1$d cilvēks - Šajā zvanā · %1$d cilvēks - Šajā zvanā · %1$d cilvēki + + Šajā zvanā (%1$d) + Šajā zvanā (%1$d) + Šajā zvanā (%1$d) + + + Signal piezvanīs (%1$d) + Signal piezvanīs (%1$d) + Signal piezvanīs (%1$d) + + + Signal nosūtīs paziņojumu (%1$d) + Signal nosūtīs paziņojumu (%1$d) + Signal nosūtīs paziņojumu (%1$d) + + + Paceltas rokas (%1$d) + Pacelta roka (%1$d) + Paceltas rokas (%1$d) @@ -2034,6 +2109,12 @@ Skatīts Multivides fails + + + Konflikts vārdu pārklāšanās dēļ + + Skatīt + \"%1$s\" netika atrasts @@ -2200,6 +2281,8 @@ Sūtīt + + 00 Pievienojiet lietotājvārdu @@ -2222,6 +2305,12 @@ Izlaist Darīts + + Šis lietotāja vārds nav pieejams. Mēģiniet izmantot citu skaitli. + + Nederīgs lietotāja vārds. Ievadiet vismaz %1$d skaitļus. + + Nederīgs lietotāja vārds. Ievadiet ne vairāk kā %1$d skaitļus. %1$d kontaktpersona lieto Signal! @@ -2423,6 +2512,10 @@ Apstrādā… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2447,6 +2540,8 @@ Atbildēts no savienotās ierīces Atteikts no savienotās ierīces. Aizņemts no savienotās ierīces. + + Kameru apvēršana ir pārvietota šeit — pieskarieties savam video, lai to izmēģinātu Šim zvanam pievienojās kāds, kuram ir mainīts drošības numurs. @@ -4026,14 +4121,16 @@ Grupas dalībnieku neizdevās noņemt. - Dalībnieks Pieprasīt Jūsu kontaktpersona Noņemt no grupas Atjaunināt kontaktpersonu Bloķēt Dzēst - Nesen mainīja profila nosaukumu no %1$s uz %2$s + + %1$s nesen mainīja sava profila vārdu no %2$s uz %3$s + + %1$s ir kontaktpersona jūsu sistēmā %1$s pievienojās @@ -5098,6 +5195,12 @@ Pievienojiet cilvēkus un grupas, no kuriem jūs vēlaties saņemt paziņojumus un zvanus, kad šos profils ir ieslēgts Pievienot cilvēkus vai grupas + + Izņēmumi + + Atļaut visus zvanus + + Paziņot par visiem pieminējumiem Pievienot @@ -5729,9 +5832,9 @@ Visi kontakti ir pārskatīti, pieskarieties \"nosūtīt\", lai turpinātu. - Jums ir %1$d kontaktpersonu, kuras, iespējams, ir pārinstalējušas Signal vai nomainījušas ierīci. Pirms stāsta kopīgošanas pārskatiet viņu drošības numurus vai apsveriet iespēju neatļaut šīm kontaktpersonām skatīt jūsu stāstu. - Jums ir %1$d kontaktpersona, kura, iespējams, ir pārinstalējusi Signal vai nomainījusi ierīci. Pirms stāsta kopīgošanas pārskatiet šīs kontaktpersonas vai kontaktpersonu drošības numurus vai apsveriet iespēju neatļaut tām skatīt jūsu stāstu. - Jums ir %1$d kontaktpersonas, kuras, iespējams, ir pārinstalējušas Signal vai nomainījušas ierīces. Pirms stāsta kopīgošanas pārskatiet viņu drošības numurus vai apsveriet iespēju neatļaut šīm kontaktpersonām skatīt jūsu stāstu. + %1$d kontaktpersonas, iespējams, ir pārinstalējušas Signal vai mainījušas ierīci. Varat pārskatīt šo personu drošības numurus vai turpināt sūtīšanu. + %1$d kontaktpersona, iespējams, ir pārinstalējusi Signal vai mainījusi ierīci. Varat pārskatīt šo personu drošības numurus vai turpināt sūtīšanu. + %1$d kontaktpersonas, iespējams, ir pārinstalējušas Signal vai mainījušas ierīci. Varat pārskatīt šo personu drošības numurus vai turpināt sūtīšanu. Apstiprināt drošības nummuru @@ -6093,12 +6196,18 @@ IBAN valsts kods netiek atbalstīts Nederīgs IBAN kods + + Jāievada vismaz divas rakstzīmes + + Nederīga e-pasta adrese iDEAL Norādiet savas bankas nosaukumu, savu vārdu, uzvārdu un e-pasta adresi. Stripe šo e-pasta adresi izmantos, lai sūtītu jums informāciju par jūsu ziedojumu. %1$s + + Ievadiet bankas rekvizītus. Signal neapkopo un neuzglabā jūsu personas informāciju. %1$s Uzzināt vairāk @@ -6285,6 +6394,8 @@ Izejošs Neatbildēts + + Neatbildēts, jo bija aktivizēts paziņojumu profils Pievienoties @@ -6395,6 +6506,8 @@ Grupas zvans Neatbildēts grupas zvans + + Neatbildēts grupas zvans, kamēr bija aktivizēts paziņojumu profils Ienākošais grupas zvans diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index b5be557c84..05d722dbc6 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -323,8 +323,12 @@ Не може да се преземе сликата. Ќе мора да ја испратите одново. Не може да се преземе видеото. Ќе мора да ја испратите одново. - - Изменето %1$s + + Изменето сега + + Изменето %1$s + + Изменето %1$s Приклучи се на повикот @@ -358,6 +362,8 @@ Незаштитена MMS порака Signal порака + + Испрати порака Ајде да се префрлиме на Molly %1$s Ве молиме изберете контакт Прилогот ги надминува ограничувањата на големината за видот на пораката што ја испраќате. @@ -1277,6 +1283,10 @@ Пропуштен гласовен повик Пропуштен видео повик + + Пропуштен гласовен повик додека беше овозможен режимот за известувања + + Пропуштен видео повик додека беше овозможен режимот за известувања Одбивте гласовен повик @@ -1828,6 +1838,8 @@ Вклучи/исклучи камера Вклучи/исклучи звук + + Дополнителни дејствија Заврши повик @@ -1844,10 +1856,68 @@ Икона што преставува слушалка на уред. + + Кренете рака + + Кренете рака + + Сакате да ја спуштите раката? + + Спуштете ја раката + + Откажи + + Кренавте рака + + Поглед + + + + %1$s има кренато рака + %1$s + %2$d имаат кренато рака + + + + + %1$s + %1$s +%2$d + + + + Проширете го приказот на крената рака + + + + Signal врска + + %1$s е во контактите на вашиот телефон + + Немате заеднички групи + + Внимателно прегледајте ги барањата + + %1$d заеднички групи + + За + + Вие + - - На овој повик · %1$d личност - На овој повик · %1$d луѓе + + (%1$d) на овој повик + (%1$d) на овој повик + + + Signal ќе ѕвони на (%1$d) + Signal ќе ѕвони на (%1$d) + + + Signal ќе го извести корисникот (%1$d) + Signal ќе ги извести корисниците (%1$d) + + + Крената рака (%1$d) + Кренати раце (%1$d) @@ -1961,6 +2031,12 @@ Прегледано Медиумски датотеки + + + Пронајден е конфликт со името + + Поглед + Не се пронајдени резултати за ”%1$s” @@ -2127,6 +2203,8 @@ Испрати + + 00 Додајте корисничко име @@ -2149,6 +2227,12 @@ Прескокни Готово + + Ова корисничко име не е достапно, обидете се со друг број. + + Неважечко корисничко име, внесете најмалку %1$d знаци. + + Неважечко корисничко име, внесете најмногу %1$d знаци. %1$d контакт е на Signal! @@ -2346,6 +2430,10 @@ Процесирање… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2370,6 +2458,8 @@ Одговорено на поврзаниот уред. Одбиено на поврзаниот уред. Зафатено на поврзаниот уред. + + Опцијата за менување на камерата е преместена тука, допрете на видеото за да испробате Некој со променет безбедносен број се приклучи на овој повик. @@ -3925,14 +4015,16 @@ Не можеше да се отстрани членот од групата. - Член Барање Ваш контакт Отстрани од група Ажурирај контакт Блокирај Избриши - Го промени своето профилно име од %1$s во %2$s + + %1$s неодамна го смени името на профилот од %2$s во %3$s + + %1$s е во контактите на вашиот телефон %1$s се приклучи @@ -4988,6 +5080,12 @@ Додајте луѓе и групи од кои сакате да добивате известувања и повици кога е вклучен овој профил Додај луѓе или групи + + Исклучоци + + Дозволи ги сите повици + + Извести секогаш кога некој ќе ме спомне Додај @@ -5602,8 +5700,8 @@ Сите врски се прегледани, допрете на испрати за да продолжите. - Имате %1$d врска која можеби преинсталирала Signal или ги сменила уредите. Можете да ги прегледате сигурносните броеви на оваа врска пред да споделите приказна или да ги отстраните од вашата приказна. - Имате %1$d врски кои можеби реинсталирале Signal или ги смениле уредите. Можете да ги прегледате сигурносните броеви на оваа врска пред да споделите приказна или да ги отстраните од вашата приказна. + Имате %1$d врска која можеби реинсталирала Signal или сменила уред. Можете да ги прегледате сигурносните броеви или да продолжите со испраќање. + Имате %1$d врски кои можеби реинсталирале Signal или смениле уреди. Можете да ги прегледате нивните сигурносни броеви или да продолжите со испраќање. Проверка на безбедносен број @@ -5961,12 +6059,18 @@ IBAN шифрата на земјата не е поддржана Неважечки IBAN број + + Најмалку 2 знака + + Неважечка е-пошта iDEAL Внесете ги податоците за вашата банка, име и е-пошта. Stripe ја користи вашата е-пошта за да ви испраќа известувања за вашата донација. %1$s + + Внесете ги податоците од вашата платежна картичка. Signal не ги собира ниту сочувува вашите лични податоци. %1$s Дознајте повеќе @@ -6152,6 +6256,8 @@ Појдовен Пропуштен + + Пропуштено додека беше овозможен режимот за известувања Приклучете се @@ -6259,6 +6365,8 @@ Групен повик Пропуштен групен повик + + Пропуштен групен повик додека беше овозможен режимот за известувања Дојдовен групен повик diff --git a/app/src/main/res/values-ml/strings.xml b/app/src/main/res/values-ml/strings.xml index 4d1540ec35..42d473d6c4 100644 --- a/app/src/main/res/values-ml/strings.xml +++ b/app/src/main/res/values-ml/strings.xml @@ -323,8 +323,12 @@ ചിത്രം ഡൗൺലോഡ് ചെയ്യാൻ കഴിയുന്നില്ല. നിങ്ങൾ അത് വീണ്ടും അയയ്‌ക്കേണ്ടതുണ്ട്. വീഡിയോ ഡൗൺലോഡ് ചെയ്യാൻ കഴിയുന്നില്ല. നിങ്ങൾ അത് വീണ്ടും അയയ്‌ക്കേണ്ടതുണ്ട്. - - എഡിറ്റ് ചെയ്തത് %1$s + + ഇപ്പോൾ എഡിറ്റ് ചെയ്തു + + %1$s എഡിറ്റ് ചെയ്തത് + + %1$s-ന് എഡിറ്റ് ചെയ്തത് കോളിൽ ചേരുക @@ -358,6 +362,8 @@ സുരക്ഷിതമല്ലാത്ത MMS Signal സന്ദേശം + + സന്ദേശം അയയ്ക്കുക നമുക്ക് Molly-ലേക്ക് മാറാം %1$s ഒരു കോൺ‌ടാക്റ്റ് തിരഞ്ഞെടുക്കുക അറ്റാച്ച്മെന്റ് നിങ്ങൾ അയയ്‌ക്കുന്ന സന്ദേശ ഇനത്തിന്റെ വലുപ്പ പരിധി കവിഞ്ഞിരിക്കുന്നു. @@ -1277,6 +1283,10 @@ മിസ്‌ഡ് വോയ്‌സ് കോൾ മിസ്‌ഡ് വീഡിയോ കോൾ + + അറിയിപ്പ് പ്രൊഫൈൽ ഓണായിരിക്കുമ്പോൾ വോയ്സ് കോൾ മിസ്സ് ആയി + + അറിയിപ്പ് പ്രൊഫൈൽ ഓണായിരിക്കുമ്പോൾ വീഡിയോ കോൾ മിസ്സ് ആയി നിങ്ങൾ ഒരു വോയ്‌സ് കോൾ നിരസിച്ചു @@ -1828,6 +1838,8 @@ ക്യാമറാ ടോഗിൾ മ്യൂട്ട് ടോഗിൾ + + അധിക പ്രവർത്തനങ്ങൾ കോൾ അവസാനിപ്പിക്കൂ @@ -1844,10 +1856,68 @@ ഉപകരണത്തിന്‍റെ ഇയർപീസിനെ പ്രതിനിധീകരിക്കുന്ന ഐക്കൺ. + + കൈ ഉയർത്തുക + + കൈ ഉയർത്തുക + + കൈ താഴ്ത്തണോ? + + കൈ താഴ്ത്തുക + + റദ്ദാക്കുക + + നിങ്ങൾ കൈ ഉയർത്തി + + കാണുക + + + + %1$s ഒരു കൈ ഉയർത്തി + %1$s ഉം %2$d ഉം ഒരു കൈ ഉയർത്തി + + + + + %1$s + %1$s +%2$d + + + + ഉയർത്തിയ കൈയുടെ കാഴ്ച വിപുലമാക്കി + + + + Signal കണക്ഷൻ + + %1$s നിങ്ങളുടെ സിസ്റ്റം കോൺടാക്റ്റുകളിൽ ഉണ്ട് + + നിങ്ങൾക്ക് പൊതുവായി ഗ്രൂപ്പുകളൊന്നുമില്ല + + അഭ്യർത്ഥനകൾ ശ്രദ്ധാപൂർവ്വം അവലോകനം ചെയ്യുക + + പൊതുവായി %1$d ഗ്രൂപ്പുകൾ ഉണ്ട് + + വിവരങ്ങൾ + + നിങ്ങൾ + - - ഈ കോളിൽ - %1$d ആളുണ്ട് - ഈ കോളിൽ - %1$d ആളുകളുണ്ട്  + + ഈ കോളിൽ (%1$d) + ഈ കോളിൽ (%1$d) + + + Signal (%1$d)- നെ വിളിക്കും + Signal (%1$d) - നെ വിളിക്കും + + + Signal (%1$d) - നെ അറിയിക്കും + Signal (%1$d) - നെ അറിയിക്കും + + + (%1$d) കൈ ഉയർത്തി + (%1$d) കൈകൾ ഉയർത്തി @@ -1961,6 +2031,12 @@ കണ്ടു മീഡിയ + + + പേരിൽ പൊരുത്തക്കേട് കണ്ടു + + കാണുക + \'%1$s\' -നായി ഫലങ്ങളൊന്നും കണ്ടെത്തിയില്ല @@ -2127,6 +2203,8 @@ അയയ്‌ക്കുക + + 00 ഒരു ഉപയോക്തൃനാമം ചേർക്കുക @@ -2149,6 +2227,12 @@ ഒഴിവാക്കുക ചെയ്‌തു + + ഈ ഉപയോക്തൃനാമം ലഭ്യമല്ല, മറ്റൊരു നമ്പർ നൽകി നോക്കുക. + + ഉപയോക്തൃനാമം അസാധുവാണ്, കുറഞ്ഞത് %1$d അക്കങ്ങൾ നൽകുക. + + ഉപയോക്തൃനാമം അസാധുവാണ്, പരമാവധി %1$d അക്കങ്ങൾ നൽകുക. Signal-ൽ‌ %1$d കോൺ‌ടാക്റ്റ് ഉണ്ട്! @@ -2346,6 +2430,10 @@ പ്രോസസ് ചെയ്യുന്നു… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2370,6 +2458,8 @@ ഒരു ബന്ധിപ്പിച്ച ഉപകരണത്തിൽ ഉത്തരം നൽകി. ബന്ധിപ്പിച്ച ഉപകരണത്തിൽ നിരസിച്ചു. ബന്ധിപ്പിച്ച ഉപകരണത്തിൽ തിരക്കിലാണ്. + + ഫ്ലിപ്പ് ക്യാമറാ ഇങ്ങോട്ട് മാറ്റി, അത് പരീക്ഷിക്കാൻ നിങ്ങളുടെ വീഡിയോയിൽ ടാപ്പ് ചെയ്യുക മാറ്റിയ സുരക്ഷാ നമ്പറുമായി ആരോ ഈ കോളിൽ ചേർന്നിട്ടുണ്ട്. @@ -3925,14 +4015,16 @@ ഗ്രൂപ്പ് അംഗത്തെ നീക്കം ചെയ്യാനായില്ല. - അംഗം അഭ്യർത്ഥിക്കുക നിങ്ങളുടെ കോൺ‌ടാക്റ്റ് ഗ്രൂപ്പിൽ നിന്ന് നീക്കം ചെയ്യൂ കോൺ‌ടാക്റ്റ് പുതുക്കുക ബ്ലോക്ക് ചെയ്യുക ഇല്ലാതാക്കൂ - അടുത്തിടെ അവരുടെ പ്രൊഫൈൽ പേര് %1$s-ൽ നിന്ന് %2$s-ലേക്ക് മാറ്റി + + %1$s, അടുത്തിടെ തന്റെ പ്രൊഫൈൽ പേര് %2$s എന്നതിൽ നിന്ന് %3$s എന്നാക്കി + + %1$s നിങ്ങളുടെ സിസ്റ്റം കോൺടാക്റ്റുകളിൽ ഉണ്ട് %1$s ചേർന്നു @@ -4988,6 +5080,12 @@ ഈ പ്രൊഫൈൽ ഓണായിരിക്കുമ്പോൾ നിങ്ങൾക്ക് അറിയിപ്പുകളും കോളുകളും ആവശ്യമുള്ള ആളുകളെയും ഗ്രൂപ്പുകളെയും ചേർക്കുക ആളുകളെയോ ഗ്രൂപ്പുകളെയോ ചേർക്കുക + + ഉള്‍പ്പെടാത്തവ + + എല്ലാ കോളുകളും അനുവദിക്കുക + + എല്ലാ സൂചനകളും അറിയിക്കുക ചേർക്കുക @@ -5602,8 +5700,8 @@ എല്ലാ കണക്ഷനുകളും അവലോകനം ചെയ്‌തു, തുടരുന്നതിന് അയയ്ക്കുക എന്നതിൽ ടാപ്പ് ചെയ്യുക. - Signal വീണ്ടും ഇൻസ്റ്റാൾ ചെയ്യുകയോ ഉപകരണങ്ങൾ മാറ്റുകയോ ചെയ്‌തിട്ടുണ്ടാകാവുന്ന %1$d കണക്ഷൻ നിങ്ങൾക്ക് ഉണ്ട്. നിങ്ങൾക്ക് അവരുടെ സുരക്ഷാ നമ്പറുകൾ ഓപ്ഷണലായി അവലോകനം ചെയ്യാം. - Signal വീണ്ടും ഇൻസ്റ്റാൾ ചെയ്യുകയോ ഉപകരണങ്ങൾ മാറുകയോ ചെയ്‌തിട്ടുണ്ടാകാവുന്ന %1$d കണക്ഷനുകൾ നിങ്ങൾക്കുണ്ട്. നിങ്ങളുടെ സ്റ്റോറി അവരുമായി പങ്കിടുന്നതിന് മുമ്പ് അവരുടെ സുരക്ഷാ നമ്പറുകൾ അവലോകനം ചെയ്യുകയോ അവരെ നിങ്ങളുടെ സ്റ്റോറിയിൽ നിന്ന് നീക്കം ചെയ്യുന്നതിനെ കുറിച്ച് ചിന്തിക്കുകയോ ചെയ്യുക. + %1$d കണക്ഷൻ വീണ്ടും Signal ഇൻസ്‌റ്റാൾ ചെയ്‌തിരിക്കാം അല്ലെങ്കിൽ ഉപകരണങ്ങൾ മാറ്റിയിരിക്കാം. നിങ്ങൾക്ക് അവരുടെ സുരക്ഷാ നമ്പർ അവലോകനം ചെയ്യാം അല്ലെങ്കിൽ അയയ്ക്കുന്നത് തുടരാം. + %1$d കണക്ഷനുകൾ വീണ്ടും Signal ഇൻസ്‌റ്റാൾ ചെയ്‌തിരിക്കാം അല്ലെങ്കിൽ ഉപകരണങ്ങൾ മാറ്റിയിരിക്കാം. നിങ്ങൾക്ക് അവരുടെ സുരക്ഷാ നമ്പറുകൾ അവലോകനം ചെയ്യാം അല്ലെങ്കിൽ അയയ്ക്കുന്നത് തുടരാം. സുരക്ഷാ നമ്പർ ഉറപ്പാക്കു @@ -5961,12 +6059,18 @@ IBAN രാജ്യ കോഡ് പിന്തുണയ്ക്കുന്നില്ല IBAN അസാധുവാണ് + + കുറഞ്ഞത് 2 പ്രതീകങ്ങൾ + + ഇമെയിൽ വിലാസം അസാധുവാണ് iDEAL നിങ്ങളുടെ ബാങ്ക്, പേര്, ഇമെയിൽ എന്നിവ നൽകുക. നിങ്ങളുടെ സംഭാവനയുമായി ബന്ധപ്പെട്ട അപ്ഡേറ്റുകൾ അയയ്ക്കാൻ Stripe ഈ ഇമെയിൽ ഉപയോഗിക്കുന്നു. %1$s + + നിങ്ങളുടെ ബാങ്ക് വിശദാംശങ്ങൾ നൽകുക. Signal നിങ്ങളുടെ വ്യക്തിപരമായ വിവരങ്ങൾ ശേഖരിക്കുകയോ സംഭരിക്കുകയോ ചെയ്യുന്നില്ല. %1$s കൂടുതലറിയുക @@ -6152,6 +6256,8 @@ ഔട്ട്ഗോയിംഗ് മിസ്‌ഡ് + + അറിയിപ്പ് പ്രൊഫൈൽ ഓണായിരിക്കുമ്പോൾ മിസ്സ് ആയി ചേരുക @@ -6259,6 +6365,8 @@ ഗ്രൂപ്പ് കോൾ മിസ്‌ഡ് ഗ്രൂപ്പ് കോൾ + + അറിയിപ്പ് പ്രൊഫൈൽ ഓണായിരിക്കുമ്പോൾ ഗ്രൂപ്പ് കോൾ മിസ്സ് ആയി ഇൻകമിംഗ് ഗ്രൂപ്പ് കോൾ diff --git a/app/src/main/res/values-mr/strings.xml b/app/src/main/res/values-mr/strings.xml index eb734a74cc..0c89acae91 100644 --- a/app/src/main/res/values-mr/strings.xml +++ b/app/src/main/res/values-mr/strings.xml @@ -323,8 +323,12 @@ प्रतिमा डाउनलोड करु शकत नाही. आपणाला तो पुन्हा पाठवण्याची गरज आहे. व्हिडिओ डाउनलोड करु शकत नाही. आपणाला तो पुन्हा पाठवण्याची गरज आहे. - - संपादित केलेले %1$s + + आत्ता संपादित केलेले + + %1$s संपादित केलेले + + %1$s संपादित केलेले कॉलमध्ये सामील व्हा @@ -358,6 +362,8 @@ असुरक्षित MMS Signal संदेश + + संदेश पाठवा Molly %1$s वर स्विच करूया कृपया एक संपर्क निवडा संलग्नचा आकार आपण पाठवत असलेल्या संदेशाच्या प्रकारासाठीच्या मर्यादेपेक्षा जास्त आहे @@ -1277,6 +1283,10 @@ चूकवलेला व्हाईस कॉल चूकवलेला व्हिडिओ कॉल + + अधिसूचना प्रोफाइल सुरू असताना मिस्ड व्हॉईस कॉल + + अधिसूचना प्रोफाइल सुरू असताना व्हिडिओ कॉल मिस केला आपण व्हाईस कॉलला नकार दिला @@ -1828,6 +1838,8 @@ कॅमेरा टॉगल करा म्यूट टॉगल करा + + अतिरिक्त क्रिया कॉल संपवा @@ -1844,10 +1856,68 @@ डिव्हाईसच्या ईअरपीसचे प्रतिनिधित्व करणारे चिन्ह. + + हात वर करा + + हात वर करा + + आपल्याला हात खाली करायचा आहे? + + हात खाली घ्या + + रद्द करा + + आपण आपला हात वर केला आहे + + बघा + + + + %1$s ने हात वर केला आहे + %1$s + %2$d नी हात वर केला आहे + + + + + %1$s + %1$s +%2$d + + + + वर केलेल्या हाताचा दृश्य विस्तार करा + + + + Signal कनेक्शन + + %1$s आपल्या सिस्टिम संपर्कामध्ये आहेत + + आपल्याकडे कोणताही सामायिक गट नाही + + विनंत्यांचे काळजीपूर्वक पुनरावलोकन करा + + सामान्यत: %1$d गट + + आपल्याबद्दल + + आपण + - - या कॉलमध्ये · %1$d व्यक्ती - या कॉलमध्ये · %1$d व्यक्ती + + (%1$d)या कॉलमध्ये + (%1$d)या कॉल्समध्ये + + + (%1$d)ला Signal रिंग करेल + (%1$d) ला Signal रिंग करेल + + + (%1$d)ला Signal सूचित करेल + (%1$d)ना Signal सूचित करेल + + + (%1$d) ने हात वर केला + (%1$d) नी हात वर केला @@ -1961,6 +2031,12 @@ बघितले मिडिया + + + नावात गोंधळ आढळला + + बघा + \'%1$s\' करिता कुठलेही परिणाम नाहीत @@ -2127,6 +2203,8 @@ पाठवा + + 00 वापरकर्तानाव जोडा @@ -2149,6 +2227,12 @@ वगळा ठीक + + हे वापरकर्ता नाव उपलब्ध नाही, दुसरा क्रमांक वापरून पहा. + + अवैध वापरकर्ता नाव, कमीत कमी %1$d अंक प्रविष्ट करा. + + अवैध वापरकर्ता नाव, कमाल %1$d अंक प्रविष्ट करा. %1$d संपर्क Signal वर आहे! @@ -2346,6 +2430,10 @@ प्रक्रिया करत आहे… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2370,6 +2458,8 @@ लिंक केलेल्या डिव्हाईसवर उत्तर दिले. लिंक केलेल्या डिव्हाईसवर नकार दिला. लिंक केलेल्या डिव्हाईसवर व्यस्त. + + फ्लिप कॅमेरा या ठिकाणी हलवण्यात आले आहे, त्याला वापरून पाहण्यासाठी आपल्या व्हिडीओवर टॅप करा कुणीतरी कॉलमध्ये सामील झाले आहे ज्यांचा सुरक्षितता नंबर बदलला आहे. @@ -3925,14 +4015,16 @@ ग्रुप सदस्य काढून टाकण्यात अयशस्वी. - सदस्य विनंती आपला संपर्क ग्रुपमधून काढून टाका संपर्क अद्यतनित करा अवरोधित करा हटवा - अलीकडे त्यांनी %1$s वरून %2$s ला नाव बदलले आहे + + %1$s नी अलिकडेच त्यांचे प्रोफाइन नाव %2$s वरून %3$s ला बदलले + + %1$s आपल्या सिस्टिम संपर्कामध्ये आहेत %1$s सामील झाले @@ -4988,6 +5080,12 @@ हे प्रोफाइल चालू असताना ज्यांच्याकडून आपल्याला सूचना आणि कॉल हवे आहे असे लोक आणि गट जोडा लोक किंवा गट जोडा + + अपवाद + + सर्व कॉल्सना परवानगी द्या + + सर्व उल्लेखांसाठी सूचित करा जोडा @@ -5602,8 +5700,8 @@ सर्व कनेक्शन्सचे पुनरावलोकन करण्यात आले आहे, पुढे सुरू ठेवण्यासाठी टॅप करा. - आपल्याकडे %1$d कनेक्शन आहे ज्यांनी Signal पुन्हा स्थापना केले किंवा डिव्हाइसेस बदलली असण्याची शक्यता आहे. आपण आपली स्टोरी त्यांच्यासोबत शेअर करण्यापूर्वी त्यांच्या सुरक्षा क्रमांकाचे पुनवरालोकन करा किंवा तुमच्या स्टोरीमधून ते काढून टाकण्याचे विचारात घ्या. - आपल्याकडे %1$d कनेक्शन्स आहेत ज्यांनी Signal पुन्हा स्थापना केले किंवा डिव्हायसेस बदलले असण्याची शक्यता आहे. आपण आपली स्टोरी त्यांच्यासोबत शेअर करण्यापूर्वी त्यांच्या सुरक्षा क्रमांकाचे पुनवरालोकन करा किंवा तुमच्या स्टोरीमधून ते काढून टाकण्याचे विचारात घ्या. + %1$d कनेक्शन ने Signal पुन्हा स्थापित केलेले असू शकते किंवा डिव्हायसेस बदललेली असू शकतात. आपण त्यांच्या सुरक्षितता क्रमांकांचे पुनरावलोकन करू शकता किंवा पाठवणे पुढे सुरू ठेऊ शकता. + %1$d कनेक्शन नी Signal पुन्हा स्थापित केले असू शकते किंवा डिव्हायसेस बदलले असू शकतात. आपण त्यांच्या सुरक्षितता क्रमांकांचे पुनरावलोकन करू शकता किंवा पाठवणे पुढे सुरू ठेऊ शकता. सुरक्षितता नंबर सत्यापित करा @@ -5961,12 +6059,18 @@ IBAN देश कोड समर्थित नाही अवैध IBAN + + किमान 2 वर्ण + + अवैध ईमेल पत्ता iDEAL आपल्या बँकेचे नाव आणि ईमेल प्रविष्ट करा. Stripe हा ईमेल आपणाला आपल्या देणगीबद्दल अद्यतने पाठवण्यास वापरते. %1$s + + आपला बँक तपशील प्रविष्ट करा. Signal आपली वैयक्तिक माहिती संकलित किंवा संग्रहित करत नाही. %1$s अधिक जाणून घ्या @@ -6152,6 +6256,8 @@ बाहेर जाणारे मिस्ड + + अधिसूचना प्रोफाइल सुरू असताना मिस झालेले सामील व्हा @@ -6259,6 +6365,8 @@ गट कॉल चुकलेले ग्रुप कॉल + + अधिसूचना प्रोफाइल सुरू असताना ग्रुप कॉल मिस केला येणारे ग्रुप कॉल diff --git a/app/src/main/res/values-ms/strings.xml b/app/src/main/res/values-ms/strings.xml index 9392c26a4d..91b20e268d 100644 --- a/app/src/main/res/values-ms/strings.xml +++ b/app/src/main/res/values-ms/strings.xml @@ -322,8 +322,12 @@ Tidak dapat memuat turun imej. Anda perlu menghantarnya semula. Tidak dapat memuat turun video. Anda perlu menghantarnya semula. - - Diedit %1$s + + Diedit Sekarang + + Diedit %1$s + + Diedit %1$s Sertai panggilan @@ -355,6 +359,8 @@ MMS yang tidak selamat Mesej Signal + + Menghantar mesej Mari beralih kepada Molly %1$s Sila pilih kenalan Lampiran melebihi had saiz untuk jenis mesej yang anda hantar. @@ -1233,6 +1239,10 @@ Panggilan suara terlepas Panggilan video terlepas + + Panggilan suara terlepas semasa profil pemberitahuan dihidupkan + + Panggilan video terlepas semasa profil pemberitahuan dihidupkan Anda menolak panggilan suara @@ -1757,6 +1767,8 @@ Togol kamera Togol redam + + Tindakan tambahan Tamatkan panggilan @@ -1773,9 +1785,62 @@ Ikon yang mewakili alat dengar peranti. + + Angkat Tangan + + Angkat Tangan + + Turunkan tangan anda? + + Turun Tangan + + Batal + + Anda mengangkat tangan + + Lihat + + + + %1$s + %2$d mengangkat tangan + + + + + %1$s +%2$d + + + + Kembangkan untuk melihat tangan yang diangkat + + + + Hubungan Signal + + %1$s berada dalam kenalan sistem anda + + Anda tidak mempunyai kumpulan yang sama + + Review permintaan sebaik-baiknya + + %1$d kumpulan yang sama + + Perihal + + Anda + - - Dalam panggilan ini · %1$d orang + + Dalam panggilan ini (%1$d) + + + Signal akan Memanggil (%1$d) + + + Signal akan Memberitahu (%1$d) + + + Mengangkat tangan (%1$d) @@ -1888,6 +1953,12 @@ Dilihat Media + + + Konflik nama ditemui + + Lihat + Keputusan tiada dijumpai untuk \'%1$s\' @@ -2054,6 +2125,8 @@ Hantar + + 00 Tambah nama pengguna @@ -2076,6 +2149,12 @@ Langkau Selesai + + Nama pengguna ini tidak tersedia, cuba nombor lain. + + Nama pengguna tidak sah, masukkan sekurang-kurangnya %1$d digit. + + Nama pengguna tidak sah, masukkan maksimum %1$d digit. %1$d kenalan menggunakan Signal! @@ -2269,6 +2348,10 @@ Memproses… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2293,6 +2376,8 @@ Dijawab pada peranti yang dipautkan. Ditolak pada peranti yang dipautkan. Sibuk pada peranti yang dipautkan. + + Flip Camera telah dialihkan ke sini, ketik video anda untuk mencuba Seseorang telah menyertai panggilan ini dengan nombor keselamatan yang telah berubah. @@ -3824,14 +3909,16 @@ Gagal mengalih keluar ahli kumpulan. - Ahli Permintaan Kenalan anda Alih keluar daripada kumpulan Kemas kini kenalan Sekat Padam - Baru-baru ini mengubah nama profil mereka daripada %1$s kepada %2$s + + %1$s baru-baru ini menukar nama profil mereka daripada %2$s kepada %3$s + + %1$s berada dalam kenalan sistem anda %1$s telah menyertai kumpulan @@ -4878,6 +4965,12 @@ Tambah orang dan kumpulan yang anda mahukan pemberitahuan dan panggilan semasa profil ini dihidupkan Tambah orang atau kumpulan + + Pengecualian + + Benarkan semua panggilan + + Pemberitahuan untuk semua sebutan Tambah @@ -5475,7 +5568,7 @@ Semua hubungan telah disemak, ketik hantar untuk meneruskan. - Anda mempunyai %1$d hubungan yang mungkin telah memasang semula Signal atau menukar peranti. Anda boleh memilih untuk menyemak nombor keselamatan mereka sebelum menghantar. + %1$d hubungan mungkin telah memasang semula Signal atau menukar peranti. Anda boleh menyemak nombor keselamatan mereka atau meneruskan penghantaran. Sahkan nombor keselamatan @@ -5829,12 +5922,18 @@ Kod negara IBAN tidak disokong IBAN tidak sah + + Minimum 2 aksara + + Alamat e-mel tidak sah iDEAL Masukkan bank, nama dan e-mel anda. Stripe menggunakan e-mel ini untuk menghantar kemas kini tentang derma anda. %1$s + + Masukkan butiran bank anda. Signal tidak mengumpul atau menyimpan maklumat peribadi anda. %1$s Ketahui lebih lanjut @@ -6019,6 +6118,8 @@ Panggilan keluar Panggilan terlepas + + Terlepas semasa profil pemberitahuan dihidupkan Sertai @@ -6123,6 +6224,8 @@ Panggilan kumpulan Panggilan kumpulan terlepas + + Panggilan kumpulan terlepas semasa profil pemberitahuan dihidupkan Panggilan kumpulan masuk diff --git a/app/src/main/res/values-my/strings.xml b/app/src/main/res/values-my/strings.xml index 672510e1e1..582b90cd51 100644 --- a/app/src/main/res/values-my/strings.xml +++ b/app/src/main/res/values-my/strings.xml @@ -322,8 +322,12 @@ ပုံ ဒေါင်းလုဒ်လုပ်၍ မရပါ။ ၎င်းကို ထပ်ပို့ရန် လိုအပ်ပါသည်။ ဗီဒီယို ဒေါင်းလုဒ်လုပ်၍ မရပါ။ ၎င်းကို ထပ်ပို့ရန် လိုအပ်ပါသည်။ - - တည်းဖြတ်ထား %1$s + + ယခု တည်းဖြတ်ထား + + တည်းဖြတ်ထား %1$s + + တည်းဖြတ်ထား %1$s ခေါ်ဆိုမှုတွင် ပါဝင်ရန် @@ -355,6 +359,8 @@ မလုံခြုံသော MMS Signal မက်ဆေ့ချ် + + မက်ဆေ့ချ် ပို့ရန် Molly %1$s ကိုပြောင်းသုံးကြပါစို့ ဆက်သွယ်လိုသည့်သူကို ရွေးချယ်ပါ ပူးတွဲဖိုင်သည် သတ်မှတ်ဖိုင်အရွယ်အစားထက်ကြီးနေသည်။ @@ -1233,6 +1239,10 @@ လွတ်သွားသော အော်ဒီယိုကောလ် လွတ်သွားသော ဗီဒီယိုကောလ် + + အသိပေးချက် ပရိုဖိုင်ကို ဖွင့်ထားစဉ် အော်ဒီယိုကောလ် လွတ်သွားသည် + + အသိပေးချက် ပရိုဖိုင်ကို ဖွင့်ထားစဉ် ဗီဒီယိုကောလ် လွတ်သွားသည် သင်သည် အော်ဒီယိုကောလ်ကို ငြင်းပယ်လိုက်သည် @@ -1757,6 +1767,8 @@ ကင်မရာ ဖွင့်/ပိတ်လုပ်ရန် အသံ ဖွင့်/ပိတ်လုပ်ရန် + + နောက်ထပ် လုပ်ဆောင်ချက်များ ဖုန်းချမည် @@ -1773,9 +1785,62 @@ စက်တစ်ခု၏ နားကြပ်ကို ကိုယ်စားပြုသည့် အိုင်ကွန်။ + + လက်မြှောက်မည် + + လက်မြှောက်မည် + + လက်ပြန်ချမည်လား။ + + လက်ပြန်ချမည် + + မလုပ်တော့ပါ + + သင် လက်မြှောက်ထားပါသည် + + ကြည့်မယ် + + + + %1$s + %2$d ဦး လက်မြှောက်ထားပါသည် + + + + + %1$s + %2$d ဦး + + + + လက်မြှောက်ထားသူများကို ဖြန့်၍ ကြည့်ရှုရန် + + + + Signal အဆက်အသွယ် + + %1$s သည် သင့်စနစ်ထဲရှိ အဆက်အသွယ်များတွင် ရှိပြီးဖြစ်သည် + + သင်တို့နှစ်ဦးစလုံး ပါဝင်သောအဖွဲ့ မရှိပါ + + တောင်းဆိုမှုများကိုအသေအချာပြန်လည်စစ်ဆေးပါ + + တူညီသော အဖွဲ့ %1$d ဖွဲ့ + + အသုံးပြုသူအကြောင်း + + သင် + - - ၎င်းခေါ်ဆိုမှုထဲတွင် - %1$d ယောက် + + ကောလ်တွင် ပါဝင်သူ (%1$d) + + + Signal သည် (%1$d) ထံ ဖုန်းခေါ်ပါမည် + + + Signal သည် (%1$d) ထံ အကြောင်းကြားပါမည် + + + လက်မြှောက်ထားသူ (%1$d ဦး) @@ -1888,6 +1953,12 @@ ကြည့်ရှုပြီး ရုပ်၊သံ၊ပုံ များ + + + အမည်ကွဲလွဲမှု တွေ့ရှိရပါသည် + + ကြည့်မယ် + \'%1$s\' ကို ရှာမတွေ့ပါ @@ -2054,6 +2125,8 @@ ပို့ပါ + + 00 သုံးစွဲသူအမည် ပေါင်းထည့်ရန် @@ -2076,6 +2149,12 @@ ကျော်ပါ။ ပြီးပါပြီ + + ဤသုံးစွဲသူအမည်ကို သုံး၍မရပါ၊ အခြားနံပါတ်တစ်ခုကို စမ်းကြည့်ပါ။ + + သုံးစွဲသူအမည် မမှန်ကန်ပါ၊ အနည်းဆုံး ဂဏန်း %1$d လုံး ရိုက်ထည့်ပါ။ + + သုံးစွဲသူအမည် မမှန်ကန်ပါ၊ အများဆုံး ဂဏန်း %1$d လုံး ရိုက်ထည့်ပါ။ အဆက်အသွယ် %1$d ဦးသည် Signal ပေါ်ရှိပါသည်! @@ -2269,6 +2348,10 @@ လုပ်ဆောင်နေဆဲ… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2293,6 +2376,8 @@ ချိတ်ဆက်ထားသော စက်ပေါ်တွင် ဖြေဆိုခဲ့ပါသည်။ ချိတ်ဆက်ထားသော စက်ပေါ်တွင် ငြင်းဆိုခဲ့ပါသည်။ ချိတ်ဆက်ထားသော စက်ပေါ်တွင် မအားပါ။ + + ကင်မရာ လှည့်ပြောင်းခလုတ်ကို ဤနေရာသို့ ရွှေ့ထားပါသည်၊ စမ်းကြည့်ရန် သင့်ဗီဒီယိုကို နှိပ်ပါ ပြောင်းလဲထားသော လုံခြုံရေးနံပါတ်ဖြင့် ခေါ်ဆိုမှုထဲ ဝင်လာသော တစ်ဦးရှိပါသည်။ @@ -3824,14 +3909,16 @@ အဖွဲ့၀င်ကို ဖယ်ရှားရန် မအောင်မြင်ပါ။ - အဖွဲ့ဝင် တောင်းခံ သင်၏အဆက်အသွယ် အဖွဲ့မှ ဖယ်ရှားရန် အဆက်အသွယ် အပ်ဒိတ်မယ် ဘလော့ခ်ရန် ဖျက်ရန် - မကြာသေးမီက ပရိုဖိုင်းအမည်ကို %1$s မှ %2$s သို့ ပြောင်းထားခဲ့ပါသည်။ + + မကြာသေးမီက %1$s သည် သူ၏ ပရိုဖိုင်အမည်ကို %2$s မှ %3$s သို့ ပြောင်းခဲ့ပါသည် + + %1$s သည် သင့်စနစ်ထဲရှိ အဆက်အသွယ်များတွင် ရှိပြီးဖြစ်သည် အဖွဲ့ထဲ %1$s ၀င်ရောက်လာသည်။ @@ -4878,6 +4965,12 @@ ဤပရိုဖိုင် ပွင့်သည့်အခါ သင် အသိပေးချက်များနှင့် ခေါ်ဆိုမှုများ ရယူလိုသူများနှင့် အဖွဲ့များကို ပေါင်းထည့်ပါ လူများနှင့် အဖွဲ့များကို ပေါင်းထည့်ရန် + + ခြွင်းချက်များ + + ကောလ်အားလုံး ခွင့်ပြုရန် + + မန်းရှင်းခေါ်မှုများအားလုံးအတွက် အသိပေးရန် ပေါင်းထည့်မယ် @@ -5475,7 +5568,7 @@ ချိတ်ဆက်မှုများ အားလုံးကို ပြန်လည်စိစစ်ထားပါသည်၊ ဆက်လုပ်ရန်အတွက် ပေးပို့မည်ကို နှိပ်ပါ။ - သင့်တွင် Signal ကို ပြန်လည်ထည့်သွင်းထားသည့် သို့မဟုတ် စက်ပြောင်းထားသည့် အဆက်အသွယ် %1$d ဦး ရှိပါသည်။ သင့်စတိုရီကို ထိုသူတို့နှင့် မျှဝေခြင်းမပြုမီ ၎င်းတို့၏ လုံခြုံရေးနံပါတ်ကို ပြန်လည်စိစစ်ပါ သို့မဟုတ် ၎င်းတို့အား သင့်စတိုရီမှ ဖယ်ရှားရန် စဉ်းစားပါ။ + အဆက်အသွယ် %1$d ဦးသည် Signal ကို ပြန်လည်ထည့်သွင်းခြင်း သို့မဟုတ် စက်ပြောင်းခြင်း ပြုလုပ်ခဲ့နိုင်ပါသည်။ ၎င်းတို့၏ လုံခြုံရေးနံပါတ်များကို သင် ပြန်လည်စိစစ်နိုင်ပါသည် သို့မဟုတ် ပေးပို့မှုကို ဆက်လုပ်နိုင်ပါသည်။ စိတ်ချရသောနံပါတ်ကို စစ်ဆေးအတည်ပြုခြင်း @@ -5829,12 +5922,18 @@ IBAN နိုင်ငံကုဒ်ကို ပံ့ပိုးပေးခြင်းမရှိပါ IBAN မမှန်ကန်ပါ + + အနည်းဆုံး စာလုံး 2 လုံး + + အီးမေးလ်လိပ်စာ မမှန်ကန်ပါ iDEAL သင်၏ ဘဏ်၊ သင့်အမည်နှင့် အီးမေးလ်ကို ရိုက်ထည့်ပါ။ Stripe သည် သင့်လှူဒါန်းမှုနှင့်ပတ်သက်၍ အပ်ဒိတ်များ ပေးပို့ရန် ထိုအီးမေးလ်ကို အသုံးပြုပါသည်။ %1$s + + သင့်ဘဏ်အချက်အလက် အသေးစိတ်ကို ဖြည့်ပါ။ Signal သည် သင်၏ ကိုယ်ရေးအချက်အလက်များကို စုဆောင်းခြင်း သို့မဟုတ် သိမ်းဆည်းခြင်း မပြုပါ။ %1$s ပိုမိုလေ့လာရန် @@ -6019,6 +6118,8 @@ အထွက် လွတ်သွားသည် + + အသိပေးချက် ပရိုဖိုင်ကို ဖွင့်ထားစဉ် လွတ်သွားသည် ပါဝင်ရန် @@ -6123,6 +6224,8 @@ အဖွဲ့လိုက် ခေါ်ဆိုမှု လွတ်သွားသော အဖွဲ့ကောလ် + + အသိပေးချက် ပရိုဖိုင်ကို ဖွင့်ထားစဉ် အဖွဲ့ကောလ် လွတ်သွားသည် အဝင် အဖွဲ့ကောလ် diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml index bbc2404d3d..e93d6db198 100644 --- a/app/src/main/res/values-nb/strings.xml +++ b/app/src/main/res/values-nb/strings.xml @@ -323,8 +323,12 @@ Kunne ikke laste ned bildet. Du må sende det på nytt. Kunne ikke laste ned videoen. Du må sende den på nytt. - - Redigert %1$s + + Nettopp redigert + + Redigert %1$s + + Redigert %1$s Bli med i samtalen @@ -358,6 +362,8 @@ Usikker MMS Signal-melding + + Send melding La oss bytte til Molly %1$s Velg en kontakt Vedlegg overskrider maksimal størrelsesgrense for gjeldende meldingstype. @@ -1277,6 +1283,10 @@ Tapt taleanrop Tapt videoanrop + + Tapt taleanrop mens varslingsprofil var slått på + + Tapt videoanrop mens varslingsprofil var slått på. Du avviste et taleanrop @@ -1828,6 +1838,8 @@ Slå på/av kamera Slå på/av lyd + + Flere valg Avslutt samtale @@ -1844,10 +1856,68 @@ Et ikon som viser øretelefoner. + + Rekk opp hånden + + Rekk opp hånden + + Vil du ta ned hånden? + + Ta ned hånden + + Avbryt + + Du rakk opp hånden + + Vis + + + + %1$s har rukket opp hånden + %1$s + %2$d har rukket opp hånden + + + + + %1$s + %1$s + %2$d + + + + Se alle opprakte hender + + + + Signal-kontakter + + %1$s står i kontaktlisten på enheten din + + Dere har ingen grupper til felles + + Gjennomgå forespørsler nøye + + %1$d grupper til felles + + Om + + Deg + - - Er i denne samtalen · %1$d person - Er i denne samtalen · %1$d personer + + I samtalen (%1$d) + I samtalen (%1$d) + + + Signal kommer til å ringe (%1$d) + Signal kommer til å ringe (%1$d) + + + Signal sender et varsel til (%1$d) + Signal sender et varsel til (%1$d) + + + Opprakt hånd (%1$d) + Opprakte hender (%1$d) @@ -1961,6 +2031,12 @@ Sett Medier + + + Navneproblem oppdaget + + Vis + Ingen resultater funnet for \'%1$s\' @@ -2127,6 +2203,8 @@ Send + + 00 Legg til et brukernavn @@ -2149,6 +2227,12 @@ Hopp over Ferdig + + Dette brukernavnet er ikke tilgjengelig. Velg et annet nummer. + + Ugyldig brukernavn. Angi minst %1$d tegn. + + Ugyldig brukernavn. Angi maks. %1$d tegn. %1$d kontakt er på Signal! @@ -2346,6 +2430,10 @@ Behandler … + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2370,6 +2458,8 @@ Besvart på en koblet enhet. Avslått på en koblet enhet. Opptatt på en koblet enhet. + + Funksjonen «Vend kamera» er flyttet hit. Trykk på videoen for å prøve det. Noen har blitt med i samtalen med et sikkerhetsnummer som er endret. @@ -3925,14 +4015,16 @@ Klarte ikke fjerne gruppemedlem. - Medlem Forespør Din kontakt Fjern fra gruppen Oppdater kontakt Blokker Slett - Endret nylig profilnavnet fra %1$s til %2$s + + %1$s endret nylig profilnavnet fra %2$s til %3$s + + %1$s står i kontaktlisten på enheten din %1$s ble med @@ -4988,6 +5080,12 @@ Legg til personer og grupper som du ønsker å motta varsler og anrop fra når denne profilen er aktiv Legg til personer eller grupper + + Unntak + + Tillat alle anrop + + Varsle meg om alle omtaler Legg til @@ -5602,8 +5700,8 @@ Alle kontaktene er gjennomgått, trykk på Send for å fortsette. - Du har %1$d kontakter som kan ha installert Signal på nytt eller byttet enhet. Du bør sjekke sikkerhetsnummeret hens eller fjerne hen fra storyen din før du eventuelt deler den med hen. - Du har %1$d kontakter som kan ha installert Signal på nytt eller byttet enhet. Du bør sjekke sikkerhetsnumrene deres eller fjerne dem fra storyen din før du eventuelt deler den med dem. + %1$d kontakt har byttet enhet eller lastet ned Signal på nytt. Du kan velge å sjekke sikkerhetsnummeret til vedkommende, eller sende meldingen. + %1$d kontakter har byttet enhet eller lastet ned Signal på nytt. Du kan velge å sjekke sikkerhetsnumrene deres, eller sende meldingene. Bekreft sikkerhetsnummer @@ -5961,12 +6059,18 @@ Landskoden støttes ikke Ugyldig IBAN-nummer + + Minst to tegn + + Ugyldig e-postadresse iDEAL Skriv inn navnet og e-posten din samt navnet på banken. Stripe sender oppdateringer om pengegaven din til denne e-postadressen. %1$s + + Angi bankinformasjonen din. Signal samler ikke inn og lagrer ikke personopplysningene dine. %1$s Les mer @@ -6152,6 +6256,8 @@ utgående ubesvart + + Tapt mens varslingsprofil var slått på Bli med @@ -6259,6 +6365,8 @@ Gruppesamtale Ubesvart gruppeanrop + + Tapt gruppeanrop mens varslingsprofil var slått på. Innkommende gruppeanrop diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 5b55334366..a09b008abf 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -20,7 +20,7 @@ Ja Nee - Wissen + Verwijderen Even geduld… Opslaan Notitie aan mezelf @@ -94,7 +94,7 @@ %1$s heeft Betalingen niet geactiveerd - Wil je hem of haar een verzoek sturen om Betalingen te activeren? + Wil je deze persoon een verzoek sturen om Betalingen te activeren? Stuur verzoek @@ -170,7 +170,7 @@ Jullie zullen weer berichten naar elkaar kunnen verzenden, kunnen bellen, en je profielnaam, -foto en -omschrijving zullen voor hem of haar weer worden bijgewerkt. Jullie zullen weer berichten naar elkaar kunnen verzenden via Signal. - Geblokkeerde mensen kunnen je niet bellen of je berichten sturen. + Geblokkeerde personen kunnen je niet bellen of je berichten sturen. Geblokkeerde personen kunnen je niet bellen en geen berichten sturen. Updates en nieuws over Signal blokkeren. @@ -309,8 +309,8 @@  Meer lezen  Meer downloaden   In afwachting - Dit bericht is gewist. - Je hebt dit bericht gewist. + Dit bericht is verwijderd. + Je hebt dit bericht verwijderd. Kan bericht niet downloaden. %1$s zal het opnieuw moeten verzenden. @@ -323,8 +323,12 @@ Kan afbeelding niet downloaden. Je zal het opnieuw moeten verzenden. Kan video niet downloaden. Je zal het opnieuw moeten verzenden. - - Bewerkt %1$s + + Bewerkt Nu + + Bewerkt %1$s + + Bewerkt %1$s Aan oproep deelnemen @@ -358,6 +362,8 @@ Onbeveiligde mms Signal-bericht + + Bericht verzenden Laten we Molly gebruiken om een gesprek te voeren %1$s Kies een gesprekspartner Bijlage overschrijdt de maximale grootte voor het type bericht dat je wilt verzenden. @@ -365,7 +371,7 @@ Omdat je niet langer lid bent van deze groep ontvang je geen nieuwe berichten en kun je geen berichten verzenden. Alleen %1$s kunnen berichten verzenden. beheerders - Bericht verzenden naar een beheerder + Bericht sturen naar een beheerder Je kunt geen groepsoproep beginnen Alleen beheerders kunnen in deze groep oproepen beginnen. Er is geen app beschikbaar op je apparaat om deze link te openen. @@ -395,7 +401,7 @@ Groep verwijderen en verlaten? Deze chat zal op al je eigen apparaten worden verwijderd. Je zult deze groep verlaten en het gesprek zal op al je eigen apparaten worden verwijderd. - Wissen + Verwijderen Verwijderen en verlaten Molly heeft toegang tot je microfoon nodig om %1$s te bellen. @@ -433,8 +439,8 @@ Contacten-app niet gevonden. - Geselecteerd bericht wissen? - Geselecteerde berichten wissen? + Geselecteerd bericht verwijderen? + Geselecteerde berichten verwijderen? Onversleuteld opslaan? @@ -458,21 +464,21 @@ Internet (versleuteld met Signal-protocol) Mms (niet versleuteld) Sms (niet versleuteld) - Aan het wissen - Berichten aan het wissen… - Voor mij wissen - Wissen voor iedereen + Aan het verwijderen + Berichten aan het verwijderen… + Verwijderen voor mezelf + Verwijderen voor iedereen - Wissen van dit apparaat + Verwijderen van dit apparaat - Overal wissen + Verwijderen van alle apparaten Dit bericht wordt verwijderd voor iedereen in de chat als ze een recente versie van Signal gebruiken. Ze kunnen zien dat je een bericht hebt verwijderd. Oorspronkelijk bericht niet gevonden Het oorspronkelijke bericht is niet langer beschikbaar Openen van bericht is mislukt Je kunt elk bericht naar rechts vegen om snel een reactie te schijven Je kunt elk bericht naar links vegen om snel een reactie te schijven - Eenmaligeweergave-media worden verwijderd nadat ze verzonden zijn + Eenmaligeweergave-media worden verwijderd na verzenden Je hebt dit bericht al bekeken Je kunt in deze chat notities voor jezelf achterlaten. Alle nieuwe notities worden gesynchroniseerd met apparaten die gekoppeld zijn met je account. %1$d groepsleden hebben dezelfde naam. @@ -483,11 +489,11 @@ Verifiëren Niet nu Je veiligheidsnummer met %1$s is veranderd - Je veiligheidsnummer met %1$s is veranderd. Waarschijnlijk heeft hij of zij Signal opnieuw geïnstalleerd of is hij of zij op een nieuwe telefoon overgestapt. Als je echt zeker wilt weten dat je nog steeds met dezelfde persoon communiceert, tik dan op ‘Verifiëren’ om het veiligheidsnummer na te kijken. Voor de meeste mensen is deze extra controle niet nodig. + Je veiligheidsnummer met %1$s is veranderd, waarschijnlijk omdat je contact Signal opnieuw heeft geïnstalleerd of van telefoon is gewisseld. Tik op ‘Verifiëren’ om het nieuwe veiligheidsnummer te bevestigen en zeker te zijn dat je nog steeds met dezelfde persoon communiceert. Dit is optioneel. Verzoek blokkeren? - %1$s zal niet langer via de groepslink lid kunnen worden van deze groep en ook niet kunnen verzoeken om lid te worden van de groep. Hij of zij kan dan nog wel lid worden via een uitnodiging. + %1$s zal niet langer via de groepslink lid kunnen worden van deze groep en ook niet kunnen verzoeken om lid te worden van de groep. Deze persoon kan dan nog wel lid worden via een uitnodiging. Verzoek blokkeren @@ -517,7 +523,7 @@ Hiermee wordt de geselecteerde chat permanent verwijderd. Hiermee worden de geselecteerde chats permanent verwijderd. - Aan het wissen + Aan het verwijderen Geselecteerde chats verwijderen… Chat gearchiveerd @@ -600,21 +606,21 @@ Wie kan mij via mijn nummer vinden? - Iedereen die jouw telefoonnummer in zijn contacten heeft staan, zal je kunnen zien als een contact op Signal. Anderen kunnen je vinden via jouw telefoonnummer als ze daarop zoeken. + Iedereen die jouw telefoonnummer in hun contacten heeft staan, zal jou kunnen zien als een contact op Signal. Anderen kunnen je vinden via jouw telefoonnummer als ze daarop zoeken. - Niemand op Signal kan je vinden met jouw telefoonnummer. + Niemand op Signal kan jou vinden met jouw telefoonnummer. Back-upgegevens terugzetten? - Je hebt nu de gelegenheid om berichten en media uit een back-upbestand terug te zetten. Let op: Herstellen vanuit een back-upbestand kan alleen tijdens de installatie van de Signal-app. Als je deze stap overslaat kun je dit op een later moment niet alsnog doen. + Herstel je berichten en media vanuit een lokale back-up. Herstellen vanuit een back-up kan alleen tijdens deze stap en kun je op een later moment niet alsnog doen. Back-upgegevens terugzetten pictogram Back-upbestand kiezen Meer lezen Geen bestandsbrowser beschikbaar - Gegevens herstellen vanuit een back-upbestand is voltooid - Om back-upbestanden te blijven maken, kies een directory waar je back-upbestanden vanaf nu in wilt opslaan. + Gegevens herstellen vanuit een back-up is voltooid + Om back-ups te blijven maken, kies een map waar je back-ups vanaf nu in wilt opslaan. Map kiezen Niet nu @@ -625,10 +631,10 @@ Back-upbestand heeft verkeerde extensie. - Back-upbestanden maken - Back-upbestanden worden versleuteld met een wachtwoord en worden alleen offline op je eigen apparaat opgeslagen. Als je het maken van back-upbestanden inschakelt maakt Signal automatisch elke dag een nieuw back-upbestand van al je gesprekken en media. Alleen de twee meest recente geslaagde back-upbestanden blijven bewaard, oudere back-upbestanden worden overschreven. - Nu een back-upbestand maken - Meest recente back-upbestand: %1$s + Back-up van chats + Back-ups worden versleuteld met een wachtwoord en alleen op je eigen apparaat opgeslagen. + Nu back-up maken + Meest recente back-up: %1$s Back-uplocatie Back-uptijd @@ -636,7 +642,7 @@ Test je back-upwachtwoord om na te gaan of je het juiste wachtwoord hebt onthouden Inschakelen Uitschakelen - "Om je gegevens uit een bestaand back-upbestand terug te plaatsen moet je Molly opnieuw installeren. Open dan de net geïnstalleerde Molly-app en tik op “Back-upgegevens terugzetten”. %1$s" + "Om je gegevens uit een back-up terug te zetten moet je Molly opnieuw installeren. Open vervolgens de app en tik op “Back-upgegevens terugzetten”. %1$s" Meer lezen Wordt nu uitgevoerd… @@ -644,7 +650,7 @@ Tot nu toe %1$d… %1$s%% tot nu toe… - Molly heeft toegang tot de opslagruimte nodig om back-upbestanden te kunnen maken, maar deze is pertinent geweigerd. Ga naar de instellingen voor deze app, tik op ‘Machtigingen’ en schakel ‘Opslagruimte’ in. + Molly heeft toegang tot de opslagruimte nodig om back-ups te kunnen maken, maar deze is pertinent geweigerd. Ga naar de instellingen voor deze app, tik op ‘Machtigingen’ en schakel ‘Opslagruimte’ in. Stel back-uptijd in @@ -703,7 +709,7 @@ Opnieuw plannen - Wissen + Verwijderen Geselecteerd gepland bericht verwijderen? @@ -711,7 +717,7 @@ Chatsessie vernieuwd - Het kan soms nodig zijn om je chatsessie te vernieuwen. Dit heeft geen invloed op de beveiliging van je chat, maar het is mogelijk dat je een bericht van je contactpersoon hebt gemist. Vraag je contactpersoon eventueel om zijn of haar meest recente berichten opnieuw te verzenden. + Het kan soms nodig zijn om je chatsessie te vernieuwen. Dit heeft geen invloed op de beveiliging van je chat, maar het is mogelijk dat je een bericht van je contactpersoon hebt gemist. Vraag je contactpersoon eventueel om de meest recente berichten opnieuw te verzenden. ‘%1$s’ ontkoppelen? @@ -794,8 +800,8 @@ Uitnodiging verstuurd %1$d uitnodigingen verstuurd - “%1$s” kan niet direct door jou worden toegevoegd aan de groep. In plaats daarvan heeft hij of zij een uitnodiging gekregen om lid te worden van de groep, en hij of zij zal geen berichten van de groep kunnen zien totdat hij of zij de uitnodiging heeft aanvaard. - Deze personen kunnen niet direct door jou aan de groep worden toegevoegd. In plaats daarvan hebben ze een uitnodiging gekregen om lid te worden van de groep, en ze zullen geen berichten van de groep kunnen zien totdat ze de uitnodiging hebben aanvaard. + “%1$s” kan niet direct door jou worden toegevoegd aan de groep.\n\nIn plaats daarvan heeft deze persoon een uitnodiging gekregen om lid te worden van de groep, en zal geen groepsberichten kunnen zien totdat die de uitnodiging aanvaard. + Deze personen kunnen niet direct door jou aan de groep worden toegevoegd.\n\n In plaats daarvan hebben ze een uitnodiging gekregen om lid te worden van de groep, en ze zullen geen groepsberichten kunnen zien totdat ze de uitnodiging hebben aanvaard. Wat zijn ‘nieuwe-stijl’ groepen? @@ -906,7 +912,7 @@ Er zijn geen door jou verzonden uitnodigingen in afwachting van een reactie. Personen die door andere leden voor de groep zijn uitgenodigd Er zijn geen door andere groepsleden verzonden uitnodigingen in afwachting van een reactie. - Profielinformatie van personen die door andere groepsleden zijn uitgenodigd wordt niet weergegeven. Pas als een genodigde er voor kiest de uitnodiging te aanvaarden, dan zullen op dat moment zijn of haar profielnaam, -foto en -omschrijving zichtbaar worden voor de groep. Zolang deze personen de uitnodiging niet aanvaard hebben zullen ze nog geen groepsberichten ontvangen. + Profielinformatie van personen die door andere groepsleden zijn uitgenodigd wordt niet weergegeven. Pas als een genodigde er voor kiest de uitnodiging te aanvaarden, dan zullen op dat moment diens profielnaam, -foto en -omschrijving zichtbaar worden voor de groep. Zolang deze personen de uitnodiging niet aanvaard hebben zullen ze nog geen groepsberichten ontvangen. Uitnodiging intrekken Uitnodigingen intrekken @@ -1117,7 +1123,7 @@ “%1$s” toevoegen aan de groep? Verzoek van “%1$s” afwijzen? - Wil je het lidmaatschapverzoek van “%1$s” afwijzen? Als je dit doet dan kan hij of zij niet opnieuw via de groepslink verzoeken om lid te worden van de groep. + Wil je het lidmaatschapverzoek van “%1$s” afwijzen? Als je dit doet dan kan deze persoon niet opnieuw via de groepslink verzoeken om lid te worden van de groep. Toevoegen Afwijzen @@ -1176,8 +1182,8 @@ Dit zal het geselecteerde bestand permanent verwijderen. Als er tekst was bijgevoegd, zal ook die worden gewist. Dit zal alle %1$d geselecteerde bestanden permanent verwijderen. Als er tekst was bijgevoegd, zal ook die worden verwijderd. - Aan het wissen - Berichten aan het wissen… + Aan het verwijderen + Berichten aan het verwijderen… Bijlagen aan het verzamelen… Sorteren op Nieuwste bovenaan @@ -1193,8 +1199,8 @@ Opslaan - Wissen - Wissen + Verwijderen + Verwijderen @@ -1216,7 +1222,7 @@ Herinner me later Weet je je Signal-pincode nog? - Signal vraagt je af en toe om je pincode te bevestigen, zodat je je pincode goed onthoudt. + We vragen je af en toe om je pincode te bevestigen, zodat je je pincode goed onthoudt. Pincode bevestigen Maak een begin Nieuwe groep @@ -1277,6 +1283,10 @@ Gemiste spraakoproep Gemiste video-oproep + + Gemiste spraakoproep terwijl het meldingsprofiel was ingeschakeld + + Gemiste video-oproep terwijl meldingsprofiel was ingeschakeld Je hebt een spraakoproep geweigerd @@ -1304,9 +1314,9 @@ - %1$s heeft zijn of haar profielnaam naar “%2$s” gewijzigd. - %1$s heeft zijn of haar profielnaam van “%2$s” naar “%3$s” gewijzigd. - %1$s heeft zijn of haar profiel aangepast. + %1$s heeft diens profielnaam naar “%2$s” gewijzigd. + %1$s heeft diens profielnaam van “%2$s” naar “%3$s” gewijzigd. + %1$s heeft diens profiel aangepast. Je hebt de groep aangemaakt. @@ -1412,14 +1422,14 @@ De groepsinstellingen zijn aangepast om uitsluitend beheerders de mogelijkheid te geven om berichten te verzenden en oproepen te beginnen. - Je hebt de groepslink ingeschakeld zonder vereiste beheerdersgoedkeuring van nieuwe leden. - Je hebt de groepslink ingeschakeld met vereiste beheerdersgoedkeuring van nieuwe leden. + Je hebt de groepslink zonder beheerdersgoedkeuring van nieuwe leden ingeschakeld. + Je hebt de groepslink met beheerdersgoedkeuring van nieuwe leden ingeschakeld. Je hebt de groepslink uitgeschakeld. - %1$s heeft de groepslink ingeschakeld zonder vereiste beheerdersgoedkeuring van nieuwe leden. - %1$s heeft de groepslink ingeschakeld met vereiste beheerdersgoedkeuring van nieuwe leden. + %1$s heeft de groepslink zonder beheerdersgoedkeuring van nieuwe leden ingeschakeld. + %1$s heeft de groepslink met beheerdersgoedkeuring van nieuwe leden ingeschakeld. %1$s heeft de groepslink uitgeschakeld. - De groepslink is ingeschakeld zonder vereiste beheerdersgoedkeuring van nieuwe leden. - De groepslink is ingeschakeld met vereiste beheerdersgoedkeuring van nieuwe leden. + De groepslink zonder beheerdersgoedkeuring van nieuwe leden is ingeschakeld. + De groepslink met beheerdersgoedkeuring van nieuwe leden is ingeschakeld. De groepslink is uitgeschakeld. Je hebt beheerdersgoedkeuring van nieuwe leden uitgeschakeld voor de groepslink. %1$s heeft beheerdersgoedkeuring van nieuwe leden uitgeschakeld voor de groepslink. @@ -1468,11 +1478,11 @@ Je hebt je veiligheidsnummer met %1$s gemarkeerd als ‘niet geverifieerd’ Je hebt je veiligheidsnummer met %1$s vanaf een ander apparaat gemarkeerd als ‘niet geverifieerd’ Een bericht van %1$s kan niet worden afgeleverd - %1$s heeft zijn of haar telefoonnummer gewijzigd. + %1$s heeft diens telefoonnummer gewijzigd. Vind je deze nieuwe functie leuk? Steun Signal met een eenmalige donatie. - Je berichtengeschiedenis met %1$s en zijn of haar nummer %2$s zijn samengevoegd. + Je berichtengeschiedenis met %1$s en diens nummer %2$s zijn samengevoegd. Je berichtengeschiedenis met %1$s en een andere chat met hem of haar zijn samengevoegd. @@ -1523,14 +1533,14 @@ Aanvaarden Gesprek voortzetten - Wissen + Verwijderen Blokkeren Deblokkeren - Wil je %1$s jou een bericht laten sturen en je naam en foto met hen delen? Je hebt deze persoon in het verleden verwijderd. - Wil je berichten van %1$s ontvangen, en sta je toe dat hij of zij je profielnaam, -foto en -omschrijving kan zien? Als je leesbevestigingen hebt ingeschakeld kan hij of zij die nog niet zien totdat je de uitnodiging hebt aanvaard. + Wil je berichten van %1$s ontvangen, en sta je toe dat diegene je profielnaam, -foto en -omschrijving kan zien? Je hebt deze persoon in het verleden verwijderd. + Wil je berichten van %1$s ontvangen, en sta je toe dat diegene je profielnaam, -foto en -omschrijving kan zien? Als je leesbevestigingen hebt ingeschakeld kan hij of zij die nog niet zien totdat je de uitnodiging hebt aanvaard. - Wil je berichten van %1$s ontvangen en sta je toe dat diegene je profielnaam en -foto kan zien? Totdat je dit contact hebt gedeblokkeerd, zul je geen berichten ontvangen. + Wil je berichten van %1$s ontvangen, en sta je toe dat diegene je profielnaam, -foto en -omschrijving kan zien? Totdat je deze persoon hebt gedeblokkeerd, zul je geen berichten ontvangen. Wil je berichten van %1$s ontvangen? Totdat je diegene hebt gedeblokkeerd, zul je geen berichten ontvangen. Wil je updates en nieuws van %1$s ontvangen? Je zult geen berichten ontvangen totdat je diegene hebt gedeblokkeerd. @@ -1541,7 +1551,7 @@ Wil je lid worden van deze groep, en sta je toe dat alle leden van de groep je profielnaam, -foto en -omschrijving kunnen zien? Als je leesbevestigingen hebt ingeschakeld kunnen de leden van deze groep die nog niet zien totdat je de uitnodiging hebt aanvaard. Wil je lid worden van deze groep, en sta je toe dat alle leden van de groep je profielnaam, -foto en -omschrijving kunnen zien? Je kunt hun berichten nog niet zien totdat je de uitnodiging hebt aanvaard. Je bent door iemand met wie je eerder een gesprek hebt gehad of met wie je in dezelfde groep zat toegevoegd aan deze groep. Wil je lid blijven van deze groep? Als je leesbevestigingen hebt ingeschakeld kunnen de leden van deze groep die nog niet zien totdat je de uitnodiging hebt aanvaard. - Wil je weer berichten van deze groep ontvangen en sta je toe dat alle leden je profielnaam en -foto kunnen zien? Totdat je de groep hebt gedeblokkeerd, zul je geen berichten ontvangen. + Wil je weer berichten van deze groep ontvangen en sta je toe dat alle leden je profielnaam, -foto en -omschrijving kunnen zien? Totdat je de groep hebt gedeblokkeerd, zul je geen berichten ontvangen. Weergeven Lid van %1$s @@ -1632,11 +1642,11 @@ Je hebt nog %1$d poging resterend. Wanneer je geen pogingen meer over hebt, dan kun je nog wel een nieuwe pincode aanmaken. Je kunt je opnieuw registreren en je account gebruiken maar je verliest wel een aantal opgeslagen instellingen, zoals je profielinformatie. Je hebt nog %1$d pogingen resterend. Wanneer je geen pogingen meer over hebt, dan kun je nog wel een nieuwe pincode aanmaken. Je kunt je opnieuw registreren en je account gebruiken maar je verliest wel een aantal opgeslagen instellingen, zoals je profielinformatie. - Signal-registratratie - Ik heb hulp nodig bij de Signal-pincode op een Android apparaat + Signal-registratratie - Ik heb hulp nodig bij de Signal-pincode op een Android-apparaat Pincode aanmaken - Je hebt geen pogingen resterend om je pincode te raden, maar je kunt nog wel gebruik blijven maken van je Signal-account door een nieuwe pincode aan te maken. Voor je privacy en veiligheid wordt je account hersteld zonder opgeslagen profielinformatie of instellingen. + Je hebt geen pogingen meer om je pincode te raden, maar je kunt nog wel gebruik blijven maken van je Signal-account door een nieuwe pincode aan te maken. Voor je privacy en veiligheid wordt je account hersteld zonder opgeslagen profielinformatie of instellingen. Nieuwe pincode aanmaken @@ -1649,7 +1659,7 @@ Als je je pincode niet meer weet, kun je een nieuwe aanmaken. - Je hebt geen pincodes meer, maar je hebt nog steeds toegang tot je Signal-account als je een nieuwe pincode aanmaakt. + Je hebt geen pogingen meer om je pincode te raden, maar je hebt nog steeds toegang tot je Signal-account als je een nieuwe pincode aanmaakt. Waarschuwing @@ -1828,6 +1838,8 @@ Wissel van camera Dempen aan/uit + + Meer acties Oproep beëindigen @@ -1844,10 +1856,68 @@ Een icoon dat de oortelefoon van een apparaat voorstelt. + + Hand opsteken + + Hand opsteken + + Hand omlaag doen? + + Hand omlaag doen + + Annuleren + + Je hebt je hand opgestoken + + Weergeven + + + + %1$s heeft een hand opgestoken + %1$s en %2$d hebben hun hand opgestoken + + + + + %1$s + %1$s + %2$d + + + + Weergave opgestoken hand uitklappen + + + + Signal-contact + + %1$s staat in je systeemcontactenlijst + + Jullie hebben geen gemeenschappelijke groepen + + Overweeg dit gespreksverzoek zorgvuldig: + + %1$d gemeenschappelijke groepen + + Over + + Jij + - - In deze oproep · %1$d persoon - In deze oproep · %1$d personen + + In deze oproep (%1$d) + In deze oproep (%1$d) + + + Signal zal (%1$d) bellen + Signal zal (%1$d) bellen + + + Signal zal (%1$d) een melding sturen + Signal zal (%1$d) een melding sturen + + + Opgestoken hand (%1$d) + Opgestoken handen (%1$d) @@ -1918,7 +1988,7 @@ Je bent nu %1$d stap verwijderd van het indienen van een foutopsporingslog. Je bent nu %1$d stappen verwijderd van het indienen van een foutopsporingslog. - Signal moet verifiëren dat je een mens bent. + We moeten verifiëren dat je een mens bent. Audio-oproep @@ -1961,6 +2031,12 @@ Gezien Eenmaligeweergave-media + + + Naamconflict gevonden + + Weergeven + Geen resultaten gevonden voor ‘%1$s’ @@ -1987,12 +2063,12 @@ Meer opties - Pincode succesvol geverifieerd. Signal zal het later opnieuw vragen. - Pincode succesvol geverifieerd. Signal zal het morgen opnieuw vragen. - Pincode succesvol geverifieerd. Signal zal het over een paar dagen opnieuw vragen. - Pincode succesvol geverifieerd. Signal zal het over een week opnieuw vragen. - Pincode succesvol geverifieerd. Signal zal het over een aantal weken opnieuw vragen. - Pincode succesvol geverifieerd. Signal zal het over een maand opnieuw vragen. + Pincode succesvol geverifieerd. We zullen het later opnieuw vragen. + Pincode succesvol geverifieerd. We zullen het morgen opnieuw vragen. + Pincode succesvol geverifieerd. We zullen het over een paar dagen opnieuw vragen. + Pincode succesvol geverifieerd. We zullen het over een week opnieuw vragen. + Pincode succesvol geverifieerd. We zullen het over een aantal weken opnieuw vragen. + Pincode succesvol geverifieerd. We zullen het over een maand opnieuw vragen. Afbeelding @@ -2035,7 +2111,7 @@ Opslaan mislukt Opgeslagen - Tip op een regel om die te wissen + Tik op een regel om die te verwijderen Uploaden Uploaden van foutopsporingslogs is mislukt Succesvol geüpload! @@ -2063,8 +2139,8 @@ Eenmalige­weergave-afbeelding Eenmalige­weergave-video Eenmalige­weergave-media - Dit bericht is gewist. - Je hebt dit bericht gewist. + Dit bericht is verwijderd. + Je hebt dit bericht verwijderd. Je hebt een verzoek gestuurd om Betalingen te activeren @@ -2082,7 +2158,7 @@ Door jou als niet geverifieerd gemarkeerd Bericht kon niet worden verwerkt Afleverings­probleem - Gespreks­verzoek + Gespreksverzoek Je hebt deze persoon verborgen. Als je hem of haar opnieuw een bericht stuurt, wordt diegene weer aan je lijst toegevoegd. Afbeelding @@ -2127,12 +2203,14 @@ Verzenden + + 00 Voeg een gebruikersnaam toe - Kies jouw gebruikersnaam. + Kies jouw gebruikersnaam Gebruikersnaam - Wissen + Verwijderen Gebruikersnaam succesvol verwijderd. Er is een netwerkfout opgetreden. Deze gebruikersnaam is al in gebruik. @@ -2149,6 +2227,12 @@ Overslaan Klaar + + Deze gebruikersnaam is niet beschikbaar, probeer een ander nummer. + + Ongeldige gebruikersnaam, voer minimaal %1$d cijfers in. + + Ongeldige gebruikersnaam, voer maximaal %1$d cijfers in. %1$d van je contactpersonen is bereikbaar via Signal. @@ -2205,8 +2289,8 @@ Concept Molly heeft toegang tot de opslagruimte nodig om iets op te slaan, maar deze is pertinent geweigerd. Ga naar de instellingen voor deze app, tik op ‘App-machtigingen’ en schakel ‘Opslagruimte’ in. Kan niet opslaan naar externe opslag zonder machtiging - Bericht wissen? - Dit bericht zal onherroepelijk worden gewist. + Bericht verwijderen? + Dit bericht zal permanent worden verwijderd. %1$s naar %2$s Jij naar %1$s @@ -2267,7 +2351,7 @@ Reageerde met %1$s op je betaling. Reageerde met %1$s op je sticker. - Dit bericht is gewist. + Dit bericht is verwijderd. Zet meldingen uit over mensen die bereikbaar zijn via Signal. Je kunt ze opnieuw aanzetten in Signal > Instellingen > Meldingen. @@ -2277,7 +2361,7 @@ Om meldingen van nieuwe berichten te ontvangen: - 1. Tik op Instellingen hieronder + 1. Tik op ‘Instellingen’ hieronder 2. %1$s Meldingen inschakelen @@ -2322,7 +2406,7 @@ Sluiten - Verwijderen + Wissen Ongeldige snelkoppeling @@ -2346,6 +2430,10 @@ Aan het verwerken… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2370,6 +2458,8 @@ Beantwoord vanaf een gekoppeld apparaat. Geweigerd vanaf een gekoppeld apparaat. Bezet omdat je al een gesprek voerde via een van je gekoppelde apparaten. + + Het icoontje om van camera te wisselen is hierheen verplaatst. Tik op je video om het uit te proberen Iemand neemt nu deel aan deze oproep met een veiligheidsnummer dat is veranderd. @@ -2682,7 +2772,7 @@ Afleveringsprobleem - Een bericht, media, sticker, emoji-reactie, of leesbevestiging van %1$s kon niet aan jou worden afgeleverd. Het kan zijn dat hij of zij iets naar je probeerde te verzenden in een één-op-één-gesprek of in een groepsgesprek. + Een bericht, media, sticker, emoji-reactie, of leesbevestiging van %1$s kon niet aan jou worden afgeleverd. Het kan zijn dat deze persoon iets naar je probeerde te verzenden in een één-op-één-gesprek of in een groepsgesprek. Een bericht, media, sticker, emoji-reactie, of een bevestiging dat een bericht gelezen is van %1$s kan niet aan jou worden afgeleverd. @@ -2967,8 +3057,8 @@ Afbeelding die illustreert waar het vervangende app-icoon zichtbaar zal zijn. Pincode uitschakelen Pincode inschakelen - Als je de pincode uitschakelt zul je wanneer je Signal opnieuw registreert alle gegevens kwijtraken, tenzij je ze handmatig herstelt vanuit een back-upbestand. Daarnaast kun je zolang de pincode is uitgeschakeld de registratievergrendeling niet inschakelen. - Met een pincode wordt informatie versleuteld opgeslagen op Signals servers zodat alleen jij er toegang toe hebt. Je kunt hierdoor je profielnaam, -foto en -omschrijving, instellingen en (geblokkeerde) contacten herstellen wanneer je Signal opnieuw installeert. Zelfs Signals ontwikkelaars kunnen je informatie niet inzien. Je hebt de pincode niet nodig om de app te openen. + Als je de pincode uitschakelt zul je wanneer je Signal opnieuw registreert alle gegevens kwijtraken, tenzij je ze handmatig herstelt vanuit een back-up. Daarnaast kun je zolang de pincode is uitgeschakeld de registratievergrendeling niet inschakelen. + Met een pincode wordt informatie versleuteld opgeslagen op Signals servers zodat alleen jij er toegang toe hebt. Je kunt hierdoor je profiel, instellingen en contacten herstellen wanneer je Signal opnieuw installeert. Je hebt de pincode niet nodig om de app te openen. Systeemstandaard Taal Signal-berichten en -oproepen @@ -2999,7 +3089,7 @@ Alle bestanden weergeven Oude berichten verwijderen? Berichtengeschiedenis wissen? - Dit zal alle gespreksgeschiedenis en media ouder dan %1$s permanent van dit apparaat verwijderen. + Dit zal alle gespreksgeschiedenis en media ouder dan %1$s permanent van dit apparaat wissen. Hiermee worden alle chats permanent ingekort tot het meest recente bericht. @@ -3008,7 +3098,7 @@ Dit zal al je gespreksgeschiedenis en media van dit apparaat permanent verwijderen. Weet je zeker dat je alle gespreksgeschiedenis wilt verwijderen? Alle gespreksgeschiedenis zal permanent worden verwijderd. Dit kan niet ongedaan worden gemaakt. - Nu alles wissen + Nu alles verwijderen Voor altijd 1 jaar 6 maanden @@ -3021,7 +3111,7 @@ Zelf ingesteld Systeem-emoji gebruiken - Om te voorkomen dat je gesprekspartner je IP-adres kan achterhalen worden Signal-oproepen met niet-contactpersonen altijd al omgeleid via de Signal-servers. Door deze optie in te schakelen wordt dat ook gedaan voor Signal-oproepen met contacten die wel in je contactenlijst staan. Dit leidt echter tot een verminderde geluids- en videokwaliteit. + Leid alle oproepen om via de Signal-servers zodat je contacten je IP-adres niet kunnen achterhalen. Als je dit inschakelt, zal de gesprekskwaliteit verminderen. Alle oproepen omleiden Betalingen @@ -3350,7 +3440,7 @@ Tekst kopiëren - Wissen + Verwijderen Doorsturen @@ -3504,14 +3594,14 @@ Voer de zojuist aangemaakte pincode opnieuw in. Bevestig je pincode. Pincode aanmaken mislukt - Je pincode is niet opgeslagen. Signal zal je later vragen een pincode aan te maken. + Je pincode is niet opgeslagen. We zullen je later vragen een pincode aan te maken. Pincode aangemaakt. Voer dezelfde pincode opnieuw in Pincode aan het aanmaken… We introduceren pincodes - Met een pincode wordt informatie versleuteld opgeslagen op Signals servers zodat alleen jij er toegang toe hebt. Je kunt hierdoor je profielnaam, -foto en -omschrijving, instellingen en (geblokkeerde) contacten herstellen wanneer je Signal opnieuw installeert. Zelfs Signals ontwikkelaars kunnen je informatie niet inzien. Je hebt de pincode niet nodig om de app te openen. + Met een pincode wordt informatie versleuteld opgeslagen op Signals servers zodat alleen jij er toegang toe hebt. Je kunt hierdoor je profiel, instellingen en contacten herstellen wanneer je Signal opnieuw installeert. Je hebt de pincode niet nodig om de app te openen. Meer lezen Registratievergrendeling = Pincode @@ -3526,7 +3616,7 @@ Om je te helpen je pincode te onthouden, vragen we je regelmatig om je pincode in te voeren. We zullen dit na verloop van tijd minder vaak vragen. Overslaan Bevestigen - Tik hier als je je pincode bent vergeten + Pincode vergeten? Foutieve pincode, probeer het opnieuw. @@ -3605,15 +3695,15 @@ Niet nu Signal-databank wordt gemigreerd Back-upwachtwoord - Back-upbestanden worden opgeslagen op het externe opslaggeheugen en versleuteld met het wachtwoord hieronder. Je hebt dit wachtwoord nodig om de back-up te herstellen. - Je hebt in de toekomst dit wachtwoord nodig om je gegevens vanuit een back-upbestand te kunnen herstellen. + Back-ups worden opgeslagen op het externe opslaggeheugen en versleuteld met het wachtwoord hieronder. Je hebt dit wachtwoord nodig om de back-up te herstellen. + Je hebt in de toekomst dit wachtwoord nodig om je gegevens vanuit een back-up te kunnen herstellen. Opslaglocatie - Ik heb dit wachtwoord opgeschreven. Zonder dit wachtwoord kan ik deze back-upbestanden niet gebruiken om gegevens terug te zetten. + Ik heb dit wachtwoord opgeschreven. Zonder dit wachtwoord kan ik back-ups niet gebruiken om gegevens terug te zetten. Back-upgegevens terugzetten Account overzetten of herstellen Account overzetten Overslaan - Back-upbestanden maken + Back-up van chats Account overzetten Alle gegevens overzetten naar een nieuw Android-apparaat Voer het back-upwachtwoord in @@ -3625,16 +3715,16 @@ Aan het controleren… %1$d berichten tot nu toe… Back-upgegevens terugzetten? - Je hebt nu de gelegenheid om berichten en media uit een back-upbestand terug te zetten. Let op: Herstellen vanuit een back-upbestand kan alleen tijdens de installatie van de Signal-app. Als je deze stap overslaat kun je dit niet op een later moment alsnog doen. - Back-upbestand-grootte: %1$s - Back-upbestand-tijdstip: %1$s - Back-upbestanden maken? - Back-upbestanden inschakelen + Herstel je berichten en media vanuit een lokale back-up. Herstellen vanuit een back-up kan alleen tijdens deze stap en kun je op een later moment niet alsnog doen. + Back-upgrootte: %1$s + Back-uptijdstip: %1$s + Lokale back-ups maken? + Back-ups inschakelen Verklaar dat je het begrijpt door het selectievakje aan te vinken. - Alle back-upbestanden wissen? - Alle lokale back-upbestanden uitschakelen en wissen? - Back-upbestanden wissen - Kies een opslaglocatie om het maken van back-upbestanden te kunnen inschakelen. + Alle back-ups verwijderen? + Back-ups uitschakelen en alle lokale back-ups verwijderen? + Back-ups verwijderen + Kies een opslaglocatie om het maken van back-ups te kunnen inschakelen. Kies een map Gekopieerd naar klembord Er is geen bestandskiezer beschikbaar. @@ -3642,18 +3732,18 @@ Verifiëren Je hebt het juiste back-upwachtwoord ingevoerd Je hebt niet het juiste back-upwachtwoord ingevoerd. - Back-upbestand aan het maken… + Back-up aan het maken… Back-up van Molly verifiëren… - Het maken van een back-upbestand is mislukt - Je back-up directory is gewist of verplaatst. + Het maken van een back-up is mislukt + Je back-up directory is verwijderd of verplaatst. Je back-upbestand is te groot om op dit volume op te slaan. - Er is niet genoeg opslagruimte om je back-upbestand op te slaan. + Er is niet genoeg opslagruimte om je back-up op te slaan. - Je recente backup kon niet gecreëerd en geverifieerd worden. Probeer het opnieuw. + Je recente back-up kon niet gecreëerd en geverifieerd worden. Probeer het opnieuw. Je back-up bevat een erg groot bestand dat niet opgeslagen kan worden. Verwijder het en probeer het opnieuw. - Tik hier om het maken van back-upbestanden in te stellen. + Tik om back-ups te beheren Verkeerd nummer? Bel me (%1$02d:%2$02d) @@ -3688,7 +3778,7 @@ Uitschakelen Bevestig je pincode Je Signal-pincode bevestigen - Zorg ervoor dat je je Signal-pincode onthoudt of op een goed beveiligde manier opslaat, want je kunt je pincode niet herstellen. Als je je pincode toch vergeet, dan verlies je mogelijk gegevens bij het opnieuw registrereren van je Signal-account. + Zorg ervoor dat je je Signal-pincode onthoudt of op een goed beveiligde manier opslaat, want je kunt je pincode niet herstellen. Als je je pincode toch vergeet, dan verlies je mogelijk gegevens bij het opnieuw registreren van je Signal-account. Foutieve pincode, probeer het opnieuw. Het is niet gelukt om registratievergrendeling in te schakelen. Het is niet gelukt om registratievergrendeling uit te schakelen. @@ -3714,22 +3804,22 @@ Alle gegevens wissen? - Hiermee wordt de app gereset en worden alle berichten gewist. De app wordt gesloten nadat dit proces is voltooid. + Hiermee wordt de app gereset en worden alle berichten verwijderd. De app wordt gesloten nadat dit proces is voltooid. Doorgaan Annuleren - Kan gegevens niet verwijderen + Kan gegevens niet wissen Account overzetten of herstellen Als je al eerder een Signal-account hebt geregistreerd, kun je je account en berichten overzetten of herstellen Overzetten vanaf een Android-apparaat - Zet je account en berichten over van je oude Android-apparaat. Hiervoor heb je toegang nodig tot je oude apparaat. + Zet je account en berichten over van je oude Android-apparaat. Hiervoor heb je toegang tot je oude apparaat nodig. Je kunt dit alleen doen als je je oude apparaat nog kunt gebruiken. - Gegevens vanuit een back-upbestand terugzetten - Je hebt nu de gelegenheid om berichten uit een back-upbestand terug te zetten. Let op: Herstellen vanuit een back-upbestand kan alleen tijdens de installatie van de Signal-app. Als je deze stap overslaat kun je dit niet op een later moment alsnog doen. + Gegevens vanuit een back-up terugzetten + Herstel je berichten en media vanuit een lokale back-up. Herstellen vanuit een back-up kan alleen tijdens deze stap en kun je op een later moment niet alsnog doen. Open Signal op je oude apparaat @@ -3747,8 +3837,8 @@ Aan het wachten tot je oude Android-apparaat verbinding maakt… Om je oude Android-apparaat te kunnen detecteren en om er verbinding mee te maken, heeft de Molly-app een machtiging nodig om je locatie te lezen. Om je oude Android-apparaat te kunnen detecteren en om er verbinding mee te maken, is het nodig dat locatiebepaling op dit apparaat is ingeschakeld. - Molly heeft wifi op je apparaat nodig om verbinding te maken met je oude Android apparaat. Wifi moet zijn ingeschakeld, maar je hoeft niet verbonden te zijn met een wifi-netwerk. - Sorry, het lijkt er op dat dit apparaat geen ondersteuning biedt voor wifi-direct. Molly heeft wifi-direct nodig om je oude Android-apparaat te detecteren en om er verbinding mee te maken. Je kunt nog wel je gegevens overzetten door op je oude Android-apparaat in Molly een back-upbestand te maken en vervolgens vanuit dat back-upbestand te herstellen op je nieuwe Android-apparaat. + Molly heeft wifi op je apparaat nodig om verbinding te maken met je oude Android-apparaat. Wifi moet zijn ingeschakeld, maar je hoeft niet verbonden te zijn met een wifi-netwerk. + Sorry, het lijkt er op dat dit apparaat geen ondersteuning biedt voor wifi-direct. Molly heeft wifi-direct nodig om je oude Android-apparaat te detecteren en om er verbinding mee te maken. Je kunt nog wel je gegevens overzetten door op je oude Android-apparaat in Molly een back-up te maken en vervolgens die back-up te herstellen op je nieuwe Android-apparaat. Back-upgegevens terugzetten Er is een onverwachte fout opgetreden bij het proberen verbinding te maken met je oude Android-apparaat. @@ -3756,9 +3846,9 @@ Er wordt gezocht naar je nieuwe Android-apparaat… Om je nieuwe Android-apparaat te kunnen detecteren en om er verbinding mee te maken, heeft de Molly-app een machtiging nodig om je locatie te lezen. Om je nieuwe Android-apparaat te kunnen detecteren en om er verbinding mee te maken, is het nodig dat locatiebepaling op dit apparaat is ingeschakeld. - Molly heeft wifi op je apparaat nodig om verbinding te maken met je nieuwe Android apparaat. Wifi moet zijn ingeschakeld, maar je hoeft niet verbonden te zijn met een wifi-netwerk. - Sorry, het lijkt er op dat dit apparaat geen ondersteuning biedt voor wifi-direct. Molly heeft wifi-direct nodig om je nieuwe Android-apparaat te detecteren en om er verbinding mee te maken. Je kunt nog wel je gegevens overzetten door op je oude Android-apparaat in Molly een back-upbestand te maken en vervolgens vanuit dat back-upbestand te herstellen op je nieuwe Android-apparaat. - Een back-upbestand maken + Molly heeft wifi op je apparaat nodig om verbinding te maken met je nieuwe Android-apparaat. Wifi moet zijn ingeschakeld, maar je hoeft niet verbonden te zijn met een wifi-netwerk. + Sorry, het lijkt er op dat dit apparaat geen ondersteuning biedt voor wifi-direct. Molly heeft wifi-direct nodig om je nieuwe Android-apparaat te detecteren en om er verbinding mee te maken. Je kunt nog wel je gegevens overzetten door op je oude Android-apparaat in Molly een back-up te maken en vervolgens die back-up te herstellen op je nieuwe Android-apparaat. + Een back-up maken Er is een onverwachte fout opgetreden bij het proberen verbinding te maken met je nieuwe Android-apparaat. @@ -3865,7 +3955,7 @@ %1$s uit deze groep verwijderen? - Wil je “%1$s” uit de groep verwijderen? Als je dit doet dan kan hij of zij niet via de groepslink opnieuw lid worden van de groep. + Wil je “%1$s” uit de groep verwijderen? Als je dit doet dan kan deze persoon niet via de groepslink opnieuw lid worden van de groep. Verwijderen Gekopieerd naar klembord @@ -3925,14 +4015,16 @@ Het verwijderen van een groepslid is mislukt. - Lid van deze groep maar staat niet in je contactenlijst Wil een gesprek met je beginnen Persoon die in je contactenlijst staat Uit de groep verwijderen Contactpersoon aanpassen Blokkeren - Wissen - Heeft recent zijn of haar profielnaam gewijzigd van “%1$s” naar “%2$s” + Verwijderen + + %1$s heeft recent diens profielnaam gewijzigd van %2$s naar %3$s + + %1$s staat in je systeemcontactenlijst %1$s neemt nu deel aan deze oproep @@ -3956,9 +4048,9 @@ Het verwijderen van je account zal: Voer je telefoonnummer in Account verwijderen - Je accountinformatie en profielfoto verwijderen - Al je berichten wissen - %1$s wissen van je betalingsaccount + Je accountinformatie en profielfoto verwijderen + Al je berichten verwijderen + %1$s verwijderen van je betalingsaccount Er is geen landcode gespecificeerd Er is geen telefoonnummer ingevuld Het telefoonnummer dat je hebt ingevuld komt niet overeen met het telefoonnummer van je Signal-account. @@ -3975,11 +4067,11 @@ Afhankelijk van het aantal groepen waarvan je lid bent kan dit een aantal minuten duren - Gebruikersgegevens worden gewist + Gebruikersgegevens worden verwijderd Account is niet verwijderd - Er is een probleem met het wissen van je gegevens. Ga na dat je apparaat met het internet is verbonden en probeer het opnieuw. + Er is een probleem met het verwijderen van je gegevens. Ga na dat je apparaat met het internet is verbonden en probeer het opnieuw. Land zoeken @@ -4276,7 +4368,7 @@ Onbekend of telefoonnummer is gewijzigd - Signal kan niet vaststellen of je telefoonnummer is gewijzigd.\n\n(Fout: %1$s) + We kunnen niet vaststellen of je telefoonnummer is gewijzigd.\n\n(Fout: %1$s) Opnieuw proberen @@ -4398,13 +4490,13 @@ Chatkleur Bewerken Kopie maken - Wissen - Kleur wissen + Verwijderen + Kleur verwijderen - Deze zelfgekozen kleur wordt nog in %1$d chat gebruikt. Wil je het voor alle chats wissen? - Deze zelfgekozen kleur wordt nog in %1$d chats gebruikt. Wil je het voor alle chats wissen? + Deze zelfgekozen kleur wordt nog in %1$d chat gebruikt. Wil je het voor alle chats verwijderen? + Deze zelfgekozen kleur wordt nog in %1$d chats gebruikt. Wil je het voor alle chats verwijderen? - Chatkleur wissen? + Chatkleur verwijderen? Effen @@ -4483,7 +4575,7 @@ Verhaal - Berichten + Bericht Video @@ -4808,7 +4900,7 @@ Dit apparaat is niet meer geregistreerd. Registreer je opnieuw om Molly op dit apparaat te blijven gebruiken. - Opnieuw registreren. + Opnieuw registreren Annuleren @@ -4842,7 +4934,7 @@ Neem contact op Ontvang een ‘%1$s’-badge - Donatie aan het verwerken… + Aan het verwerken… Fout bij het verwerken van donatie @@ -4988,6 +5080,12 @@ Voeg mensen en groepen toe waarvan je meldingen wilt ontvangen terwijl dit meldingsprofiel actief is Personen of groepen toevoegen + + Uitzonderingen + + Alle oproepen toestaan + + Alle vermeldingen melden Toevoegen @@ -5002,15 +5100,15 @@ Ingeschakeld - Meldingsprofiel wissen + Meldingsprofiel verwijderen “%1$s” verwijderd. Ongedaan maken - Meldingsprofiel wissen? + Meldingsprofiel verwijderen? - Wissen + Verwijderen Meldingsprofiel bewerken @@ -5124,7 +5222,7 @@ Donatie-afschrift - Hoeveelheid + Bedrag Bedankt dat je Signal steunt. Jouw bijdrage helpt bij het voortzetten van onze missie om open-broncode privacy-technologieën te ontwikkelen waarmee van miljoenen mensen de vrijheid van expressie wordt gewaarborgd en waarmee beveiligde communicatie over de hele wereld mogelijk wordt gemaakt. Bewaar dit afschrift als je een inwoner bent van de Verenigde Staten van Amerika, zodat je je donatie kunt opgeven bij je belastingaangifte. Signal Technology Foundation is een van belasting uitgezonderde organisatie zonder winstoogmerk volgens sectie 501c3 van het Internal Revenue Code (het Amerikaanse belastingreglement). Ons federale-belasting-ID is 82–4506840. @@ -5192,9 +5290,9 @@ Verhaal van %1$s - Verhaal wissen? + Verhaal verwijderen? - Dit verhaal zal voor jou en voor iedereen die het heeft ontvangen worden gewist. + Dit verhaal zal voor jou en voor iedereen die het heeft ontvangen worden verwijderd. Kan niet opslaan @@ -5257,7 +5355,7 @@ Tekst kopiëren - Wissen + Verwijderen Mijn verhaal @@ -5310,11 +5408,11 @@ Hem of haar in je systeemcontactenlijst te hebben staan - "Je Signal-contacten kunnen jouw profielnaam en -foto zien en kunnen je “Mijn verhaal”-plaatsingen zien tenzij je die specifiek voor hem of haar verbergt." + "Je Signal-contacten kunnen jouw profielnaam, -foto en -omschrijving zien en kunnen je “Mijn verhaal”-plaatsingen zien tenzij je die specifiek voor hem of haar verbergt." Persoon toevoegen - Aangepast verhaal wissen + Aangepast verhaal verwijderen %1$s verwijderen? @@ -5361,11 +5459,11 @@ Verwijderen - Aangepast verhaal wissen? + Aangepast verhaal verwijderen? \"%1$s\" en updates die op dit verhaal zijn gedeeld, worden verwijderd. - Wissen + Verwijderen @@ -5377,7 +5475,7 @@ Uploaden - Uitschakelen en wissen + Uitschakelen en verwijderen @@ -5545,7 +5643,7 @@ Verhaal verwijderen - Verhaal wissen + Verhaal verwijderen Verhaal verwijderen? @@ -5553,11 +5651,11 @@ Verwijderen - Verhaal wissen? + Verhaal verwijderen? - Aangepast verhaal \"%1$s\" wissen? + Aangepast verhaal \"%1$s\" verwijderen? - Wissen + Verwijderen %1$d dag resterend @@ -5602,8 +5700,8 @@ Alle contacten zijn beoordeeld, tik op versturen om verder te gaan. - Je hebt %1$d contact die Signal misschien opnieuw geïnstalleerd heeft of van toestel is veranderd. Bekijk voordat je je verhaal deelt het veiligheidsnummer of overweeg om je verhaal niet te delen met deze persoon. - Je hebt %1$d contacten die Signal misschien opnieuw geïnstalleerd hebben of van toestel zijn veranderd. Bekijk voordat je je verhaal met hen deelt hun veiligheidsnummers of overweeg om je verhaal niet met hen te delen. + %1$d contact heeft mogelijk Signal opnieuw geïnstalleerd of is van toestel veranderd. Je kunt het veiligheidsnummer bekijken of doorgaan met verzenden. + %1$d contacten hebben mogelijk Signal opnieuw geïnstalleerd of zijn van toestel veranderd. Je kunt hun veiligheidsnummer bekijken of doorgaan met verzenden. Veiligheidsnummer verifiëren @@ -5667,7 +5765,7 @@ Verhalen uitzetten? - Je kunt geen verhalen meer delen of bekijken. Updates die je recent op je verhaal hebt gedeeld, worden ook gewist. + Je kunt geen verhalen meer delen of bekijken. Updates die je recent op je verhaal hebt gedeeld, worden ook verwijderd. Verhaalprivacy @@ -5789,15 +5887,15 @@ Er wordt niet langer automatisch een back-up van je chats gemaakt. - Maak back-up van chats + Back-up van chats maken Niet nu Om back-ups opnieuw in te schakelen: - Tik op de onderstaande knop \"Ga naar instellingen\" + Tik op de onderstaande knop ‘Ga naar instellingen’ - Zet \"Sta wekkers en herinneringen toe\" aan. + Zet ‘Sta wekkers en herinneringen toe’ aan. Ga naar instellingen @@ -5961,12 +6059,18 @@ IBAN-landcode wordt niet ondersteund Ongeldig IBAN-nummer + + Minimaal 2 tekens + + Ongeldig e-mailadres iDEAL Vul je bank, naam en e-mailadres in. Stripe gebruikt je e-mailadres om je op de hoogte te houden over je donatie. %1$s + + Vul je bankgegevens in. Signal verzamelt of bewaart jouw persoonlijke gegevens niet. %1$s Meer lezen @@ -6152,6 +6256,8 @@ Uitgaand Gemist + + Gemist terwijl meldingsprofiel was ingeschakeld Deelnemen @@ -6173,28 +6279,28 @@ Chat openen - Berichtdetails + Oproepdetails Selecteren - Wissen + Verwijderen - Wissen… + Verwijderen… - Wissen mislukt. + Verwijderen mislukt. - Link kan niet gewist worden. Controleer je verbinding en probeer het opnieuw. - Niet alle oproeplinks konden gewist worden. Controleer je verbinding en probeer het opnieuw. + Link kan niet verwijderd worden. Controleer je verbinding en probeer het opnieuw. + Niet alle oproeplinks konden verwijderd worden. Controleer je verbinding en probeer het opnieuw. Oproepgeschiedenis gewist Oproepgeschiedenis wissen? - Hiermee wordt de oproepgeschiedenis voorgoed verwijderd + Hiermee wordt de oproepgeschiedenis voorgoed gewist Oproepgeschiedenis wissen @@ -6212,7 +6318,7 @@ Alles selecteren - Wissen + Verwijderen %1$d oproep verwijderen? %1$d oproepen verwijderen? @@ -6259,6 +6365,8 @@ Groepsoproep Gemiste groepsoproep + + Gemiste groepsoproep terwijl meldingsprofiel was ingeschakeld Inkomende groepsoproep @@ -6339,7 +6447,7 @@ Annuleren - Verzend + Verzenden diff --git a/app/src/main/res/values-pa/strings.xml b/app/src/main/res/values-pa/strings.xml index 4f1d875537..77aadba27b 100644 --- a/app/src/main/res/values-pa/strings.xml +++ b/app/src/main/res/values-pa/strings.xml @@ -323,8 +323,12 @@ ਤਸਵੀਰ ਨੂੰ ਡਾਊਨਲੋਡ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ। ਤੁਹਾਨੂੰ ਇਹ ਦੁਬਾਰਾ ਭੇਜਣੀ ਪਵੇਗੀ। ਵੀਡੀਓ ਨੂੰ ਡਾਊਨਲੋਡ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ। ਤੁਹਾਨੂੰ ਇਹ ਦੁਬਾਰਾ ਭੇਜਣੀ ਪਵੇਗੀ। - - ਸੋਧਿਆ ਗਿਆ %1$s + + ਹੁਣੇ ਸੋਧਿਆ ਗਿਆ + + %1$s ਸੋਧਿਆ ਗਿਆ + + %1$s ਸੋਧਿਆ ਗਿਆ ਕਾਲ ਵਿੱਚ ਸ਼ਾਮਲ ਹੋਵੋ @@ -358,6 +362,8 @@ ਅਸੁਰੱਖਿਅਤ MMS Signal ਸੁਨੇਹਾ + + ਸੁਨੇਹਾ ਭੇਜੋ ਆਓ Molly %1$s ਨੂੰ ਅਪਣਾਈਏ ਕਿਰਪਾ ਕਰਕੇ ਕੋਈ ਸੰਪਰਕ ਚੁਣੋ ਅਟੈਚਮੈਂਟ ਦਾ ਅਕਾਰ ਤੁਹਾਡੇ ਦੁਆਰਾ ਭੇਜੇ ਜਾ ਰਹੇ ਸੁਨੇਹੇ ਦੀ ਕਿਸਮ ਲਈ ਅਕਾਰ ਦੀ ਸੀਮਾ ਤੋਂ ਵੱਧ ਗਿਆ ਹੈ। @@ -1277,6 +1283,10 @@ ਵੌਇਸ ਕਾਲ ਮਿਸ ਹੋਈ ਵੀਡੀਓ ਕਾਲ ਮਿਸ ਹੋਈ + + ਸੂਚਨਾ ਪ੍ਰੋਫਾਈਲ ਦੇ ਚਾਲੂ ਹੋਣ ਦੌਰਾਨ ਵੌਇਸ ਕਾਲ ਮਿਸ ਹੋਈ + + ਸੂਚਨਾ ਪ੍ਰੋਫਾਈਲ ਦੇ ਚਾਲੂ ਹੋਣ ਦੌਰਾਨ ਵੀਡੀਓ ਕਾਲ ਮਿਸ ਹੋਈ ਤੁਸੀਂ ਵੌਇਸ ਕਾਲ ਨੂੰ ਅਸਵੀਕਾਰ ਕੀਤਾ @@ -1828,6 +1838,8 @@ ਕੈਮਰਾ ਬਦਲੋ ਮਿਊਟ ਨੂੰ ਬਦਲੋ + + ਹੋਰ ਐਕਸ਼ਨ ਕਾਲ ਖਤਮ ਕਰੋ @@ -1844,10 +1856,68 @@ ਡਿਵਾਈਸ ਦੇ ਈਅਰਪੀਸ ਨੂੰ ਦਰਸਾਉਣ ਵਾਲਾ ਆਈਕਨ। + + ਹੱਥ ਉੱਪਰ ਕਰੋ + + ਹੱਥ ਉੱਪਰ ਕਰੋ + + ਕੀ ਹੱਥ ਨੀਚੇ ਕਰਨਾ ਹੈ? + + ਹੱਥ ਨੀਚੇ ਕਰੋ + + ਰੱਦ ਕਰੋ + + ਤੁਸੀਂ ਆਪਣਾ ਹੱਥ ਉੱਪਰ ਕੀਤਾ ਹੈ + + ਵੇਖੋ + + + + %1$s ਨੇ ਹੱਥ ਉੱਪਰ ਕੀਤਾ ਹੈ + %1$s + %2$d ਨੇ ਹੱਥ ਉੱਪਰ ਕੀਤਾ ਹੈ + + + + + %1$s + %1$s +%2$d + + + + ਉੱਪਰ ਕੀਤੇ ਗਏ ਹੱਥ ਦੇ ਵਿਊ਼ ਨੂੰ ਵੱਡਾ ਕਰੋ + + + + Signal ਕਨੈਕਸ਼ਨ + + %1$s ਤੁਹਾਡੇ ਸਿਸਟਮ ਦੇ ਸੰਪਰਕਾਂ ਵਿੱਚ ਸ਼ਾਮਲ ਹਨ + + ਤੁਹਾਡੇ ਕੋਈ ਸਾਂਝੇ ਸਮੂਹ ਨਹੀਂ ਹਨ + + ਬੇਨਤੀਆਂ ਦੀ ਧਿਆਨ ਨਾਲ ਸਮੀਖਿਆ ਕਰੋ + + %1$d ਗਰੁੱਪ ਸਾਂਝੇ ਹਨ + + ਜਾਣ-ਪਛਾਣ + + ਤੁਸੀਂ + - - ਇਸ ਕਾਲ ਵਿੱਚ %1$d ਵਿਅਕਤੀ - ਇਸ ਕਾਲ ਵਿੱਚ %1$d ਲੋਕ + + ਇਸ ਕਾਲ ਵਿੱਚ (%1$d) + ਇਸ ਕਾਲ ਵਿੱਚ (%1$d) + + + Signal (%1$d) ਨੂੰ ਰਿੰਗ ਕਰੇਗਾ + Signal (%1$d) ਨੂੰ ਰਿੰਗ ਕਰੇਗਾ + + + Signal (%1$d) ਨੂੰ ਸੂਚਿਤ ਕਰੇਗਾ + Signal (%1$d) ਨੂੰ ਸੂਚਿਤ ਕਰੇਗਾ + + + ਹੱਥ ਉੱਪਰ ਕੀਤਾ ( %1$d) + ਹੱਥ ਉੱਪਰ ਕੀਤੇ (%1$d) @@ -1961,6 +2031,12 @@ ਵੇਖਿਆ ਮੀਡੀਆ + + + ਨਾਂ ਸੰਬੰਧੀ ਵਿਵਾਦ ਲੱਭਿਆ ਹੈ + + ਵੇਖੋ + %1$sਲਈ ਕੋਈ ਨਤੀਜੇ ਨਹੀਂ ਮਿਲੇ @@ -2127,6 +2203,8 @@ ਭੇਜੋ + + 00 ਵਰਤੋਂਕਾਰ ਨਾਂ ਸ਼ਾਮਲ ਕਰੋ @@ -2149,6 +2227,12 @@ ਛੱਡੋ ਮੁਕੰਮਲ + + ਇਹ ਵਰਤੋਂਕਾਰ ਨਾਂ ਉਪਲਬਧ ਨਹੀਂ ਹੈ, ਕੋਈ ਹੋਰ ਨੰਬਰ ਅਜ਼ਮਾਓ। + + ਵਰਤੋਂਕਾਰ ਨਾਂ ਅਵੈਧ ਹੈ, ਘੱਟ ਤੋਂ ਘੱਟ %1$d ਅੰਕ ਦਰਜ ਕਰੋ। + + ਵਰਤੋਂਕਾਰ ਨਾਂ ਅਵੈਧ ਹੈ, ਵੱਧ ਤੋਂ ਵੱਧ %1$d ਅੰਕ ਦਰਜ ਕਰੋ। %1$d ਸੰਪਰਕ Signal ਉੱਤੇ ਹੈ! @@ -2346,6 +2430,10 @@ ਕਾਰਵਾਈ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2370,6 +2458,8 @@ ਲਿੰਕ ਕੀਤੀ ਡਿਵਾਈਸ ’ਤੇ ਜਵਾਬ ਦਿੱਤਾ। ਲਿੰਕ ਕੀਤੀ ਡਿਵਾਈਸ ’ਤੇ ਇਨਕਾਰ ਕਰ ਦਿੱਤਾ। ਲਿੰਕ ਕੀਤੀ ਡਿਵਾਈਸ ’ਤੇ ਵਿਅਸਤ। + + ਕੈਮਰਾ ਪਲਟਣ ਵਾਲੇ ਬਟਨ ਨੂੰ ਹੁਣ ਇੱਥੇ ਖਿਸਕਾ ਦਿੱਤਾ ਗਿਆ ਹੈ, ਇਸ ਨੂੰ ਅਜ਼ਮਾਉਣ ਲਈ ਆਪਣੀ ਵੀਡੀਓ ਉੱਤੇ ਟੈਪ ਕਰੋ ਕਾਲ ਵਿੱਚ ਕੋਈ ਅਜਿਹਾ ਵਿਅਕਤੀ ਸ਼ਾਮਲ ਹੋਇਆ ਹੈ ਜਿਸਦਾ ਸੁਰੱਖਿਆ ਨੰਬਰ ਬਦਲ ਗਿਆ ਹੈ। @@ -3925,14 +4015,16 @@ ਗਰੁੱਪ ਮੈਂਬਰ ਹਟਾਉਣ ਲਈ ਅਸਫਲ ਰਹੇ। - ਮੈਂਬਰ ਬੇਨਤੀ ਤੁਹਾਡਾ ਸੰਪਰਕ ਗਰੁੱਪ ਵਿੱਚੋਂ ਹਟਾਓ ਸੰਪਰਕ ਅੱਪਡੇਟ ਕਰੋ ਪਾਬੰਦੀ ਲਗਾਓ ਮਿਟਾਓ - ਹਾਲ ਹੀ ਵਿੱਚ ਆਪਣਾ ਪ੍ਰੋਫ਼ਾਈਲ ਨਾਂ %1$s ਤੋਂ ਬਦਲ ਕੇ %2$s ਰੱਖਿਆ। + + %1$s ਨੇ ਹਾਲ ਹੀ ਵਿੱਚ ਆਪਣਾ ਪ੍ਰੋਫ਼ਾਈਲ ਨਾਂ %2$s ਤੋਂ ਬਦਲ ਕੇ %3$s ਰੱਖ ਲਿਆ ਹੈ। + + %1$s ਤੁਹਾਡੇ ਸਿਸਟਮ ਦੇ ਸੰਪਰਕਾਂ ਵਿੱਚ ਸ਼ਾਮਲ ਹਨ %1$s ਸ਼ਾਮਲ ਹੋਏ @@ -4988,6 +5080,12 @@ ਜਦੋਂ ਇਹ ਪ੍ਰੋਫ਼ਾਈਲ ਚਾਲੂ ਹੋਵੇ ਤਾਂ ਲੋਕ ਤੇ ਗਰੁੱਪ, ਜਿੰਨਾਂ ਤੋਂ ਤੁਸੀਂ ਨੋਟੀਫਿਕੇਸ਼ਨ ਤੇ ਕਾਲਾਂ ਲੈਣੀਆਂ ਚਾਹੋ, ਨੂੰ ਜੋੜੋ ਲੋਕ ਜਾਂ ਗਰੁੱਪ ਜੋੜੋ + + ਛੋਟ + + ਸਭ ਕਾਲਾਂ ਨੂੰ ਇਜਾਜ਼ਤਾਂ ਦਿਓ + + ਸਾਰੇ ਜ਼ਿਕਰਾਂ ਲਈ ਸੂਚਨਾ ਦਿਓ ਜੋੜੋ @@ -5602,8 +5700,8 @@ ਸਾਰੇ ਕਨੈਕਸ਼ਨਾਂ ਦੀ ਸਮੀਖਿਆ ਹੋ ਗਈ ਹੈ, ਅੱਗੇ ਜਾਰੀ ਰੱਖਣ ਲਈ \"ਭੇਜੋ\" \'ਤੇ ਟੈਪ ਕਰੋ। - ਤੁਹਾਡੇ %1$d ਕਨੈਕਸ਼ਨ ਨੇ ਸ਼ਾਇਦ Signal ਨੂੰ ਦੁਆਰਾ ਇੰਸਟਾਲ ਕੀਤਾ ਹੈ ਜਾਂ ਆਪਣੇ ਡਿਵਾਈਸਾਂ ਨੂੰ ਬਦਲਿਆ ਹੈ। ਉਹਨਾਂ ਨਾਲ ਆਪਣੀ ਸਟੋਰੀ ਸਾਂਝੀ ਕਰਨ ਤੋਂ ਪਹਿਲਾਂ ਉਹਨਾਂ ਦੇ ਸੁਰੱਖਿਆ ਨੰਬਰ ਦੀ ਸਮੀਖਿਆ ਕਰੋ ਜਾਂ ਉਹਨਾਂ ਨੂੰ ਆਪਣੀ ਸਟੋਰੀ ਵਿੱਚੋਂ ਹਟਾਉਣ ਬਾਰੇ ਵਿਚਾਰ ਕਰੋ। - ਤੁਹਾਡੇ %1$d ਕਨੈਕਸ਼ਨਾਂ ਨੇ ਸ਼ਾਇਦ Signal ਨੂੰ ਦੁਆਰਾ ਇੰਸਟਾਲ ਕੀਤਾ ਹੈ ਜਾਂ ਆਪਣੇ ਡਿਵਾਈਸਾਂ ਨੂੰ ਬਦਲਿਆ ਹੈ। ਉਹਨਾਂ ਨਾਲ ਆਪਣੀ ਸਟੋਰੀ ਸਾਂਝੀ ਕਰਨ ਤੋਂ ਪਹਿਲਾਂ ਉਹਨਾਂ ਦੇ ਸੁਰੱਖਿਆ ਨੰਬਰ ਦੀ ਸਮੀਖਿਆ ਕਰੋ ਜਾਂ ਉਹਨਾਂ ਨੂੰ ਆਪਣੀ ਸਟੋਰੀ ਵਿੱਚੋਂ ਹਟਾਉਣ ਬਾਰੇ ਵਿਚਾਰ ਕਰੋ। + %1$d ਕਨੈਕਸ਼ਨ ਨੇ ਸ਼ਾਇਦ Signal ਨੂੰ ਦੁਬਾਰਾ ਇੰਸਟਾਲ ਕੀਤਾ ਹੈ ਜਾਂ ਡਿਵਾਈਸਾਂ ਨੂੰ ਬਦਲਿਆ ਹੈ। ਤੁਸੀਂ ਉਹਨਾਂ ਦੇ ਸੁਰੱਖਿਆ ਨੰਬਰ ਦੀ ਸਮੀਖਿਆ ਕਰ ਸਕਦੇ ਹੋ ਜਾਂ ਭੇਜਣਾ ਜਾਰੀ ਰੱਖ ਸਕਦੇ ਹੋ। + %1$d ਕਨੈਕਸ਼ਨਾਂ ਨੇ ਸ਼ਾਇਦ Signal ਨੂੰ ਦੁਬਾਰਾ ਇੰਸਟਾਲ ਕੀਤਾ ਹੈ ਜਾਂ ਡਿਵਾਈਸਾਂ ਨੂੰ ਬਦਲਿਆ ਹੈ। ਤੁਸੀਂ ਉਹਨਾਂ ਦੇ ਸੁਰੱਖਿਆ ਨੰਬਰਾਂ ਦੀ ਸਮੀਖਿਆ ਕਰ ਸਕਦੇ ਹੋ ਜਾਂ ਭੇਜਣਾ ਜਾਰੀ ਰੱਖ ਸਕਦੇ ਹੋ। ਸੁਰੱਖਿਆ ਨੰਬਰ ਦੀ ਤਸਦੀਕ ਕਰੋ @@ -5961,12 +6059,18 @@ IBAN ਦੇਸ਼ ਕੋਡ ਸਮਰਥਿਤ ਨਹੀਂ ਹੈ ਅਵੈਧ IBAN + + ਘੱਟ ਤੋਂ ਘੱਟ 2 ਅੱਖਰ + + ਈਮੇਲ ਪਤਾ ਅਵੈਧ ਹੈ iDEAL ਆਪਣੇ ਬੈਂਕ, ਨਾਮ ਅਤੇ ਈਮੇਲ ਪਤਾ ਦਰਜ ਕਰੋ। Stripe ਤੁਹਾਨੂੰ ਤੁਹਾਡੇ ਦਾਨ ਬਾਰੇ ਅੱਪਡੇਟ ਭੇਜਣ ਲਈ ਇਸ ਈਮੇਲ ਦੀ ਵਰਤੋਂ ਕਰਦਾ ਹੈ। %1$s + + ਆਪਣੇ ਬੈਂਕ ਦੇ ਵੇਰਵੇ ਦਰਜ ਕਰੋ। Signal ਤੁਹਾਡੀ ਨਿੱਜੀ ਜਾਣਕਾਰੀ ਨੂੰ ਇਕੱਠਾ ਜਾਂ ਸਟੋਰ ਨਹੀਂ ਕਰਦਾ ਹੈ। %1$s ਹੋਰ ਜਾਣੋ @@ -6152,6 +6256,8 @@ ਆਊਟਗੋਇੰਗ ਕਾਲ ਮਿਸਡ ਕਾਲ + + ਸੂਚਨਾ ਪ੍ਰੋਫਾਈਲ ਦੇ ਚਾਲੂ ਹੋਣ ਦੌਰਾਨ ਕਾਲ ਮਿਸ ਹੋਈ ਸ਼ਾਮਲ ਹੋਵੋ @@ -6259,6 +6365,8 @@ ਗਰੁੱਪ ਕਾਲ ਮਿਸ ਹੋਈਆਂ ਗਰੁੱਪ ਕਾਲ + + ਸੂਚਨਾ ਪ੍ਰੋਫਾਈਲ ਦੇ ਚਾਲੂ ਹੋਣ ਦੌਰਾਨ ਗਰੁੱਪ ਕਾਲ ਮਿਸ ਹੋਈ ਇਨਕਮਿੰਗ ਗਰੁੱਪ ਕਾਲ diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index e2723907cd..39a5543f5c 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -325,8 +325,12 @@ Nie udało się pobrać obrazu. Wyślij go ponownie. Nie udało się pobrać wideo. Wyślij je ponownie. - - Edytowana %1$s + + Edytowano teraz + + Edytowano %1$s + + Edytowano %1$s Dołącz do rozmowy @@ -364,6 +368,8 @@ Nieszyfrowany MMS Wiadomość Signal + + Wyślij wiadomość Zacznij używać Molly %1$s Proszę wybierz kontakt Rozmiar tego załącznika przekracza limit wiadomości. @@ -1365,6 +1371,10 @@ Nieodebrane połączenie głosowe Nieodebrane połączenie wideo + + Nieodebrane połączenie głosowe przy włączonym profilu powiadomień + + Nieodebrane połączenie wideo przy włączonym profilu powiadomień Odrzucono połączenie głosowe @@ -1970,6 +1980,8 @@ Przełącz aparat Przełącz wyciszenie + + Dodatkowe działania Zakończ rozmowę @@ -1986,12 +1998,80 @@ Ikona przedstawiająca głośnik przedni urządzenia. + + Podnieś rękę + + Podnieś rękę + + Opuścić rękę? + + Opuść rękę + + Anuluj + + Podnosisz rękę + + Zobacz + + + + %1$s podnosi rękę + %1$s + %2$d podnoszą rękę + %1$s + %2$d podnoszą rękę + %1$s + %2$d podnoszą rękę + + + + + %1$s + %1$s +%2$d + %1$s +%2$d + %1$s +%2$d + + + + Rozwiń widok podniesionych rąk + + + + Kontakt Signal + + %1$s znajduje się w kontaktach systemowych + + Nie macie żadnych wspólnych grup + + Przejrzyj uważnie wszystkie prośby + + Liczba wspólnych grup: %1$d + + O mnie + + Ty + - - W tej rozmowie · %1$d osoba - W tej rozmowie · %1$d osoby - W tej rozmowie · %1$d osób - W tej rozmowie · %1$d osoby + + W tym połączeniu (%1$d) + W tym połączeniu (%1$d) + W tym połączeniu (%1$d) + W tym połączeniu (%1$d) + + + Signal zadzwoni do (%1$d) + Signal zadzwoni do (%1$d) + Signal zadzwoni do (%1$d) + Signal zadzwoni do (%1$d) + + + Signal powiadomi (%1$d) + Signal powiadomi (%1$d) + Signal powiadomi (%1$d) + Signal powiadomi (%1$d) + + + Podniesione ręce (%1$d) + Podniesione ręce (%1$d) + Podniesione ręce (%1$d) + Podniesione ręce (%1$d) @@ -2107,6 +2187,12 @@ Obejrzano Multimedia + + + Znaleziono konflikt nazw + + Zobacz + Brak wyników dla \"%1$s\" @@ -2273,6 +2359,8 @@ Wyślij + + 00 Dodaj nazwę użytkownika @@ -2295,6 +2383,12 @@ Pomiń Gotowe + + Ta nazwa użytkownika jest niedostępna, spróbuj innego numeru. + + Nieprawidłowa nazwa użytkownika, wprowadź co najmniej %1$d cyfry. + + Nieprawidłowa nazwa użytkownika, wprowadź maks. %1$d cyfr. %1$dkontakt jest w Signal! @@ -2500,6 +2594,10 @@ Przetwarzanie… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2524,6 +2622,8 @@ Odebrane na połączonym urządzeniu. Odrzucone na połączonym urządzeniu. Zajęte na połączonym urządzeniu. + + Opcja Przełącz kamerę została przeniesiona tutaj. Stuknij w swoje wideo, aby ją wypróbować Ktoś ze zmienionym numerem bezpieczeństwa dołączył do tej rozmowy. @@ -4127,14 +4227,16 @@ Usuwanie członka grupy nie powiodło się. - Członek Prośba Twój kontakt Usuń z grupy Uaktualnij kontakt Zablokuj Usuń - Ostatnio zmienił(a) nazwę profilu z %1$s na %2$s + + %1$s ostatnio zmienił(a) nazwę profilu z %2$s na %3$s + + %1$s znajduje się w kontaktach systemowych %1$s dołączył(a) @@ -5208,6 +5310,12 @@ Dodaj osoby i grupy, których powiadomienia i połączenia chcesz otrzymywać, gdy ten profil jest włączony Dodaj osoby lub grupy + + Wyjątki + + Zezwalaj na wszystkie połączenia + + Powiadamiaj o wszystkich wzmiankach Dodaj @@ -5856,10 +5964,10 @@ Wszystkie kontakty zostały przejrzane. Stuknij w opcję „wyślij”, aby kontynuować. - Wśród kontaktów masz %1$d osobę, która mogła ponownie zainstalować aplikację Signal albo zmienić urządzenie. Przed udostępnieniem jej Relacji zweryfikuj jej numery bezpieczeństwa lub rozważ usunięcie tej osoby ze swojej Relacji. - Wśród kontaktów masz %1$d osoby, które mogły ponownie zainstalować aplikację Signal albo zmienić urządzenie. Przed udostępnieniem im Relacji zweryfikuj ich numery bezpieczeństwa lub rozważ usunięcie tych osób ze swojej Relacji. - Wśród kontaktów masz %1$d osób, które mogły ponownie zainstalować aplikację Signal albo zmienić urządzenie. Przed udostępnieniem im Relacji zweryfikuj ich numery bezpieczeństwa lub rozważ usunięcie tych osób ze swojej Relacji. - Wśród kontaktów masz %1$d osoby, która mogła ponownie zainstalować aplikację Signal albo zmienić urządzenie. Przed udostępnieniem jej Relacji zweryfikuj jej numery bezpieczeństwa lub rozważ usunięcie tej osoby ze swojej Relacji. + Wśród kontaktów masz %1$d osobę, która mogła ponownie zainstalować aplikację Signal albo zmienić urządzenie. Możesz zweryfikować jej numer bezpieczeństwa lub wysłać bez weryfikacji. + Wśród kontaktów masz %1$d osoby, które mogły ponownie zainstalować aplikację Signal albo zmienić urządzenie. Możesz zweryfikować ich numer bezpieczeństwa lub wysłać bez weryfikacji. + Wśród kontaktów masz %1$d osób, które mogły ponownie zainstalować aplikację Signal albo zmienić urządzenie. Możesz zweryfikować ich numer bezpieczeństwa lub wysłać bez weryfikacji. + Wśród kontaktów masz %1$d osób, które mogły ponownie zainstalować aplikację Signal albo zmienić urządzenie. Możesz zweryfikować ich numer bezpieczeństwa lub wysłać bez weryfikacji. Zweryfikuj numer bezpieczeństwa @@ -6225,12 +6333,18 @@ Kod kraju IBAN nie jest obsługiwany Nieprawidłowy IBAN + + Minimum 2 znaki + + Nieprawidłowy adres e-mail iDEAL Wprowadź nazwę banku, imię i nazwisko oraz adres e-mail. Stripe będzie wysyłać na ten adres e-mail powiadomienia dotyczące darowizny. %1$s + + Wprowadź swoje dane bankowe. Signal nie gromadzi ani nie przechowuje Twoich danych osobowych. %1$s Dowiedz się więcej @@ -6418,6 +6532,8 @@ Wychodzące Nieodebrane + + Nieodebrane przy włączonym profilu powiadomień Dołącz @@ -6531,6 +6647,8 @@ Połączenie grupowe Nieodebrane połączenie grupowe + + Nieodebrane połączenie grupowe przy włączonym profilu powiadomień Nadchodzące połączenie grupowe diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 4e597b0a34..c587c0e0e7 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -323,8 +323,12 @@ Não foi possível baixar a imagem. Você terá que enviar a imagem novamente. Não foi possível baixar o vídeo. Você terá que enviar o vídeo novamente. - - Editado há %1$s + + Editado agora + + Editado há %1$s + + Editado há %1$s Participar da chamada @@ -358,6 +362,8 @@ MMS inseguro Mensagem no Signal + + Enviar mensagem Vamos mudar para o Molly %1$s Favor escolher um contato O anexo excede os limites de tamanho para o tipo de mensagem sendo enviada. @@ -1277,6 +1283,10 @@ Chamada de voz perdida Chamada de vídeo perdida + + Chamada de voz perdida com o perfil de notificação ativado + + Chamada de vídeo perdida com o perfil de notificação ativado Você recusou uma chamada de voz @@ -1828,6 +1838,8 @@ Ativar/desativar câmera Ativar/desativar som + + Mais ações Encerrar @@ -1844,10 +1856,68 @@ Um ícone que representa o fone de ouvido de um dispositivo. + + Levantar a mão + + Levantar a mão + + Quer abaixar a mão? + + Abaixar a mão + + Cancelar + + Você levantou a mão + + Exibir + + + + %1$s levantou a mão + %1$s + %2$d levantaram a mão + + + + + %1$s + %1$s +%2$d + + + + Expandir visualização da mão levantada + + + + Contatos do Signal + + %1$s está nos seus contatos do sistema + + Você não tem grupos em comum + + Analise os pedidos com atenção + + %1$d grupos em comum + + Sobre mim + + Você + - - Na ligação · %1$d pessoa - Na ligação · %1$d pessoas + + Nesta chamada (%1$d) + Nesta chamada (%1$d) + + + O Signal vai ligar para (%1$d) + O Signal vai ligar para (%1$d) + + + O Signal vai notificar (%1$d) + O Signal vai notificar (%1$d) + + + Mão levantada (%1$d) + Mãos levantadas (%1$d) @@ -1961,6 +2031,12 @@ Vista Mídia + + + Conflito de nome encontrado + + Exibir + Nenhum resultado encontrado para \'%1$s\' @@ -2127,6 +2203,8 @@ Enviar + + 00 Adicionar um nome de usuário @@ -2149,6 +2227,12 @@ Pular Pronto + + Este nome de usuário não está disponível. Tente outro número. + + Nome de usuário inválido, insira no mínimo %1$d dígitos. + + Nome de usuário inválido, insira no mínimo %1$d dígitos. %1$d contato está no Signal! @@ -2346,6 +2430,10 @@ Processando… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2370,6 +2458,8 @@ Atendido em um dispositivo vinculado. Recusado em um dispositivo vinculado. Ocupado em um dispositivo vinculado. + + Agora você pode girar a câmera aqui. Toque no seu vídeo para testar Alguém entrou nessa chamada com um número de segurança que foi alterado. @@ -3925,14 +4015,16 @@ Falha ao remover o membro do grupo. - Membro Pedido Seu contato Remover do grupo Atualizar o contato Bloquear Excluir - Mudou recentemente o nome de perfil de %1$s para %2$s + + %1$s mudou seu nome de perfil recentemente de %2$s para %3$s + + %1$s está nos seus contatos do sistema %1$s entrou @@ -4988,6 +5080,12 @@ Adicione pessoas e grupos dos quais você deseja receber notificações e ligações quando este perfil estiver ativado Adicionar pessoas ou grupos + + Exceções + + Permitir todas as chamadas + + Notifique-me quando eu for mencionada(o) Adicionar @@ -5602,8 +5700,8 @@ Todos os contatos foram analisados, toque em enviar para continuar. - Você tem %1$d contato que pode ter reinstalado o Signal ou trocado de aparelho. Também é possível verificar o número de segurança dele antes de enviar. - Você tem %1$d contatos que podem ter reinstalado o Signal ou trocado de aparelho. Também é possível verificar o número de segurança desses contatos antes de enviar. + %1$d contato pode ter reinstalado o Signal ou trocado de aparelho. Confira o número de segurança ou continue enviando. + %1$d contatos podem ter reinstalado o Signal ou trocado de aparelhos. Confira os números de segurança ou continue enviando. Verificar número de segurança @@ -5961,12 +6059,18 @@ O código do país do IBAN não é aceito IBAN inválido + + Mínimo de 2 caracteres + + Endereço de e-mail inválido iDEAL Insira seu banco, nome e e-mail. O Stripe usa este e-mail para enviar atualizações sobre sua doação. %1$s + + Insira seus dados bancários. O Signal não coleta nem armazena suas informações pessoais. %1$s Saiba mais @@ -6152,6 +6256,8 @@ Realizada Perdida + + Perdida(s) com o perfil de notificação ativado Entrar @@ -6259,6 +6365,8 @@ Chamada em grupo Chamada de grupo perdida + + Chamada de grupo perdida com o perfil de notificação ativado Recebendo chamada de grupo diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index dfa4c0a7b4..d64253782d 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -323,8 +323,12 @@ Não é possível transferir a imagem. {0} terá de a enviar outra vez. Não é possível transferir o vídeo. {0} terá de a enviar outra vez. - - Editada %1$s + + Editada Agora + + Editada %1$s + + Editada %1$s Entrar na chamada @@ -358,6 +362,8 @@ MMS inseguro Mensagem do Signal + + Enviar mensagem Vamos mudar para o Molly %1$s Por favor escolha um contacto O anexo tem um tamanho superior ao limite do tipo de mensagem que está a enviar. @@ -1277,6 +1283,10 @@ Chamada de voz perdida Videochamada perdida + + Chamada de voz perdida no modo perfil de notificação + + Videochamada perdida no modo perfil de notificação Rejeitou uma chamada de voz @@ -1535,7 +1545,7 @@ Quer deixar que %1$s lhe envie mensagens? Não receberá nenhuma mensagem deste utilizador até o desbloquear. Obter atualizações e novidades de %1$s? Não receberá nenhuma atualização desse utilizador até o desbloquear. Deseja continuar este chat com este grupo e partilhar o seu nome e fotografia com eles? - This Legacy Group can no longer be used. Create a new group to activate new features like @mentions and admins. + Este \"grupo legado\" já não pode ser usado. Crie um novo grupo para ativar novas funcionalidades como @menções e admins. Este \'Grupo legado\' deixou de poder ser utilizado porque é demasiadamente grande. O tamanho máximo do grupo é de %1$d. Deseja continuar este chat com %1$s e partilhar o seu nome e fotografia? Deseja juntar-se a este grupo e partilhar o seu nome e fotografia com os seus membros? Eles não sabem que viu as mensagens deles até que aceite. @@ -1828,6 +1838,8 @@ Comutar câmara Silenciar/ativar som + + Ações adicionais Terminar chamada @@ -1844,10 +1856,68 @@ Um ícone a representar um fone de ouvido do dispositivo. + + Levantar mão + + Levantar mão + + Baixar a mão? + + Baixar mão + + Cancelar + + Levantou a mão + + Ver + + + + %1$s levantou a mão + %1$s + %2$d levantaram a mão + + + + + %1$s + %1$s +%2$d + + + + Expandir visualização de mão levantada + + + + Contacto do Signal + + %1$s está nos seus contactos do sistema + + Não têm grupos em comum + + Revejo os pedidos de forma cuidada + + %1$d grupos em comum + + Acerca + + Você + - - · %1$d pessoa nesta chamada - · %1$d pessoas nesta chamada + + Nesta chamada (%1$d) + Nesta chamada (%1$d) + + + O Signal irá chamar (%1$d) + O Signal irá chamar (%1$d) + + + O Signal irá notificar (%1$d) + O Signal irá notificar (%1$d) + + + Mão levantada (%1$d) + Mãos levantadas (%1$d) @@ -1961,6 +2031,12 @@ Vista(s) Multimédia + + + Encontrado conflito de nome + + Ver + Sem resultados para \'%1$s\' @@ -2127,6 +2203,8 @@ Enviar + + 00 Adicione um nome de utilizador @@ -2149,6 +2227,12 @@ Saltar Concluído + + Este nome de utilizador não está disponível, experimente outro número. + + Nome de utilizador inválido, insira um mínimo de %1$d dígitos. + + Nome de utilizador inválido, insira um máximo de %1$d dígitos. %1$d contacto está no Signal! @@ -2346,6 +2430,10 @@ A processar… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2370,6 +2458,8 @@ Atendida num dispositivo associado. Recusada num dispositivo associado. Ocupado num dispositivo associado. + + A Flip Camera foi movida para cá, toque no seu vídeo para experimentar Alguém se juntou a esta chamada com um número de segurança modificado. @@ -3925,14 +4015,16 @@ Falha ao remover o membro do grupo. - Membro Pedido O seu contacto Remover do grupo Atualizar contacto Bloquear Eliminar - Alterou recentemente o seu nome de perfil de %1$s para %2$s + + %1$s alterou recentemente o seu nome de perfil de %2$s para %3$s + + %1$s está nos seus contactos do sistema %1$s juntou-se @@ -4988,6 +5080,12 @@ Adicione as pessoas ou grupos dos quais deseja receber notificações e chamadas quando este perfil se encontra ativo Adicionar pessoas ou grupos + + Exceções + + Permitir todas as chamadas + + Notificar em todas as menções Adicionar @@ -5602,8 +5700,8 @@ Todos os contactos foram analisados, toque em enviar para continuar. - %1$d dos seus contactos talvez tenha reinstalado o Signal ou mudado de dispositivo. Antes de partilhar a história com esse contacto, verifique os seus números de segurança ou pondere removê-lo da sua história. - %1$d dos seus contactos talvez tenham reinstalado o Signal ou mudado de dispositivo. Antes de partilhar a história com eles, verifique os seus números de segurança ou pondere removê-los da sua história. + %1$d contacto pode ter reinstalado o Signal ou mudado de dispositivo. Pode verificar o seu número de segurança ou prosseguir a enviar. + %1$d contactos podem ter reinstalado o Signal ou mudado de dispositivo. Pode verificar os seus números de segurança ou prosseguir e enviar. Verificar número de segurança @@ -5961,12 +6059,18 @@ O código de país do IBAN não é suportado IBAN inválido + + Mínimo 2 caracteres + + Email inválido iDEAL Insira o seu banco, nome e email. O Stripe usa este email para lhe enviar atualizações acerca da sua doação. %1$s + + Insira os seus detalhes bancários. O Signal não recolhe nem armazena as suas informações pessoais. %1$s Saber mais @@ -6152,6 +6256,8 @@ Saída Perdida + + Perdida durante modo perfil de notificação Entrar @@ -6259,6 +6365,8 @@ Chamada de grupo Chamada de grupo perdida + + Chamada de grupo perdida durante perfil de notificação Chamada de grupo recebida diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index cd47fd14ed..344045c0ff 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -324,8 +324,12 @@ Nu se poate descărca imaginea. Va trebui să o trimiți din nou. Nu se poate descărca videoclipul. Va trebui să îl trimiți din nou. - - Editat la %1$s + + Editat acum + + Editat la %1$s + + Editat la %1$s Alătură-te apelului @@ -361,6 +365,8 @@ MMS nesecurizat Mesaj Signal + + Trimite mesaj Hai să folosim Molly %1$s Te rugăm alege un contact Atașamentul depășește limita de mărime pentru tipul de mesaj pe care-l trimiți. @@ -1321,6 +1327,10 @@ Apel vocal nepreluat Apel video nepreluat + + Missed voice call while notification profile on + + Missed video call while notification profile on Ai respins un apel vocal @@ -1899,6 +1909,8 @@ Schimbă camera Comută mute + + Acțiuni suplimentare Închide @@ -1915,11 +1927,74 @@ O pictogramă care reprezintă casca unui dispozitiv. + + Ridică mâna + + Ridică mâna + + Lași mâna jos? + + Coboară mâna + + Anulează + + Ți-ai ridicat mâna + + Vizualizare + + + + %1$s a ridicat mâna + %1$s + %2$d au ridicat mâna + %1$s + %2$d au ridicat mâna + + + + + %1$s + %1$s +%2$d + %1$s +%2$d + + + + Extinde vizualizarea mâinilor ridicate + + + + Conexiunea Signal + + %1$s este în contactele tale de sistem + + Nu aveți grupuri în comun + + Examinează cu atenție solicitările + + %1$d grupuri în comun + + Despre + + Tu + - - În acest apel · %1$d persoană - În acest apel · %1$d persoane - În acest apel · %1$d de persoane + + În acest apel (%1$d) + În acest apel (%1$d) + În acest apel (%1$d) + + + Signal va suna (%1$d) + Signal va suna (%1$d) + Signal va suna (%1$d) + + + Signal va trimite o notificare (%1$d) + Signal va trimite o notificare (%1$d) + Signal va trimite o notificare (%1$d) + + + Mână ridicată (%1$d) + Mâini ridicate (%1$d) + Mâini ridicate (%1$d) @@ -2034,6 +2109,12 @@ Vizualizat Media + + + Conflictul numelui descoperit + + Vizualizare + Nu s-a găsit nici un rezultat pentru \'%1$s\' @@ -2200,6 +2281,8 @@ Trimite + + 00 Adaugă un nume de utilizator @@ -2222,6 +2305,12 @@ Omite Gata + + This username is not available, try another number. + + Invalid username, enter a minimum of %1$d digits. + + Invalid username, enter a maximum of %1$d digits. %1$d contact este pe Signal! @@ -2423,6 +2512,10 @@ Procesare… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2447,6 +2540,8 @@ Răspuns de pe un dispozitiv asociat. Respins de pe un dispozitiv asociat. Ocupat de pe un dispozitiv asociat. + + Butonul Schimbă camera a fost mutat aici, atinge videoclipul pentru a-l încerca Cineva s-a alăturat apelului cu un număr de siguranță ce s-a schimbat. @@ -4026,14 +4121,16 @@ Eliminarea membrului grupului nu a reușit. - Membru Cerere Contactul tău Elimină din grup Actualizează contactul Blochează Șterge - Recent și-au schimbat numele profilului din %1$s în %2$s + + %1$s și-a schimbat recent numele de profil de la %2$s la %3$s + + %1$s este în contactele tale de sistem %1$s s-a alăturat @@ -5098,6 +5195,12 @@ Adaugă persoane și grupuri de la care vrei să primești notificări și apeluri atunci când profilul este activ Adaugă persoane sau grupuri + + Excepții + + Permite toate apelurile + + Notifică pentru toate mențiunile Adaugă @@ -5729,9 +5832,9 @@ Toate conexiunile au fost evaluate, atinge trimite pentru a continua. - Ai %1$d persoană de contact care e posibil să fi reinstalat Signal sau să-și fi schimbat dispozitivul. Înainte de a-ți distribui povestea către aceasta, poți să-i revizuiești numărul de siguranță sau să o elimini din povestea ta. - Ai %1$d persoane de contact care e posibil să fi reinstalat Signal sau să-și fi schimbat dispozitivul. Înainte de a-ți distribui povestea către acestea, poți să le revizuiești numerele de siguranță sau să le elimini din povestea ta. - Ai %1$d de persoane de contact care e posibil să fi reinstalat Signal sau să-și fi schimbat dispozitivul. Înainte de a-ți distribui povestea către acestea, poți să le revizuiești numerele de siguranță sau să le elimini din povestea ta. + %1$d connection may have reinstalled Signal or changed devices. You may review their safety number or continue with the send. + %1$d connections may have reinstalled Signal or changed devices. You may review their safety numbers or continue with the send. + %1$d connections may have reinstalled Signal or changed devices. You may review their safety numbers or continue with the send. Verifică numărul de siguranță @@ -6093,12 +6196,18 @@ Codul țării IBAN nu este acceptat IBAN nevalid + + Minim 2 caractere + + Adresa email nevalidă iDEAL Introdu banca, numele și adresa de e-mail. Stripe folosește acest e-mail pentru a trimite actualizări despre donația ta. %1$s + + Introdu detaliile tale bancare. Signal nu colectează și nu păstrează informațiile personale. %1$s Află mai multe @@ -6285,6 +6394,8 @@ Trimise Ratate + + Missed while notification profile on Alătură-te @@ -6395,6 +6506,8 @@ Apel de grup Apel de grup ratat + + Missed group call while notification profile on Apel de intrare de grup diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 13b517efbd..19b228edc7 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -325,8 +325,12 @@ Невозможно загрузить изображение. Вам нужно отправить его снова. Невозможно загрузить видео. Вам нужно отправить его снова. - - Изменено %1$s + + Изменено только что + + Изменено %1$s + + Изменено %1$s Присоединиться к звонку @@ -364,6 +368,8 @@ Незащищённое MMS Сообщение Signal + + Отправить сообщение Давайте перейдем на Molly %1$s Пожалуйста, выберите контакт Вложение превышает предельный размер для того типа сообщения, которое вы отправляете. @@ -1365,6 +1371,10 @@ Пропущенный голосовой звонок Пропущенный видеозвонок + + Пропущенный аудиозвонок при включённом профиле уведомлений + + Пропущенный видеозвонок при включённом профиле уведомлений Вы отклонили голосовой звонок @@ -1970,6 +1980,8 @@ Переключение камеры Отключение звука + + Дополнительные действия Завершить звонок @@ -1986,12 +1998,80 @@ Значок, изображающий наушник устройства. + + Поднять руку + + Поднять руку + + Опустить руку? + + Опустить руку + + Отменить + + Вы подняли руку + + Просмотреть + + + + %1$s поднял(-а) руку + %1$s и %2$d подняли руку + %1$s и %2$d подняли руку + %1$s и %2$d подняли руку + + + + + %1$s + %1$s +%2$d + %1$s +%2$d + %1$s +%2$d + + + + Развернуть вид поднявших руку + + + + Контакт Signal + + %1$s есть в ваших системных контактах + + У вас нет общих групп + + Внимательно проверяйте запросы + + %1$d общие группы + + Обо мне + + Вы + - - В этом звонке · %1$d человек - В этом звонке · %1$d человека - В этом звонке · %1$d человек - В этом звонке · %1$d человек + + В этом звонке (%1$d) + В этом звонке (%1$d) + В этом звонке (%1$d) + В этом звонке (%1$d) + + + Signal будет звонить (%1$d) + Signal будет звонить (%1$d) + Signal будет звонить (%1$d) + Signal будет звонить (%1$d) + + + Signal вышлет уведомление (%1$d) + Signal вышлет уведомление (%1$d) + Signal вышлет уведомление (%1$d) + Signal вышлет уведомление (%1$d) + + + Поднятые руки (%1$d) + Поднятые руки (%1$d) + Поднятые руки (%1$d) + Поднятые руки (%1$d) @@ -2107,6 +2187,12 @@ Просмотрено Медиа + + + Найден конфликт имен + + Просмотреть + Не найдено результатов для «%1$s» @@ -2273,6 +2359,8 @@ Отправить + + 00 Добавить имя пользователя @@ -2295,6 +2383,12 @@ Пропустить Готово + + Это имя пользователя недоступно, попробуйте другой номер. + + Неверное имя пользователя, введите минимум %1$d цифры. + + Неверное имя пользователя, введите максимум %1$d цифр. %1$d ваш контакт уже в Signal! @@ -2500,6 +2594,10 @@ Обработка… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2524,6 +2622,8 @@ Принят на привязанном устройстве. Отклонён на привязанном устройстве. Занято на привязанном устройстве. + + Флип-камера была перемещена сюда. Нажмите на ваше видео, чтобы попробовать её в действии Кто-то с изменившимся кодом безопасности присоединился к этому звонку. @@ -4127,14 +4227,16 @@ Не удалось удалить участника группы. - Участник Запрос Ваш контакт Удалить из группы Обновить контакт Заблокировать Удалить - Недавно изменил(-а) своё имя профиля с %1$s на %2$s + + %1$s недавно изменил(-а) своё имя профиля с %2$s на %3$s + + %1$s есть в ваших системных контактах %1$s присоединился(-лась) @@ -5208,6 +5310,12 @@ Добавьте людей и групп, от которых вы хотите получать уведомления и звонки, когда включён этот профиль Добавить людей или групп + + Исключения + + Разрешить все звонки + + Уведомлять обо всех упоминаниях Добавить @@ -5856,10 +5964,10 @@ Все контакты были проверены, нажмите «отправить», чтобы продолжить. - У вас есть контакты (%1$d), которые, возможно, переустановили Signal или сменили устройства. Прежде чем поделиться с ними своей историей, просмотрите их коды безопасности или подумайте о том, чтобы удалить их из своей истории. - У вас есть контакты (%1$d), которые, возможно, переустановили Signal или сменили устройства. Прежде чем поделиться с ними своей историей, просмотрите их коды безопасности или подумайте о том, чтобы удалить их из своей истории. - У вас есть контакты (%1$d), которые, возможно, переустановили Signal или сменили устройства. Прежде чем поделиться с ними своей историей, просмотрите их коды безопасности или подумайте о том, чтобы удалить их из своей истории. - У вас есть контакты (%1$d), которые, возможно, переустановили Signal или сменили устройства. Прежде чем поделиться с ними своей историей, просмотрите их коды безопасности или подумайте о том, чтобы удалить их из своей истории. + %1$d контакт, который, возможно, переустановил Signal или сменил устройство. Вы можете просмотреть его код безопасности или продолжить отправку. + %1$d контакта, которые, возможно, переустановили Signal или сменили устройство. Вы можете просмотреть их коды безопасности или продолжить отправку. + %1$d контактов, которые, возможно, переустановили Signal или сменили устройство. Вы можете просмотреть их коды безопасности или продолжить отправку. + %1$d контакта, которые, возможно, переустановили Signal или сменили устройство. Вы можете просмотреть их коды безопасности или продолжить отправку. Подтвердить код безопасности @@ -6225,12 +6333,18 @@ Код страны IBAN не поддерживается Неверный IBAN + + Минимум два символа + + Недействительный адрес электронной почты iDEAL Введите свой банк, имя и адрес электронной почты. Stripe использует этот адрес электронной почты для отправки обновлений о вашем пожертвовании. %1$s + + Введите ваши банковские реквизиты. Signal не собирает и не хранит вашу личную информацию. %1$s Узнать больше @@ -6418,6 +6532,8 @@ Исходящие Пропущенные + + Пропущенные при включённом профиле уведомлений Войти @@ -6531,6 +6647,8 @@ Групповой звонок Пропущенный групповой звонок + + Пропущенный групповой звонок при включённом профиле уведомлений Входящий групповой звонок diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index 3e22562f1e..9513b927ac 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -325,8 +325,12 @@ Fotku sa nepodarilo stiahnuť. Musíte ju odoslať znova. Video sa nepodarilo stiahnuť. Musíte ho odoslať znova. - - Upravené %1$s + + Teraz upravené + + Upravené %1$s + + Upravené %1$s Pripojiť sa k hovoru @@ -364,6 +368,8 @@ Nezabezpečená MMS Signal správa + + Poslať správu Prejdime na Molly %1$s Prosím vyberte kontakt Príloha presahuje limit veľkosti pre tento typ správy. @@ -1365,6 +1371,10 @@ Zmeškaný hlasový hovor Zmeškaný videohovor + + Zmeškaný hlasový hovor pri zapnutom profile upozornení + + Zmeškaný videohovor pri zapnutom profile upozornení Odmietli ste hlasový hovor @@ -1970,6 +1980,8 @@ Zapnúť/vypnúť kameru Zapnúť/vypnúť zvuky + + Dalšie možnosti Ukončiť hovor @@ -1986,12 +1998,80 @@ Ikona predstavujúca slúchadlo zariadenia. + + Zdvihnúť ruku + + Zdvihnúť ruku + + Znížiť ruku? + + Znížiť ruku + + Zrušiť + + Zdvihli ste ruku + + Zobraziť + + + + Používateľ %1$s zdvihol ruku + %1$s + %2$d zdvihli ruku + %1$s + %2$d zdvihlo ruku + %1$s + %2$d zdvihlo ruku + + + + + %1$s + %1$s +%2$d + %1$s +%2$d + %1$s +%2$d + + + + Rozbaliť náhľad používateľov so zdvihnutou rukou + + + + Signal spojenie + + %1$s je vo vašich systémových kontaktoch + + Nemáte žiadne spoločné skupiny + + Požiadavky pozorne overte + + Počet spoločných skupín: %1$d + + Detaily + + Vy + - - V tomto hovore · %1$d osoba - V tomto hovore · %1$d ľudia - V tomto hovore · %1$d ľudí - V tomto hovore · %1$d ľudí + + V tomto hovore (%1$d) + V tomto hovore (%1$d) + V tomto hovore (%1$d) + V tomto hovore (%1$d) + + + Signal zavolá používateľovi (%1$d) + Signal zavolá používateľom (%1$d) + Signal zavolá používateľom (%1$d) + Signal zavolá používateľom (%1$d) + + + Signal upozorní používateľa (%1$d) + Signal upozorní používateľov (%1$d) + Signal upozorní používateľov (%1$d) + Signal upozorní používateľov (%1$d) + + + Zdvihnuté ruky (%1$d) + Zdvihnuté ruky (%1$d) + Zdvihnuté ruky (%1$d) + Zdvihnuté ruky (%1$d) @@ -2107,6 +2187,12 @@ Zobrazené Médiá + + + Boli nájdené rovnaké mená + + Zobraziť + Žiadne výsledky pre \"%1$s\" @@ -2273,6 +2359,8 @@ Odoslať + + 00 Pridajte používateľské meno @@ -2295,6 +2383,12 @@ Preskočiť Hotovo + + Toto používateľské meno nie je dostupné, skúste iné číslo. + + Neplatné používateľské meno, zadajte minimálne %1$d číslice. + + Neplatné používateľské meno, zadajte maximálne %1$d číslic. %1$d váš kontakt začal používať Signal! @@ -2500,6 +2594,10 @@ Spracováva sa… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2524,6 +2622,8 @@ Prijaté na pripojenom zariadení. Odmietnuté na pripojenom zariadení. Zaneprázdnené na pripojenom zariadení. + + Flip Camera bola presunutá sem, ťuknite na video a vyskúšajte to K tomuto hovoru sa pripojil niekto so zmeneným bezpečnostným číslom. @@ -4127,14 +4227,16 @@ Nepodarilo sa odstrániť člena skupiny. - Člen Žiadosť Váš kontakt Odstrániť zo skupiny Aktualizovať kontakt Blokovať Vymazať - Nedávno zmenil/a názov svojho profilu z %1$s na %2$s + + Používateľ %1$s si nedávno zmenil svoje používateľské meno z %2$s na %3$s + + %1$s je vo vašich systémových kontaktoch %1$s sa pripojil(a) k hovoru @@ -5208,6 +5310,12 @@ Pridajte ľudí a skupiny, od ktorých chcete dostávať upozornenia a hovory, keď je tento profil zapnutý Pridajte ľudí alebo skupiny + + Výnimky + + Povoliť všetky hovory + + Upozorniť na všetky zmienky Pridať @@ -5856,10 +5964,10 @@ Všetky spojenia boli preverené, pokračujte ťuknutím na tlačidlo „Odoslať“. - %1$d z vašich spojení mohlo preinštalovať Signal alebo zmeniť zariadenie. Pred odoslaním príbehu skontrolujte jeho bezpečnostné čísla alebo zvážte jeho odstránenie z vášho príbehu. - %1$d z vašich spojení mohli preinštalovať Signal alebo zmeniť zariadenia. Pred odoslaním príbehu skontrolujte ich bezpečnostné čísla alebo zvážte ich odstránenie z vášho príbehu. - %1$d z vašich spojení mohlo preinštalovať Signal alebo zmeniť zariadenia. Pred odoslaním príbehu skontrolujte ich bezpečnostné čísla alebo zvážte ich odstránenie z vášho príbehu. - %1$d z vašich spojení mohlo preinštalovať Signal alebo zmeniť zariadenia. Pred odoslaním príbehu skontrolujte ich bezpečnostné čísla alebo zvážte ich odstránenie z vášho príbehu. + %1$d z vašich spojení mohlo preinštalovať Signal alebo zmeniť zariadenie. Môžete skontrolovať ich bezpečnostné číslo alebo pokračovať v odosielaní. + %1$d z vašich spojení mohli preinštalovať Signal alebo zmeniť zariadenie. Môžete skontrolovať ich bezpečnostné čísla alebo pokračovať v odosielaní. + %1$d z vašich spojení mohlo preinštalovať Signal alebo zmeniť zariadenie. Môžete skontrolovať ich bezpečnostné číslo alebo pokračovať v odosielaní. + %1$d z vašich spojení mohlo preinštalovať Signal alebo zmeniť zariadenie. Môžete skontrolovať ich bezpečnostné čísla alebo pokračovať v odosielaní. Overiť bezpečnostné číslo @@ -6225,12 +6333,18 @@ Kód krajiny pre IBAN nie je podporovaný Neplatné číslo IBAN + + Minimálne 2 znaky + + Neplatná emailová adresa iDEAL Zadajte svoju banku, meno a e-mail. Stripe používa tento e-mail na odosielanie aktualizácií o vašom príspevku. %1$s + + Zadajte svoje bankové údaje. Signal nezhromažďuje ani neukladá vaše osobné údaje. %1$s Zistiť viac @@ -6418,6 +6532,8 @@ Odchádzajúci Zmeškaný + + Zmeškané pri zapnutom profile upozornení Pridať sa @@ -6531,6 +6647,8 @@ Skupinový hovor Zmeškaný skupinový hovor + + Zmeškaný skupinový hovor pri zapnutom profile upozornení Prichádzajúci skupinový hovor diff --git a/app/src/main/res/values-sl/strings.xml b/app/src/main/res/values-sl/strings.xml index d5ea814ebc..5fa3db8e93 100644 --- a/app/src/main/res/values-sl/strings.xml +++ b/app/src/main/res/values-sl/strings.xml @@ -325,8 +325,12 @@ Slike ni mogoče prenesti. Ponovno jo boste morali poslati. Videoposnetka ni mogoče prenesti. Ponovno ga boste morali poslati. - - Urejeno pred %1$s + + Pravkar urejeno + + Urejeno pred %1$s + + Urejeno pred %1$s Pridruži se klicu @@ -364,6 +368,8 @@ Nezavarovan MMS Sporočilo Signal + + Pošlji sporočilo Začni uporabljati aplikacijo Molly %1$s Prosimo, izberite uporabnika_co Velikost priponke presega omejitev velikosti za ta tip sporočil. @@ -1365,6 +1371,10 @@ Zgrešen glasovni klic Zgrešen video klic + + Neodgovorjen zvočni klic, medtem ko je profil za obvestila vklopljen + + Neodgovorjen video klic, medtem ko je profil za obvestila vklopljen Zavrnili ste glasovni klic @@ -1970,6 +1980,8 @@ Ugasni / vklopi kamero Ugasni / vklopi utišanje + + Dodatna dejanja Zaključi klic @@ -1986,12 +1998,80 @@ Ikona, ki predstavlja slušalko naprave. + + Dvigni roko + + Dvigni roko + + Želite spustiti roko? + + Spusti roko + + Prekliči + + Dvignili ste roko + + Ogled + + + + %1$s je dvignil_a roko + %1$s + %2$d sta dvignila roko + %1$s + %2$d so dvignili roko + %1$s + %2$d jih je dvignilo roko + + + + + %1$s + %1$s + %2$d + %1$s + %2$d + %1$s + %2$d + + + + Razširite pogled dvignjene roke + + + + Signal povezava + + %1$s je med vašimi sistemskimi stiki + + Nimata skupne skupine + + Pazljivo preglejte prošnje + + %1$d skupnih skupin + + Več + + Vi + - - Na tem klicu · %1$d oseba - Na tem klicu · %1$d osebi - Na tem klicu · %1$d osebe - Na tem klicu · %1$d ljudi + + V tem klicu (%1$d) + V tem klicu (%1$d) + V tem klicu (%1$d) + V tem klicu (%1$d) + + + Signal bo zvonil (%1$d) + Signal bo zvonil (%1$d) + Signal bo zvonil (%1$d) + Signal bo zvonil (%1$d) + + + Signal bo obvestil (%1$d) + Signal bo obvestil (%1$d) + Signal bo obvestil (%1$d) + Signal bo obvestil (%1$d) + + + Dvignjena roka (%1$d) + Dvignjeni roki (%1$d) + Dvignjene roke (%1$d) + Dvignjene roke (%1$d) @@ -2107,6 +2187,12 @@ Ogledano Medijske datoteke + + + To ime uporablja več uporabnikov + + Ogled + Iskanje po \'%1$s\' ni vrnilo nobenega rezultata. @@ -2273,6 +2359,8 @@ Pošlji + + 00 Dodajte uporabniško ime @@ -2295,6 +2383,12 @@ Preskoči OK + + To uporabniško ime ni na voljo, poskusite z drugo številko. + + Neveljavno uporabniško ime, vnesite najmanj %1$d številki. + + Neveljavno uporabniško ime, vnesite največ %1$d številk. %1$d stik je na Signalu! @@ -2500,6 +2594,10 @@ V obdelavi … + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2524,6 +2622,8 @@ Odgovorjeno na povezani napravi. Zavrnjeno na povezani napravi. Zasedeno na povezani napravi. + + Gumb za Obrni kamero je bila premaknjen sem, tapnite na video, da ga preizkusite Klicu se je pridružil nekdo s spremenjenim varnostnim številom. @@ -4127,14 +4227,16 @@ Odstranitev člana_ice ni uspela. - Član Prošnja Vaš stik Odstrani iz skupine Posodobite stik Blokiraj Izbriši - Uporabnik_ca je pred kratkim spremenil_a ime profila iz %1$s v %2$s + + %1$s je pred kratkim spremenil_a svoje ime iz %2$s v %3$s + + %1$s je med vašimi sistemskimi stiki Pridružil_a se je %1$s @@ -5208,6 +5310,12 @@ Dodajte uporabnike_ce in skupine od katerih želite prejemati obvestila in klice, kadar je ta profil vklučen. Dodaj uporabnike_ce ali skupine + + Izjeme + + Dovoli vse klice + + Obveščanje ob omembah Dodaj @@ -5856,10 +5964,10 @@ Vse povezave so bile pregledane, tapnite pošlji za nadaljevanje. - Imate %1$d povezavo, ki je morda znova namestila Signal ali zamenjala napravo. Preden delite svojo Zgodbo, preglejte varnostne številke te osebe ali jo odstranite iz svoje Zgodbe. - Imate %1$d povezavi, ki sta morda znova namestili Signal ali zamenjali napravo. Preden delite svojo Zgodbo, preglejte varnostne številke teh oseb ali ju odstranite iz svoje Zgodbe. - Imate %1$d povezave, ki so morda znova namestile Signal ali zamenjale napravo. Preden delite svojo Zgodbo, preglejte varnostne številke teh oseb ali jih odstranite iz svoje Zgodbe. - Imate %1$d povezav, ki so morda znova namestile Signal ali zamenjale napravo. Preden delite svojo Zgodbo, preglejte varnostne številke teh oseb ali jih odstranite iz svoje Zgodbe. + %1$d povezava je morda znova namestila Signal ali zamenjala napravo. Po želji lahko pregledate varnostno številko te osebe ali nadaljujete s pošiljanjem. + %1$d povezavi sta morda znova namestili Signal ali zamenjali napravo. Po želji lahko pregledate varnostni številki teh oseb ali nadaljujete s pošiljanjem. + %1$d povezave so morda znova namestile Signal ali zamenjale napravo. Po želji lahko pregledate varnostne številke teh oseb ali nadaljujete s pošiljanjem. + %1$d povezav je morda znova namestilo Signal ali zamenjalo napravo. Po želji lahko pregledate varnostne številke teh oseb ali nadaljujete s pošiljanjem. Preglej varnostno število @@ -6225,12 +6333,18 @@ Koda države IBAN ni podprta Neveljaven IBAN + + Najmanj 2 znaka + + Nepravilen e-naslov iDEAL Vnesite svojo banko, ime in e-pošto. Stripe uporablja to e-pošto, da vam pošilja posodobitve o vaši donaciji. %1$s + + Vnesite svoje bančne podatke. Signal ne zbira in ne hrani vaših osebnih podatkov. %1$s Preberite več @@ -6418,6 +6532,8 @@ Odhodni Zgrešeni + + Zamujeno, medtem ko je profil za obvestila vklopljen Pridruži se @@ -6531,6 +6647,8 @@ Skupinski klic Neodgovorjen skupinski klic + + Neodgovorjen skupinski klic, medtem ko je profil za obvestila vklopljen Dohodni skupinski klic diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-sq/strings.xml index a3114c29b5..b9a906f09b 100644 --- a/app/src/main/res/values-sq/strings.xml +++ b/app/src/main/res/values-sq/strings.xml @@ -323,8 +323,12 @@ Imazhi nuk mund të shkarkohet. Duhet ta dërgosh përsëri. Videoja nuk mund të shkarkohet. Duhet ta dërgosh përsëri. - - Përpunuar %1$s + + Përpuno tani + + Modifikuar në %1$s + + Modifikuar në %1$s Bashkoju thirrjes @@ -358,6 +362,8 @@ MMS i pasigurt Mesazh Signal + + Dërgoni mesazh Le të kalojmë tek Molly %1$s Ju lutemi, zgjidhni një kontakt Bashkëngjitja tejkalon kufijtë e madhësisë për llojin e mesazhit që po dërgoni. @@ -1277,6 +1283,10 @@ Thirrje zanore e humbur Thirrje video e humbur + + Thirrje zanore e humbur edhe pse njoftimet janë aktive + + Thirrje me video e humbur edhe pse njoftimi i profilit është aktiv Refuzove thirrjen zanore @@ -1828,6 +1838,8 @@ Ndrysho kamerën Aktivizo zërin + + Veprime të tjera Përfundoje thirrjen @@ -1844,10 +1856,68 @@ Ikonë që përfaqëson kufjen e një pajisjeje. + + Ngri dorën + + Ngri dorën + + Ul dorën? + + Ul dorën + + Anulo + + Ke ngritur dorën + + Shihni + + + + %1$s ka ngritur dorën + %1$s + %2$d kanë ngritur dorën + + + + + %1$s + %1$s +%2$d + + + + Zgjero pamjen e dorës së ngritur + + + + Lidhja e Signal + + %1$s është në kontaktet e sistemit + + Nuk keni grupe të përbashkëta + + Shqyrtoni me kujdes kërkesat + + %1$d grupe të përbashkët + + Rreth + + Ju + - - Në këtë thirrje · %1$d person - Në këtë thirrje · %1$d vetë + + Në këtë telefonatë (%1$d) + Në këtë telefonatë (%1$d) + + + Do të bjerë zilja e Signal (%1$d) + Do të bjerë zilja e Signal (%1$d) + + + Do të të vijë një njoftim në Signal (%1$d) + Do të të vijë një njoftim në Signal (%1$d) + + + Dorë e ngritur (%1$d) + Duar të ngritura (%1$d) @@ -1961,6 +2031,12 @@ Parë Media + + + Ka përplasje emri + + Shihni + S\\’u gjetën përfundime për \'%1$s\' @@ -2127,6 +2203,8 @@ Dërgoje + + 00 Shto një emër përdoruesi @@ -2149,6 +2227,12 @@ Anashkaloje U bë + + Ky emër përdoruesi nuk është i disponueshëm, provo një numër tjetër. + + Emri i pavlefshëm përdoruesi, vendos minimumi %1$d shifra. + + Emri i pavlefshëm përdoruesi, vendos miksimumi %1$d shifra. %1$d kontakt është në Signal! @@ -2346,6 +2430,10 @@ Në përpunim + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2370,6 +2458,8 @@ U përgjigj në një pajisje të lidhur. Hedhur tej në një pajisje të lidhur. I zënë në një pajisje të lidhur. + + Kamera e kthimit është zhvendosur këtu, kliko videon për ta provuar Dikush ka hyrë në këtë thirrje me një numër sigurie që ka ndryshuar. @@ -3925,14 +4015,16 @@ S’u hoq dot anëtari i grupit. - Anëtar Kërkesë Kontakti juaj Hiqe nga grupi Përditësoni kontaktin Blloko Fshije - Ndryshoi së fundi emrin e vet të profilit nga %1$s në %2$s + + %1$s ka ndryshuar së fundmi emrin e profilit nga %2$s në %3$s + + %1$s është në kontaktet e sistemit Erdhi %1$s @@ -4988,6 +5080,12 @@ Shtoni persona dhe grupe prej të cilëve doni njoftimeve dhe thirrje, kur ky profil është i aktivizuar Shtoni njerëz ose grupe + + Përjashtime + + Lejo të gjitha thirrjet + + Njofto për të gjitha përmendjet Shtoje @@ -5602,8 +5700,8 @@ Të gjitha kontaktet janë shqyrtuar; kliko dërgo për të vazhduar. - Ke %1$d kontakt që mund të ketë instaluar sërish Signal ose të ketë ndryshuar pajisje. Para se të shpërndash postimin e përkohshëm me ta, rishiko numrat e tyre të sigurisë ose shih mundësinë e heqjes së tyre nga postimi i përkohshëm. - Ke %1$d kontakte që mund të kenë instaluar sërish Signal ose të kenë ndryshuar pajisje. Para se të shpërndash postimin e përkohshëm me ta, rishiko numrat e tyre të sigurisë ose shih mundësinë e heqjes së tyre nga postimi i përkohshëm. + %1$d kontakt mund të ketë riinstaluar Signal ose ka ndryshuar pajisje. Mund të rishikosh numrin e tyre të sigurisë ose të vazhdosh me dërgimin. + %1$d kontakte mund të kenë riinstaluar Signal ose kanë ndryshuar pajisje. Mund të rishikosh numrin e tyre të sigurisë ose të vazhdosh me dërgimin. Verifiko numër siguri @@ -5961,12 +6059,18 @@ Kodi i shtetit IBAN nuk mbështetet IBAN i pavlefshëm + + Minimumi 2 karaktere + + Adresë e pavlefshme emaili iDEAL Shkruaj emrin e bankës, emrin tënd dhe emailin. Stripe e përdor këtë email për të të dërguar përditësime rreth dhurimit. %1$s + + Vendos të dhënat e tua bankare. Signal nuk mbledh dhe as nuk ruan të dhënat e tua personale. %1$s Mëso më shumë @@ -6152,6 +6256,8 @@ Dalëse E humbur + + Të humbura edhe pse njoftimet janë aktive Bashkohu @@ -6259,6 +6365,8 @@ Thirrje grupi Thirrje e humbur në grup + + Thirrje në grup e humbur edhe pse njoftimet janë aktive Thirrje hyrëse në grup diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 3ac8aa92f2..164737271e 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -323,8 +323,12 @@ Преузимање слике није успело. Мораћете поново да је пошаљете. Преузимање видео снимка није успело. Мораћете поново да га пошаљете. - - Измењено %1$s + + Измењено сада + + Измењено %1$s + + Измењено %1$s Придружите се позиву @@ -358,6 +362,8 @@ Необезбеђени ММС Signal poruka + + Пошаљите поруку Пребацимо се на Molly %1$s Одредите примаоца Прилог прекорачује ограничење величине за тип поруке коју шаљете. @@ -1277,6 +1283,10 @@ Пропуштен гласовни позив Пропуштен видео позив + + Пропуштен гласовни позив док је профил за обавештења био укључен + + Пропуштен гласовни позив док је профил за обавештења био укључен Одбили сте гласовни позив @@ -1828,6 +1838,8 @@ Укључи/искључи камеру Укључи/искључи звук + + Додатне радње Заврши позив @@ -1844,10 +1856,68 @@ Икона која представља слушалицу уређаја. + + Подигни руку + + Подигни руку + + Желите ли да спустите руку? + + Спусти руку + + Откажи + + Подигли сте руку + + Више + + + + %1$s је подигао/ла руку + %1$s + %2$d су подигли руку + + + + + %1$s + %1$s +%2$d + + + + Прошири приказ подигнуте руке + + + + Signal веза + + %1$s је у вашим системским контактима + + Немате заједничких група + + Пажљиво прегледајте захтеве + + Заједничких група: %1$d + + О контакту + + Ви + - - У овом позиву · Особа: %1$d - У овом позиву · Особа: %1$d + + У овом позиву (%1$d) + У овом позиву (%1$d) + + + Signal ће позвати (%1$d) + Signal ће позвати (%1$d) + + + Signal ће обавестити (%1$d) + Signal ће обавестити (%1$d) + + + Подигнута рука (%1$d) + Подигнуте руке (%1$d) @@ -1961,6 +2031,12 @@ Погледано Медији + + + Пронађен је конфликт у именима + + Више + Нема резултата за \'%1$s\' @@ -2127,6 +2203,8 @@ Пошаљи + + 00 Унесите корисничко име @@ -2149,6 +2227,12 @@ Прескочи Готово + + Ово корисничко име није доступно, покушајте са другим бројем. + + Неважеће корисничко име – унесите најмање %1$d цифре. + + Неважеће корисничко име – унесите најмање %1$d цифара. %1$d контакт је на Signal-у! @@ -2346,6 +2430,10 @@ Обрађује се… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2370,6 +2458,8 @@ Одговорено на другом уређају. Одбијено на другом уређају. У току на другом уређају. + + Опција за окретање камере је премештена овде, додирните видео да је испробате Неко се придружио овом позиву са сигурноснем бројем који се променио. @@ -3925,14 +4015,16 @@ Уклањање члана групе није успело. - Члан Захтев Ваш контакт Уклони из групе Ажурирај контакт Блокирај Обриши - Недавно су променили име профила од %1$s на %2$s + + %1$s је недавно променио/ла име на профилу са %2$s на %3$s + + %1$s је у вашим системским контактима %1$s се придружио @@ -4988,6 +5080,12 @@ Додајте људе и групе од којих желите да добијате обавештења када је овај профил укључен Додај људе или групе + + Изузеци + + Дозволи све позиве + + Обавести за сва помињања Додај @@ -5602,8 +5700,8 @@ Sve veze su pregledane, dodirnite „Pošalji“ da nastavite. - Њих %1$d је можда поново инсталирало Signal или променило уређај. Пре него што поделите причу са тим везама, прегледајте њихове сигурносне бројеве или размислите о томе да их уклоните из приче. - Њих %1$d је можда поново инсталирало Signal или променило уређај. Пре него што поделите причу са тим везама, прегледајте њихове сигурносне бројеве или размислите о томе да их уклоните из приче. + Њих %1$d је можда поново инсталирало Signal или променило уређај. Можете да прегледате њихове сигурносне бројеве или наставите са слањем. + Њих %1$d је можда поново инсталирало Signal или променило уређај. Можете да прегледате њихове сигурносне бројеве или наставите са слањем. Овери безбедносни број @@ -5961,12 +6059,18 @@ Шифра земље за IBAN није подржана Неважећи IBAN + + Најмање 2 знака + + Неважећа имејл адреса iDEAL Унесите назив банке, име и презиме и имејл. Stripe користи овај имејл да вам шаље информације о вашој донацији. %1$s + + Унесите податке о банци. Signal не прикупља нити чува ваше податке о личности. %1$s Сазнајте више @@ -6152,6 +6256,8 @@ Одлазни Пропуштени + + Пропуштено док је профил за обавештења био укључен Придружите се @@ -6259,6 +6365,8 @@ Групни позив Пропуштен групни позив + + Пропуштен групни позив док је профил за обавештења био укључен Долазни групни позив diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 3683b94125..62db8ce1d9 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -323,8 +323,12 @@ Det går inte att ladda ner bilden. Du behöver skicka den igen. Det går inte att ladda ner videon. Du behöver skicka den igen. - - Redigerad %1$s + + Redigerad nu + + Redigerad %1$s + + Redigerad %1$s Gå med i samtalet @@ -358,6 +362,8 @@ Osäkert MMS Signal-meddelande + + Skicka meddelande Låt oss använda Molly %1$s Välj en kontakt Bilagan överskrider storleksgränserna för den typ av meddelande du skickar. @@ -1277,6 +1283,10 @@ Missat röstsamtal Missat videosamtal + + Missat röstsamtal medan aviseringsprofilen var aktiverad + + Missat videosamtal medan aviseringsprofilen var aktiverad Du avböjde ett röstsamtal @@ -1828,6 +1838,8 @@ Växla kamera Växla tysta + + Ytterligare åtgärder Avsluta samtal @@ -1844,10 +1856,68 @@ En ikon som representerar en enhets hörsnäcka. + + Räck upp handen + + Räck upp handen + + Ta ner handen? + + Ta ner handen + + Avbryt + + Du räckte upp handen + + Visa + + + + %1$s räcker upp handen + %1$s + %2$d räcker upp handen + + + + + %1$s + %1$s +%2$d + + + + Expandera vyn över uppräckta händer + + + + Signal-förbindelse + + %1$s finns i dina systemkontakter + + Ni har inga gemensamma grupper + + Granska förfrågningar noggrant + + %1$d grupper gemensamt + + Om + + Du + - - I detta samtal · %1$d person - I detta samtal · %1$d personer + + I detta samtal (%1$d) + I detta samtal (%1$d) + + + Signal kommer att ringa (%1$d) + Signal kommer att ringa (%1$d) + + + Signal kommer att meddela (%1$d) + Signal kommer att meddela (%1$d) + + + Räckt upp handen (%1$d) + Uppräckta händer (%1$d) @@ -1961,6 +2031,12 @@ Visat Media + + + Namnkonflikt hittades + + Visa + Inga resultat hittades för \"%1$s\" @@ -2127,6 +2203,8 @@ Skicka + + 00 Lägg till ett användarnamn @@ -2149,6 +2227,12 @@ Hoppa över Klar + + Det här användarnamnet är inte tillgängligt. Prova ett annat nummer. + + Ogiltigt användarnamn. Ange minst %1$d siffror. + + Ogiltigt användarnamn. Ange högst %1$d siffror. %1$d kontakt finns på Signal! @@ -2346,6 +2430,10 @@ Bearbetar … + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2370,6 +2458,8 @@ Besvarades på en länkad enhet. Avböjde på en länkad enhet. Upptagen på en länkad enhet. + + Vänd kamera har flyttats hit. Tryck på din video för att prova Någon har gått med i detta samtal med ett säkerhetsnummer som har ändrats. @@ -3925,14 +4015,16 @@ Det gick inte att ta bort gruppmedlemmen. - Medlem Förfrågan Din kontakt Ta bort från grupp Uppdatera kontakt Blockera Ta bort - Nyligen ändrat sitt profilnamn från %1$s till %2$s + + %1$s ändrade nyligen sitt profilnamn från %2$s till %3$s + + %1$s finns i dina systemkontakter %1$s gick med @@ -4988,6 +5080,12 @@ Lägg till personer och grupper du vill ha aviseringar och samtal från när denna profil är på Lägg till personer eller grupper + + Undantag + + Tillåt alla samtal + + Meddela för alla omnämnanden Lägg till @@ -5602,8 +5700,8 @@ Alla förbindelser har granskats. Klicka på skicka för att fortsätta. - Du har %1$d förbindelse som kan ha installerat om Signal eller bytt enhet. Innan du delar din berättelse med hen, granska säkerhetsnumret eller överväg att ta bort hen från din story. - Du har %1$d förbindelser som kan ha installerat om Signal eller bytt enhet. Innan du delar din berättelse med dem, granska deras säkerhetsnummer eller överväg att ta bort dem från din story. + %1$d förbindelse kan ha installerat om Signal eller bytt enhet. Du kan granska hens säkerhetsnummer eller fortsätta med att skicka. + %1$d förbindelser kan ha installerat om Signal eller bytt enhet. Du kan granska deras säkerhetsnummer eller fortsätta med att skicka. Verifiera säkerhetsnummer @@ -5961,12 +6059,18 @@ IBAN-landskod stöds inte Ogiltigt IBAN + + Minst 2 tecken + + Ogiltig e-postadress iDEAL Ange din bank, namn och e-postadress. Stripe använder det här e-postadressen för att skicka uppdateringar om din donation. %1$s + + Ange dina bankuppgifter. Signal samlar inte in eller lagrar dina personuppgifter. %1$s Läs mer @@ -6152,6 +6256,8 @@ Utgående Missat + + Missad när aviseringsprofilen var på Gå med @@ -6259,6 +6365,8 @@ Gruppsamtal Missat gruppsamtal + + Missat gruppsamtal medan aviseringsprofilen var aktiverad Inkommande gruppsamtal diff --git a/app/src/main/res/values-sw/strings.xml b/app/src/main/res/values-sw/strings.xml index 501d1238b0..d930e628af 100644 --- a/app/src/main/res/values-sw/strings.xml +++ b/app/src/main/res/values-sw/strings.xml @@ -323,8 +323,12 @@ Huwezi kupakua picha. Utahitaji kuituma tena. Huwezi kupakua video. Utahitaji kuituma tena. - - Imehaririwa %1$s + + Imehaririwa Sasa + + Imehaririwa %1$s + + Imehaririwa %1$s Jiunge na mazungumzo ya simu @@ -358,6 +362,8 @@ MMS isiyo salama Ujumbe wa Signal + + Tuma ujumbe Wacha tubadili kwa Molly 1%1$s Tafadhali chagua mawasiliano Kiambatisho kimezidi ukubwa wa aina ya ujumbe unaotuma @@ -1277,6 +1283,10 @@ Simu ya kawaida fifi Simu ya video fifi + + Simu ya sauti ambayo haijapokelewa wakati wasifu wa arifa ulipowashwa + + Simu ya video ambayo haijapokelewa wakati wasifu wa arifa ulipokuwa umewashwa Umekataa simu ya kawaida @@ -1828,6 +1838,8 @@ Geuza kamera Geuza kunyamazisha + + Hatua za ziada Mwisho wa simu @@ -1844,10 +1856,68 @@ Ikoni inayowakilisha kisikilizishi cha kifaa. + + Inua Mkono + + Inua Mkono + + Je ushushe mkono wako? + + Shusha Mkono + + Ghairi + + Umeinua mkono + + Tazama + + + + %1$s ameinua mkono + %1$s + %2$d wameinua mkono + + + + + %1$s + %1$s +%2$d + + + + Panua muonekano wa mkono kuinuliwa + + + + Muunganisho wa Signal + + %1$s yupo kwenye wawasiliani wako + + Hamna vikundi mnavyoshiriki pamoja + + Kagua maombi kwa uangalifu + + Vikundi %1$d mnavyoshiriki pamoja + + Kuhusu + + Wewe + - - Katika mazungumzo haya ya simu· Watu %1$d - Katika mazungumzo haya ya simu· Watu %1$d + + (%1$d) kwenye simu hii + (%1$d) kwenye simu hii + + + Signal Itaita (%1$d) + Signal Itaita (%1$d) + + + Signal Itamtaarifu (%1$d) + Signal Itawataarifu (%1$d) + + + Mkono ulioinuliwa (%1$d) + Mikono iliyoinuliwa (%1$d) @@ -1961,6 +2031,12 @@ Imetazamwa Media + + + Hakuna mgongano wa jina uliopatikana + + Tazama + Hakuna matokeo yamepatikana kwa \'%1$s\' @@ -2127,6 +2203,8 @@ Tuma + + 00 Ongeza jina la mtumiaji @@ -2149,6 +2227,12 @@ Ruka Imekamilika + + Jina hili la mtumiaji halipo, jaribu nambari nyingine. + + Jina la mtumiaji lisilo sahihi, weka tarakimu zisizopungua %1$d. + + Jina la mtumiaji lisilo sahihi, weka tarakimu zisizozidi %1$d. Anwani %1$diko katika Signal! @@ -2346,6 +2430,10 @@ Inachakata… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2370,6 +2458,8 @@ Umejibu kwenye kifaa kilichounganishwa. Umekataa kwenye kifaa kilichounganishwa. Una shughuli kwenye kifaa kilichounganishwa. + + Kamera Mbinjuko imehamishiwa hapa, gusa video yako ili kuijaribu Mtu fulani amejiunga na mazungumzo haya ya simu kwa kutumia nambari ya usalama ambayo imebadilika. @@ -3925,14 +4015,16 @@ Imeshindikana kuondoa mshiriki wa kikundi. - Mwanachama Omba Mwasiliani wako Ondoa kwenye kikundi Sasisha mwasiliani Zuia Futa - Hivi majuzi alibadilisha jina la wasifu wake kutoka %1$s hadi %2$s + + Hivi karibuni %1$s amebadili jina la wasifu wake kutoka %2$s kwenda %3$s + + %1$s yupo kwenye wawasiliani wako %1$s amejiunga @@ -4988,6 +5080,12 @@ Ongeza watu au vikundi unavyotaka kupokea arifa na simu kutoka wakati ambapo wasifu huu utawashwa. Ongeza watu au vikundi + + Isipokuwa + + Ruhusu simu zote + + Toa arifa zote za kutajwa Ongeza @@ -5602,8 +5700,8 @@ Miungano yote imehakikishwa, bonyeza tuma kuendelea. - Una muwasiliani %1$d ambaye huenda akawa amesakinisha upya Signal au kubadilisha vifaa. Una hiari ya kukagua nambari zao za usalama kabla ya kutuma. - Una wawasiliani %1$d ambao huenda wakawa wamesakinisha upya Signal au kubadili vifaa. Una hiari ya kukagua nambari zao za usalama kabla ya kutuma au fikiria kuziondoa kutoka kwenye stori zako. + Muwasiliani %1$d anaweza akawa amesakinisha tena Signal au amebadili vifaa. Unaweza kuangalia nambari yao ya usalama au kuendelea kutuma. + Wawasiliani %1$d wanaweza wakawa wamesakinisha tena Signal au wamebadili vifaa. Unaweza kuangalia nambari zao za usalama au kuendelea kutuma. Thibitisha nambari salama @@ -5961,12 +6059,18 @@ Msimbo wa nchi wa IBAN hautumiki Nambari ya IBAN isiyo halali + + Kiwango cha chini herufi 2 + + Anuani ya barua pepe isiyo sahihi iDEAL Ingiza benki yako, jina na barua pepe. Stripe inatumia barua pepe hii kukutumia taarifa kuhusu mchango wako. %1$s + + Weka taarifa zako za benki. Signal haikusanyi wala kuhifadhi taarifa zako binafsi. %1$s Jifunze zaidi @@ -6152,6 +6256,8 @@ Simu Inayopigwa Simu Fifi + + Simu haikupokelewa wakati wasifu wa arifa umewashwa Jiunge @@ -6259,6 +6365,8 @@ Mazungumzo ya simu ya kikundi Simu fifi ya kikundi + + Simu ya kikundi ambayo haijapokelewa wakati wasifu wa arifa ulipowashwa Simu ya kikundi inayopigwa diff --git a/app/src/main/res/values-ta/strings.xml b/app/src/main/res/values-ta/strings.xml index ab4db7290c..8ad632b475 100644 --- a/app/src/main/res/values-ta/strings.xml +++ b/app/src/main/res/values-ta/strings.xml @@ -323,8 +323,12 @@ படத்தைப் பதிவிறக்க முடியவில்லை. நீங்கள் மீண்டும் அதை அனுப்ப வேண்டும். வீடியோவைப் பதிவிறக்க முடியவில்லை. நீங்கள் மீண்டும் அதை அனுப்ப வேண்டும். - - %1$s திருத்தப்பட்டது + + இப்போது திருத்தப்பட்டது + + திருத்தப்பட்டது %1$s + + திருத்தப்பட்டது %1$s அழைப்பில் சேரவும் @@ -358,6 +362,8 @@ பாதுகாப்பற்ற MMS Signal செய்தி + + அனுப்பு செய்தி %1$s நாம் Mollyக்கு மாறுவோம் தயவு செய்து ஒரு தொடர்பை தேர்ந்தெடு நீங்கள் அனுப்பும் இணைப்பு செய்தி அளவு வரம்பை மீறுகிறது . @@ -1277,6 +1283,10 @@ தவறவிட்ட குரல் அழைப்பு தவறவிட்ட வீடியோ அழைப்பு + + Missed voice call while notification profile on + + Missed video call while notification profile on குரல் அழைப்பை மறுத்துவிட்டீர்கள் @@ -1535,7 +1545,7 @@ உங்களுக்கு %1$s மெசேஜ் அனுப்ப அனுமதிக்கிறீர்களா? நீங்கள் தடை நீக்கம் செய்யும் வரை நீங்கள் எந்த மெசேஜையும் பெறமாட்டீர்கள். %1$s -இடமிருந்து அறிவிப்புகள் மற்றும் செய்திகளைப் பெற வேண்டுமா? நீங்கள் தடை நீக்கம் செய்யும் வரை எந்த அறிவிப்புகளையும் பெறமாட்டீர்கள். இந்த குழுவுடன் உங்கள் சாட்டைத் தொடர, உங்கள் பெயரையும் புகைப்படத்தையும் அதன் உறுப்பினர்களுடன் பகிர்ந்து கொள்ளவா? - This Legacy Group can no longer be used. Create a new group to activate new features like @mentions and admins. + இந்த லெகசி குழுவை இனி பயன்படுத்த முடியாது. @குறிப்புகள் மற்றும் நிர்வாகிகள் போன்ற புதிய அம்சங்களைச் செயல்படுத்த புதிய குழுவை உருவாக்கவும். இந்த மரபு குழு மிகப் பெரியது, எனவே இதை இனி பயன்படுத்த முடியாது. அதிகபட்ச குழு அளவு: %1$d %1$s உடன் சாட்டைத் தொடரவும் மற்றும் அவர்களுடன் உங்கள் பெயரையும் புகைப்படத்தையும் பகிர வேண்டுமா? இந்த குழுவில் சேர்ந்து உங்கள் பெயர் மற்றும் புகைப்படத்தை அதன் உறுப்பினர்களுடன் பகிர்ந்து கொள்ள விரும்புகிறீர்களா? நீங்கள் ஏற்றுக்கொள்ளும் வரை அவர்களின் செய்திகளை நீங்கள் பார்த்திருப்பதை அவர்கள் அறிய மாட்டார்கள். @@ -1828,6 +1838,8 @@ கேமராவை நிலைமாற்று ஒலியடக்கியை நிலைமாற்று + + கூடுதல் நடவடிக்கைகள் அழைப்பை முடி @@ -1844,10 +1856,68 @@ டிவைஸின் இயர்பீஸைக் குறிக்கும் ஐகான். + + கையை உயர்த்துக + + கையை உயர்த்துக + + உங்கள் கையைத் தாழ்த்துகிறீர்களா? + + கையைத் தாழ்த்தவும் + + ரத்துசெய் + + நீங்கள் கையை உயர்த்தியுள்ளீர்கள் + + காண்க + + + + %1$s கையை உயர்த்தியுள்ளார் + %1$s + %2$d கையை உயர்த்தியுள்ளனர் + + + + + %1$s + %1$s +%2$d + + + + உயர்த்தப்பட்ட கை காட்சியை விரிவுபடுத்தவும் + + + + சிக்னல் இணைப்பு + + உங்கள் சிஸ்டம் தொடர்புகளில் %1$s உள்ளார் + + உங்களுக்குப் பொதுவான குழுக்கள் எதுவும் இல்லை + + கோரிக்கைகளை கவனமாக மதிப்பாய்வு செய்யவும் + + %1$d பொதுவான குழுக்கள் + + அவரைப் பற்றி + + நீங்கள் + - - இந்த அழைப்பில் %1$d நபர் இருக்கிறார் - இந்த அழைப்பில் %1$d பேர் உள்ளனர் + + இந்த அழைப்பில் (%1$d) + இந்த அழைப்பில் (%1$d) + + + Signal (%1$d) நபரை அழைக்கும் + Signal (%1$d) பேரை அழைக்கும் + + + Signal (%1$d) நபருக்குத் தெரியப்படுத்தும் + Signal (%1$d) பேருக்குத் தெரியப்படுத்தும் + + + உயர்த்திய கை (%1$d) + உயர்த்திய கைகள் (%1$d) @@ -1961,6 +2031,12 @@ பார்க்கப்பட்டவை ஊடகம் + + + பெயர் முரண்பாடு கண்டறியப்பட்டது + + காண்க + \'%1$s\' க்கான முடிவுகள் எதுவும் கிடைக்கவில்லை @@ -2127,6 +2203,8 @@ அனுப்புக + + 00 ஒரு பயனர் பெயரைச் சேர்க்கவும் @@ -2149,6 +2227,12 @@ தவிர் முடிந்தது + + This username is not available, try another number. + + Invalid username, enter a minimum of %1$d digits. + + Invalid username, enter a maximum of %1$d digits. %1$d தொடர்பு Signal-இல் உள்ளார்! @@ -2346,6 +2430,10 @@ செயலாக்கப்படுகிறது… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2370,6 +2458,8 @@ இணைக்கப்பட்ட சாதனத்தில் பதிலளிக்கப்பட்டது. இணைக்கப்பட்ட சாதனத்தில் மறுக்கப்பட்டது. இணைக்கப்பட்ட சாதனத்தில் பயனர் அழைப்பில் இருக்கிறார். + + ஃபிளிப் கேமரா இங்கு நகர்த்தப்பட்டுள்ளது, அதைப் பயன்படுத்திப் பார்க்க உங்கள் வீடியோவைத் தட்டவும் மாற்றப்பட்ட பாதுகாப்பு எண்ணுடன் யாரோ இந்த அழைப்பில் சேர்ந்துள்ளனர். @@ -3925,14 +4015,16 @@ குழு உறுப்பினரை அகற்ற முடியவில்லை. - உறுப்பினர் கோரிக்கை உங்கள் தொடர்பு குழுவிலிருந்து அகற்று தொடர்பைப் புதுப்பிக்கவும் தடைசெய் நீக்கு - சமீபத்தில் அவர்கள் தங்கள் சுயவிவரப் பெயரை %1$s இலிருந்து %2$s ஆக மாற்றினர் + + %1$s சமீபத்தில் தனது ப்ரொஃபைல் பெயரை %2$s -இல் இருந்து %3$s ஆக மாற்றினார் + + உங்கள் சிஸ்டம் தொடர்புகளில் %1$s உள்ளார் %1$s சேர்ந்தார் @@ -4988,6 +5080,12 @@ இந்தச் சுயவிவரம் இயக்கத்தில் இருக்கும் போது நீங்கள் அறிவிப்புகளையும் அழைப்புகளையும் பெற விரும்பும் நபர்களையும் குழுக்களையும் சேர்க்கவும் நபர்கள் அல்லது குழுக்களைச் சேர்க்கவும் + + விதிவிலக்குகள் + + அனைத்து அழைப்புகளையும் அனுமதி + + அனைத்து குறிப்புகளுக்கும் தெரிவிக்கவும் சேர்க்கவும் @@ -5602,8 +5700,8 @@ அனைத்து இணைப்புகளும் மதிப்பாய்வு செய்யப்பட்டுள்ளன, தொடர அனுப்பு என்பதைத் தட்டவும். - உங்களிடம் Signalஐ மீண்டும் நிறுவிய அல்லது டிவைஸ்களை மாற்றிய %1$d இணைப்பு உள்ளது. உங்கள் ஸ்டோரியை அவர்களுடன் பகிர்வதற்கு முன் அவர்களின் பாதுகாப்பு எண்களை மதிப்பாய்வு செய்யவும் அல்லது உங்கள் ஸ்டோரியிலிருந்து அவற்றை அகற்றவும். - உங்களிடம் Signalஐ மீண்டும் நிறுவிய அல்லது டிவைஸ்களை மாற்றிய %1$d இணைப்புகள் உள்ளது. உங்கள் ஸ்டோரியை அவர்களுடன் பகிர்வதற்கு முன் அவர்களின் பாதுகாப்பு எண்களை மதிப்பாய்வு செய்யவும் அல்லது உங்கள் ஸ்டோரியிலிருந்து அவற்றை அகற்றவும். + %1$d connection may have reinstalled Signal or changed devices. You may review their safety number or continue with the send. + %1$d connections may have reinstalled Signal or changed devices. You may review their safety numbers or continue with the send. பாதுகாப்பு எண்ணைச் சரிபார்க்கவும் @@ -5961,12 +6059,18 @@ IBAN நாட்டுக் குறியீடு ஆதரிக்கப்படவில்லை தவறான IBAN + + குறைந்தபட்சம் 2 எழுத்துக்குறிகள் + + தவறான மின்னஞ்சல் முகவரி iDEAL உங்கள் வங்கி, பெயர் மற்றும் மின்னஞ்சலை உள்ளிடவும். உங்கள் நன்கொடை குறித்த அறிவிப்புகளை உங்களுக்கு அனுப்ப ஸ்ட்ரைப் இந்த மின்னஞ்சலைப் பயன்படுத்துகிறது. %1$s + + உங்கள் வங்கி விவரங்களை உள்ளிடவும். Signal உங்கள் தனிப்பட்ட தகவல்களைச் சேகரிக்கவோ சேமிக்கவோ செய்யாது. %1$s மேலும் அறிக @@ -6152,6 +6256,8 @@ அவுட்கோயிங் தவறவிட்டவை + + Missed while notification profile on சேரவும் @@ -6259,6 +6365,8 @@ குழு அழைப்பு தவறிய குழு அழைப்பு + + Missed group call while notification profile on உள்வரும் குழு அழைப்பு diff --git a/app/src/main/res/values-te/strings.xml b/app/src/main/res/values-te/strings.xml index a0775377a1..d61838afa7 100644 --- a/app/src/main/res/values-te/strings.xml +++ b/app/src/main/res/values-te/strings.xml @@ -323,8 +323,12 @@ చిత్రాన్ని డౌన్‌లోడ్ చేసుకోలేరు. దానిని మీరు మళ్ళీ పంపాలి. వీడియోను డౌన్‌లోడ్ చేసుకోలేరు. దానిని మీరు మళ్ళీ పంపాలి. - - సవరించబడింది %1$s + + ఇప్పుడే సవరించబడింది + + సవరించబడింది %1$s + + సవరించబడింది %1$s కాల్‌లో చేరండి @@ -358,6 +362,8 @@ అసురక్షిత ఎమ్మెమ్మెస్ Signal మెసేజ్ + + సందేశం పంపాల మనం Molly %1$sకు మారుదాం దయచేసి ఒక పరిచయం ఎంచుకోండి జోడింపు మీరు పంపే సందేశం రకం పరిమాణ పరిమితి మించిపోయింది. @@ -1277,6 +1283,10 @@ మిస్డ్ వాయిస్ కాల్ మిస్డ్ వీడియో కాల్ + + నోటిఫికేషన్ ప్రొఫైల్ ఆన్‌లో ఉన్నప్పుడు మిస్ అయిన స్వర కాల్ + + నోటిఫికేషన్ ప్రొఫైల్ ఆన్‌లో ఉన్నప్పుడు మిస్ అయిన వీడియో కాల్ మీరు ఒక వాయిస్ కాల్‌ను నిరాకరించారు @@ -1828,6 +1838,8 @@ కెమెరాను టోగుల్ చేస్తుంది మ్యూట్‌ను టోగుల్ చేస్తుంది + + అదనపు చర్యలు కాల్ ముగించండి @@ -1844,10 +1856,68 @@ ఒక పరికరపు ఇయర్‌పీస్‌ను సూచించే ఒక ఐకాన్. + + చేయి పైకెత్తండి + + చేయి పైకెత్తండి + + మీ చేతిని దించేదా? + + చేయి దించండి + + రద్దు చేయండి + + మీరు మీ చేతిని ఎత్తారు + + వీక్షణ + + + + %1$s చేయి ఎత్తారు + %1$s + %2$d చేయి ఎత్తారు + + + + + %1$s + %1$s +%2$d + + + + చేయి ఎత్తిన వీక్షణను విస్తరించండి + + + + Signal కనెక్షన్ + + %1$s మీ సిస్టమ్ కాంటాక్ట్‌లలో ఉన్నారు + + మీకు ఉమ్మడిగా ఎలాంటి గ్రూపులు లేవు + + అభ్యర్థనలను జాగ్రత్తగా సమీక్షించండి + + %1$d గ్రూప్‌లు ఉమ్మడిగా ఉన్నాయి + + గురించి + + మీరు + - - ఈ కాల్‌లో · %1$dవ్యక్తి - ఈ కాల్‌లో · %1$d వ్యక్తులు + + ఈ కాల్‌లో (%1$d) + ఈ కాల్స్‌లో (%1$d) + + + Signal (%1$d)కు రింగ్ చేస్తుంది + Signal (%1$d)లకు రింగ్ చేస్తుంది + + + Signal, (%1$d)కు తెలియచేస్తుంది + Signal, (%1$d)లకు తెలియచేస్తుంది + + + చేయి ఎత్తారు (%1$d) + చేతులు ఎత్తారు (%1$d) @@ -1961,6 +2031,12 @@ వీక్షించినవి మీడియా + + + పేరు వైరుధ్యం కనుగొనబడింది + + వీక్షణ + \'%1$s\' కోసం ఫలితాలు కనుగొనబడలేదు @@ -2127,6 +2203,8 @@ పంపుము + + 00 యూజర్‌నేమ్‌ను జోడించండి @@ -2149,6 +2227,12 @@ దాటవేయి పూర్తయింది + + ఈ యూజర్‌నేమ్ అందుబాటులో లేదు, మరొక సంఖ్యను ప్రయత్నించండి. + + యూజర్‌నేమ్ చెల్లుబాటు కాదు, కనీసం %1$d అంకెలను ఎంటర్ చేయండి. + + యూజర్‌నేమ్ చెల్లుబాటు కాదు, గరిష్టంగా %1$d అంకెలను ఎంటర్ చేయండి. %1$d పరిచయం Signal‌లో ఉంది! @@ -2346,6 +2430,10 @@ ప్రక్రియ చేస్తోంది… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2370,6 +2458,8 @@ లింక్ చేయబడిన పరికరంలో సమాధానం ఇవ్వబడింది. లింక్ చేయబడిన పరికరంలో తిరస్కరించబడింది. లింక్ చేయబడిన పరికరంలో బిజీ. + + ఫ్లిప్ కెమెరా ఇక్కడకు తరలించబడింది, దీన్ని ప్రయత్నించడానికి మీ వీడియోను తట్టండి మారిన భద్రతా నంబర్‌తో ఎవరో ఈ కాల్‌లో చేరారు. @@ -3925,14 +4015,16 @@ గ్రూప్ సభ్యుడిని తొలగించడంలో విఫలమైంది. - సభ్యుడు అభ్యర్థన మీ పరిచయం గ్రూప్ నుండి తొలగించండి పరిచయాన్ని నవీకరించండి బ్లాక్ చేయండి తొలగించండి - ఇటీవల వారి ప్రొఫైల్ పేరును %1$s నుండి %2$s గా మార్చారు + + %1$s ఇటీవల వారి ప్రొఫైల్ పేరును %2$s నుండి %3$s‌కు మార్చారు + + %1$s మీ సిస్టమ్ కాంటాక్ట్‌లలో ఉన్నారు %1$s చేరారు @@ -4988,6 +5080,12 @@ ఈ ప్రొఫైల్ ఆన్ అయినప్పుడు నోటిఫికేషన్‌లు మరియు కాల్స్‌ని మీరు కోరుకునే వ్యక్తులు మరియు గ్రూపులను జోడించండి. వ్యక్తులు లేదా గ్రూపులను జోడించండి + + మినహాయింపులు + + అన్ని కాల్స్‌ను అనుమతించండి + + అన్ని ప్రస్తావనల కొరకు ప్రకటించండి చేర్చు @@ -5602,8 +5700,8 @@ అన్ని కనెక్షన్‌లు సమీక్షించబడ్డాయి, కొనసాగడానికి పంపండి మీద తట్టండి. - మీకు %1$d కనెక్షన్ ఉంది, వారు Signal ను మళ్ళీ ఇన్‌స్టాల్ చేసి ఉండవచ్చు లేదా పరికరాలను మార్చి ఉండవచ్చు. మీ కథను వారితో పంచుకునే ముందు వారి భద్రతా సంఖ్యలను సమీక్షించండి లేదా వాటిని మీ కథ నుండి తొలగించేందుకు పరిగణించండి. - మీకు %1$d కనెక్షన్స్ ఉన్నాయి, వారు Signal ను మళ్ళీ ఇన్‌స్టాల్ చేసి ఉండవచ్చు లేదా పరికరాలను మార్చి ఉండవచ్చు. మీ కథను వారితో పంచుకునే ముందు వారి భద్రతా సంఖ్యలను సమీక్షించండి లేదా వాటిని మీ కథ నుండి తొలగించేందుకు పరిగణించండి. + %1$d కనెక్షన్ Signal ను మళ్ళీ ఇన్‌స్టాల్ చేసి ఉండవచ్చు లేదా పరికరాలను మార్చి ఉండవచ్చు. మీరు వారి భద్రతా సంఖ్యను సమీక్షించవచ్చు లేదా పంపడాన్ని కొనసాగించవచ్చు. + %1$d కనెక్షన్‌లు Signal ను మళ్ళీ ఇన్‌స్టాల్ చేసి ఉండవచ్చు లేదా పరికరాలను మార్చి ఉండవచ్చు. మీరు వారి భద్రతా సంఖ్యను సమీక్షించవచ్చు లేదా పంపడాన్ని కొనసాగించవచ్చు. భద్రత సంఖ్యను ధృవీకరించండి @@ -5961,12 +6059,18 @@ IBAN దేశం కోడ్ మద్దతు ఇవ్వబడలేదు చెల్లని IBAN + + కనీసం 2 అక్షరాలు ఉండాలి + + ఈమెయిల్ చిరునామా చెల్లనిది iDEAL మీ బ్యాంక్, పేరు మరియు ఈమెయిల్‌ను ఎంటర్ చేయండి. మీ విరాళం గురించి మీకు అప్‌డేట్‌లను పంపడానికి Stripe ఈ ఈమెయిల్‌ను ఉపయోగిస్తుంది. %1$s + + మీ బ్యాంక్ వివరాలు ఎంటర్ చేయండి. Signal మీ వ్యక్తిగత సమాచారాన్ని సేకరించదు లేదా నిల్వ చేయదు. %1$s మరింత తెలుసుకోండి @@ -6152,6 +6256,8 @@ అవుట్ గోయింగ్ మిస్డ్ + + నోటిఫికేషన్ ప్రొఫైల్ ఆన్‌లో ఉన్నప్పుడు మిస్ అయింది చేరండి @@ -6259,6 +6365,8 @@ సమూహ కాల్ మిస్ అయిన గ్రూప్ కాల్ + + నోటిఫికేషన్ ప్రొఫైల్ ఆన్‌లో ఉన్నప్పుడు మిస్ అయిన గ్రూప్ కాల్ ఇన్‌కమింగ్ గ్రూప్ కాల్ diff --git a/app/src/main/res/values-th/strings.xml b/app/src/main/res/values-th/strings.xml index 5289da0dfe..0df9a4310d 100644 --- a/app/src/main/res/values-th/strings.xml +++ b/app/src/main/res/values-th/strings.xml @@ -322,8 +322,12 @@ ไม่สามารถดาวน์โหลดรูปภาพได้ คุณจะต้องส่งใหม่อีกครั้ง ไม่สามารถดาวน์โหลดวิดีโอได้ คุณจะต้องส่งใหม่อีกครั้ง - - แก้ไข %1$s + + แก้ไข เมื่อสักครู่ + + แก้ไข %1$s + + แก้ไข %1$s เข้าร่วมการโทร @@ -355,6 +359,8 @@ MMS ที่ไม่ปลอดภัย ข้อความ Signal + + ส่งข้อความ เปลี่ยนมาใช้ Molly กันเถอะ %1$s โปรดเลือกผู้ติดต่อ แฟ้มแนบมีขนาดใหญ่เกินกำหนดสำหรับชนิดของข้อความที่คุณกำลังจะส่ง @@ -1233,6 +1239,10 @@ การโทรด้วยเสียงที่ไม่ได้รับ วิดีโอคอลที่ไม่ได้รับ + + สายโทรเสียงที่ไม่ได้รับระหว่างใช้โปรไฟล์การแจ้งเตือน + + วิดีโอคอลที่ไม่ได้รับระหว่างใช้โปรไฟล์การแจ้งเตือน คุณปฏิเสธการโทรด้วยเสียง @@ -1757,6 +1767,8 @@ เปิดปิดกล้อง เปิดปิดการแจ้งเตือน + + ตัวเลือกเพิ่มเติม วางสาย @@ -1773,9 +1785,62 @@ ไอคอนรูปลำโพงของอุปกรณ์ + + ยกมือ + + ยกมือ + + เอามือลงหรือไม่ + + เอามือลง + + ยกเลิก + + คุณยกมือ + + ดู + + + + %1$s + %2$d ยกมือ + + + + + %1$s +%2$d + + + + ขยายดูหน้ารวมสมาชิกที่ยกมือ + + + + เครือข่าย Signal + + %1$s อยู่ในรายชื่อผู้ติดต่อของระบบคุณ + + คุณไม่มีกลุ่มร่วมกัน + + พิจารณาคำขออย่างระวัง + + มี %1$d กลุ่มร่วมกัน + + เกี่ยวกับ + + คุณ + - - มีผู้เข้าร่วมการโทร %1$d คน + + ในสายนี้ (%1$d) + + + Signal จะส่งเสียงเรียกสาย (%1$d) + + + Signal จะส่งการแจ้งเตือน (%1$d) + + + ยกมือ (%1$d) @@ -1888,6 +1953,12 @@ ดูแล้ว สื่อ + + + พบชื่อซ้ำ + + ดู + ไม่พบข้อมูลเกี่ยวกับ \'%1$s\' @@ -2054,6 +2125,8 @@ ส่ง + + 00 เพิ่มชื่อผู้ใช้ @@ -2076,6 +2149,12 @@ ข้าม เสร็จสิ้น + + ไม่สามารถใช้ชื่อผู้ใช้นี้ได้ ลองกรอกตัวเลขอื่น + + ชื่อผู้ใช้ไม่ถูกต้อง ต้องกรอกตัวเลขอย่างน้อย %1$d หลัก + + ชื่อผู้ใช้ไม่ถูกต้อง ต้องกรอกตัวเลขไม่เกิน %1$d หลัก มีผู้ติดต่อ %1$d คนใช้งาน Signal! @@ -2269,6 +2348,10 @@ กำลังดำเนินการ… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2293,6 +2376,8 @@ มีการรับสายบนอุปกรณ์ที่เชื่อมโยงอยู่ ถูกปฏิเสธสายบนอุปกรณ์ที่เชื่อมโยงอยู่ ติดสายบนอุปกรณ์ที่เชื่อมโยงอยู่ + + ปุ่มพลิกหน้ากล้องย้ายมาอยู่ที่นี่ แตะที่วิดีโอของคุณเพื่อลองใช้งาน มีคนเข้าร่วมสายนี้ด้วยหมายเลขความปลอดภัยที่เปลี่ยนไป @@ -3824,14 +3909,16 @@ ลบสมาชิกออกจากกลุ่มไม่สำเร็จ - สมาชิก คำขอ ผู้ติดต่อของคุณ ลบจากกลุ่ม ปรับปรุงผู้ติดต่อ บล็อก ลบ - เพิ่งพึ่งเปลี่ยนชื่อโปรไฟล์ของเธอจาก %1$s เป็น %2$s + + %1$s เพิ่งเปลี่ยนชื่อโปรไฟล์จาก %2$s เป็น %3$s + + %1$s อยู่ในรายชื่อผู้ติดต่อของระบบคุณ %1$s ได้เข้าร่วมกลุ่ม @@ -4878,6 +4965,12 @@ เพิ่มคนและกลุ่มที่คุณต้องการการแจ้งเตือนและการโทรเมื่อโปรไฟล์นี้มีการใช้งาน เพิ่มคนหรือกลุ่ม + + ข้อยกเว้น + + อนุญาตการโทรทั้งหมด + + แจ้งเตือนการกล่าวถึงทั้งหมด เพิ่ม @@ -5475,7 +5568,7 @@ ตรวจสอบผู้ติดต่อทั้งหมดแล้ว แตะส่งเพื่อดำเนินการต่อ - คุณมีเครือข่าย %1$d คน ที่อาจติดตั้ง Signal ใหม่หรือเปลี่ยนอุปกรณ์ ก่อนที่จะแชร์สตอรี่กับเครือข่ายคนดังกล่าว ลองตรวจสอบหมายเลขความปลอดภัยหรือลบเครือข่ายคนนั้นออกจากสตอรี่ของคุณ + เครือข่าย %1$d คนอาจติดตั้ง Signal ใหม่หรือเปลี่ยนอุปกรณ์ คุณสามารถลองตรวจสอบหมายเลขความปลอดภัยของเครือข่ายก่อนหรือยืนยันการส่ง ตรวจยืนยันหมายเลขความปลอดภัย @@ -5829,12 +5922,18 @@ ไม่รองรับรหัสประเทศของหมายเลข IBAN ที่ระบุ หมายเลข IBAN ไม่ถูกต้อง + + ต้องมีอักขระ 2 ตัวขึ้นไป + + อีเมลไม่ถูกต้อง iDEAL โปรดกรอกข้อมูลธนาคาร ชื่อ และอีเมลของคุณ Stripe จะส่งข้อมูลอัปเดตเกี่ยวกับการบริจาคของคุณไปยังอีเมลนี้ %1$s + + โปรดกรอกข้อมูลธนาคารของคุณ โดย Signal จะไม่รวบรวมหรือจัดเก็บข้อมูลส่วนบุคคลของคุณ %1$s เรียนรู้เพิ่มเติม @@ -6019,6 +6118,8 @@ โทรออก ไม่ได้รับ + + สายที่ไม่ได้รับระหว่างใช้โปรไฟล์การแจ้งเตือน เข้าร่วม @@ -6123,6 +6224,8 @@ โทรกลุ่ม สายโทรกลุ่มที่ไม่ได้รับ + + สายโทรกลุ่มที่ไม่ได้รับระหว่างใช้โปรไฟล์การแจ้งเตือน สายโทรกลุ่มขาเข้า diff --git a/app/src/main/res/values-tl/strings.xml b/app/src/main/res/values-tl/strings.xml index a5a831216b..fc57a8d925 100644 --- a/app/src/main/res/values-tl/strings.xml +++ b/app/src/main/res/values-tl/strings.xml @@ -323,8 +323,12 @@ Hindi ma-download ang image. Kailangan mo itong i-send ulit. Hindi ma-download ang video Kailangan mo itong i-send ulit. - - In-edit %1$s + + Kaka-edit Lang + + In-edit %1$s + + In-edit %1$s Mag-join sa call @@ -358,6 +362,8 @@ Hindi secure na MMS Signal message + + Mag-send ng message Lumipat tayo sa Molly %1$s Pumili ng kontak Ang kalakip ay lampas sa limitasyon ng laki para sa uri ng mensahe na ipinapadala mo. @@ -1277,6 +1283,10 @@ Missed voice call Missed video call + + May hindi nasagot na voice call habang naka-on ang notification profile + + May hindi nasagot na video call habang naka-on ang notification profile Nag-decline ka sa voice call @@ -1828,6 +1838,8 @@ I-adjust ang camera I-adjust ang mute + + Iba pang actions Tapusin ang tawag @@ -1844,10 +1856,68 @@ Isang icon na nagre-represent ng earpiece ng device. + + Itaas ang Kamay + + Itaas ang Kamay + + Gusto mo bang ibaba ang iyong kamay? + + Ibaba ang Kamay + + I-cancel + + Itinaas mo ang iyong kamay + + Tingnan + + + + Si %1$s ay nagtaas ng kamay + Sila %1$s + %2$d ay nagtaas ng kamay + + + + + %1$s + %1$s +%2$d + + + + Palakihin ang raised hand view + + + + Signal connection + + Si %1$s ay nasa system contacts mo + + Wala kayong common groups + + I-review ang requests nang maigi + + %1$d groups in common + + About + + Ikaw + - - In this call · %1$d person - In this call · %1$d people + + Sa tawag na ito (%1$d) + Sa tawag na ito (%1$d) + + + Tatawagan ng Signal si (%1$d) + Tatawagan ng Signal sila (%1$d) + + + Ino-notify ng Signal si (%1$d) + Ino-notify ng Signal sila (%1$d) + + + Nakataas na kamay (%1$d) + Mga nakataas na kamay (%1$d) @@ -1961,6 +2031,12 @@ Nakita na Media + + + May nakitang conflict sa mga pangalan + + Tingnan + Walang nakitang resulta para sa \'%1$s\' @@ -2127,6 +2203,8 @@ Ipadala + + 00 Mag-add ng username @@ -2149,6 +2227,12 @@ Laktawan Tapos na + + Hindi available ang username na ito, sumubok ng ibang number. + + Invalid username, maglagay ng minimum na %1$d digits. + + Invalid username, maglagay ng maximum na %1$d digits. %1$d kontak ay nasa Signal! @@ -2346,6 +2430,10 @@ Processing… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2370,6 +2458,8 @@ Answered on a linked device. Declined on a linked device. Busy on a linked device. + + Nilipat dito ang Flip Camera, i-tap ang video mo para subukan ito May nag-join sa call na may bagong safety number. @@ -3925,14 +4015,16 @@ Hindi matanggal ang group member. - Kasapi Request Your contact Tanggalin mula sa group I-update ang contact I-block Burahin - Recently changed their profile name from %1$s to %2$s + + Pinalitan ni %1$s ang kanyang profile name ng %3$s mula sa %2$s kamakailan + + Si %1$s ay nasa system contacts mo %1$s joined @@ -4988,6 +5080,12 @@ I-add ang people at groups kung saan mo gustong maka-receive ng notifications at calls kapag naka-turn on ang profile na ito Mag-add ng people o groups + + Exceptions + + I-allow lahat ng calls + + Mag-notify sa lahat ng mentions Mag-add @@ -5602,8 +5700,8 @@ Na-review na ang lahat ng connections, i-tap ang send para magpatuloy. - Mayroon kang %1$d connection na maaaring nag-reinstall ng Signal o nagpalit ng device. Bago mag-share ng story sa kanya, i-review ang kanyang safety numbers o tanggalin muna siya sa story mo. - Mayroon kang %1$d connections na maaaring nag-reinstall ng Signal o nagpalit ng device. Bago mag-share ng story sa kanila, i-review ang kanilang safety numbers o tanggalin muna sila sa story mo. + Ang %1$d connection mo ay maaaring nag-reinstall ng Signal o nagpalit ng devices. Pwede mong i-review ang kanyang safety number o magpatuloy sa pag-send. + Ang %1$d connections mo ay maaaring nag-reinstall ng Signal o nagpalit ng devices. Pwede mong i-review ang kanilang safety number o magpatuloy sa pag-send. Beripikahin ang numerong pangkaligtasan @@ -5961,12 +6059,18 @@ Hindi suportado ang IBAN country code Invalid ang IBAN + + Minimum 2 characters + + Invalid email address iDEAL Ilagay ang iyong bangko, pangalan, at email. Gagamitin ng Stripe ang email na ito para padalhan ka ng updates tungkol sa donation mo. %1$s + + Ilagay ang iyong bank details. Hindi kinokolekta o itinatago ng Signal ang iyong personal na impormasyon. %1$s Matuto pa @@ -6152,6 +6256,8 @@ Outgoing Missed + + Hindi nasagot habang naka-on ang notification profile Sumali @@ -6259,6 +6365,8 @@ Group call Missed group call + + May hindi nasagot na group call habang naka-on ang notification profile Incoming group call diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 7498f2cb69..0ba322ff42 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -323,8 +323,12 @@ Resim indirilemiyor. Resmi tekrar göndermen gerekecek. Video indirilemiyor. Videoyu tekrar göndermen gerekecek. - - %1$s Düzenlendi + + Şimdi düzenlendi + + %1$s saatinde düzenlendi + + %1$s saatinde düzenlendi Aramaya katıl @@ -358,6 +362,8 @@ Şifresiz MMS Signal iletisi + + İleti gönder Molly uygulamasından konuşalım %1$s Lütfen bir kişi seçin Eklenti gönderdiğiniz ileti türü için olan boyut limitini aşıyor. @@ -1277,6 +1283,10 @@ Cevapsız sesli arama Cevapsız görüntülü arama + + Bildirim profili açıkken cevapsız sesli arama + + Bildirim profili açıkken cevapsız görüntülü arama Sesli aramayı reddettin @@ -1828,6 +1838,8 @@ Kamerayı değiştir Sessizlik durumunu değiştir + + Ek eylemler Aramayı sonlandır @@ -1844,10 +1856,68 @@ Cihaz kulaklığını temsil eden bir simge. + + El Kaldır + + El Kaldır + + Elin insin mi? + + El İndir + + İptal + + Elini kaldırdın + + Görüntüle + + + + %1$s elini kaldırdı + %1$s ve %2$d el kaldırdı + + + + + %1$s + %1$s +%2$d + + + + Kaldırılmış el görünümünü genişlet + + + + Signal bağlantısı + + %1$s sistem kişilerin arasında + + Ortak grubunuz yok + + İstekleri dikkatlice inceleyin + + %1$d ortak grup + + Hakkında + + Siz + - - Bu aramada · %1$d kişi - Bu aramada · %1$d kişi + + Bu aramada (%1$d) + Bu aramada (%1$d) + + + Signal zili çalacak (%1$d) + Signal zili çalacak (%1$d) + + + Signal bildirim gönderecek (%1$d) + Signal bildirim gönderecek (%1$d) + + + El kaldıran (%1$d) + El kaldıranlar (%1$d) @@ -1961,6 +2031,12 @@ Görüldü İçerik + + + Ad çakışması bulundu + + Görüntüle + \'%1$s\' için hiç sonuç bulunamadı. @@ -2127,6 +2203,8 @@ Gönder + + 00 Kullanıcı adı ekle @@ -2149,6 +2227,12 @@ Atla Tamam + + Bu kullanıcı adı kullanılamıyor, başka bir numara dene. + + Geçersiz kullanıcı adı, minimum %1$d hane gir. + + Geçersiz kullanıcı adı, maksimum %1$d hane gir. %1$d kişi Signal\'e katıldı! @@ -2346,6 +2430,10 @@ İşleniyor… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2370,6 +2458,8 @@ Bağlı cihazdan yanıtlandı. Bağlı cihazdan reddedildi. Bağlı cihazda meşgul. + + Kamerayı Döndür buraya taşındı, denemek için videona dokun Güvenlik numarası değişen biri bu aramaya katıldı. @@ -3925,14 +4015,16 @@ Grup üyesi çıkarılamadı. - Üye İstek Kişiniz Gruptan çıkar Kişiyi güncelle Engelle Sil - Yakın zamanda profil adı %1$s iken %2$s olarak değiştirdi + + %1$s yakın zamanda profil adını %2$s iken %3$s olarak değiştirdi + + %1$s sistem kişilerin arasında %1$s katıldı @@ -4988,6 +5080,12 @@ Bu profil açıkken bildirim ve çağrı almak istediğiniz kişileri ve grupları ekleyin Kişi veya grup ekle + + İstisnalar + + Tüm aramalara izin ver + + Tüm bahsetmeleri bildir Ekle @@ -5602,8 +5700,8 @@ Tüm bağlantılar gözden geçirildi, devam etmek için gönder butonuna dokun. - Signal\'i yeniden yüklemiş veya cihazını değiştirmiş olabilecek %1$d bağlantın var. Hikayeni paylaşmadan önce, güvenlik numaralarını inceleyebilir veya hikayenden kaldırmayı düşünebilirsin. - Signal\'i yeniden yüklemiş veya cihazlarını değiştirmiş olabilecek %1$d bağlantın var. Hikayeni paylaşmadan önce, güvenlik numaralarını inceleyebilir veya hikayenden kaldırmayı düşünebilirsin. + %1$d bağlantı, Signal\'i yeniden yüklemiş veya cihazları değiştirmiş olabilir. Güvenlik numarasını inceleyebilir veya göndermeye devam edebilirsin. + %1$d bağlantı, Signal\'i yeniden yüklemiş veya cihazları değiştirmiş olabilir. Güvenlik numaralarını inceleyebilir veya göndermeye devam edebilirsin. Güvenlik numarasını doğrulayın @@ -5961,12 +6059,18 @@ IBAN ülke kodu desteklenmiyor Geçersiz IBAN + + Minimum 2 karakter + + Geçersiz e-posta adresi iDEAL Bankanı, adını ve e-posta adresini gir. Stripe, sana bağışınla ilgili güncellemeleri göndermek için bu e-postayı kullanır. %1$s + + Banka bilgilerini gir. Signal, kişisel bilgilerini toplamaz veya saklamaz. %1$s Daha fazlasını öğren @@ -6152,6 +6256,8 @@ Giden Cevapsız + + Bildirim profili açıkken cevaplanmadı Katıl @@ -6259,6 +6365,8 @@ Grup görüşmesi Cevapsız grup araması + + Bildirim profili açıkken cevapsız grup araması Gelen grup araması diff --git a/app/src/main/res/values-ug/strings.xml b/app/src/main/res/values-ug/strings.xml index 1a8e13c173..af568c90e1 100644 --- a/app/src/main/res/values-ug/strings.xml +++ b/app/src/main/res/values-ug/strings.xml @@ -322,8 +322,12 @@ رەسىم چۈشۈرۈلمىدى. {0} ئۇنى قايتا يوللىشى كېرەك. ۋىدېيو چۈشۈرۈلمىدى. {0} ئۇنى قايتا يوللىشى كېرەك. - - تەھرىرلەنگەن ۋاقىت %1$s + + ھازىرلا تەھرىرلەنگەن + + تەھرىرلەنگەن ۋاقىت %1$s + + تەھرىرلەنگەن ۋاقىت %1$s چاقىرىققا قوشۇلۇش @@ -355,6 +359,8 @@ شىفىرلانمىغان كۆپ ۋاسىتە ئۇچۇرى Signal ئۇچۇرى + + ئۇچۇر ئەۋەت Molly دا پاراڭلىشايلى%1$s بىر ئالاقىلەشكۈچى تاللاڭ قوشۇمچە ھۆججەت سىز ئەۋەتىۋاتقان ئۇچۇر تىپىنىڭ سىغىم چېكىدىن ئېشىپ كەتتى. @@ -1233,6 +1239,10 @@ ئېلىنمىغان ئاۋازلىق چاقىرىق سۆزلەشمىگەن سىن چاقىرىش + + ئۇقتۇرۇش ئارخىپى ئوچۇق ۋاقىتتا رەت قىلىنغان ئاۋازلىق چاقىرىق + + ئۇقتۇرۇش ئارخىپى ئوچۇق ۋاقىتتا رەت قىلىنغان ۋىدېيولۇق چاقىرىق بىر ئاۋازلىق چاقىرىقنى رەت قىلدىڭىز @@ -1757,6 +1767,8 @@ كامېرا ئالماشتۇرۇش ئۈنلۈك ئۈنسىز ئالماشتۇرۇش + + قوشۇمچە مەشغۇلات توختات @@ -1773,9 +1785,62 @@ ئۈسكۈنە تىڭشىغۇچىغا ۋەكىللىك قىلىدىغان سىنبەلگە. + + قول كۆتۈرۈش + + قول كۆتۈرۈش + + قولىڭىزنى چۈشۈرەمسىز؟ + + قول چۈشۈرۈش + + بىكار قىلىش + + قولىڭىزنى كۆتۈردىڭىز + + كۆرسەت + + + + %1$s + %2$d قول كۆتۈردى + + + + + %1$s +%2$d + + + + قول كۆتۈرۈش كۆرۈنۈشىنى يېيىش + + + + Signal ئۇلىنىشى + + %1$s سىزنىڭ سىستېما ئالاقىداش تىزىملىكىڭىزدە + + سىلەردە ئورتاق گۇرۇپپا يوق + + تەلەپلەرنى ئەستايىدىللىق بىلەن تەكشۈرۈڭ + + ئورتاق گۇرۇپپا %1$d + + ھەققىدە + + سىز + - - بۇ چاقىرىقتا · %1$d كىشى بار + + بۇ چاقىرىقتا (%1$d) + + + Signal قوڭغۇرىقى (%1$d) + + + Signal (%1$d) گە ئ‍ۇقتۇرۇش قىلىدۇ + + + قول كۆتۈرگەنلەر (%1$d) @@ -1888,6 +1953,12 @@ كۆرگەن ۋاسىتە + + + ئىسىم توقۇنۇشى بايقالدى + + كۆرسەت + «%1$s» ئۈچۈن ھېچقانداق نەتىجە بايقالمىدى @@ -2054,6 +2125,8 @@ ئەۋەت + + 00 ئىشلەتكۈچى نامى قوشۇش @@ -2076,6 +2149,12 @@ ئاتلا بولدى + + بۇ نامنى ئىشلەتكىلى بولمايدۇ، باشقا نومۇر سىناپ بېقىڭ. + + ئىشلەتكۈچى نامى ئىناۋەتسىز، ئاز دېگەندە %1$d خانە كىرگۈزۈڭ. + + ئىشلەتكۈچى نامى ئىناۋەتسىز، كۆپ دېگەندە %1$d خانە كىرگۈزۈڭ. %1$d ئالاقىداش Signal ئىشلىتىدۇ! @@ -2269,6 +2348,10 @@ بىر تەرەپ قىلىۋاتىدۇ… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2293,6 +2376,8 @@ باغلانغان بىر ئۈسكۈنىدە جاۋاب قايتۇردى. باغلانغان بىر ئۈسكۈنىدە رەت قىلدى. باغلانغان بىر ئۈسكۈنىدە ئالدىراش. + + كامېرا ئالماشتۇرۇش كۇنۇپكىسى بۇ يەرگە يۆتكەلدى، ۋىدېيونى چېكىپ ئۇنى سىناپ بېقىڭ بىرەيلەن ئۆزگەرتىلگەن بىخەتەرلىك نومۇرى بىلەن بۇ چاقىرىققا قوشۇلدى. @@ -3824,14 +3909,16 @@ گۇرۇپپا ئەزاسىنى چىقىرىۋېتىش مەغلۇپ بولدى. - ئەزا ئىلتىماس ئالاقەداشىڭىز گۇرۇپپىدىن چىقار ئالاقەداش يېڭىلا چەكلەش ئۆچۈر - يېقىندا ئارخىپ نامىنى %1$s دىن %2$s گە ئۆزگەرتتى + + %1$s يېقىندا ئارخىپ نامىنى %2$s دىن %3$s گە ئۆزگەرتتى + + %1$s سىزنىڭ سىستېما ئالاقىداش تىزىملىكىڭىزدە %1$s قېتىلدى @@ -4878,6 +4965,12 @@ بۇ ئارخىپ ئوچۇق ۋاقىتتا سىز ئۇقتۇرۇش ۋە تېلېفون قوبۇل قىلىشنى خالىغان كىشىلەر ۋە گۇرۇپپىلارنى قوشۇڭ ئادەم ياكى گۇرۇپپا قوشۇش + + ئايرىم ئەھۋاللار + + بارلىق چاقىرىقلارغا رۇخسەت + + بارلىق تىلغا ئېلىشلار ئۇقتۇرۇلسۇن قوشۇش @@ -5475,7 +5568,7 @@ بارلىق ئالاقىداشلار تەكشۈرۈلدى. داۋاملاشتۇرۇش ئۈچۈن يوللاشنى چېكىڭ. - %1$d ئالاقىدىشىڭىز Signal نى قايتا قاچىلىغان ياكى ئۈسكۈنىسىنى ئۆزگەرتكەن بولۇشى مۇمكىن. ئۇلار بىلەن ھېكايىڭىزنىڭ ئورتاقلىشىشتىن بۇرۇن ئۇلارنىڭ بىخەتەرلىك نومۇرىنى تەكشۈرۈپ كۆرسىڭىز ياكى ئۇلارنىڭ ھېكايىڭىزدىن چىقىرىۋېتىشنى ئويلىشىپ كۆرسىڭىز بولىدۇ. + %1$d ئالاقىداش Signal نى قايتا قاچىلىغان ياكى ئۈسكۈنىسىنى ئۆزگەرتكەن بولۇشى مۇمكىن. ئۇلارنىڭ بىخەتەرلىك نومۇرىنى تەكشۈرۈپ باقسىڭىز ياكى داۋاملىق يوللىسىڭىز بولىدۇ. بىخەتەرلىك نومۇر دەلىللە @@ -5829,12 +5922,18 @@ IBAN دۆلەت كودىنى قوللىمايدۇ IBAN نومۇرى ئىناۋەتسىز + + ئەڭ ئاز بولغاندا 2 ھەرپ + + ئىناۋەتسىز ئېلخەت ئادرېسى iDEAL بانكا، ئىسىم ۋە تورخەت ئادرېسىنى كىرگۈزۈڭ. Stripe بۇ تورخەت ئادرېسىنى ئىشلىتىپ سىزگە ئىئانىڭىز توغرىسىدىكى خەۋەرلەرنى يوللايدۇ. %1$s + + بانكا تەپسىلاتىنى كىرگۈزۈڭ. Signal شەخسىي ئۇچۇرلىرىڭىزنى توپلىمايدۇ ياكى ساقلىمايدۇ. %1$s تەپسىلاتى @@ -6019,6 +6118,8 @@ ئۇرۇلغىنى ئېلىنمىغىنى + + ئۇقتۇرۇش ئارخىپى ئوچۇق ۋاقىتتا رەت قىلىنغانلار قوشۇلۇش @@ -6123,6 +6224,8 @@ گۇرۇپپا چاقىرىقى ئېلىنمىغان گۇرۇپپا چاقىرىقى + + ئۇقتۇرۇش ئارخىپى ئوچۇق ۋاقىتتا رەت قىلىنغان گۇرۇپپا چاقىرىقى كەلگەن گۇرۇپپا چاقىرىقى diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index b8d35d40e2..599049ad8a 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -325,8 +325,12 @@ Не вдалося завантажити зображення. Потрібно буде надіслати його ще раз. Не вдалося завантажити відео. Потрібно буде надіслати його ще раз. - - Змінено %1$s + + Змінено зараз + + Змінено %1$s + + Змінено %1$s Приєднатися до дзвінка @@ -364,6 +368,8 @@ Незахищене MMS Повідомлення Signal + + Надіслати повідомлення Перейдімо до Molly %1$s Будь ласка, виберіть контакт Вкладення перевищує обмеження за розміром для типу повідомлення, яке ви надсилаєте. @@ -1365,6 +1371,10 @@ Пропущений голосовий виклик Пропущений відеовиклик + + Пропущено голосовий виклик під час увімкненого профілю сповіщень + + Пропущено відеовиклик під час увімкненого профілю сповіщень Ви відхилили голосовий виклик @@ -1970,6 +1980,8 @@ Перемкнути камеру Перемкнути звук + + Додаткові дії Завершити виклик @@ -1986,12 +1998,80 @@ Значок, який представляє навушники пристрою. + + Підняти руку + + Підняти руку + + Опустити руку? + + Опустити руку + + Скасувати + + Ви підняли руку + + Детальніше + + + + %1$s піднімає руку + %1$s + %2$d підняли руки + %1$s + %2$d підняли руки + %1$s + %2$d підняли руки + + + + + %1$s + %1$s +%2$d + %1$s +%2$d + %1$s +%2$d + + + + Розгорнути перегляд тих, хто підняв руки + + + + Контакт Signal + + %1$s є у ваших системних контактах + + Ви не маєте спільних груп + + Переглядайте запити уважно + + Спільні групи: %1$d + + Про себе + + Ви + - - У цьому дзвінку · %1$d осіб - У цьому дзвінку · %1$d особа - У цьому дзвінку · %1$d осіб - У цьому дзвінку · %1$d осіб + + У цьому виклику (%1$d) + У цьому виклику (%1$d) + У цьому виклику (%1$d) + У цьому виклику (%1$d) + + + Signal зателефонує (%1$d) + Signal зателефонує (%1$d) + Signal зателефонує (%1$d) + Signal зателефонує (%1$d) + + + Signal надішле сповіщення (%1$d) + Signal надішле сповіщення (%1$d) + Signal надішле сповіщення (%1$d) + Signal надішле сповіщення (%1$d) + + + Піднята рука (%1$d) + Підняті руки (%1$d) + Підняті руки (%1$d) + Підняті руки (%1$d) @@ -2107,6 +2187,12 @@ Переглянуто Медіа + + + Виявлено конфлікт імен + + Детальніше + Не знайдено результатів для \'%1$s\' @@ -2273,6 +2359,8 @@ Надіслати + + 00 Додайте ім\'я користувача @@ -2295,6 +2383,12 @@ Пропустити Готово + + Це ім\'я користувача недоступне, спробуйте інший номер. + + Недійсне ім\'я користувача, введіть мінімум %1$d цифри. + + Недійсне ім\'я користувача, введіть максимум %1$d цифр. %1$d контакт у Signal! @@ -2500,6 +2594,10 @@ Обробка… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2524,6 +2622,8 @@ Розмову прийнято на прив\'язаному пристрої. Відхилений на прив\'язаному пристрої. Зайнято на прив\'язаному пристрої. + + Тепер кнопка перемикання камери знаходиться тут. Торкніться свого відео, щоб спробувати Хтось щойно приєднався до дзвінка зі зміненим кодом безпеки. @@ -4127,14 +4227,16 @@ Не вдалося Вилучити учасника групи. - Учасник Запит Серед ваших контактів Вилучити з групи Оновити контакт Заблокувати Видалити - Нещодавно змінили назву свого профілю з %1$s на %2$s + + Нещодавня дія: %1$s змінює своє ім\'я профілю (%2$s → %3$s) + + %1$s є у ваших системних контактах Приєднався: %1$s @@ -5208,6 +5310,12 @@ Додайте людей та групи, від яких бажаєте отримувати сповіщення, коли цей профіль увімкнено Додати людей чи групи + + Виключення + + Дозволити всі виклики + + Сповіщати про всі згадування Додати @@ -5856,10 +5964,10 @@ Усі довірені контакти було переглянуто. Натисніть «відправити», щоб продовжити. - %1$d ваш контакт міг заново встановити Signal або змінити пристрій. За бажанням ви можете переглянути код безпеки перед надсиланням. - %1$d ваші контакти могли заново встановити Signal або змінити пристрої. За бажанням ви можете переглянути їхні коди безпеки перед надсиланням. - %1$d ваших контактів могли заново встановити Signal або змінити пристрої. За бажанням ви можете переглянути їхні коди безпеки перед надсиланням. - %1$d вашого контакту могли заново встановити Signal або змінити пристрої. За бажанням ви можете переглянути їхні коди безпеки перед надсиланням. + Можливо, %1$d ваш контакт перевстановив Signal або змінив пристрій. Ви можете переглянути код безпеки або надіслати без перегляду. + Можливо, %1$d ваші контакти перевстановили Signal або змінили пристрій. Ви можете переглянути код безпеки або надіслати без перегляду. + Можливо, %1$d ваших контактів перевстановили Signal або змінили пристрій. Ви можете переглянути код безпеки або надіслати без перегляду. + Можливо, %1$d ваших контактів перевстановили Signal або змінили пристрій. Ви можете переглянути код безпеки або надіслати без перегляду. Підтвердити код безпеки @@ -6225,12 +6333,18 @@ Код країни IBAN не підтримується Недійсний IBAN + + Мінімум 2 символи + + Невірна електронна адреса iDEAL Введіть свій банк, своє ім\'я та електрону адресу. Stripe використовує вашу електронну адресу, щоб надсилати оновлення щодо внеску. %1$s + + Введіть свої банківські реквізити. Signal не збирає і не зберігає вашу особисту інформацію. %1$s Подробиці @@ -6418,6 +6532,8 @@ Вихідний Пропущений + + Пропущено під час увімкненого профілю сповіщень Приєднатись @@ -6531,6 +6647,8 @@ Груповий дзвінок Пропущений груповий дзвінок + + Пропущено груповий виклик під час увімкненого профілю сповіщень Вхідний груповий дзвінок diff --git a/app/src/main/res/values-ur/strings.xml b/app/src/main/res/values-ur/strings.xml index d3feeb5ea2..448fe7e1a1 100644 --- a/app/src/main/res/values-ur/strings.xml +++ b/app/src/main/res/values-ur/strings.xml @@ -323,8 +323,12 @@ تصویر ڈاؤن لوڈ نہیں کر سکتے۔ آپ کو اسے دوبارہ بھیجنے کی ضرورت ہو گی۔ ویڈیو ڈاؤن لوڈ نہیں کر سکتے۔ آپ کو اسے دوبارہ بھیجنے کی ضرورت ہو گی۔ - - ترمیم شدہ %1$s + + ابھی ترمیم کردہ + + %1$s قبل ترمیم کی گئی + + %1$s قبل ترمیم کی گئی کال میں شامل ہوں @@ -358,6 +362,8 @@ غیر محفوظ ایم ایم ایس Signal پیغام + + پیغام بھیجیں چلیں Molly کو چلاتے ہیں%1$s براۓ مہربانی رابطہ منتخب کریں جو پیغام آپ بھیج رہے ہیں منسلک ہونے کیلئے اس کا حجم حد سے زیادہ ہے۔ @@ -1277,6 +1283,10 @@ مسڈ وائس کال مسڈ ویڈیو کال + + اطلاعات کی پروفائل آن ہونے کے دوران مسڈ وائس کال + + اطلاعات کی پروفائل آن ہونے کے دوران مسڈ ویڈیو کال آپ نے وائس کال مسترد کر دی @@ -1828,6 +1838,8 @@ کیمرہ ٹوگل کریں خاموش ٹوگل کریں + + اضافی افعال کال ختم کریں @@ -1844,10 +1856,68 @@ ایک آئیکن ڈیوائس کے ایئر پیس کو ظاہر کر رہا ہے۔ + + ہاتھ کھڑا کریں + + ہاتھ کھڑا کریں + + اپنا ہاتھ نیچے کریں؟ + + ہاتھ نیچے کریں + + منسوخ کریں + + آپ نے اپنا ہاتھ اٹھایا + + دیکھیں + + + + %1$s نے ہاتھ اٹھایا + %1$s + %2$d نے ہاتھ اٹھایا + + + + + %1$s + %1$s +%2$d + + + + اٹھائے گئے ہاتھوں کا ویو پھیلائیں + + + + Signal کنکشن + + %1$s آپ کے سسٹم کے روابط میں موجود ہے + + آپ کے کوئی مشترکہ گروپس نہیں + + درخواستوں کا بغور جائزہ لیں + + %1$d مشترکہ گروپس + + متعلق + + آپ + - - اس کال میں · %1$d شخص - اس کال میں · %1$d لوگ + + اس کال میں (%1$d) + اس کال میں (%1$d) + + + Signal (%1$d) کو رنگ بھیجے گا + Signal (%1$d) کو رنگ بھیجے گا + + + Signal (%1$d) کو مطلع کرے گا + Signal (%1$d) کو مطلع کرے گا + + + ہاتھ اٹھایا (%1$d) + ہاتھوں کو اٹھایا (%1$d) @@ -1961,6 +2031,12 @@ دیکھا ہوا میڈیا + + + متصادم نام پایا گیا ہے + + دیکھیں + \'%1$s\' کے لیے کوئی نتیجہ نہیں ملا @@ -2127,6 +2203,8 @@ بھیجیں + + 00 صارفی نام شامل کریں @@ -2149,6 +2227,12 @@ چھوڑ دو ہو گیا + + یہ یوزر نیم دستیاب نہیں ہے، کوئی اور نمبر آزمائیں۔ + + غلط یوزر نیم، کم از کم %1$d ہندسے درج کریں۔ + + غلط یوزر نیم، زیادہ سے زیادہ %1$d ہندسے درج کریں۔ %1$d رابطے Signal پر ہیں! @@ -2346,6 +2430,10 @@ پروسیسنگ… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2370,6 +2458,8 @@ منسلک آلہ پر جواب دیا گیا۔ منسلک آلہ سے انکار کردیا۔ منسلک ڈیوائس پر مصروف۔ + + فلپ کیمرہ یہاں منتقل کر دیا گیا ہے، اسے آزمانے کے لیے اپنی ویڈیو پر ٹیپ کریں کسی نے حفاظتی نمبر کے ساتھ اس کال میں شمولیت اختیار کی ہے جو بدل گیا ہے۔ @@ -3925,14 +4015,16 @@ گروپ ممبر کو ہٹانے میں ناکام۔ - اراکین درخواست آپ کا رابطہ گروپ سے ہٹائیں رابطہ اپ ڈیٹ کریں بلاک کریں حذف کریں - حال ہی میں اپنے پروفائل کا نام %1$s سے تبدیل کرکے %2$s کردیا + + %1$s نے حال ہی میں اپنی پروفائل کا نام %2$s سے %3$s میں تبدیل کیا ہے + + %1$s آپ کے سسٹم کے روابط میں موجود ہے %1$sشامل ہوئے @@ -4988,6 +5080,12 @@ اس پروفائل کے آن ہونے پر ان افراد اور گروپس کو شامل کریں جن سے آپ اطلاعات اور کالز وصول کرنا چاہتے ہیں افراد یا گروپس شامل کریں + + استثنات + + تمام کالز کو اجازت دیں + + تمام مینشنز کے لیے اطلاع دیں شامل کریں @@ -5602,8 +5700,8 @@ تمام کنکشنز کا جائزہ لیا گیا ہے، جاری رکھنے کے لیے بھیجیں پر ٹیپ کریں۔ - آپ کے %1$d کنیکشن نے ہو سکتا ہے Signal کو دوبارہ انسٹال یا ڈیوائسز کو تبدیل کیا ہو۔ اپنی سٹوری ان کے ساتھ شیئر کرنے سے پہلے ان کے حفاظتی نمبرز کا جائزہ لیں یا ان کو اپنی سٹوری سے ہٹانے پر غور کریں۔ - آپ کے %1$d کنیکشنز نے ہو سکتا ہے Signal کو دوبارہ انسٹال یا ڈیوائسز کو تبدیل کیا ہو۔ اپنی سٹوری ان کے ساتھ شیئر کرنے سے پہلے ان کے حفاظتی نمبرز کا جائزہ لیں یا ان کو اپنی سٹوری سے ہٹانے پر غور کریں۔ + ہو سکتا ہے کہ %1$d کنکشن نے Signal دوبارہ انسٹال کی ہو یا ڈیوائسز تبدیل کی ہوں۔ آپ ان کے حفاظتی نمبر کا جائزہ لے سکتے ہیں یا ارسال کرنے کو جاری رکھ سکتے ہیں۔ + ہو سکتا ہے کہ %1$d کنکشنز نے Signal دوبارہ انسٹال کی ہو یا ڈیوائسز تبدیل کی ہوں۔ آپ ان کے حفاظتی نمبرز کا جائزہ لے سکتے ہیں یا ارسال کرنے کو جاری رکھ سکتے ہیں۔ حفاظتی نمبر کی تصدیق کریں @@ -5961,12 +6059,18 @@ IBAN ملکی کوڈ سپورٹ کردہ نہیں ہے غلط IBAN + + کم از کم 2 حروف + + غیر مستند ای میل ایڈریس iDEAL اپنا بینک، نام اور ای میل درج کریں۔ Stripe آپ کے عطیہ کے حوالے سے آپ کو اپ ڈیٹس بھیجنے کے لیے آپ کا ای میل استعمال کرتی ہے۔ %1$s + + اپنی بینک کی تفصیلات درج کریں۔ Signal آپ کی ذاتی معلومات جمع یا اسٹور نہیں کرتا۔ %1$s مزید جانیں @@ -6152,6 +6256,8 @@ جانے والی مسڈ + + اطلاعات کی پروفائل آن ہونے کے دوران مس ہوئیں شامل ہوں @@ -6259,6 +6365,8 @@ گروپ کال مس کردہ گروپ کال + + اطلاعات کی پروفائل آن ہونے کے دوران مسڈ گروپ کال آنے والی گروپ کال diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 5d97f5fb6c..76aad86a5d 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -322,8 +322,12 @@ Không thể tải ảnh xuống. Bạn cần gửi lại. Không thể tải video xuống. Bạn cần gửi lại. - - Đã chỉnh sửa %1$s + + Đã chỉnh sửa Vừa xong + + Đã chỉnh sửa %1$s + + Đã chỉnh sửa %1$s Tham gia cuộc gọi @@ -355,6 +359,8 @@ MMS không bảo mật Tin nhắn Signal + + Gửi tin nhắn Hãy chuyển sang dùng Molly %1$s Vui lòng chọn một liên lạc Tệp đính kèm vượt quá giới hạn kích cỡ cho loại tin nhắn mà bạn đang gửi. @@ -1233,6 +1239,10 @@ Cuộc gọi thoại bị nhỡ Cuộc gọi video bị nhỡ + + Cuộc gọi thoại bị nhỡ khi cấu hình thông báo được bật + + Cuộc gọi video bị nhỡ khi cấu hình thông báo được bật Bạn đã từ chối cuộc gọi thoại @@ -1757,6 +1767,8 @@ Bật camera Bật tắt tiếng + + Các thao tác khác Kết thúc cuộc gọi @@ -1773,9 +1785,62 @@ Biểu tượng thể hiện phần tai nghe của một thiết bị. + + Giơ Tay + + Giơ Tay + + Bạn muốn hạ tay? + + Hạ Tay + + Hủy + + Bạn đã giơ tay + + Xem + + + + %1$s + %2$d đã giơ tay + + + + + %1$s +%2$d + + + + Mở rộng giao diện hiển thị người đã giơ tay + + + + Liên hệ Signal + + %1$s có trong danh bạ trên hệ thống của bạn + + Các bạn không có nhóm chung + + Kiểm tra yêu cầu một cách kĩ lưỡng + + %1$d nhóm chung + + Thông tin + + Bạn + - - Trong cuộc gọi này · %1$d người + + Trong cuộc gọi này (%1$d) + + + Signal sẽ Đổ chuông (%1$d) + + + Signal sẽ Thông báo (%1$d) + + + Đã giơ tay (%1$d) @@ -1888,6 +1953,12 @@ Đã xem Đa phương tiện + + + Có xung đột về tên + + Xem + Không có kết quả cho \'%1$s\' @@ -2054,6 +2125,8 @@ Gửi + + 00 Thêm tên người dùng @@ -2076,6 +2149,12 @@ Bỏ qua Xong + + Tên người dùng này không khả dụng, thử một số khác. + + Tên người dùng không hợp lệ, nhập tối thiểu %1$d ký tự. + + Tên người dùng không hợp lệ, nhập tối đa %1$d ký tự. %1$d liên hệ của bạn đang sử dụng Signal! @@ -2269,6 +2348,10 @@ Đang xử lý… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2293,6 +2376,8 @@ Đã trả lời trên một thiết bị liên kết. Đã từ chối trên một thiết bị liên kết. Đã để máy bận trên một thiết bị liên kết. + + Nút Đảo Camera đã được chuyển đến đây, nhấn vào video của bạn để thử Có người với mã số an toàn đã thay đổi vừa tham gia cuộc gọi này. @@ -3824,14 +3909,16 @@ Không thể xóa thành viên nhóm. - Thành viên Yêu cầu Liên hệ của bạn Xóa khỏi nhóm Cập nhật liên hệ Chặn Xóa - Vừa thay đổi tên trên hồ sơ cá nhân từ %1$s sang %2$s + + %1$s đã thay đổi tên hồ sơ của mình gần đây từ %2$s thành %3$s + + %1$s có trong danh bạ trên hệ thống của bạn %1$s đã tham gia @@ -4878,6 +4965,12 @@ Thêm người và nhóm bạn muốn nhận thông báo và cuộc gọi khi cấu hình này được bật Thêm người hoặc nhóm + + Ngoại trừ + + Đồng ý tất cả cuộc gọi + + Thông báo cho tất cả đề cập Thêm @@ -5475,7 +5568,7 @@ Tất cả liên hệ đã được xem xét, nhấn gửi để tiếp tục. - Bạn có %1$d liên hệ có thể đã cài đặt lại Signal hoặc đã đổi thiết bị. Bạn có thể chọn duyệt lại mã số an toàn của họ trước khi gửi. + %1$d liên hệ có thể đã cài đặt lại Signal hoặc thay đổi thiết bị. Bạn có thể kiểm tra lại mã số an toàn của họ hoặc tiếp tục gửi. Xác minh mã số an toàn @@ -5829,12 +5922,18 @@ Mã quốc gia của số IBAN không được hỗ trợ IBAN không hợp lệ + + Tối thiểu 2 ký tự + + Địa chỉ email không hợp lệ iDEAL Nhập tên ngân hàng, tên và địa chỉ email của bạn. Stripe sử dụng địa chỉ email này để gửi thông tin cập nhật về khoản ủng hộ của bạn. %1$s + + Nhập thông tin ngân hàng của bạn. Signal không thu thập hay lưu trữ thông tin cá nhân của bạn. %1$s Tìm hiểu thêm @@ -6019,6 +6118,8 @@ Cuộc gọi đi Cuộc gọi nhỡ + + Bị nhỡ khi cấu hình thông báo được bật Tham gia @@ -6123,6 +6224,8 @@ Cuộc gọi nhóm Cuộc gọi nhóm bị nhỡ + + Cuộc gọi nhóm bị nhỡ khi cấu hình thông báo được bật Cuộc gọi nhóm đến diff --git a/app/src/main/res/values-yue/strings.xml b/app/src/main/res/values-yue/strings.xml index a9e12f3b94..db9f71b373 100644 --- a/app/src/main/res/values-yue/strings.xml +++ b/app/src/main/res/values-yue/strings.xml @@ -322,8 +322,12 @@ 下載唔到圖像。你要再傳送多次。 下載唔到影片。你要再傳送多次。 - - 編輯咗 (%1$s前) + + 編輯咗 而家 + + 編輯咗 (%1$s前) + + 編輯咗 (%1$s) 加入通話 @@ -355,6 +359,8 @@ 唔安全嘅多媒體短訊 Signal 訊息 + + 傳送訊息 不如我哋齊齊轉會 Molly %1$s 請揀一位聯絡人吖 附件太大,擠唔落您而家寫緊嘅呢類訊息度。 @@ -1233,6 +1239,10 @@ 未接嘅語音通話 未接嘅視像通話 + + 通知設定檔開啟時未接語音通話 + + 通知設定檔開啟時未接視像通話 你拒絕咗一個語音通話 @@ -1757,6 +1767,8 @@ 切換相機狀態 切換靜音狀態 + + 其他操作 收線 @@ -1773,9 +1785,62 @@ 代表裝置耳機嘅圖示。 + + 舉手 + + 舉手 + + 係咪想放低手? + + 放低手 + + 取消 + + 你舉咗手 + + 睇下 + + + + %1$s + %2$d 舉咗手 + + + + + %1$s + %2$d + + + + 展開舉手視圖 + + + + Signal 人脈 + + %1$s 係你嘅聯絡人 + + 你哋冇共同群組 + + 請求要金睛火眼睇清楚 + + %1$d 個共同群組 + + 關於 + + + - - 嚟到呢次通話 · %1$d 人 + + 喺呢個通話入面 (%1$d) + + + Signal 會打俾 (%1$d) + + + Signal 會通知 (%1$d) + + + 舉咗手 (%1$d) @@ -1888,6 +1953,12 @@ 已睇過 媒體 + + + 系統發現有人撞名 + + 睇下 + 搵唔到同「%1$s」相關嘅嘢 @@ -2054,6 +2125,8 @@ 傳送 + + 00 新增用戶名稱 @@ -2076,6 +2149,12 @@ 飛過 搞掂 + + 呢個用戶名稱唔用得,試吓其他號碼啦。 + + 用戶名稱無效,請輸入至少 %1$d 位數字。 + + 用戶名稱無效,請輸入最多 %1$d 位數字。 %1$d 位聯絡人開咗 Signal 喇! @@ -2269,6 +2348,10 @@ 處理緊… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2293,6 +2376,8 @@ 已用連結咗嘅機接聽。 已用連結咗嘅機拒接。 用連結咗嘅機佔線中。 + + 「切換鏡頭」已經搬咗嚟呢度,㩒一下視像畫面試吓啦 有人加入咗呢個通話,而佢嘅安全碼已經郁動過。 @@ -3824,14 +3909,16 @@ 移除唔到群組成員。 - 成員 請求 您嘅聯絡人 喺群組度移除 更新聯絡人 封鎖 刪除 - 最近將佢先前個人資料嘅名 %1$s 轉咗做 %2$s + + %1$s 最近將個人資料嘅名由 %2$s 改咗做 %3$s + + %1$s 係你嘅聯絡人 %1$s 已加入 @@ -4878,6 +4965,12 @@ 呢壇吾揀單濟渡落實嘅時候,您想收到邊啲人同埋谷嘅通知,就喺度圓善入閘 加人或者谷 + + 例外情況 + + 批准所有通話 + + 為所有提及開啟通知 加入去 @@ -5475,7 +5568,7 @@ 已經審查晒所有人脈,㩒一吓傳送就可以繼續。 - 你有 %1$d 個人脈可能重新安裝咗 Signal 或者更換咗裝置。同佢哋分享你嘅限時動態之前,請先審查佢哋嘅安全碼,或者可以考慮喺你嘅限時動態入面將佢哋移除。 + %1$d 個人脈可能重新安裝咗 Signal 或者更換咗裝置。你可以檢查佢哋嘅安全碼或者繼續傳送訊息。 驗證安全碼 @@ -5829,12 +5922,18 @@ 未支援 IBAN 國家/地區代碼 IBAN 無效 + + 最少 2 個字元 + + 電郵地址無效 iDEAL 請輸入你嘅銀行、姓名同電郵地址。Stripe 會用呢個電郵地址向你發送有關捐款嘅最新消息。 %1$s + + 請輸入你嘅銀行資料。 Signal 唔會收集或者儲存你嘅個人資料。%1$s 了解詳情 @@ -6019,6 +6118,8 @@ 打出 未接來電 + + 通知設定檔開啟時未接通話 加入 @@ -6123,6 +6224,8 @@ 成谷通話 未接嘅群組通話 + + 通知設定檔開啟時未接群組通話 群組通話來電 diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 6d1baf7c67..585b2d5ac1 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -322,8 +322,12 @@ 无法下载图片。您需要重发一遍。 无法下载视频。您需要重发一遍。 - - %1$s前编辑 + + 刚刚编辑 + + 已编辑 %1$s + + 已编辑 %1$s 加入通话 @@ -355,6 +359,8 @@ 不安全彩信 发送 Signal 消息 + + 发送消息 用 Molly 来聊天吧 %1$s 请选择联系人 附件超过当前消息类型的大小限制。 @@ -1233,6 +1239,10 @@ 未接语音来电 未接视频来电 + + 语音通话由于开启通知配置而未接通 + + 视频通话由于开启通知配置而未接通 您拒接了语音通话 @@ -1757,6 +1767,8 @@ 切换相机 切换静音 + + 其他操作 结束呼叫 @@ -1773,9 +1785,62 @@ 设备耳机图标。 + + 举手 + + 举手 + + 要放下吗? + + 放下 + + 取消 + + 您举手了 + + 查看 + + + + %1$s + %2$d 举手了 + + + + + %1$s + %2$d + + + + 展开举手视图 + + + + Signal 密友 + + %1$s在您的系统通讯录中 + + 你们没有共同群组 + + 请仔细审查请求 + + %1$d 个共同群组 + + 关于 + + + - - 此通话 · %1$d位用户 + + 通话成员(%1$d) + + + Signal 将会拨给(%1$d) + + + Signal 将会通知(%1$d) + + + 举手(%1$d) @@ -1888,6 +1953,12 @@ 已查看 媒体 + + + 发现名字冲突 + + 查看 + 没有找到关于“%1$s”的信息 @@ -2054,6 +2125,8 @@ 发送 + + 00 添加用户名 @@ -2076,6 +2149,12 @@ 跳过 完成 + + 此用户名不可用,请尝试另一个号码。 + + 用户名无效,请至少输入 %1$d 位数。 + + 用户名无效,请至多输入 %1$d 位数。 %1$d 个联系人在使用 Signal! @@ -2269,6 +2348,10 @@ 正在处理中…… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2293,6 +2376,8 @@ 已在在其它设备上接听。 已在其它设备上拒接。 在其它设备上忙碌未接。 + + 翻转摄像头功能移到了这里,赶快点击你的视频试试吧 有人用变更过的安全码加入了这次通话 @@ -3824,14 +3909,16 @@ 移除群组成员失败。 - 成员 入群请求 您的联系人 从群组移除 更新联系人 屏蔽 删除 - 最近更改了其用户名 %1$s 为 %2$s + + %1$s最近将其个人资料名称从%2$s更改为%3$s + + %1$s在您的系统通讯录中 %1$s已加入 @@ -4878,6 +4965,12 @@ 添加您启用此配置时想要接收通知或接到通话的用户和群组 添加用户或群组 + + 例外情况 + + 允许所有来电 + + 被提及时均通知 添加 @@ -5475,7 +5568,7 @@ 已检查所有联系人,请点击发送以继续操作。 - 您有 %1$d 位密友可能已重新安装 Signal 或更改设备。在与他们分享您的动态之前,请检查对方的安全码或考虑将他们从您的动态中移除。 + %1$d 位密友可能已重新安装 Signal 或更改设备。您可以检查对方的安全码或继续发送。 验证安全代码 @@ -5829,12 +5922,18 @@ 不支持 IBAN 国家/地区代码 IBAN 无效 + + 至少 2 个字符 + + 无效的电子邮件地址 iDEAL 请输入您的银行、名称和电子邮件地址。Stripe 将使用此电子邮件地址给您发送关于您的捐款的最新信息。%1$s + + 请输入您的银行信息。Signal 不会收集或储存您的个人信息。%1$s 了解详情 @@ -6019,6 +6118,8 @@ 去电 未接 + + 由于开启通知配置而未接通 加入 @@ -6123,6 +6224,8 @@ 群组通话 未接群组通话 + + 群组通话由于开启通知配置而未接通 拨入群组通话 diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index b2a6043cf5..b4a90232fc 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -322,8 +322,12 @@ 無法下載圖像。你需要再次傳送。 無法下載影片。你需要再次傳送。 - - %1$s 前已編輯 + + 已編輯 現在 + + %1$s 前已編輯 + + %1$s 已編輯 加入通話 @@ -355,6 +359,8 @@ 不安全的多媒體短訊 Signal 訊息 + + 傳送訊息 我們不如改用 Molly 吧 %1$s 請選擇一個聯絡人 附件超出您正在傳送的訊息類型的大小上限。 @@ -1233,6 +1239,10 @@ 未接的語音通話 未接的視訊通話 + + 通知設定檔開啟時有未接語音通話 + + 通知設定檔開啟時未接視訊通話 你拒絕了一個語音通話 @@ -1757,6 +1767,8 @@ 切換攝影機 切換靜音 + + 其他操作 結束通話 @@ -1773,9 +1785,62 @@ 代表裝置耳機的圖示。 + + 舉手 + + 舉手 + + 要放下手? + + 放下手 + + 取消 + + 你舉了手 + + 檢視 + + + + %1$s + %2$d 舉了手 + + + + + %1$s + %2$d + + + + 展開已舉手畫面 + + + + Signal 人脈 + + %1$s 在你的系統聯絡人中 + + 你沒有共同的群組 + + 請仔細檢閱請求 + + %1$d 個共同群組 + + 關於 + + + - - 現已在此通話中 · %1$d 人 + + 在此通話中 (%1$d) + + + Signal 會打電話給 (%1$d) + + + Signal 會通知 (%1$d) + + + 舉了手 (%1$d) @@ -1888,6 +1953,12 @@ 已看過 媒體 + + + 發現名稱衝突 + + 檢視 + 找不到符合「%1$s」的任何結果 @@ -2054,6 +2125,8 @@ 傳送 + + 00 新增用戶名稱 @@ -2076,6 +2149,12 @@ 略過 完成 + + 此用戶名稱無法使用,請嘗試其他號碼。 + + 無效的用戶名稱,輸入至少 %1$d 位數。 + + 無效的用戶名稱,輸入最多 %1$d 位數。 %1$d 個聯絡人現已使用 Signal! @@ -2269,6 +2348,10 @@ 正在處理… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2293,6 +2376,8 @@ 已在連結的裝置上接聽。 已在連結的裝置上拒接。 在連結的裝置上繁忙中。 + + 「切換鏡頭」已移動到此處,點一下你的影片來嘗試 已加入此通話的某人,其安全碼已變更。 @@ -3824,14 +3909,16 @@ 移除群組成員失敗。 - 成員 請求 您的聯絡人 從群組中移除 更新聯絡人 封鎖 刪除 - 最近將個人資料名稱 %1$s 改為 %2$s + + %1$s 最近將個人資料名稱從 %2$s 更改為 %3$s + + %1$s 在你的系統聯絡人中 %1$s 已加入 @@ -4878,6 +4965,12 @@ 新增當啟用此情景時您想收到哪些人和群組的通知和通話 新增他人或群組 + + 例外 + + 允許所有通話 + + 通知所有提及 新增 @@ -5475,7 +5568,7 @@ 已檢查所有人脈,請點按傳送以繼續操作。 - 你有 %1$d 位聯絡人可能已重新安裝 Signal 或更換裝置。與對方分享你的限時動態之前,請先檢查他們的安全碼,或考慮將他們從你的限時動態中移除。 + %1$d 位聯絡人可能已重新安裝 Signal 或更換裝置。你可以檢查他們的安全碼或繼續傳送訊息。 驗證安全碼 @@ -5829,12 +5922,18 @@ 未支援 IBAN 國家代碼 IBAN 無效 + + 最少 2 個字元 + + 電郵地址無效 iDEAL 請輸入你的銀行、名稱和電郵地址。 Stripe 會使用此電郵向你發送有關捐款的最新訊息。 %1$s + + 請輸入你的銀行資料。 Signal 不會收集或儲存你的個人資料。%1$s 了解更多 @@ -6019,6 +6118,8 @@ 撥出 未接來電 + + 通知設定檔開啟時未接通話 加入 @@ -6123,6 +6224,8 @@ 群組通話 未接群組的通話 + + 通知設定檔開啟時未接群組通話 群組通話來電 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 9273ac71e3..a5ad52fb6e 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -322,8 +322,12 @@ 無法下載圖像。你需要再次傳送。 無法下載影片。你需要再次傳送。 - - %1$s 前已編輯 + + 已編輯 現在 + + %1$s 前已編輯 + + %1$s 前已編輯 加入通話 @@ -355,6 +359,8 @@ 不安全的多媒體訊息 Signal 訊息 + + 傳送訊息 我們要不要切換到 Molly %1$s 請選擇聯絡人 當前訊息的附檔類型已經超越大小限制。 @@ -1233,6 +1239,10 @@ 未接的語音通話 錯過視訊電話 + + 通知設定檔開啟時有未接語音通話 + + 通知設定檔開啟時未接視訊通話 你拒絕了一個語音通話 @@ -1757,6 +1767,8 @@ 切換攝影機 切換靜音 + + 其他操作 掛斷 @@ -1773,9 +1785,62 @@ 代表裝置耳機的圖示。 + + 舉手 + + 舉手 + + 要放下手? + + 放下手 + + 取消 + + 你舉了手 + + 檢視 + + + + %1$s + %2$d 舉了手 + + + + + %1$s + %2$d + + + + 展開已舉手畫面 + + + + Signal 人脈 + + %1$s 在你的系統聯絡人中 + + 你沒有共同的群組 + + 仔細審核請求 + + %1$d 個共同群組 + + 關於 + + + - - 在此通話中·%1$d個人 + + 在此通話中 (%1$d) + + + Signal 會打電話給 (%1$d) + + + Signal 會通知 (%1$d) + + + 舉了手 (%1$d) @@ -1888,6 +1953,12 @@ 已查看 媒體 + + + 發現名稱衝突 + + 檢視 + 沒有找到「%1$s」的結果 @@ -2054,6 +2125,8 @@ 傳送 + + 00 Add a username @@ -2076,6 +2149,12 @@ 略過 完成 + + 此用戶名稱無法使用,請嘗試其他號碼。 + + 無效的用戶名稱,輸入至少 %1$d 位數。 + + 無效的用戶名稱,輸入最多 %1$d 位數。 有%1$d個聯絡人使用Signal @@ -2269,6 +2348,10 @@ 正在處理… + + %1$1.1f MB/%2$2.1f MB + + %1$1.1f MB @@ -2293,6 +2376,8 @@ 在已連結的裝置回答。 拒絕在已連結的裝置。 已連結的裝置忙線。 + + 「切換鏡頭」已移動到此處,點一下你的影片來嘗試 有人加入了此通話,並更改了一個安全碼。 @@ -3824,14 +3909,16 @@ 移除群組成員失敗。 - 成員 要求 你的聯絡人 從群組中移除 更新聯絡人 封鎖 刪除 - 最近將他們的個人資料名稱從%1$s更改為%2$s + + %1$s 最近將個人資料名稱從 %2$s 更改為 %3$s + + %1$s 在你的系統聯絡人中 %1$s 已加入 @@ -4878,6 +4965,12 @@ 新增你希望在此描述開啟時收到通知和來電的人和群組 新增組員或群組 + + 例外 + + 允許所有來電 + + 通知所有提及 新增 @@ -5475,7 +5568,7 @@ 所有聯絡人已審核完畢,點選「傳送」即可繼續。 - 你有 %1$d 位聯絡人可能已重新安裝 Signal 或更換裝置。與對方分享你的限時動態之前,請先檢查他們的安全碼,或考慮將他們從你的限時動態中移除。 + %1$d 位聯絡人可能已重新安裝 Signal 或更換裝置。你可以檢查他們的安全碼或繼續傳送訊息。 驗證安全碼 @@ -5829,12 +5922,18 @@ 未支援 IBAN 國家代碼 IBAN 無效 + + 最少 2 個字元 + + 電郵地址無效 iDEAL 請輸入你的銀行、名稱和電郵地址。 Stripe 會使用此電郵向你發送有關捐款的最新訊息。 %1$s + + 請輸入你的銀行資料。 Signal 不會收集或儲存你的個人資料。%1$s 了解更多 @@ -6019,6 +6118,8 @@ 撥出 未接來電 + + 通知設定檔開啟時未接通話 加入 @@ -6123,6 +6224,8 @@ 群組通話 未接群組的通話 + + 通知設定檔開啟時未接群組通話 群組通話來電 diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index d628e44af4..d5dc31cef9 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -80,6 +80,14 @@ 0dp 320dp + 4dp + 48dp + + 320dp + 120dp + + 16dp + 38dp 28dp @@ -161,8 +169,9 @@ 64dp 32dp 24dp - 4dp - 50dp + 2dp + 4dp + 44dp 80dp 70dp @@ -197,7 +206,7 @@ 18dp 48dp - 14dp + 8dp 8dp 0dp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 03dfdae109..5e65bc7617 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -323,8 +323,12 @@ Can\'t download image. You will need to send it again. Can\'t download video. You will need to send it again. - - Edited\u2000%1$s + + Edited\u2000Now + + Edited\u2000%1$s + + Edited\u2000%1$s Join call @@ -358,6 +362,8 @@ Insecure MMS Signal message + + Send message Let\'s switch to Molly %1$s Please choose a contact Attachment exceeds size limits for the type of message you\'re sending. @@ -1276,6 +1282,10 @@ Missed voice call Missed video call + + Missed voice call while notification profile on + + Missed video call while notification profile on You declined a voice call @@ -1828,6 +1838,8 @@ Toggle camera Toggle mute + + Additional actions End call @@ -1844,10 +1856,68 @@ An icon representing a device\'s earpiece. + + Raise hand + + Raise hand + + Lower your hand? + + Lower hand + + Cancel + + You raised your hand + + View + + + + %1$s has raised a hand + %1$s + %2$d have raised a hand + + + + + %1$s + %1$s +%2$d + + + + Expand raised hand view + + + + Signal connection + + %1$s is in your system contacts + + You have no groups in common + + Review requests carefully + + %1$d groups in common + + About + + You + - - In this call · %1$d person - In this call · %1$d people + + In this call (%1$d) + In this call (%1$d) + + + Signal will Ring (%1$d) + Signal will Ring (%1$d) + + + Signal will Notify (%1$d) + Signal will Notify (%1$d) + + + Raised hand (%1$d) + Raised hands (%1$d) @@ -1963,6 +2033,12 @@ Viewed Media + + + Name conflict found + + View + No results found for \'%s\' @@ -2129,6 +2205,8 @@ Send + + 00 Add a username @@ -2151,6 +2229,12 @@ Skip Done + + This username is not available, try another number. + + Invalid username, enter a minimum of %1$d digits. + + Invalid username, enter a maximum of %1$d digits. %d contact is on Signal! @@ -2348,6 +2432,10 @@ Processing… + + %1.1f MB/%2.1f MB + + %1.1f MB @@ -2372,6 +2460,8 @@ Answered on a linked device. Declined on a linked device. Busy on a linked device. + + Flip Camera has been moved here, tap your video to try it out Someone has joined this call with a safety number that has changed. @@ -3924,14 +4014,16 @@ Failed to remove group member. - Member Request Your contact Remove from group Update contact Block Delete - Recently changed their profile name from %1$s to %2$s + + %1$s recently changed their profile name from %2$s to %3$s + + %1$s is in your system contacts %1$s joined @@ -4987,6 +5079,12 @@ Add people and groups you want notifications and calls from when this profile is on Add people or groups + + Exceptions + + Allow all calls + + Notify for all mentions Add @@ -5601,8 +5699,8 @@ All connections have been reviewed, tap send to continue. - You have %1$d connection who may have reinstalled Signal or changed devices. Before sharing your story with them review their safety numbers or consider removing them from your story. - You have %1$d connections who may have reinstalled Signal or changed devices. Before sharing your story with them review their safety numbers or consider removing them from your story. + %1$d connection may have reinstalled Signal or changed devices. You may review their safety number or continue with the send. + %1$d connections may have reinstalled Signal or changed devices. You may review their safety numbers or continue with the send. Verify safety number @@ -5960,12 +6058,18 @@ IBAN country code is not supported Invalid IBAN + + Minimum 2 characters + + Invalid email address iDEAL Enter your bank, name and email. Stripe uses this email to send you updates about your donation. %1$s + + Enter your bank details. Signal does not collect or store your personal information. %1$s Learn more @@ -6151,6 +6255,8 @@ Outgoing Missed + + Missed while notification profile on Join @@ -6258,6 +6364,8 @@ Group call Missed group call + + Missed group call while notification profile on Incoming group call diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index bad354fff7..2eeeebfaa7 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -30,7 +30,7 @@ @android:color/black -